Compare commits

...

507 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
Jannes Höke 37271e4a02 Merge pull request #127 from python-telegram-bot/chatclass
API updates Oct and Nov
2015-12-16 16:32:19 +01:00
Jannes Höke 0e78606d37 Release v3.1 2015-12-16 16:25:14 +01:00
Jannes Höke cb0736fb3d update sphinx docs 2015-12-16 16:23:18 +01:00
Jannes Höke e0233a59a0 fix docstrings for sphinx 2015-12-16 16:18:48 +01:00
Jannes Höke 6e158042e1 API updates Oct and Nov 2015-12-16 15:31:02 +01:00
Jannes Höke d791a6ee89 Update group link 2015-12-16 02:57:33 +01:00
leandrotoledo 5a3e142358 Updating one more ref to old repo 2015-12-14 21:51:17 -02:00
leandrotoledo 37a58fe1ad Update TODO on README 2015-12-14 21:41:22 -02:00
leandrotoledo 80281cc66d Moving to python-telegram-bot organization 2015-12-14 21:36:22 -02:00
Jannes Höke fffa9d8a7c Update README.rst 2015-12-14 12:22:25 +01:00
Jannes Höke 806b71030f Update group link 2015-12-13 21:24:05 +01:00
Jannes Höke 764c5dac53 Add Logo to README 2015-12-13 21:18:59 +01:00
Jannes Höke 007dc5807a move error disables to .travis.yml 2015-12-11 18:52:23 +01:00
Jannes Höke 16534872a7 add pylintrc ignoring import-error,no-name-in-module to fix travis 2015-12-11 18:38:29 +01:00
Jannes Höke a13245dfbf change 'wb' to 'rb' in certificate open call 2015-12-09 12:59:34 +01:00
Jannes Höke 6622ea3ec0 Release version 3.0.0 2015-12-08 00:07:21 +01:00
Jannes Höke bcadfc5398 Release version 3.0.0 2015-12-07 23:49:19 +01:00
Jannes Höke 89ecb04a85 Catch URLError while getting updates 2015-12-07 23:45:21 +01:00
Jannes Höke 56ab40d94f increase time in test multiple messages 2015-12-01 21:58:39 +01:00
Jannes Höke f5ac1ae067 debug message after processing update 2015-12-01 21:45:12 +01:00
Jannes Höke 23507202e8 Merge branch 'Balduro-boteventhandler' 2015-12-01 18:08:38 +01:00
Jannes Höke 185bff7117 update example 2015-12-01 18:06:42 +01:00
Gerd Augsburg 9f07900d99 Added Balduro to AUTHORS.rst and fixed line length 2015-12-01 15:16:04 +01:00
Gerd Augsburg 809fe4b74f Updated tests for start_webhook 2015-12-01 14:48:55 +01:00
Gerd Augsburg 8a4d5c5de7 Separate start_webhook and setWebhook
I think starting and setting the webhook should be two different tasks.
Because the internal webhook and the adress you see from outside can be
quite diffrent through apache etc. It provides more flexibility.
2015-12-01 14:11:27 +01:00
Jannes Höke 1976822dd0 Add documentation for Updater and Dispatcher 2015-12-01 01:01:28 +01:00
Jannes Höke e60694a8cc update test_webhook and add test_webhook_no_ssl 2015-11-30 23:00:56 +01:00
Jannes Höke 14b4c1ac62 Allow starting webhook without SSL (issue #112) 2015-11-30 23:00:32 +01:00
Leandro Toledo d5890d403b Merge pull request #109 from leandrotoledo/boteventhandler
Indrucing "Updater" and "Dispatcher"
2015-11-30 08:22:36 -02:00
Jannes Höke ef569b68c6 update docs 2015-11-24 21:07:13 +01:00
Jannes Höke 628a4c4eac final renaming of files and flake8 2015-11-24 21:06:55 +01:00
Jannes Höke 83d2ca3aa7 assertIsNone(...) to assertTrue(None is ...) 2015-11-24 20:40:40 +01:00
Jannes Höke 29cdfe671e test removehandler methods 2015-11-24 20:34:38 +01:00
Jannes Höke f21b6046c5 test error in handler, other webhook server functionalites 2015-11-24 19:35:02 +01:00
Jannes Höke 8090658ab4 log warning 2015-11-24 19:33:41 +01:00
Jannes Höke f49f592f0d log warning 2015-11-24 19:33:03 +01:00
Jannes Höke c2853fa1ef WebhookServer: Lock serve_forever() and shutdown() 2015-11-24 15:39:00 +01:00
Jannes Höke e1f3f346bc Test for Update.idle() 2015-11-24 14:57:54 +01:00
Jannes Höke 1005ad57ce Improve signal handling 2015-11-23 17:40:39 +01:00
Jannes Höke 0b72acc7c8 handle SIGINT, SIGTERM and SIGABRT 2015-11-23 03:45:47 +01:00
Jannes Höke 8d267ed896 use logging 2015-11-22 19:20:16 +01:00
Jannes Höke 83812f3af6 rename boteventhandler.py to updater.py 2015-11-22 19:20:05 +01:00
Jannes Höke 658b5ff1c0 update tests 2015-11-22 19:16:49 +01:00
Jannes Höke 593153128c Update examples 2015-11-22 19:15:37 +01:00
Jannes Höke d91f210f7c refactor BotEventHandler to Updater, improve documentation, add idle() method 2015-11-22 19:15:17 +01:00
Jannes Höke 65177e791f raise error instead of quietly exiting on bad SSL cert 2015-11-22 16:46:17 +01:00
Jannes Höke 15501e185b renamed broadcaster.py to dispatcher.py 2015-11-22 14:52:41 +01:00
Jannes Höke 1782d0d19b refactor Broadcaster to Dispatcher 2015-11-22 14:47:38 +01:00
Jannes Höke 88fbf3b5cf remove broadcaster argument from __init__ 2015-11-22 14:27:05 +01:00
Jannes Höke c7a1d8dca3 test additional arguments 2015-11-22 14:08:33 +01:00
Jannes Höke 3e732a0736 fix multiple additional arguments 2015-11-22 14:07:20 +01:00
Jannes Höke 628e1b743a add bits of documentation 2015-11-22 13:58:40 +01:00
Jannes Höke e4de3f00b8 Add optional handler argument 'args' and documentation on handlers 2015-11-22 13:57:59 +01:00
Jannes Höke 32c021fdd5 Don't create new bot when broadcaster is passed 2015-11-22 13:30:23 +01:00
Jannes Höke a6e5a71e05 Check SSL-Certificate, if possible 2015-11-22 01:03:29 +01:00
Jannes Höke 4d87e236be Use random port for travis and py2&/py3 compability 2015-11-21 23:22:41 +01:00
Jannes Höke 29ab0556f0 test Webhook completed 2015-11-21 23:09:44 +01:00
Jannes Höke fca8aee177 improve logging 2015-11-21 23:09:19 +01:00
Jannes Höke 6a80a33aef test for error on getUpdates and began to test webhook 2015-11-21 21:22:58 +01:00
Jannes Höke 7c9928c58f Let shutdown return immediately if server is not running 2015-11-21 21:22:11 +01:00
Jannes Höke 0cd7aa92ae more logging 2015-11-21 21:21:09 +01:00
Jannes Höke 6626d4534e test @run_async 2015-11-21 19:35:24 +01:00
Jannes Höke 6b309397e8 send less messages in more time so travis can take its time 2015-11-21 16:18:19 +01:00
Jannes Höke 618df51811 flake8 2015-11-21 16:04:06 +01:00
Jannes Höke d38add1a1c really? 2015-11-21 15:50:39 +01:00
Jannes Höke 1c7e784662 add setWebhook to mock bot 2015-11-21 15:48:45 +01:00
Jannes Höke 0006294f29 update tests 2015-11-21 15:45:45 +01:00
Jannes Höke c1f194a310 change all start() to start_polling() 2015-11-21 15:31:23 +01:00
Jannes Höke cccd6e5baf Merge branch 'master' of github.com:leandrotoledo/python-telegram-bot into boteventhandler 2015-11-21 03:29:37 +01:00
Jannes Höke a0baa68cf3 Merge pull request #105 from rahiel/master
handle "Unauthorized" exceptions in echobot
2015-11-20 18:28:08 +01:00
Rahiel Kasim 62b79df0a4 handle "Unauthorized" exceptions in echobot 2015-11-20 14:41:12 +01:00
Jannes Höke 1246039872 Merge pull request #104 from mASOUDd/boteventhandler
remove trailing whitespaces
2015-11-17 16:05:41 +01:00
Masoud Naservand ed6dd76ae8 Corrected trailing whitespaces 2015-11-17 18:27:22 +03:30
Jannes Höke 70de2bd3f1 Merge pull request #102 from franciscod/patch-1
README: add link to TwitterForwarderBot
2015-11-17 09:08:38 +01:00
Francisco Demartino 8bde310ec7 README: add link to TwitterForwarderBot 2015-11-17 01:34:00 -03:00
Jannes Höke be368f7d74 add example for webhook 2015-11-17 00:30:21 +01:00
Jannes Höke 9defc41774 correct docstrings 2015-11-17 00:04:28 +01:00
Jannes Höke ee8b78aa0d pep8 and docs 2015-11-16 20:43:35 +01:00
Jannes Höke 0b8fd24771 fixed issues with python2 2015-11-16 20:35:27 +01:00
Jannes Höke 396dc6cd3c Implement webhook listener server 2015-11-16 13:05:57 +01:00
Jannes Höke ba3d174fde Change eh.start() to eh.start_polling() 2015-11-16 13:05:17 +01:00
Jannes Höke ab313488ad Usage instructions for bots 2015-11-15 20:13:03 +01:00
Jannes Höke d808462fb6 Simple bot 2015-11-15 20:02:09 +01:00
Jannes Höke 71e73c3999 logging 2015-11-15 19:21:35 +01:00
Jannes Höke a55df8b5e1 update example 2015-11-15 19:14:12 +01:00
Jannes Höke f59efe0f4b forward related update to errorhandlers 2015-11-15 19:12:42 +01:00
Jannes Höke 1b79a57673 Merge branch 'master' of github.com:leandrotoledo/python-telegram-bot into boteventhandler 2015-11-15 17:38:37 +01:00
Jannes Höke ae59a18e2d update example 2015-11-15 17:37:01 +01:00
Jannes Höke 12201f392d graceful stopping of threads, use logging, reuse of broadcaster 2015-11-15 17:36:38 +01:00
leandrotoledo 8f3fe123e3 Fix travis 2015-11-13 10:53:31 -02:00
Jannes Höke 04050ca883 Merge branch 'master' of github.com:leandrotoledo/python-telegram-bot into boteventhandler 2015-11-13 11:05:26 +01:00
Jannes Höke c3b3d51286 Merge pull request #99 from rahiel/readme
improve echobot with cleaner code and basic exception handling
2015-11-12 19:21:29 +01:00
Jannes Höke 10451509f1 fix for issue #79 2015-11-12 19:15:39 +01:00
Rahiel Kasim ff0d724f32 improve echobot with cleaner code and basic exception handling 2015-11-12 17:26:25 +01:00
Leandro Toledo c64e577049 Fixes pylint for Py2.6 2015-11-11 12:41:07 -02:00
Leandro Toledo f08dca0af6 Fixes pylint for Py2.6 2015-11-11 12:39:09 -02:00
Leandro Toledo 8c0bcbf5bb Fixes typo on .travis.yml 2015-11-11 12:28:34 -02:00
Leandro Toledo 596fc2aeda Adding OSX and lint/pep8 tests to Travis 2015-11-11 12:27:07 -02:00
Leandro Toledo 968b19e8b7 Rolling back travis settings 2015-11-11 12:07:43 -02:00
Leandro Toledo 7993328ff7 Enable travis for PRs 2015-11-11 12:04:23 -02:00
Leandro Toledo 16349ac69a Releasing v2.9.2 2015-11-11 11:31:20 -02:00
Jannes Höke 45712c52f1 Merge branch 'master' of github.com:leandrotoledo/python-telegram-bot into boteventhandler 2015-11-11 14:27:58 +01:00
Leandro Toledo 235dcd2ad2 Merge branch 'master' of https://github.com/leandrotoledo/python-telegram-bot 2015-11-11 11:25:05 -02:00
Leandro Toledo ed92d1c254 Adds requirements.txt to MANIFEST 2015-11-11 11:24:49 -02:00
Jannes Höke 0b4c23f50e improve error handling on timeouts 2015-11-11 14:06:10 +01:00
Jannes Höke 63c895c0a0 run_async moved to broadcaster and uses a thread pool now 2015-11-11 13:33:03 +01:00
Jannes Höke 3162bc60e9 cleaned up code 2015-11-11 13:32:11 +01:00
leandrotoledo 206802cf4d Minor fix on docs 2015-11-10 20:33:14 -02:00
leandrotoledo 5eb4f8e5cd Minor pep8 fix 2015-11-10 20:31:51 -02:00
Jannes Höke 0a27e3927b merge 2015-11-10 23:17:31 +01:00
Jannes Höke 401add78d3 fix big mistake 2015-11-10 23:15:37 +01:00
Jannes Höke af375eb402 improved code quality 2015-11-10 23:15:37 +01:00
Jannes Höke 2797111ced First tests for BotEventHandler and Broadcaster 2015-11-10 23:15:37 +01:00
Jannes Höke d7d4889c50 use run_async decorator in example 2015-11-10 23:15:37 +01:00
Jannes Höke bf356e124f add run_async decorator 2015-11-10 23:15:37 +01:00
Jannes Höke d7fd43ca79 Import BotEventHandler and Broadcaster 2015-11-10 23:15:37 +01:00
Jannes Höke 42e11d169e Update AUTHORS.rst 2015-11-10 23:15:37 +01:00
Jannes Höke c1fae0b5ee example bot for BotEventHandler 2015-11-10 23:15:37 +01:00
Jannes Höke ef99bab435 initial commit for BotEventHandler and Broadcaster 2015-11-10 23:15:37 +01:00
Jannes Höke 93afd3eabe remove check for network_delay 2015-11-10 23:12:20 +01:00
Jannes Höke 61dac76bee don't send network_delay to telegram servers 2015-11-10 22:40:19 +01:00
Jannes Höke 245f5abc45 further improved formatting 2015-11-10 21:32:54 +01:00
Jannes Höke 0f8f6584ce Fix for issue #81 2015-11-10 20:36:26 +01:00
Jannes Höke dd498ded00 Update to version 2.9.1 2015-11-10 20:22:58 +01:00
Jannes Höke 5d2999818b Update to version 2.9.1 2015-11-10 20:19:12 +01:00
Jannes Höke eb1f197ff4 2.9.1 release and generally improved formatting 2015-11-10 20:10:44 +01:00
Jannes Höke 4bc03ed56a Add parameter network_delay for slow connections and increase default to 2 seconds 2015-11-10 19:47:21 +01:00
Leandro Toledo 9191d333ca Adds future as requirement 2015-11-10 12:27:18 -02:00
Leandro Toledo c6533a2d69 Releasing v2.9 2015-11-10 12:21:04 -02:00
Leandro Toledo 6c13762c93 Fixes PEP8 and Lint minor issues 2015-11-10 12:10:50 -02:00
Leandro Toledo 06c09b96cf Fixes travis to support future module 2015-11-10 12:04:21 -02:00
Leandro Toledo 8a5ec3b2a1 Merge pull request #92 from jh0ker/emoticons
Decode Emoji byte strings into unicode strings if using Python 3 (Alternative)
2015-11-10 11:58:57 -02:00
Leandro Toledo 21c26aed2f Merge pull request #96 from leandrotoledo/revert-85-master
Revert 1879cff82d
2015-11-10 11:55:50 -02:00
Leandro Toledo 5fe1481ae9 Revert 1879cff82d 2015-11-10 11:55:42 -02:00
Leandro Toledo 1879cff82d Merge pull request #85 from jh0ker/master
Decode Emoji byte strings into unicode strings if using Python 3
2015-11-10 11:55:19 -02:00
Jannes Höke cd5e805692 added documentation 2015-11-10 05:16:16 +01:00
Jannes Höke 90a77ab7a1 Merge pull request #87 from franciscod/patch-1
Remove `.encode('utf-8')` from echobot
2015-11-10 04:58:21 +01:00
Jannes Höke 3928e27cf4 Merge pull request #90 from rahiel/master
raise exception if telegram times out on long-polling
2015-11-10 04:56:18 +01:00
Leandro Toledo 45b1124553 Merge pull request #95 from rahiel/readme
fix LGPLv3 badge
2015-11-09 16:53:47 -02:00
Rahiel Kasim 252b43dcb7 fix LGPLv3 badge 2015-11-09 19:06:30 +01:00
Jannes Höke c19a84ac05 improved code quality 2015-11-06 00:24:01 +01:00
Jannes Höke 404fdc2dd7 First tests for BotEventHandler and Broadcaster 2015-11-06 00:23:34 +01:00
Jannes Höke 2ec56abe7e use run_async decorator in example 2015-11-05 16:01:25 +01:00
Jannes Höke 63a8700258 add run_async decorator 2015-11-05 16:01:08 +01:00
Jannes Höke d363185031 Import BotEventHandler and Broadcaster 2015-11-05 15:22:13 +01:00
Jannes Höke aabaaa9049 Update AUTHORS.rst 2015-11-05 14:07:28 +01:00
Jannes Höke eb2be61eac example bot for BotEventHandler 2015-11-05 13:52:57 +01:00
Jannes Höke 0688691974 initial commit for BotEventHandler and Broadcaster 2015-11-05 13:52:33 +01:00
Leandro Toledo d3d5c1e907 Merge pull request #91 from jh0ker/useroptional
Make user_from optional to work with channels channels
2015-11-04 09:52:00 -02:00
Jannes Höke cbf66d411d add jh0ker to AUTHORS.rst 2015-11-03 14:47:26 +01:00
Jannes Höke 0e5b48f3b4 Add tests for Emoji class, since it now contains actual code 2015-11-03 14:47:13 +01:00
Jannes Höke df7cfdc8d0 create file requirements.txt and add 'future' 2015-11-03 14:42:16 +01:00
Jannes Höke 89015e5ecc fix for sending into a channel 2015-11-03 13:46:23 +01:00
Jannes Höke 025d4c9a75 fix emojis for python3 2015-11-03 12:24:44 +01:00
Jannes Höke d3bea4c3b4 make user_from optional, because of channels 2015-11-03 10:28:06 +01:00
Jannes Höke a6c12adda2 make user_from optional, because of channels 2015-11-03 10:20:45 +01:00
Rahiel Kasim dcb9129809 raise exception if telegram times out on long-polling 2015-11-02 20:30:37 +01:00
Francisco Demartino bd1f171f51 Remove .encode('utf-8') from echobot
This was making the bot throw TypeError on py3.

Closes #86
2015-10-23 16:48:26 -03:00
Jannes Höke 54efb034a4 add jh0ker to AUTHORS.rst 2015-10-23 01:18:05 +02:00
Jannes Höke fe94f146bb Add tests for Emoji class, since it now contains actual code 2015-10-23 01:16:10 +02:00
Jannes Höke 22b492762b Decode Emoji byte strings into unicode strings if using Python 3 (missed something) 2015-10-23 00:32:10 +02:00
Jannes Höke fea0c5cc2b Decode Emoji byte strings into unicode strings if using Python 3 2015-10-23 00:29:26 +02:00
Leandro Toledo 688d6af541 Releasing v2.8.7 2015-10-08 11:31:48 -03:00
Leandro Toledo bcbd32cdd3 Merge branch 'master' of https://github.com/leandrotoledo/python-telegram-bot 2015-10-08 11:30:16 -03:00
Leandro Toledo 3fa8b97ed2 Type as optional for GroupChat 2015-10-08 11:30:02 -03:00
Leandro Toledo 7cd4e2e205 Update README.rst 2015-10-08 10:40:27 -03:00
Leandro Toledo f2b0cb46a2 Releasing v2.8.6 2015-10-08 10:23:12 -03:00
Leandro Toledo cf5d184766 Add type to User and GroupChat classes 2015-10-08 10:19:05 -03:00
Leandro Toledo 5738dc553f Releasing v2.8.5 2015-09-24 09:29:23 -03:00
Leandro Toledo 386accab80 Merge pull request #74 from leandrotoledo/http_bad_gateway_as_except
Handles HTTP Bad Gateway error (503) on request module #63
2015-09-24 09:26:19 -03:00
Leandro Toledo e7686db759 Merge pull request #73 from leandrotoledo/stop_casting_unicode_fields
Fixes regression on Audio and Document models for unicode fields #65
2015-09-24 09:26:10 -03:00
Leandro Toledo 6c9490f2c6 Handles HTTP Bad Gateway error (503) on request module #63 2015-09-24 09:17:50 -03:00
Leandro Toledo 855ab19dea Fixes regression on Audio and Document models for unicode fields #65 2015-09-24 09:10:33 -03:00
Leandro Toledo 797a3e6ea4 Update README.rst 2015-09-22 09:41:31 -03:00
Leandro Toledo bbd443d397 Merge pull request #71 from zeehio/patch-1
[doc] fix "Telegram sticker" -> "Telegram user"
2015-09-22 09:39:19 -03:00
Sergio Oller 4e1597c614 [doc] fix "Telegram user" -> "telegram sticker" 2015-09-22 11:52:10 +02:00
Leandro Toledo 75e338d5df Releasing v2.8.4 2015-09-20 12:52:40 -03:00
Leandro Toledo 1919f873c0 Fix tests 2015-09-20 12:47:02 -03:00
Leandro Toledo a1f35355f6 Keep bleeding edge features on testing branch 2015-09-20 12:43:32 -03:00
Leandro Toledo c4c17e8036 Merge branch 'testing' 2015-09-20 12:34:34 -03:00
Leandro Toledo 3024c1ce3d Merge branch 'master' into testing 2015-09-20 12:34:02 -03:00
Leandro Toledo b79530b10c Adding File and its tests 2015-09-20 12:28:10 -03:00
Leandro Toledo 778c63a6d3 Merge pull request #68 from leandrotoledo/revert-67-feature/requests
Revert "Feature/requests"
2015-09-16 00:22:03 -03:00
Leandro Toledo f623db06ea Revert "Feature/requests" 2015-09-16 00:21:45 -03:00
Leandro Toledo 026673dc05 Merge pull request #67 from peczony/feature/requests
Feature/requests
2015-09-16 00:17:38 -03:00
Leandro Toledo 6893da5dd3 Update keywords for pip package 2015-09-16 00:15:51 -03:00
pecheny d5a9c185f0 use requests instead of urllib if possible; add timeout and decorator to get and post 2015-09-14 19:13:22 +03:00
Leandro Toledo cbdeacd22d Releasing v2.8.3 2015-09-10 21:00:05 -03:00
Leandro Toledo 1e2fdedc45 Removing command_handler from master (under dev) 2015-09-10 20:44:23 -03:00
Leandro Toledo c1c0e66233 Merge branch 'testing' 2015-09-10 20:37:41 -03:00
Leandro Toledo eb557e0eba Keeping features under development off this release 2015-09-10 20:37:25 -03:00
Leandro Toledo a7ac4193fe PEP8, lint and TelegramError class refactor 2015-09-10 20:08:24 -03:00
Leandro Toledo 354bfcad79 legacy tests now run using BaseTest 2015-09-10 20:06:27 -03:00
Leandro Toledo a1e12d424c Fix inverted lat/long 2015-09-10 16:59:08 -03:00
Leandro Toledo 38c6d002c8 Merge pull request #60 from ergoz/ISSUE-58
Add Markdown support for sendMessage method.[#58]
2015-09-10 15:25:01 -03:00
ErgoZ c8a14bf34d Merge branch 'testing' of https://github.com/leandrotoledo/python-telegram-bot into ISSUE-58
Conflicts:
	telegram/__init__.py
2015-09-10 20:42:56 +03:00
ErgoZ 53c44f14bd Add Markdown support for sendMessage method. 2015-09-10 20:15:20 +03:00
Leandro Toledo e75deea25c Merge pull request #59 from ergoz/PR57-Fix
Code cleanup for filename. Filename uses only for sendDocument.
2015-09-10 08:56:11 -03:00
ErgoZ 17b8bb4881 Code cleanup for filename. Filename uses only for sendDocument. 2015-09-10 14:46:49 +03:00
leandrotoledo 009862593b Fix Py3 2015-09-09 09:34:45 -03:00
leandrotoledo e2fa052f54 Send JSON requests over urlencoded post data 2015-09-09 09:29:23 -03:00
leandrotoledo dcfe08dbda Add sticker as an inputfile 2015-09-09 09:28:58 -03:00
leandrotoledo dc2dfa24ad Adding .sublime 2015-09-09 09:12:59 -03:00
leandrotoledo ce58f45c4f Adding .sublime 2015-09-09 09:12:35 -03:00
Leandro Toledo 7059930b5d Merge pull request #55 from njittam/testing
add a father command. and hide commands in help.
2015-09-09 09:08:39 -03:00
leandrotoledo 77977b99b1 Add tests to custom filename arg #56 2015-09-08 15:52:10 -03:00
Leandro Toledo ed77afaab9 Merge pull request #57 from ergoz/testing
Add ability for custom filename in send methods and InputFile
2015-09-08 15:22:58 -03:00
ErgoZ Riftbit Vaper c7f2add463 small pretty fix for self.data.pop('filename') 2015-09-08 21:17:54 +03:00
ErgoZ Riftbit Vaper 8a074fd719 Delete unused check (forget from previous local version) 2015-09-08 21:13:11 +03:00
leandrotoledo cfa5e8a6fe Using nose to run tests 2015-09-08 15:03:04 -03:00
leandrotoledo c6bf13e407 Using nose to test on travis 2015-09-08 15:02:28 -03:00
ErgoZ Riftbit Vaper 6237bb636c Fix InputFile class for python 3+ support 2015-09-08 20:54:50 +03:00
ErgoZ Riftbit Vaper 0496781501 Update AUTHORS.rst 2015-09-08 20:45:44 +03:00
ErgoZ 4a761d0611 Add ability to set custom filename (fix InputFile class)
For commands that uses InputFile class
2015-09-08 20:43:28 +03:00
ErgoZ 714adc13ee Add ability to set custom filename
For commands that uses InputFile class
2015-09-08 20:42:23 +03:00
leandrotoledo fa387d1821 Ignore coveralls token settings 2015-09-07 20:11:46 -03:00
leandrotoledo 073d43fd75 Improve tests for GroupChat and User 2015-09-07 20:11:02 -03:00
leandrotoledo 5c0eeac036 Fix coveralls 2015-09-07 20:06:34 -03:00
leandrotoledo 64ca4d7b82 Fix travis, this time for real 2015-09-07 16:53:01 -03:00
leandrotoledo 6d7542ce50 Fix travis 2015-09-07 16:33:00 -03:00
leandrotoledo 0cfaaa590c Fix travis 2015-09-07 16:29:39 -03:00
leandrotoledo 48bb8a2413 Fix travis 2015-09-07 16:28:00 -03:00
leandrotoledo 36dc1633f5 Move old tests to legacy dir until get new ones written 2015-09-07 15:54:50 -03:00
leandrotoledo e1edeb7bec Improve design of this class 2015-09-07 15:54:12 -03:00
leandrotoledo a409fc511d Improve raise of empty chat_id 2015-09-07 15:53:43 -03:00
leandrotoledo 69d705a99f strize raw String properties for Telegram Objects 2015-09-07 15:53:09 -03:00
leandrotoledo cdcf2481ba Update Makefile to support multiple tests 2015-09-07 15:47:50 -03:00
leandrotoledo 5c4f0f152a Much better, such wow Location tests 2015-09-07 14:31:08 -03:00
leandrotoledo f29a6f0e36 Update Document tests class 2015-09-07 14:17:41 -03:00
leandrotoledo 1b0c3731de Update Base tests class 2015-09-07 14:12:58 -03:00
leandrotoledo 4be946d116 Much better, such wow GroupChat tests 2015-09-07 14:10:57 -03:00
leandrotoledo 73e36e3b73 Much better, such wow User tests 2015-09-07 14:03:20 -03:00
leandrotoledo 6141e76693 Much better, such wow Update tests 2015-09-07 13:55:28 -03:00
leandrotoledo 61ef876532 Much better, such wow Contact tests 2015-09-07 13:42:51 -03:00
leandrotoledo 060442e0ae Much better, such wow Sticker tests 2015-09-07 12:10:29 -03:00
leandrotoledo 2254440671 Much better, such wow Document tests 2015-09-07 11:55:55 -03:00
leandrotoledo 65a224884f Much better, such wow Audio/Voice/Video tests 2015-09-07 11:22:25 -03:00
leandrotoledo 0218aa2f4e Much better, such wow Voice tests 2015-09-07 09:56:51 -03:00
leandrotoledo 28e81ee9f6 Much better, such wow Audio tests 2015-09-07 09:56:41 -03:00
mattijn 6ffcdcf2fe removed unused imports 2015-09-07 05:13:08 +02:00
mattijn 89bab207b6 pep8 2015-09-07 05:11:08 +02:00
mattijn 69aa1c252b added an easier way to get frequent commands from message. 2015-09-07 04:55:00 +02:00
mattijn 5b8f41b3c1 pep 8 command_handler.py 2015-09-07 04:17:43 +02:00
mattijn ea5c690e7a add a help for extra commands. 2015-09-07 03:38:07 +02:00
mattijn 9145f24efa added a father command. and some other a way to hide commands in the help. 2015-09-07 03:13:40 +02:00
leandrotoledo c590d9c2fd Moving _requestURL method to utils package 2015-09-05 11:56:06 -03:00
leandrotoledo cfd401f22b Moving _requestURL method to utils package 2015-09-05 11:55:55 -03:00
leandrotoledo 7f790c3cb2 Releasing v2.8.2 2015-09-04 23:23:41 -03:00
leandrotoledo 30749c3b66 Merging testing 2015-09-04 23:21:31 -03:00
leandrotoledo 8f0447156b Merge branch 'testing' 2015-09-04 23:20:31 -03:00
leandrotoledo df000f8086 Fix regression on ReplyMarkup and add certificate to is_inputfile method 2015-09-04 23:20:14 -03:00
Leandro Toledo 3d835527e0 Update README.rst 2015-09-04 23:07:03 -03:00
Leandro Toledo 323052a6c9 Update README.rst 2015-09-04 23:05:14 -03:00
Leandro Toledo ef4871cce1 Update README.rst 2015-09-04 19:14:54 -03:00
leandrotoledo f355af1d2b Restoring new features after rebase 2015-09-04 19:11:31 -03:00
leandrotoledo 121e365348 Oops, restoring file on testing 2015-09-04 19:07:20 -03:00
leandrotoledo 82f1d18f4b Releasing v2.8.1 2015-09-04 19:00:54 -03:00
leandrotoledo 79cdb6cafe Adding test to check if thumbs are PhotoSize instances 2015-09-04 18:58:48 -03:00
leandrotoledo 49ff02dcf4 Fix regression on Telegram objects with thumb properties 2015-09-04 18:50:26 -03:00
leandrotoledo be23ff5d29 Fix codeclimate 2015-09-04 18:35:35 -03:00
leandrotoledo 72a7355c2f Fix codeclimate 2015-09-04 18:33:51 -03:00
leandrotoledo 64cf9ed941 Fix codeclimate 2015-09-04 18:29:37 -03:00
leandrotoledo bb84113272 Releasing 2.8 2015-09-04 18:19:05 -03:00
leandrotoledo d740ce89cf Keep off some features still in progress 2015-09-04 18:11:47 -03:00
leandrotoledo 245238b3a2 Keep off some features still in progress 2015-09-04 18:03:04 -03:00
Leandro Toledo e693922fe8 Merge pull request #54 from leandrotoledo/testing
Merge Testing branch
2015-09-04 17:59:37 -03:00
leandrotoledo 62f06da897 Move command_handler tests to tests/ folder 2015-09-04 17:54:30 -03:00
leandrotoledo 05b7fda4a1 Add certificate arg to setWebhook function 2015-09-04 17:53:39 -03:00
leandrotoledo 910959b672 Raises error when chat_id is not set 2015-09-04 17:15:44 -03:00
Leandro Toledo 881b0b3d06 Update README.rst 2015-09-02 13:23:11 -03:00
Leandro Toledo 95ecd44c0e Merge pull request #51 from njittam/testing
Adding command handler
2015-09-02 13:20:33 -03:00
njittam 9241534ba6 Create command_handler_example.py
this is an example of how to write a commandHandler
2015-09-02 16:21:11 +02:00
njittam 256d219862 A command handler
I saw in the TODO list on pypi that you wanted a commandhandler. 
I am not sure about the run and run_once commands.
and  I didn't implement logging yet.
2015-09-02 16:17:57 +02:00
njittam 8fddd3b027 test for commandHandler
Not everything has a test. and I'm quite new to unit testing so I'm not sure if I am doing this the right way.
2015-09-02 16:11:21 +02:00
njittam 3f3cb1edc5 Update AUTHORS.rst 2015-09-02 16:04:15 +02:00
Leandro Toledo f037a8c776 Update README.rst 2015-08-29 10:55:23 -03:00
Leandro Toledo 98c489c44f Fix Python 3 2015-08-28 17:53:31 -03:00
Leandro Toledo a86fc6c2ac Improving the design of existing Telegram classes 2015-08-28 17:45:44 -03:00
Leandro Toledo 1c4595123c Add improvements to tests 2015-08-28 13:28:58 -03:00
Leandro Toledo 942706e20f Add PyPy to travis 2015-08-28 13:09:47 -03:00
Leandro Toledo 6837bef9bb Fix tests for Py2.6 2015-08-28 13:07:12 -03:00
Leandro Toledo 6132e65dc3 Fix travis 2015-08-28 13:04:36 -03:00
Leandro Toledo 0f924508f7 Fix tests and minor changes 2015-08-28 13:02:02 -03:00
Leandro Toledo b20f5af1e1 Improving the design of existing Telegram classes and adding docstrings 2015-08-28 12:19:30 -03:00
Leandro Toledo ce58f72566 Merge branch 'testing' of https://github.com/leandrotoledo/python-telegram-bot into testing 2015-08-28 11:11:32 -03:00
Leandro Toledo f4839a3afe Merge pull request #47 from mASOUDd/testing
Extended the Bot class. Added a dispatching mechanism, and decorators to add commands.
2015-08-28 10:50:18 -03:00
Masoud Naservand 25f9eb7898 Extended the Bot class. Added a dispatching mechanism,
and decorators to add command to the bot the flask routing style, e.g:
>>> @bot.command('/start')
... def start(command, user_id):
...    return ('Hello, there', None, None)
2015-08-28 18:08:03 +04:30
Leandro Toledo c625b9a449 Update README.rst 2015-08-26 21:45:09 -03:00
Leandro Toledo e239315cca Merge pull request #46 from rahiel/master
add remaining chat actions
2015-08-25 11:22:03 -03:00
rahiel ccdb999e37 add remaining chat actions 2015-08-25 13:45:02 +02:00
Leandro Toledo 2f99b785b2 Merge pull request #45 from rahiel/master
use long polling in examples
2015-08-24 09:26:20 -03:00
rahiel 41eb45918c use long polling in examples 2015-08-24 11:46:33 +02:00
leandrotoledo d43e292499 Adding required comments 2015-08-21 23:22:58 -03:00
leandrotoledo fdb5f2339c Improving the design of existing Telegram classes and adding docstrings 2015-08-21 23:15:29 -03:00
leandrotoledo d03a394075 Refactoring, improving the design of existing message class and adding docstrings 2015-08-21 14:49:07 -03:00
Leandro Toledo 3658b4231c Update README.rst 2015-08-21 14:09:13 -03:00
leandrotoledo 1d6e9502cb Adding botan.io support 2015-08-20 21:07:49 -03:00
Leandro Toledo 92ca92341a Merge pull request #43 from rahiel/master
confirm processed message in examples
2015-08-20 15:49:21 -03:00
rahiel 0691b1e971 confirm processed message in examples 2015-08-20 19:58:57 +02:00
leandrotoledo 8746222cf6 Releasing v2.7.1 2015-08-19 16:16:06 -03:00
leandrotoledo 32dc05ed36 Fix lines too long 2015-08-19 16:08:03 -03:00
leandrotoledo ba5902c1d4 Fix serialization when message has reply_to_message, new_chat_participant or new_chat_photo #42 2015-08-19 15:41:09 -03:00
Leandro Toledo 80371c9f6e Merge branch 'master' of https://github.com/leandrotoledo/python-telegram-bot 2015-08-17 13:15:41 -03:00
Leandro Toledo a8fd6b5061 Releasing v2.7 2015-08-17 13:15:29 -03:00
Leandro Toledo 53f5911aad Update README.rst 2015-08-17 11:45:28 -03:00
Leandro Toledo d4870148c7 Add telegram.voice to docs and minor docstring fixes #39 2015-08-17 11:40:21 -03:00
Leandro Toledo 6e2881b31b Adding support for Voice object and sendVoice method #39 2015-08-17 11:34:42 -03:00
Leandro Toledo 686aecb914 Fix tests for Python 3 2015-08-17 10:01:17 -03:00
Leandro Toledo 35f31bf136 Merge branch 'master' of https://github.com/leandrotoledo/python-telegram-bot 2015-08-17 09:57:07 -03:00
Leandro Toledo 56f6845969 Adding is_json tests and fixes json serialization when a forwared message #38 2015-08-17 09:56:51 -03:00
Leandro Toledo 505df01ae1 Update AUTHORS.rst 2015-08-15 15:19:17 -03:00
Leandro Toledo 3be58b759f Releasing v2.6.1 2015-08-15 15:06:32 -03:00
Leandro Toledo 5dc1e4cac1 Use imghdr instead re to match image headers. Fixes #37 2015-08-15 15:00:28 -03:00
Leandro Toledo 2cecca8324 Releasing v2.6.0 2015-08-14 16:36:39 -03:00
104 changed files with 9655 additions and 1977 deletions
+5
View File
@@ -0,0 +1,5 @@
languages:
Python: true
exclude_paths:
- "telegram/emoji.py"
- "tests/*"
+5
View File
@@ -0,0 +1,5 @@
[run]
source = telegram
[report]
omit = tests/
+11
View File
@@ -43,6 +43,7 @@ htmlcov/
nosetests.xml
coverage.xml
*,cover
.coveralls.yml
# Translations
*.mo
@@ -57,3 +58,13 @@ docs/_build/
# PyBuilder
target/
.idea/
# Sublime Text 2
*.sublime*
# unitests files
telegram.mp3
telegram.mp4
telegram.ogg
telegram.png
telegram.webp
+7 -1
View File
@@ -4,9 +4,15 @@ python:
- "2.7"
- "3.3"
- "3.4"
- "pypy"
- "pypy3"
install:
- pip install coveralls
- pip install -r requirements.txt
- pip install -r requirements-dev.txt
script:
coverage run telegram_test.py
- 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:
coveralls
+10
View File
@@ -9,10 +9,20 @@ Contributors
The following wonderful people contributed directly or indirectly to this project:
- `Avanatiker <https://github.com/Avanatiker>`_
- `Balduro <https://github.com/Balduro>`_
- `bimmlerd <https://github.com/bimmlerd>`_
- `ErgoZ Riftbit Vaper <https://github.com/ergoz>`_
- `franciscod <https://github.com/franciscod>`_
- `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>`_
-114
View File
@@ -1,114 +0,0 @@
2015-08-12
Released 2.5.3
telegram.Bot now supports to be unpickled
2015-08-11
Released 2.5.2
New changes from Telegram Bot API have been applied
telegram.Bot now supports to be pickled
Return empty str instead None when message.text is empty
2015-08-10
Released 2.5.1
Moved from GPLv2 to LGPLv3
2015-08-09
Released 2.5
Fixes logging calls in API
2015-08-08
Released 2.4
Fixes Emoji class for Python 3
PEP8 improvements
2015-08-08
Released 2.3
Fixes ForceReply class
Remove logging.basicConfig from library
2015-07-25
Released 2.2
Allows debug=True when initializing telegram.Bot
2015-07-20
Released 2.1
Fix to_dict for Document and Video
2015-07-19
Released 2.0
Fixes bugs
Improves __str__ over to_json()
Creates abstractclass TelegramObject
2015-07-15
Released 1.9
Python 3 officially supported
PEP8 improvements
2015-07-12
Released 1.8
Fixes crash when replying an unicode text message (special thanks to JRoot3D)
2015-07-11
Released 1.7
Fixes crash when username is not defined on chat (special thanks to JRoot3D)
2015-07-10
Released 1.6
Improvements for GAE support
2015-07-10
Released 1.5
Fixes randomly unicode issues when using InputFile
2015-07-10
Released 1.4
requests lib is no longer required
Google App Engine (GAE) is supported
2015-07-10
Released 1.3
Added support to setWebhook (special thanks to macrojames)
2015-07-09
Released 1.2
CustomKeyboard classes now available
Emojis available
PEP8 improvements
2015-07-08
Released 1.1
PyPi package now available
2015-07-08
Released 1.0
Initial checkin of python-telegram-bot
+321
View File
@@ -0,0 +1,321 @@
**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*
- The ``chat``-field in ``Message`` is now of type ``Chat``. (API update Oct 8 2015)
- ``Message`` now contains the optional fields ``supergroup_chat_created``, ``migrate_to_chat_id``, ``migrate_from_chat_id`` and ``channel_chat_created``. (API update Nov 2015)
**2015-12-08**
*Released 3.0.0*
- Introducing the ``Updater`` and ``Dispatcher`` classes
**2015-11-11**
*Released 2.9.2*
- Error handling on request timeouts has been improved
**2015-11-10**
*Released 2.9.1*
- Add parameter ``network_delay`` to Bot.getUpdates for slow connections
**2015-11-10**
*Released 2.9*
- Emoji class now uses ``bytes_to_native_str`` from ``future`` 3rd party lib
- Make ``user_from`` optional to work with channels
- Raise exception if Telegram times out on long-polling
*Special thanks to @jh0ker for all hard work*
**2015-10-08**
*Released 2.8.7*
- Type as optional for ``GroupChat`` class
**2015-10-08**
*Released 2.8.6*
- Adds type to ``User`` and ``GroupChat`` classes (pre-release Telegram feature)
**2015-09-24**
*Released 2.8.5*
- Handles HTTP Bad Gateway (503) errors on request
- Fixes regression on ``Audio`` and ``Document`` for unicode fields
**2015-09-20**
*Released 2.8.4*
- ``getFile`` and ``File.download`` is now fully supported
**2015-09-10**
*Released 2.8.3*
- Moved ``Bot._requestURL`` to its own class (``telegram.utils.request``)
- Much better, such wow, Telegram Objects tests
- Add consistency for ``str`` properties on Telegram Objects
- Better design to test if ``chat_id`` is invalid
- Add ability to set custom filename on ``Bot.sendDocument(..,filename='')``
- Fix Sticker as ``InputFile``
- Send JSON requests over urlencoded post data
- Markdown support for ``Bot.sendMessage(..., parse_mode=ParseMode.MARKDOWN)``
- Refactor of ``TelegramError`` class (no more handling ``IOError`` or ``URLError``)
**2015-09-05**
*Released 2.8.2*
- Fix regression on Telegram ReplyMarkup
- Add certificate to ``is_inputfile`` method
**2015-09-05**
*Released 2.8.1*
- Fix regression on Telegram objects with thumb properties
**2015-09-04**
*Released 2.8*
- TelegramError when ``chat_id`` is empty for send* methods
- ``setWebhook`` now supports sending self-signed certificate
- Huge redesign of existing Telegram classes
- Added support for PyPy
- Added docstring for existing classes
**2015-08-19**
*Released 2.7.1*
- Fixed JSON serialization for ``message``
**2015-08-17**
*Released 2.7*
- Added support for ``Voice`` object and ``sendVoice`` method
- Due backward compatibility performer or/and title will be required for ``sendAudio``
- Fixed JSON serialization when forwarded message
**2015-08-15**
*Released 2.6.1*
- Fixed parsing image header issue on < Python 2.7.3
**2015-08-14**
*Released 2.6.0*
- Depreciation of ``require_authentication`` and ``clearCredentials`` methods
- Giving ``AUTHORS`` the proper credits for their contribution for this project
- ``Message.date`` and ``Message.forward_date`` are now ``datetime`` objects
**2015-08-12**
*Released 2.5.3*
- ``telegram.Bot`` now supports to be unpickled
**2015-08-11**
*Released 2.5.2*
- New changes from Telegram Bot API have been applied
- ``telegram.Bot`` now supports to be pickled
- Return empty ``str`` instead ``None`` when ``message.text`` is empty
**2015-08-10**
*Released 2.5.1*
- Moved from GPLv2 to LGPLv3
**2015-08-09**
*Released 2.5*
- Fixes logging calls in API
**2015-08-08**
*Released 2.4*
- Fixes ``Emoji`` class for Python 3
- ``PEP8`` improvements
**2015-08-08**
*Released 2.3*
- Fixes ``ForceReply`` class
- Remove ``logging.basicConfig`` from library
**2015-07-25**
*Released 2.2*
- Allows ``debug=True`` when initializing ``telegram.Bot``
**2015-07-20**
*Released 2.1*
- Fix ``to_dict`` for ``Document`` and ``Video``
**2015-07-19**
*Released 2.0*
- Fixes bugs
- Improves ``__str__`` over ``to_json()``
- Creates abstract class ``TelegramObject``
**2015-07-15**
*Released 1.9*
- Python 3 officially supported
- ``PEP8`` improvements
**2015-07-12**
*Released 1.8*
- Fixes crash when replying an unicode text message (special thanks to JRoot3D)
**2015-07-11**
*Released 1.7*
- Fixes crash when ``username`` is not defined on ``chat`` (special thanks to JRoot3D)
**2015-07-10**
*Released 1.6*
- Improvements for GAE support
**2015-07-10**
*Released 1.5*
- Fixes randomly unicode issues when using ``InputFile``
**2015-07-10**
*Released 1.4*
- ``requests`` lib is no longer required
- Google App Engine (GAE) is supported
**2015-07-10**
*Released 1.3*
- Added support to ``setWebhook`` (special thanks to macrojames)
**2015-07-09**
*Released 1.2*
- ``CustomKeyboard`` classes now available
- Emojis available
- ``PEP8`` improvements
**2015-07-08**
*Released 1.1*
- PyPi package now available
**2015-07-08**
*Released 1.0*
- Initial checkin of python-telegram-bot
+116 -25
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/leandrotoledo/python-telegram-bot/blob/master/CHANGES
.. _AUTHORS.rst: https://github.com/leandrotoledo/python-telegram-bot/blob/master/AUTHORS.rst
.. _AUTHORS.rst: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/AUTHORS.rst
+1 -1
View File
@@ -1 +1 @@
include LICENSE LICENSE.lesser Makefile
include LICENSE LICENSE.lesser Makefile requirements.txt
+33 -6
View File
@@ -1,7 +1,10 @@
help:
@echo " clean remove unwanted stuff"
@echo " lint check style with flake8"
@echo " test run tests"
.PHONY: clean pep8 lint test install
PYLINT := pylint
NOSETESTS := nosetests
PEP257 := pep257
PEP8 := flake8
PIP := pip
clean:
rm -fr build
@@ -10,8 +13,32 @@ clean:
find . -name '*.pyo' -exec rm -f {} \;
find . -name '*~' -exec rm -f {} \;
pep257:
$(PEP257) telegram
pep8:
$(PEP8) telegram
lint:
flake8 --doctests --max-complexity 10 telegram
$(PYLINT) -E telegram --disable=no-name-in-module,import-error
test:
python telegram_test.py
$(NOSETESTS) -v
install:
$(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)"
+193 -30
View File
@@ -1,37 +1,44 @@
Python Telegram Bot
.. image:: https://github.com/python-telegram-bot/logos/blob/master/logo-text/png/ptb-logo-text_768.png?raw=true
:align: center
:target: https://github.com/python-telegram-bot/logos
:alt: python-telegram-bot Logo
A Python wrapper around the Telegram Bot API.
By `Leandro Toledo <leandrotoledodesouza@gmail.com>`_
*Stay tuned for library updates and new releases on our* `Telegram Channel <http://telegram.me/pythontelegrambotchannel>`_.
.. image:: https://img.shields.io/pypi/v/python-telegram-bot.svg
:target: https://pypi.python.org/pypi/python-telegram-bot
:alt: PyPi Package Version
.. image:: https://img.shields.io/pypi/dm/python-telegram-bot.svg
:target: https://pypi.python.org/pypi/python-telegram-bot
:alt: PyPi Package Monthly Download
.. image:: https://readthedocs.org/projects/python-telegram-bot/badge/?version=latest
:target: https://readthedocs.org/projects/python-telegram-bot/?badge=latest
:alt: Documentation Status
.. image:: https://img.shields.io/github/license/leandrotoledo/python-telegram-bot.svg
.. image:: https://img.shields.io/pypi/l/python-telegram-bot.svg
:target: http://www.gnu.org/licenses/lgpl-3.0.html
:alt: LGPLv3 License
.. image:: https://travis-ci.org/leandrotoledo/python-telegram-bot.svg?branch=master
:target: https://travis-ci.org/leandrotoledo/python-telegram-bot
.. image:: https://travis-ci.org/python-telegram-bot/python-telegram-bot.svg?branch=master
:target: https://travis-ci.org/python-telegram-bot/python-telegram-bot
:alt: Travis CI Status
.. image:: https://codeclimate.com/github/leandrotoledo/python-telegram-bot/badges/gpa.svg
:target: https://codeclimate.com/github/leandrotoledo/python-telegram-bot
.. image:: https://codeclimate.com/github/python-telegram-bot/python-telegram-bot/badges/gpa.svg
:target: https://codeclimate.com/github/python-telegram-bot/python-telegram-bot
:alt: Code Climate
.. image:: https://coveralls.io/repos/leandrotoledo/python-telegram-bot/badge.svg?branch=master&service=github
:target: https://coveralls.io/github/leandrotoledo/python-telegram-bot?branch=master
.. image:: https://coveralls.io/repos/python-telegram-bot/python-telegram-bot/badge.svg?branch=master&service=github
:target: https://coveralls.io/github/python-telegram-bot/python-telegram-bot?branch=master
:alt: Coveralls
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg
:target: https://telegram.me/joinchat/ALnA-AJQm5TcNEiy2G_4cQ
:alt: Telegram Group
=================
Table of contents
=================
@@ -50,13 +57,17 @@ Table of contents
- `Getting started`_
1. `API`_
1. `The Updater class`_
2. `Logging`_
2. `API`_
3. `Examples`_
4. `Documentation`_
3. `JobQueue`_
4. `Logging`_
5. `Examples`_
6. `Documentation`_
- `License`_
@@ -89,10 +100,12 @@ sendAudio Yes
sendDocument Yes
sendSticker Yes
sendVideo Yes
sendVoice Yes
sendLocation Yes
sendChatAction Yes
getUpdates Yes
getUserProfilePhotos Yes
getFile Yes
setWebhook Yes
========================= ============
@@ -107,6 +120,8 @@ Python Version *Supported?*
2.7 Yes
3.3 Yes
3.4 Yes
PyPy Yes
PyPy3 Yes
============== ============
=============
@@ -125,13 +140,17 @@ Or upgrade to the latest version::
_`Getting the code`
===================
The code is hosted at https://github.com/leandrotoledo/python-telegram-bot
The code is hosted at https://github.com/python-telegram-bot/python-telegram-bot
Check out the latest development version anonymously with::
$ git clone https://github.com/leandrotoledo/python-telegram-bot
$ 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
@@ -146,10 +165,94 @@ _`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.
As with the old method, we'll need an Access Token. To generate an Access Token, we have to talk to `BotFather <https://telegram.me/botfather>`_ and follow a few simple steps (described `here <https://core.telegram.org/bots#botfather>`_).
First, we create an ``Updater`` object::
>>> from telegram import Updater
>>> updater = Updater(token='token')
For quicker access to the ``Dispatcher`` used by our ``Updater``, we can introduce it locally::
>>> dispatcher = updater.dispatcher
Now, we need to define a function that should process a specific type of update::
>>> def start(bot, update):
... bot.sendMessage(chat_id=update.message.chat_id, text="I'm a bot, please talk to me!")
We want this function to be called on a Telegram message that contains the ``/start`` command, so we need to register it in the dispatcher::
>>> dispatcher.addTelegramCommandHandler('start', start)
The last step is to tell the ``Updater`` to start working::
>>> updater.start_polling()
Our bot is now up and running (go ahead and try it)! It's not doing anything yet, besides answering to the ``/start`` command. Let's add another handler function and register it::
>>> def echo(bot, update):
... bot.sendMessage(chat_id=update.message.chat_id, text=update.message.text)
...
>>> dispatcher.addTelegramMessageHandler(echo)
Our bot should now reply to all messages that are not a command with a message that has the same content.
People might try to send commands to the bot that it doesn't understand, so we should get that covered as well::
>>> def unknown(bot, update):
... bot.sendMessage(chat_id=update.message.chat_id, text="Sorry, I didn't understand that command.")
...
>>> dispatcher.addUnknownTelegramCommandHandler(unknown)
Let's add some functionality to our bot. We want to add the ``/caps`` command, that will take some text as parameter and return it in all caps. We can get the arguments that were passed to the command in the handler function simply by adding it to the parameter list::
>>> def caps(bot, update, args):
... text_caps = ' '.join(args).upper()
... bot.sendMessage(chat_id=update.message.chat_id, text=text_caps)
...
>>> dispatcher.addTelegramCommandHandler('caps', caps)
To enable our bot to respond to inline queries, we can add the following (you will also have to talk to BotFather)::
>>> from telegram import InlineQueryResultArticle
>>> def inline_caps(bot, update):
... # If you activated inline feedback, updates might either contain
... # inline_query or chosen_inline_result, the other one will be None
... if update.inline_query:
... query = bot.update.inline_query.query
... results = list()
... results.append(InlineQueryResultArticle(query.upper(), 'Caps', text_caps))
... bot.answerInlineQuery(update.inline_query.id, results)
...
>>> dispatcher.addTelegramInlineHandler(inline_caps)
Now it's time to stop the bot::
>>> updater.stop()
Check out more examples in the `examples folder <https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples>`_!
------
_`API`
------
Note: Using the ``Bot`` class directly is the 'old' method, but some of this is still important information, even if you're using the ``Updater`` class!
The API is exposed via the ``telegram.Bot`` class.
To generate an Access Token you have to talk to `BotFather <https://telegram.me/botfather>`_ and follow a few simple steps (described `here <https://core.telegram.org/bots#botfather>`_).
@@ -186,17 +289,29 @@ To post a text message::
>>> bot.sendMessage(chat_id=chat_id, text="I'm sorry Dave I'm afraid I can't do that.")
To post a text message with markdown::
>>> bot.sendMessage(chat_id=chat_id, text="*bold* _italic_ [link](http://google.com).", parse_mode=telegram.ParseMode.MARKDOWN)
To post a text message with Html style::
>>> bot.sendMessage(chat_id=chat_id, text="<b>bold</b> <i>italic</i> <a href="http://google.com">link</a>.", parse_mode=telegram.ParseMode.HTML)
To post an Emoji (special thanks to `Tim Whitlock <http://apps.timwhitlock.info/emoji/tables/unicode>`_)::
>>> bot.sendMessage(chat_id=chat_id, text=telegram.Emoji.PILE_OF_POO)
To post an image file via URL (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 an audio file::
To post an image file from disk::
>>> bot.sendAudio(chat_id=chat_id, audio=open('tests/telegram.ogg', 'rb'))
>>> bot.sendPhoto(chat_id=chat_id, photo=open('tests/test.png', 'rb'))
To post a voice file from disk::
>>> bot.sendVoice(chat_id=chat_id, voice=open('tests/telegram.ogg', 'rb'))
To tell the user that something is happening on bot's side::
@@ -213,14 +328,50 @@ To hide `Custom Keyboards <https://core.telegram.org/bots#keyboards>`_::
>>> reply_markup = telegram.ReplyKeyboardHide()
>>> bot.sendMessage(chat_id=chat_id, text="I'm back.", reply_markup=reply_markup)
To download a file (you will need its file_id)::
>>> file_id = message.voice.file_id
>>> newFile = bot.getFile(file_id)
>>> newFile.download('voice.ogg')
There are many more API methods, to read the full API documentation::
$ pydoc telegram.Bot
-----------
_`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
@@ -237,14 +388,28 @@ _`Examples`
Here follows some examples to help you to get your own Bot up to speed:
- `echobot <https://github.com/leandrotoledo/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/leandrotoledo/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.
================
_`Documentation`
================
@@ -261,12 +426,10 @@ 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/00b9c0f802509b949c1563d56eb053b0>`_.
Feel free to join to our `Telegram group <https://telegram.me/joinchat/ALnA-AJQm5TcNEiy2G_4cQ>`_.
=======
_`TODO`
=======
Patches and bug reports are `welcome <https://github.com/leandrotoledo/python-telegram-bot/issues/new>`_, just please keep the style consistent with the original source.
- Add commands handler.
Patches and bug reports are `welcome <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_, just please keep the style consistent with the original source.
+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 = '2.5'
version = '3.3'
# The full version, including alpha/beta/rc tags.
release = '2.5.1'
release = '3.3.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -1,7 +1,7 @@
telegram.groupchat module
telegram.chat module
=========================
.. automodule:: telegram.groupchat
.. automodule:: telegram.chat
:members:
:undoc-members:
:show-inheritance:
@@ -0,0 +1,7 @@
telegram.choseninlineresult module
==================================
.. automodule:: telegram.choseninlineresult
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.dispatcher module
=========================
.. automodule:: telegram.dispatcher
: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:
+8 -1
View File
@@ -9,13 +9,19 @@ Submodules
telegram.audio
telegram.base
telegram.bot
telegram.updater
telegram.dispatcher
telegram.jobqueue
telegram.inlinequery
telegram.inlinequeryresult
telegram.choseninlineresult
telegram.chataction
telegram.contact
telegram.document
telegram.emoji
telegram.error
telegram.forcereply
telegram.groupchat
telegram.chat
telegram.inputfile
telegram.location
telegram.message
@@ -29,6 +35,7 @@ Submodules
telegram.user
telegram.userprofilephotos
telegram.video
telegram.voice
Module contents
---------------
+7
View File
@@ -0,0 +1,7 @@
telegram.updater module
=========================
.. automodule:: telegram.updater
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.voice module
=====================
.. automodule:: telegram.voice
:members:
:undoc-members:
:show-inheritance:
+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/>
+177
View File
@@ -0,0 +1,177 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Example Bot to show some of the functionality of the library
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot.
First, a few handler functions are defined. Then, those functions are passed to
the Dispatcher and registered at their respective places.
Then, the bot is started and the CLI-Loop is entered, where all text inputs are
inserted into the update queue for the bot to handle.
Usage:
Repeats messages with a delay.
Reply to last chat from the command line by typing "/reply <text>"
Type 'stop' on the command line to stop the bot.
"""
from telegram import Updater
from telegram.dispatcher import run_async
from time import sleep
import logging
# Enable Logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
# We use this var to save the last chat id, so we can reply to it
last_chat_id = 0
# Define a few (command) handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error.
def start(bot, update):
""" Answer in Telegram """
bot.sendMessage(update.message.chat_id, text='Hi!')
def help(bot, update):
""" Answer in Telegram """
bot.sendMessage(update.message.chat_id, text='Help!')
def any_message(bot, update):
""" Print to console """
# Save last chat_id to use in reply handler
global last_chat_id
last_chat_id = update.message.chat_id
logger.info("New message\nFrom: %s\nchat_id: %d\nText: %s" %
(update.message.from_user,
update.message.chat_id,
update.message.text))
def unknown_command(bot, update):
""" Answer in Telegram """
bot.sendMessage(update.message.chat_id, text='Command not recognized!')
@run_async
def message(bot, update, **kwargs):
"""
Example for an asynchronous handler. It's not guaranteed that replies will
be in order when using @run_async. Also, you have to include **kwargs in
your parameter list. The kwargs contain all optional parameters that are
"""
sleep(2) # IO-heavy operation here
bot.sendMessage(update.message.chat_id, text='Echo: %s' %
update.message.text)
# These handlers are for updates of type str. We use them to react to inputs
# on the command line interface
def cli_reply(bot, update, args):
"""
For any update of type telegram.Update or str that contains a command, you
can get the argument list by appending args to the function parameters.
Here, we reply to the last active chat with the text after the command.
"""
if last_chat_id is not 0:
bot.sendMessage(chat_id=last_chat_id, text=' '.join(args))
def cli_noncommand(bot, update, update_queue):
"""
You can also get the update queue as an argument in any handler by
appending it to the argument list. Be careful with this though.
Here, we put the input string back into the queue, but as a command.
To learn more about those optional handler parameters, read:
http://python-telegram-bot.readthedocs.org/en/latest/telegram.dispatcher.html
"""
update_queue.put('/%s' % update)
def unknown_cli_command(bot, update):
logger.warn("Command not found: %s" % update)
def error(bot, update, error):
""" Print error to console """
logger.warn('Update %s caused error %s' % (update, error))
def main():
# Create the EventHandler and pass it your bot's token.
token = 'TOKEN'
updater = Updater(token, workers=10)
# Get the dispatcher to register handlers
dp = updater.dispatcher
# This is how we add handlers for Telegram messages
dp.addTelegramCommandHandler("start", start)
dp.addTelegramCommandHandler("help", help)
dp.addUnknownTelegramCommandHandler(unknown_command)
# Message handlers only receive updates that don't contain commands
dp.addTelegramMessageHandler(message)
# Regex handlers will receive all updates on which their regex matches
dp.addTelegramRegexHandler('.*', any_message)
# String handlers work pretty much the same
dp.addStringCommandHandler('reply', cli_reply)
dp.addUnknownStringCommandHandler(unknown_cli_command)
dp.addStringRegexHandler('[^/].*', cli_noncommand)
# All TelegramErrors are caught for you and delivered to the error
# handler(s). Other types of Errors are not caught.
dp.addErrorHandler(error)
# Start the Bot and store the update Queue, so we can insert updates
update_queue = updater.start_polling(poll_interval=0.1, timeout=10)
'''
# Alternatively, run with webhook:
updater.bot.setWebhook(webhook_url='https://example.com/%s' % token,
certificate=open('cert.pem', 'rb'))
update_queue = updater.start_webhook('0.0.0.0',
443,
url_path=token,
cert='cert.pem',
key='key.key')
# Or, if SSL is handled by a reverse proxy, the webhook URL is already set
# and the reverse proxy is configured to deliver directly to port 6000:
update_queue = updater.start_webhook('0.0.0.0',
6000)
'''
# Start CLI-Loop
while True:
try:
text = raw_input()
except NameError:
text = input()
# Gracefully stop the event handler
if text == 'stop':
updater.stop()
break
# else, put the text into the update queue to be handled by our handlers
elif len(text) > 0:
update_queue.put(text)
if __name__ == '__main__':
main()
-69
View File
@@ -1,69 +0,0 @@
#!/usr/bin/env python
#
# Simple Bot to reply 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
import time
LAST_UPDATE_ID = None
def main():
global LAST_UPDATE_ID
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# Telegram Bot Authorization Token
bot = telegram.Bot('TOKEN')
# This will be our global variable to keep the latest update_id when requesting
# for updates. It starts with the latest update_id if available.
try:
LAST_UPDATE_ID = bot.getUpdates()[-1].update_id
except IndexError:
LAST_UPDATE_ID = None
while True:
echo(bot)
time.sleep(3)
def echo(bot):
global LAST_UPDATE_ID
# Request updates from last updated_id
for update in bot.getUpdates(offset=LAST_UPDATE_ID):
if LAST_UPDATE_ID < update.update_id:
# chat_id is required to reply any message
chat_id = update.message.chat_id
message = update.message.text.encode('utf-8')
if (message):
# Reply the message
bot.sendMessage(chat_id=chat_id,
text=message)
# Updates global offset to get the new updates
LAST_UPDATE_ID = update.update_id
if __name__ == '__main__':
main()
+75
View File
@@ -0,0 +1,75 @@
#!/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 Echobot example, repeats messages.
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__)
# 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 echo(bot, update):
bot.sendMessage(update.message.chat_id, text=update.message.text)
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
def main():
# Create the EventHandler and pass it your bot's token.
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
# 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.addTelegramMessageHandler(echo)
# log all errors
dp.addErrorHandler(error)
# Start the Bot
updater.start_polling()
# Run the bot until the you presses Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
+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()
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env python
# encoding: utf-8
#
# Robô Ed Telegram Bot
# This program is dedicated to the public domain under the CC0 license.
import logging
import telegram
import urllib
def main():
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
bot = telegram.Bot('TOKEN') # Telegram Bot Authorization Token
LAST_UPDATE_ID = bot.getUpdates()[-1].update_id # Get lastest update
while True:
for update in bot.getUpdates(offset=LAST_UPDATE_ID, timeout=10):
text = update.message.text
chat_id = update.message.chat.id
update_id = update.update_id
if text:
roboed = ed(text) # Ask something to Robô Ed
bot.sendMessage(chat_id=chat_id, text=roboed)
LAST_UPDATE_ID = update_id + 1
def ed(text):
url = 'http://www.ed.conpet.gov.br/mod_perl/bot_gateway.cgi?server=0.0.0.0%3A8085&charset_post=utf-8&charset=utf-8&pure=1&js=0&tst=1&msg=' + text
data = urllib.urlopen(url).read()
return data.strip()
if __name__ == '__main__':
main()
-56
View File
@@ -1,56 +0,0 @@
#!/usr/bin/env python
# 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'
import logging
import telegram
import urllib
def main():
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
bot = telegram.Bot('TOKEN') # Telegram Bot Authorization Token
LAST_UPDATE_ID = bot.getUpdates()[-1].update_id # Get lastest update
while True:
for update in bot.getUpdates(offset=LAST_UPDATE_ID):
text = update.message.text
chat_id = update.message.chat.id
update_id = update.update_id
if LAST_UPDATE_ID < update_id: # If newer than the initial
# LAST_UPDATE_ID
if text:
roboed = ed(text) # Ask something to Robô Ed
bot.sendMessage(chat_id=chat_id, text=roboed)
LAST_UPDATE_ID = update_id
def ed(text):
url = 'http://www.ed.conpet.gov.br/mod_perl/bot_gateway.cgi?server=0.0.0.0%3A8085&charset_post=utf-8&charset=utf-8&pure=1&js=0&tst=1&msg=' + text
data = urllib.urlopen(url).read()
return data.strip()
if __name__ == '__main__':
main()
+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
+1
View File
@@ -0,0 +1 @@
future
+16 -4
View File
@@ -13,17 +13,29 @@ def read(*paths):
return f.read()
def requirements():
"""Build the requirements list for this project"""
requirements_list = []
with open('requirements.txt') as requirements:
for install in requirements:
requirements_list.append(install.strip())
return requirements_list
setup(
name='python-telegram-bot',
version='2.5.3',
version='3.3',
author='Leandro Toledo',
author_email='leandrotoledodesouza@gmail.com',
author_email='devs@python-telegram-bot.org',
license='LGPLv3',
url='https://github.com/leandrotoledo/python-telegram-bot',
keywords='telegram bot api',
url='https://github.com/python-telegram-bot/python-telegram-bot',
keywords='python telegram bot api wrapper',
description='A Python wrapper around the Telegram Bot API',
long_description=(read('README.rst')),
packages=find_packages(exclude=['tests*']),
install_requires=requirements(),
include_package_data=True,
classifiers=[
'Development Status :: 5 - Production/Stable',
+31 -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 Lesser Public License as published by
@@ -16,17 +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/].
__author__ = 'leandrotoledodesouza@gmail.com'
__version__ = '2.5.3'
"""A library that provides a Python interface to the Telegram Bot API"""
from .base import TelegramObject
from .user import User
from .message import Message
from .update import Update
from .groupchat import GroupChat
from .chat import Chat
from .photosize import PhotoSize
from .audio import Audio
from .voice import Voice
from .document import Document
from .sticker import Sticker
from .video import Video
@@ -38,14 +36,34 @@ from .replymarkup import ReplyMarkup
from .replykeyboardmarkup import ReplyKeyboardMarkup
from .replykeyboardhide import ReplyKeyboardHide
from .forcereply import ForceReply
from .inputfile import InputFile
from .error import TelegramError
from .inputfile import InputFile
from .file import File
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', 'Emoji', 'TelegramError', 'InputFile', 'ReplyMarkup',
'ForceReply', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup',
'UserProfilePhotos', 'ChatAction', 'Location', 'Contact',
'Video', 'Sticker', 'Document', 'Audio', 'PhotoSize', 'GroupChat',
'Update', 'Message', 'User', 'TelegramObject', 'NullHandler']
__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', 'JobQueue',
'InlineQuery', 'ChosenInlineResult', 'InlineQueryResultArticle',
'InlineQueryResultGif', 'InlineQueryResultPhoto',
'InlineQueryResultMpeg4Gif', 'InlineQueryResultVideo',
'UpdateQueue')
+44 -19
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,33 +17,57 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Audio."""
from telegram import TelegramObject
class Audio(TelegramObject):
"""This object represents a Telegram Audio.
Attributes:
file_id (str):
duration (int):
performer (str):
title (str):
mime_type (str):
file_size (int):
Args:
file_id (str):
duration (int):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
performer (Optional[str]):
title (Optional[str]):
mime_type (Optional[str]):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
duration,
mime_type=None,
file_size=None):
self.file_id = file_id
self.duration = duration
self.mime_type = mime_type
self.file_size = file_size
**kwargs):
# Required
self.file_id = str(file_id)
self.duration = int(duration)
# Optionals
self.performer = kwargs.get('performer', '')
self.title = kwargs.get('title', '')
self.mime_type = str(kwargs.get('mime_type', ''))
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
def de_json(data):
return Audio(file_id=data.get('file_id', None),
duration=data.get('duration', None),
mime_type=data.get('mime_type', None),
file_size=data.get('file_size', None))
"""
Args:
data (str):
def to_dict(self):
data = {'file_id': self.file_id,
'duration': self.duration}
if self.mime_type:
data['mime_type'] = self.mime_type
if self.file_size:
data['file_size'] = self.file_size
return data
Returns:
telegram.Audio:
"""
if not data:
return None
return Audio(**data)
+30 -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,13 +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."""
import json
from abc import ABCMeta, abstractmethod
from abc import ABCMeta
class TelegramObject(object):
"""Base class for most telegram object"""
"""Base class for most telegram objects."""
__metaclass__ = ABCMeta
@@ -34,11 +36,34 @@ class TelegramObject(object):
@staticmethod
def de_json(data):
"""
Args:
data (str):
Returns:
telegram.TelegramObject:
"""
raise NotImplementedError
def to_json(self):
"""
Returns:
str:
"""
return json.dumps(self.to_dict())
@abstractmethod
def to_dict(self):
return
"""
Returns:
dict:
"""
data = dict()
for key, value in self.__dict__.items():
if value:
if hasattr(value, 'to_dict'):
data[key] = value.to_dict()
else:
data[key] = value
return data
+313 -161
View File
@@ -1,7 +1,9 @@
#!/usr/bin/env python
# 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
@@ -16,45 +18,65 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Bot."""
import json
try:
from urllib.parse import urlencode
from urllib.request import urlopen, Request
from urllib.error import HTTPError, URLError
except ImportError:
from urllib import urlencode
from urllib2 import urlopen, Request
from urllib2 import HTTPError, URLError
import functools
import logging
from telegram import (User, Message, Update, UserProfilePhotos, TelegramError,
ReplyMarkup, InputFile, TelegramObject, NullHandler)
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)
H = NullHandler()
logging.getLogger(__name__).addHandler(H)
class Bot(TelegramObject):
"""This object represents a Telegram Bot.
Attributes:
id (int):
first_name (str):
last_name (str):
username (str):
name (str):
Args:
token (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
base_url (Optional[str]):
"""
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
else:
self.base_url = base_url + self.token
self.base_file_url = 'https://api.telegram.org/file/bot%s' % self.token
self.bot = None
self.log = logging.getLogger(__name__)
self.logger = logging.getLogger(__name__)
def info(func):
"""
Returns:
"""
@functools.wraps(func)
def decorator(self, *args, **kwargs):
"""
decorator
"""
if not self.bot:
self.getMe()
@@ -65,36 +87,48 @@ class Bot(TelegramObject):
@property
@info
def id(self):
"""int: """
return self.bot.id
@property
@info
def first_name(self):
"""str: """
return self.bot.first_name
@property
@info
def last_name(self):
"""str: """
return self.bot.last_name
@property
@info
def username(self):
"""str: """
return self.bot.username
@property
def name(self):
"""str: """
return '@%s' % self.username
def log(func):
"""
Returns:
A telegram.Message instance representing the message posted.
"""
logger = logging.getLogger(func.__module__)
@functools.wraps(func)
def decorator(self, *args, **kwargs):
logger.debug('Entering: %s' % func.__name__)
"""
decorator
"""
logger.debug('Entering: %s', func.__name__)
result = func(self, *args, **kwargs)
logger.debug(result)
logger.debug('Exiting: %s' % func.__name__)
logger.debug('Exiting: %s', func.__name__)
return result
return decorator
@@ -105,28 +139,43 @@ class Bot(TelegramObject):
"""
@functools.wraps(func)
def decorator(self, *args, **kwargs):
"""
decorator
"""
url, data = func(self, *args, **kwargs)
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
json_data = self._requestUrl(url, 'POST', data=data)
data = self._parseAndCheckTelegram(json_data)
if data is True:
return data
return Message.de_json(data)
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.
@@ -137,10 +186,9 @@ class Bot(TelegramObject):
"""
url = '%s/getMe' % self.base_url
json_data = self._requestUrl(url, 'GET')
data = self._parseAndCheckTelegram(json_data)
result = request.get(url)
self.bot = User.de_json(data)
self.bot = User.de_json(result)
return self.bot
@@ -149,15 +197,18 @@ class Bot(TelegramObject):
def sendMessage(self,
chat_id,
text,
parse_mode=None,
disable_web_page_preview=None,
reply_to_message_id=None,
reply_markup=None):
**kwargs):
"""Use this method to send text messages.
Args:
chat_id:
Unique identifier for the message recipient - telegram.User or
telegram.GroupChat id.
Unique identifier for the message recipient - telegram.Chat id.
parse_mode:
Send Markdown, if you want Telegram apps to show bold, italic and
inline URLs in your bot's message. For the moment, only Telegram
for Android supports this. [Optional]
text:
Text of the message to be sent.
disable_web_page_preview:
@@ -178,6 +229,8 @@ class Bot(TelegramObject):
data = {'chat_id': chat_id,
'text': text}
if parse_mode:
data['parse_mode'] = parse_mode
if disable_web_page_preview:
data['disable_web_page_preview'] = disable_web_page_preview
@@ -193,10 +246,10 @@ class Bot(TelegramObject):
Args:
chat_id:
Unique identifier for the message recipient - User or GroupChat id.
Unique identifier for the message recipient - Chat id.
from_chat_id:
Unique identifier for the chat where the original message was sent
- User or GroupChat id.
- Chat id.
message_id:
Unique message identifier.
@@ -222,13 +275,12 @@ class Bot(TelegramObject):
chat_id,
photo,
caption=None,
reply_to_message_id=None,
reply_markup=None):
**kwargs):
"""Use this method to send photos.
Args:
chat_id:
Unique identifier for the message recipient - User or GroupChat id.
Unique identifier for the message recipient - Chat id.
photo:
Photo to send. You can either pass a file_id as String to resend a
photo that is already on the Telegram servers, or upload a new
@@ -262,20 +314,34 @@ class Bot(TelegramObject):
def sendAudio(self,
chat_id,
audio,
reply_to_message_id=None,
reply_markup=None):
duration=None,
performer=None,
title=None,
**kwargs):
"""Use this method to send audio files, if you want Telegram clients to
display the file as a playable voice message. For this to work, your
audio must be in an .ogg file encoded with OPUS (other formats may be
sent as telegram.Document).
display them in the music player. Your audio must be in an .mp3 format.
On success, the sent Message is returned. Bots can currently send audio
files of up to 50 MB in size, this limit may be changed in the future.
For backward compatibility, when both fields title and description are
empty and mime-type of the sent file is not "audio/mpeg", file is sent
as playable voice message. In this case, your audio must be in an .ogg
file encoded with OPUS. This will be removed in the future. You need to
use sendVoice method instead.
Args:
chat_id:
Unique identifier for the message recipient - User or GroupChat id.
Unique identifier for the message recipient - Chat id.
audio:
Audio file to send. You can either pass a file_id as String to
resend an audio that is already on the Telegram servers, or upload
a new audio file using multipart/form-data.
duration:
Duration of sent audio in seconds. [Optional]
performer:
Performer of sent audio. [Optional]
title:
Title of sent audio. [Optional]
reply_to_message_id:
If the message is a reply, ID of the original message. [Optional]
reply_markup:
@@ -292,6 +358,13 @@ class Bot(TelegramObject):
data = {'chat_id': chat_id,
'audio': audio}
if duration:
data['duration'] = duration
if performer:
data['performer'] = performer
if title:
data['title'] = title
return url, data
@log
@@ -299,17 +372,20 @@ class Bot(TelegramObject):
def sendDocument(self,
chat_id,
document,
reply_to_message_id=None,
reply_markup=None):
filename=None,
**kwargs):
"""Use this method to send general files.
Args:
chat_id:
Unique identifier for the message recipient - User or GroupChat id.
Unique identifier for the message recipient - Chat id.
document:
File to send. You can either pass a file_id as String to resend a
file that is already on the Telegram servers, or upload a new file
using multipart/form-data.
filename:
File name that shows in telegram message (it is usefull when you
send file generated by temp module, for example). [Optional]
reply_to_message_id:
If the message is a reply, ID of the original message. [Optional]
reply_markup:
@@ -326,6 +402,9 @@ class Bot(TelegramObject):
data = {'chat_id': chat_id,
'document': document}
if filename:
data['filename'] = filename
return url, data
@log
@@ -333,13 +412,12 @@ class Bot(TelegramObject):
def sendSticker(self,
chat_id,
sticker,
reply_to_message_id=None,
reply_markup=None):
**kwargs):
"""Use this method to send .webp stickers.
Args:
chat_id:
Unique identifier for the message recipient - User or GroupChat id.
Unique identifier for the message recipient - Chat id.
sticker:
Sticker to send. You can either pass a file_id as String to resend
a sticker that is already on the Telegram servers, or upload a new
@@ -363,20 +441,19 @@ class Bot(TelegramObject):
return url, data
@log
@message
def sendVideo(self,
chat_id,
video,
duration=None,
caption=None,
reply_to_message_id=None,
reply_markup=None):
timeout=None,
**kwargs):
"""Use this method to send video files, Telegram clients support mp4
videos (other formats may be sent as telegram.Document).
Args:
chat_id:
Unique identifier for the message recipient - User or GroupChat id.
Unique identifier for the message recipient - Chat id.
video:
Video to send. You can either pass a file_id as String to resend a
video that is already on the Telegram servers, or upload a new
@@ -386,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:
@@ -407,6 +487,50 @@ class Bot(TelegramObject):
if caption:
data['caption'] = caption
return self._post_message(url, data, kwargs, timeout=timeout)
@log
@message
def sendVoice(self,
chat_id,
voice,
duration=None,
**kwargs):
"""Use this method to send audio files, if you want Telegram clients to
display the file as a playable voice message. For this to work, your
audio must be in an .ogg file encoded with OPUS (other formats may be
sent as Audio or Document). On success, the sent Message is returned.
Bots can currently send audio files of up to 50 MB in size, this limit
may be changed in the future.
Args:
chat_id:
Unique identifier for the message recipient - Chat id.
voice:
Audio file to send. You can either pass a file_id as String to
resend an audio that is already on the Telegram servers, or upload
a new audio file using multipart/form-data.
duration:
Duration of sent audio in seconds. [Optional]
reply_to_message_id:
If the message is a reply, ID of the original message. [Optional]
reply_markup:
Additional interface options. A JSON-serialized object for a
custom reply keyboard, instructions to hide keyboard or to force a
reply from the user. [Optional]
Returns:
A telegram.Message instance representing the message posted.
"""
url = '%s/sendVoice' % self.base_url
data = {'chat_id': chat_id,
'voice': voice}
if duration:
data['duration'] = duration
return url, data
@log
@@ -415,13 +539,12 @@ class Bot(TelegramObject):
chat_id,
latitude,
longitude,
reply_to_message_id=None,
reply_markup=None):
**kwargs):
"""Use this method to send point on the map.
Args:
chat_id:
Unique identifier for the message recipient - User or GroupChat id.
Unique identifier for the message recipient - Chat id.
latitude:
Latitude of location.
longitude:
@@ -457,14 +580,14 @@ class Bot(TelegramObject):
Args:
chat_id:
Unique identifier for the message recipient - User or GroupChat id.
Unique identifier for the message recipient - Chat id.
action:
Type of action to broadcast. Choose one, depending on what the user
is about to receive:
- ChatAction.TYPING for text messages,
- ChatAction.UPLOAD_PHOTO for photos,
- ChatAction.UPLOAD_VIDEO or upload_video for videos,
- ChatAction.UPLOAD_AUDIO or upload_audio for audio files,
- ChatAction.UPLOAD_VIDEO for videos,
- ChatAction.UPLOAD_AUDIO for audio files,
- ChatAction.UPLOAD_DOCUMENT for general files,
- ChatAction.FIND_LOCATION for location data.
"""
@@ -476,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,
@@ -506,16 +682,43 @@ class Bot(TelegramObject):
if limit:
data['limit'] = limit
json_data = self._requestUrl(url, 'POST', data=data)
data = self._parseAndCheckTelegram(json_data)
result = request.post(url, data)
return UserProfilePhotos.de_json(data)
return UserProfilePhotos.de_json(result)
@log
def getFile(self,
file_id):
"""Use this method to get basic info about a file and prepare it for
downloading. For the moment, bots can download files of up to 20MB in
size.
Args:
file_id:
File identifier to get info about.
Returns:
Returns a telegram.File object
"""
url = '%s/getFile' % self.base_url
data = {'file_id': file_id}
result = request.post(url, data)
if result.get('file_path'):
result['file_path'] = '%s/%s' % (self.base_file_url,
result['file_path'])
return File.de_json(result)
@log
def getUpdates(self,
offset=None,
limit=100,
timeout=0):
timeout=0,
network_delay=2.):
"""Use this method to receive incoming updates using long polling.
Args:
@@ -531,6 +734,11 @@ class Bot(TelegramObject):
timeout:
Timeout in seconds for long polling. Defaults to 0, i.e. usual
short polling.
network_delay:
Additional timeout in seconds to allow the response from Telegram
to take some time when using long polling. Defaults to 2, which
should be enough for most connections. Increase it if it takes very
long for data to be transmitted from and to the Telegram servers.
Returns:
A list of telegram.Update objects are returned.
@@ -546,20 +754,20 @@ class Bot(TelegramObject):
if timeout:
data['timeout'] = timeout
json_data = self._requestUrl(url, 'POST', data=data)
data = self._parseAndCheckTelegram(json_data)
result = request.post(url, data, network_delay=network_delay)
if data:
self.log.info(
'Getting updates: %s' % [u['update_id'] for u in data])
if result:
self.logger.info(
'Getting updates: %s', [u['update_id'] for u in result])
else:
self.log.info('No new updates found.')
self.logger.debug('No new updates found.')
return [Update.de_json(x) for x in data]
return [Update.de_json(x) for x in result]
@log
def setWebhook(self,
webhook_url):
webhook_url=None,
certificate=None):
"""Use this method to specify a url and receive incoming updates via an
outgoing webhook. Whenever there is an update for the bot, we will send
an HTTPS POST request to the specified url, containing a
@@ -576,89 +784,25 @@ class Bot(TelegramObject):
"""
url = '%s/setWebhook' % self.base_url
data = {'url': webhook_url}
data = {}
if webhook_url:
data['url'] = webhook_url
if certificate:
data['certificate'] = certificate
json_data = self._requestUrl(url, 'POST', data=data)
data = self._parseAndCheckTelegram(json_data)
result = request.post(url, data)
return True
return result
def _requestUrl(self,
url,
method,
data=None):
"""Request an URL.
Args:
url:
The web location we want to retrieve.
method:
Either POST or GET.
data:
A dict of (str, unicode) key/value pairs.
Returns:
A JSON object.
"""
if method not in ('POST', 'GET'):
raise ValueError(
"Method '%s' is neither 'POST' nor 'GET'" % method)
if method == 'POST':
try:
if InputFile.is_inputfile(data):
data = InputFile(data)
request = Request(
url,
data=data.to_form(),
headers=data.headers
)
return urlopen(request).read()
else:
return urlopen(
url,
urlencode(data).encode()
).read()
except IOError as e:
raise TelegramError(str(e))
except HTTPError as e:
raise TelegramError(str(e))
except URLError as e:
raise TelegramError(str(e))
if method == 'GET':
try:
return urlopen(url).read()
except URLError as e:
raise TelegramError(str(e))
def _parseAndCheckTelegram(self,
json_data):
"""Try and parse the JSON returned from Telegram and return an empty
dictionary if there is any error.
Args:
json_data:
JSON results from Telegram Bot API.
Returns:
A JSON parsed as Python dict with results.
"""
try:
data = json.loads(json_data.decode())
if not data['ok']:
raise TelegramError(data['description'])
except ValueError:
if '<title>403 Forbidden</title>' in json_data:
raise TelegramError({'message': 'API must be authenticated'})
raise TelegramError({'message': 'JSON decoding'})
return data['result']
@staticmethod
def de_json(data):
pass
def to_dict(self):
"""
Returns:
dict:
"""
data = {'id': self.id,
'username': self.username,
'first_name': self.username}
@@ -669,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
+71
View File
@@ -0,0 +1,71 @@
#!/usr/bin/env python
# pylint: disable=C0103,W0622
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Chat."""
from telegram import TelegramObject
class Chat(TelegramObject):
"""This object represents a Telegram Chat.
Attributes:
id (int):
type (str): Can be 'private', 'group', 'supergroup' or 'channel'
title (str): Title, for channels and group chats
username (str): Username, for private chats and channels if available
first_name (str): First name of the other party in a private chat
last_name (str): Last name of the other party in a private chat
Args:
id (int):
type (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
type (Optional[str]):
"""
def __init__(self,
id,
type,
**kwargs):
# Required
self.id = int(id)
self.type = type
# Optionals
self.title = kwargs.get('title', '')
self.username = kwargs.get('username', '')
self.first_name = kwargs.get('first_name', '')
self.last_name = kwargs.get('last_name', '')
@staticmethod
def de_json(data):
"""
Args:
data (dict):
Returns:
telegram.Chat:
"""
if not data:
return None
return Chat(**data)
+11 -3
View File
@@ -1,7 +1,9 @@
#!/usr/bin/env python
# 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
@@ -16,11 +18,17 @@
# 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."""
class ChatAction(object):
"""This object represents a Telegram ChatAction."""
TYPING = 'typing'
UPLOAD_PHOTO = 'upload_photo'
RECORD_VIDEO = 'upload_video'
RECORD_AUDIO = 'upload_audio'
RECORD_VIDEO = 'record_video'
UPLOAD_VIDEO = 'upload_video'
RECORD_AUDIO = 'record_audio'
UPLOAD_AUDIO = 'upload_audio'
UPLOAD_DOCUMENT = 'upload_document'
FIND_LOCATION = 'find_location'
+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
+37 -18
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,33 +17,51 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Contact."""
from telegram import TelegramObject
class Contact(TelegramObject):
"""This object represents a Telegram Contact.
Attributes:
phone_number (str):
first_name (str):
last_name (str):
user_id (int):
Args:
phone_number (str):
first_name (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
last_name (Optional[str]):
user_id (Optional[int]):
"""
def __init__(self,
phone_number,
first_name,
last_name=None,
user_id=None):
self.phone_number = phone_number
**kwargs):
# Required
self.phone_number = str(phone_number)
self.first_name = first_name
self.last_name = last_name
self.user_id = user_id
# Optionals
self.last_name = kwargs.get('last_name', '')
self.user_id = int(kwargs.get('user_id', 0))
@staticmethod
def de_json(data):
return Contact(phone_number=data.get('phone_number', None),
first_name=data.get('first_name', None),
last_name=data.get('last_name', None),
user_id=data.get('user_id', None))
"""
Args:
data (str):
def to_dict(self):
data = {'phone_number': self.phone_number,
'first_name': self.first_name}
if self.last_name:
data['last_name'] = self.last_name
if self.user_id:
data['user_id'] = self.user_id
return data
Returns:
telegram.Contact:
"""
if not data:
return None
return Contact(**data)
+706
View File
@@ -0,0 +1,706 @@
#!/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."""
import logging
from functools import wraps
from inspect import getargspec
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
async_threads = set()
""":type: set[Thread]"""
async_lock = Lock()
def run_async(func):
"""
Function decorator that will run the function in a new thread. A function
decorated with this will have to include **kwargs in their parameter list,
which will contain all optional parameters.
Args:
func (function): The function to run in the thread.
Returns:
function:
"""
# TODO: handle exception in async threads
# set a threading.Event to notify caller thread
@wraps(func)
def pooled(*pargs, **kwargs):
"""
A wrapper to run a thread in a thread pool
"""
result = func(*pargs, **kwargs)
semaphore.release()
with async_lock:
async_threads.remove(current_thread())
return result
@wraps(func)
def async_func(*pargs, **kwargs):
"""
A wrapper to run a function in a thread
"""
thread = Thread(target=pooled, args=pargs, kwargs=kwargs)
semaphore.acquire()
with async_lock:
async_threads.add(thread)
thread.start()
return thread
return async_func
class Dispatcher:
"""
This class dispatches all kinds of updates to its registered handlers.
A handler is a function that usually takes the following parameters
bot:
The telegram.Bot instance that received the message
update:
The update that should be handled by the handler
Error handlers take an additional parameter
error:
The TelegramError instance that was raised during processing the
update
All handlers, except error handlers, can also request more information by
appending one or more of the following arguments in their argument list for
convenience
update_queue:
The Queue instance which contains all new updates and is
processed by the Dispatcher. Be careful with this - you might
create an infinite loop.
args:
If the update is an instance str or telegram.Update, this will be
a list that contains the content of the message split on spaces,
except the first word (usually the command).
Example: '/add item1 item2 item3' -> ['item1', 'item2', 'item3']
For updates that contain inline queries, they will contain the
whole query split on spaces.
For other updates, args will be None
In some cases handlers may need some context data to process the update. To
procedure just queue in update_queue.put(update, context=context) or
processUpdate(update,context=context).
context:
Extra data for handling updates.
For regex-based handlers, you can also request information about the match.
For all other handlers, these will be None
groups:
A tuple that contains the result of
re.match(matcher, ...).groups()
groupdict:
A dictionary that contains the result of
re.match(matcher, ...).groupdict()
Args:
bot (telegram.Bot): The bot object that should be passed to the
handlers
update_queue (telegram.UpdateQueue): The synchronized queue that will
contain the updates.
"""
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 = {}
self.string_command_handlers = {}
self.type_handlers = {}
self.unknown_telegram_command_handlers = []
self.unknown_string_command_handlers = []
self.error_handlers = []
self.logger = logging.getLogger(__name__)
self.running = False
self.__stop_event = Event()
self.__exception_event = exception_event or Event()
global semaphore
if not semaphore:
semaphore = BoundedSemaphore(value=workers)
else:
self.logger.info("Semaphore already initialized, skipping.")
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 started')
while 1:
try:
# Pop update from update queue.
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
try:
self.processUpdate(update, context)
self.logger.debug('Processed Update: %s with context %s'
% (update, context))
# Dispatch any errors
except TelegramError as te:
self.logger.warn("Error was raised while processing Update.")
try:
self.dispatchError(update, te)
# Log errors in error handlers
except:
self.logger.exception("An uncaught error was raised while "
"handling the error")
# All other errors should not stop the thread, just print them
except:
self.logger.exception("An uncaught error was raised while "
"processing an update")
self.running = False
self.logger.info('Dispatcher thread stopped')
def stop(self):
"""
Stops the thread
"""
if self.running:
self.__stop_event.set()
while self.running:
sleep(0.1)
self.__stop_event.clear()
def processUpdate(self, update, context=None):
"""
Processes a single update.
Args:
update (any):
"""
handled = False
# Custom type handlers
for t in self.type_handlers:
if isinstance(update, t):
self.dispatchType(update, context)
handled = True
# string update
if type(update) is str and update.startswith('/'):
self.dispatchStringCommand(update, context)
handled = True
elif type(update) is str:
self.dispatchRegex(update, context)
handled = True
# An error happened while polling
if isinstance(update, TelegramError):
self.dispatchError(None, update)
handled = True
# Telegram update (regex)
if isinstance(update, Update) and update.message is not None:
self.dispatchRegex(update, context)
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(
"Received update of unknown type %s" % type(update)))
# Add Handlers
def addTelegramMessageHandler(self, handler):
"""
Registers a message handler in the Dispatcher.
Args:
handler (function): A function that takes (Bot, Update, *args) as
arguments.
"""
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.
Args:
command (str): The command keyword that this handler should be
listening to.
handler (function): A function that takes (Bot, Update, *args) as
arguments.
"""
if command not in self.telegram_command_handlers:
self.telegram_command_handlers[command] = []
self.telegram_command_handlers[command].append(handler)
def addTelegramRegexHandler(self, matcher, handler):
"""
Registers a regex handler in the Dispatcher. If handlers will be
called if re.match(matcher, update.message.text) is True.
Args:
matcher (str/__Regex): A regex string or compiled regex object that
matches on messages that handler should be listening to
handler (function): A function that takes (Bot, Update, *args) as
arguments.
"""
if matcher not in self.telegram_regex_handlers:
self.telegram_regex_handlers[matcher] = []
self.telegram_regex_handlers[matcher].append(handler)
def addStringCommandHandler(self, command, handler):
"""
Registers a string-command handler in the Dispatcher.
Args:
command (str): The command keyword that this handler should be
listening to.
handler (function): A function that takes (Bot, str, *args) as
arguments.
"""
if command not in self.string_command_handlers:
self.string_command_handlers[command] = []
self.string_command_handlers[command].append(handler)
def addStringRegexHandler(self, matcher, handler):
"""
Registers a regex handler in the Dispatcher. If handlers will be
called if re.match(matcher, string) is True.
Args:
matcher (str/__Regex): A regex string or compiled regex object that
matches on the string input that handler should be listening to
handler (function): A function that takes (Bot, Update, *args) as
arguments.
"""
if matcher not in self.string_regex_handlers:
self.string_regex_handlers[matcher] = []
self.string_regex_handlers[matcher].append(handler)
def addUnknownTelegramCommandHandler(self, handler):
"""
Registers a command handler in the Dispatcher, that will receive all
commands that have no associated handler.
Args:
handler (function): A function that takes (Bot, Update, *args) as
arguments.
"""
self.unknown_telegram_command_handlers.append(handler)
def addUnknownStringCommandHandler(self, handler):
"""
Registers a string-command handler in the Dispatcher, that will
receive all commands that have no associated handler.
Args:
handler (function): A function that takes (Bot, str, *args) as
arguments.
"""
self.unknown_string_command_handlers.append(handler)
def addErrorHandler(self, handler):
"""
Registers an error handler in the Dispatcher.
Args:
handler (function): A function that takes (Bot, TelegramError) as
arguments.
"""
self.error_handlers.append(handler)
def addTypeHandler(self, the_type, handler):
"""
Registers a type handler in the Dispatcher. This allows you to send
any type of object into the update queue.
Args:
the_type (type): The type this handler should listen to
handler (function): A function that takes (Bot, type, *args) as
arguments.
"""
if the_type not in self.type_handlers:
self.type_handlers[the_type] = []
self.type_handlers[the_type].append(handler)
# Remove Handlers
def removeTelegramMessageHandler(self, handler):
"""
De-registers a message handler.
Args:
handler (any):
"""
if handler in self.telegram_message_handlers:
self.telegram_message_handlers.remove(handler)
def removeTelegramInlineHandler(self, handler):
"""
De-registers an inline query handler.
Args:
handler (any):
"""
if handler in self.telegram_inline_handlers:
self.telegram_inline_handlers.remove(handler)
def removeTelegramCommandHandler(self, command, handler):
"""
De-registers a command handler.
Args:
command (str): The command
handler (any):
"""
if command in self.telegram_command_handlers \
and handler in self.telegram_command_handlers[command]:
self.telegram_command_handlers[command].remove(handler)
def removeTelegramRegexHandler(self, matcher, handler):
"""
De-registers a regex handler.
Args:
matcher (str/__Regex): The regex matcher object or string
handler (any):
"""
if matcher in self.telegram_regex_handlers \
and handler in self.telegram_regex_handlers[matcher]:
self.telegram_regex_handlers[matcher].remove(handler)
def removeStringCommandHandler(self, command, handler):
"""
De-registers a string-command handler.
Args:
command (str): The command
handler (any):
"""
if command in self.string_command_handlers \
and handler in self.string_command_handlers[command]:
self.string_command_handlers[command].remove(handler)
def removeStringRegexHandler(self, matcher, handler):
"""
De-registers a regex handler.
Args:
matcher (str/__Regex): The regex matcher object or string
handler (any):
"""
if matcher in self.string_regex_handlers \
and handler in self.string_regex_handlers[matcher]:
self.string_regex_handlers[matcher].remove(handler)
def removeUnknownTelegramCommandHandler(self, handler):
"""
De-registers an unknown-command handler.
Args:
handler (any):
"""
if handler in self.unknown_telegram_command_handlers:
self.unknown_telegram_command_handlers.remove(handler)
def removeUnknownStringCommandHandler(self, handler):
"""
De-registers an unknown-command handler.
Args:
handler (any):
"""
if handler in self.unknown_string_command_handlers:
self.unknown_string_command_handlers.remove(handler)
def removeErrorHandler(self, handler):
"""
De-registers an error handler.
Args:
handler (any):
"""
if handler in self.error_handlers:
self.error_handlers.remove(handler)
def removeTypeHandler(self, the_type, handler):
"""
De-registers a type handler.
Args:
handler (any):
"""
if the_type in self.type_handlers \
and handler in self.type_handlers[the_type]:
self.type_handlers[the_type].remove(handler)
def dispatchTelegramCommand(self, update, context=None):
"""
Dispatches an update that contains a command.
Args:
command (str): The command keyword
update (telegram.Update): The Telegram update that contains the
command
"""
command = update.message.text.split(' ')[0][1:].split('@')[0]
if command in self.telegram_command_handlers:
self.dispatchTo(self.telegram_command_handlers[command], update,
context=context)
else:
self.dispatchTo(self.unknown_telegram_command_handlers, update,
context=context)
def dispatchRegex(self, update, context=None):
"""
Dispatches an update to all string or telegram regex handlers that
match the string/message content.
Args:
update (str, Update): The update that should be checked for matches
"""
if isinstance(update, Update):
handlers = self.telegram_regex_handlers
to_match = update.message.text
elif isinstance(update, str):
handlers = self.string_regex_handlers
to_match = update
for matcher in handlers:
m = match(matcher, to_match)
if m:
for handler in handlers[matcher]:
self.call_handler(handler,
update,
groups=m.groups(),
groupdict=m.groupdict(),
context=context)
def dispatchStringCommand(self, update, context=None):
"""
Dispatches a string-update that contains a command.
Args:
update (str): The string input
"""
command = update.split(' ')[0][1:]
if command in self.string_command_handlers:
self.dispatchTo(self.string_command_handlers[command], update,
context=context)
else:
self.dispatchTo(self.unknown_string_command_handlers, update,
context=context)
def dispatchType(self, update, context=None):
"""
Dispatches an update of any type.
Args:
update (any): The update
"""
for t in self.type_handlers:
if isinstance(update, t):
self.dispatchTo(self.type_handlers[t], update, context=context)
def dispatchTelegramMessage(self, update, context=None):
"""
Dispatches an update that contains a regular message.
Args:
update (telegram.Update): The Telegram update that contains the
message.
"""
self.dispatchTo(self.telegram_message_handlers, update,
context=context)
def dispatchTelegramInline(self, update, context=None):
"""
Dispatches an update that contains an inline update.
Args:
update (telegram.Update): The Telegram update that contains the
message.
"""
self.dispatchTo(self.telegram_inline_handlers, update, context=None)
def dispatchError(self, update, error):
"""
Dispatches an error.
Args:
update (any): The pdate that caused the error
error (telegram.TelegramError): The Telegram error that was raised.
"""
for handler in self.error_handlers:
handler(self.bot, update, error)
def dispatchTo(self, handlers, update, **kwargs):
"""
Dispatches an update to a list of handlers.
Args:
handlers (list): A list of handler-functions.
update (any): The update to be dispatched
"""
for handler in handlers:
self.call_handler(handler, update, **kwargs)
def call_handler(self, handler, update, **kwargs):
"""
Calls an update handler. Checks the handler for keyword arguments and
fills them, if possible.
Args:
handler (function): An update handler function
update (any): An update
"""
target_kwargs = {}
fargs = getargspec(handler).args
'''
async handlers will receive all optional arguments, since we can't
their argument list.
'''
is_async = 'pargs' == getargspec(handler).varargs
if is_async or 'update_queue' in fargs:
target_kwargs['update_queue'] = self.update_queue
if is_async or 'args' in fargs:
if isinstance(update, Update) and update.message:
args = update.message.text.split(' ')[1:]
elif isinstance(update, Update) and update.inline_query:
args = update.inline_query.query.split(' ')
elif isinstance(update, str):
args = update.split(' ')[1:]
else:
args = None
target_kwargs['args'] = args
if is_async or 'groups' in fargs:
target_kwargs['groups'] = kwargs.get('groups', None)
if is_async or 'groupdict' in fargs:
target_kwargs['groupdict'] = kwargs.get('groupdict', None)
if is_async or 'context' in fargs:
target_kwargs['context'] = kwargs.get('context', None)
handler(self.bot, update, **target_kwargs)
+43 -32
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,45 +17,55 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Document."""
from telegram import TelegramObject
from telegram import PhotoSize, TelegramObject
class Document(TelegramObject):
"""This object represents a Telegram Document.
Attributes:
file_id (str):
thumb (:class:`telegram.PhotoSize`):
file_name (str):
mime_type (str):
file_size (int):
Args:
file_id (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
thumb (Optional[:class:`telegram.PhotoSize`]):
file_name (Optional[str]):
mime_type (Optional[str]):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
thumb=None,
file_name=None,
mime_type=None,
file_size=None):
self.file_id = file_id
self.thumb = thumb
self.file_name = file_name
self.mime_type = mime_type
self.file_size = file_size
**kwargs):
# Required
self.file_id = str(file_id)
# Optionals
self.thumb = kwargs.get('thumb')
self.file_name = kwargs.get('file_name', '')
self.mime_type = str(kwargs.get('mime_type', ''))
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
def de_json(data):
if 'thumb' in data:
from telegram import PhotoSize
thumb = PhotoSize.de_json(data['thumb'])
else:
thumb = None
"""
Args:
data (str):
return Document(file_id=data.get('file_id', None),
thumb=thumb,
file_name=data.get('file_name', None),
mime_type=data.get('mime_type', None),
file_size=data.get('file_size', None))
Returns:
telegram.Document:
"""
if not data:
return None
def to_dict(self):
data = {'file_id': self.file_id}
if self.thumb:
data['thumb'] = self.thumb.to_dict()
if self.file_name:
data['file_name'] = self.file_name
if self.mime_type:
data['mime_type'] = self.mime_type
if self.file_size:
data['file_size'] = self.file_size
return data
data['thumb'] = PhotoSize.de_json(data.get('thumb'))
return Document(**data)
+861 -843
View File
File diff suppressed because it is too large Load Diff
+63 -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
@@ -16,11 +17,67 @@
# 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."""
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):
"""Base class for Telegram errors."""
"""This object represents a Telegram Error."""
@property
def message(self):
'''Returns the first argument used to construct this error.'''
return self.args[0]
def __init__(self, message):
"""
Args:
message (str):
Returns:
"""
super(TelegramError, self).__init__()
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')
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram File."""
from os.path import basename
from telegram import TelegramObject
from telegram.utils.request import download as _download
class File(TelegramObject):
"""This object represents a Telegram File.
Attributes:
file_id (str):
file_size (str):
file_path (str):
Args:
file_id (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
file_size (Optional[int]):
file_path (Optional[str]):
"""
def __init__(self,
file_id,
**kwargs):
# Required
self.file_id = str(file_id)
# Optionals
self.file_size = int(kwargs.get('file_size', 0))
self.file_path = str(kwargs.get('file_path', ''))
@staticmethod
def de_json(data):
"""
Args:
data (str):
Returns:
telegram.File:
"""
if not data:
return None
return File(**data)
def download(self,
custom_path=None):
"""
Args:
custom_path (str):
"""
url = self.file_path
if custom_path:
filename = custom_path
else:
filename = basename(url)
_download(url, filename)
+32 -11
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,24 +17,44 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram ForceReply."""
from telegram import ReplyMarkup
class ForceReply(ReplyMarkup):
"""This object represents a Telegram ForceReply.
Attributes:
force_reply (bool):
selective (bool):
Args:
force_reply (bool):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
selective (Optional[bool]):
"""
def __init__(self,
force_reply=True,
selective=None):
self.force_reply = force_reply
self.selective = selective
**kwargs):
# Required
self.force_reply = bool(force_reply)
# Optionals
self.selective = bool(kwargs.get('selective', False))
@staticmethod
def de_json(data):
return ForceReply(force_reply=data.get('force_reply', None),
selective=data.get('selective', None))
"""
Args:
data (str):
def to_dict(self):
data = {'force_reply': self.force_reply}
if self.selective:
data['selective'] = self.selective
return data
Returns:
telegram.ForceReply:
"""
if not data:
return None
return ForceReply(**data)
+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)
+73 -41
View File
@@ -1,7 +1,9 @@
#!/usr/bin/env python
# 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
@@ -16,6 +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."""
try:
from email.generator import _make_boundary as choose_boundary
@@ -24,19 +27,22 @@ try:
except ImportError:
from mimetools import choose_boundary
from urllib2 import urlopen
import mimetypes
import os
import re
import sys
import imghdr
from .error import TelegramError
from telegram import TelegramError
DEFAULT_MIME_TYPE = 'application/octet-stream'
USER_AGENT = 'Python Telegram Bot' \
' (https://github.com/leandrotoledo/python-telegram-bot)'
' (https://github.com/python-telegram-bot/python-telegram-bot)'
class InputFile(object):
"""This object represents a Telegram InputFile."""
def __init__(self,
data):
self.data = data
@@ -51,31 +57,66 @@ class InputFile(object):
if 'photo' in data:
self.input_name = 'photo'
self.input_file = data.pop('photo')
if 'sticker' in data:
self.input_name = 'sticker'
self.input_file = data.pop('sticker')
if 'video' in data:
self.input_name = 'video'
self.input_file = data.pop('video')
if 'voice' in data:
self.input_name = 'voice'
self.input_file = data.pop('voice')
if 'certificate' in data:
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()
self.filename = os.path.basename(self.input_file.name)
self.mimetype = mimetypes.guess_type(self.filename)[0] or \
DEFAULT_MIME_TYPE
if 'filename' in data:
self.filename = self.data.pop('filename')
elif isinstance(self.input_file, file):
self.filename = os.path.basename(self.input_file.name)
elif from_url:
self.filename = os.path.basename(self.input_file.url)\
.split('?')[0].split('&')[0]
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):
"""
Returns:
str:
"""
return {'User-agent': USER_AGENT,
'Content-type': self.content_type}
@property
def content_type(self):
"""
Returns:
str:
"""
return 'multipart/form-data; boundary=%s' % self.boundary
def to_form(self):
"""
Returns:
str:
"""
form = []
form_boundary = '--' + self.boundary
@@ -102,9 +143,14 @@ class InputFile(object):
form.append('--' + self.boundary + '--')
form.append('')
return self._parse(form)
return InputFile._parse(form)
def _parse(self, form):
@staticmethod
def _parse(form):
"""
Returns:
str:
"""
if sys.version_info > (3,):
# on Python 3 form needs to be byte encoded
encoded_form = []
@@ -122,50 +168,36 @@ class InputFile(object):
"""Check if the content file is an image by analyzing its headers.
Args:
stream:
A str representing the content of a file.
stream (str): A str representing the content of a file.
Returns:
The str mimetype of an image.
str: The str mimetype of an image.
"""
try:
header = stream[:10]
image = imghdr.what(None, stream)
if image:
return 'image/%s' % image
if re.match(b'GIF8', header):
return 'image/gif'
if re.match(b'\x89PNG', header):
return 'image/png'
if re.match(b'\xff\xd8\xff\xe0\x00\x10JFIF', header) or \
re.match(b'\xff\xd8\xff\xe1(.*){2}Exif', header):
return 'image/jpeg'
except IndexError as e:
raise TelegramError(str(e))
raise TelegramError({'message': 'Could not parse file content'})
raise TelegramError('Could not parse file content')
@staticmethod
def is_inputfile(data):
"""Check if the request is a file request
"""Check if the request is a file request.
Args:
data:
A dict of (str, unicode) key/value pairs
data (str): A dict of (str, unicode) key/value pairs
Returns:
bool
"""
if data:
file_types = ['audio', 'document', 'photo', 'video']
file_types = ['audio', 'document', 'photo', 'sticker', 'video',
'voice', 'certificate']
file_type = [i for i in list(data.keys()) if i in file_types]
if file_type:
file_content = data[file_type[0]]
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
+27 -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,23 +17,40 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Location."""
from telegram import TelegramObject
class Location(TelegramObject):
"""This object represents a Telegram Sticker.
Attributes:
longitude (float):
latitude (float):
Args:
longitude (float):
latitude (float):
"""
def __init__(self,
longitude,
latitude):
self.longitude = longitude
self.latitude = latitude
# Required
self.longitude = float(longitude)
self.latitude = float(latitude)
@staticmethod
def de_json(data):
return Location(longitude=data.get('longitude', None),
latitude=data.get('latitude', None))
"""
Args:
data (str):
def to_dict(self):
data = {'longitude': self.longitude,
'latitude': self.latitude}
return data
Returns:
telegram.Location:
"""
if not data:
return None
return Location(**data)
+184 -196
View File
@@ -1,7 +1,9 @@
#!/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 <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,232 +18,218 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Message."""
from telegram import TelegramObject
from datetime import datetime
from time import mktime
from telegram import (Audio, Contact, Document, Chat, Location, PhotoSize,
Sticker, TelegramObject, User, Video, Voice)
class Message(TelegramObject):
"""This object represents a Telegram Message.
Note:
* In Python `from` is a reserved word, use `from_user` instead.
Attributes:
message_id (int):
from_user (:class:`telegram.User`):
date (:class:`datetime.datetime`):
forward_from (:class:`telegram.User`):
forward_date (:class:`datetime.datetime`):
reply_to_message (:class:`telegram.Message`):
text (str):
audio (:class:`telegram.Audio`):
document (:class:`telegram.Document`):
photo (List[:class:`telegram.PhotoSize`]):
sticker (:class:`telegram.Sticker`):
video (:class:`telegram.Video`):
voice (:class:`telegram.Voice`):
caption (str):
contact (:class:`telegram.Contact`):
location (:class:`telegram.Location`):
new_chat_participant (:class:`telegram.User`):
left_chat_participant (:class:`telegram.User`):
new_chat_title (str):
new_chat_photo (List[:class:`telegram.PhotoSize`]):
delete_chat_photo (bool):
group_chat_created (bool):
Args:
message_id (int):
from_user (:class:`telegram.User`):
date (:class:`datetime.datetime`):
chat (:class:`telegram.Chat`):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
forward_from (Optional[:class:`telegram.User`]):
forward_date (Optional[:class:`datetime.datetime`]):
reply_to_message (Optional[:class:`telegram.Message`]):
text (Optional[str]):
audio (Optional[:class:`telegram.Audio`]):
document (Optional[:class:`telegram.Document`]):
photo (Optional[List[:class:`telegram.PhotoSize`]]):
sticker (Optional[:class:`telegram.Sticker`]):
video (Optional[:class:`telegram.Video`]):
voice (Optional[:class:`telegram.Voice`]):
caption (Optional[str]):
contact (Optional[:class:`telegram.Contact`]):
location (Optional[:class:`telegram.Location`]):
new_chat_participant (Optional[:class:`telegram.User`]):
left_chat_participant (Optional[:class:`telegram.User`]):
new_chat_title (Optional[str]):
new_chat_photo (Optional[List[:class:`telegram.PhotoSize`]):
delete_chat_photo (Optional[bool]):
group_chat_created (Optional[bool]):
"""
def __init__(self,
message_id,
from_user,
date,
chat,
forward_from=None,
forward_date=None,
reply_to_message=None,
text=None,
audio=None,
document=None,
photo=None,
sticker=None,
video=None,
caption=None,
contact=None,
location=None,
new_chat_participant=None,
left_chat_participant=None,
new_chat_title=None,
new_chat_photo=None,
delete_chat_photo=None,
group_chat_created=None):
self.message_id = message_id
**kwargs):
# Required
self.message_id = int(message_id)
self.from_user = from_user
self.date = date
self.chat = chat
self.forward_from = forward_from
self.forward_date = forward_date
self.reply_to_message = reply_to_message
self.text = text
self.audio = audio
self.document = document
self.photo = photo
self.sticker = sticker
self.video = video
self.caption = caption
self.contact = contact
self.location = location
self.new_chat_participant = new_chat_participant
self.left_chat_participant = left_chat_participant
self.new_chat_title = new_chat_title
self.new_chat_photo = new_chat_photo
self.delete_chat_photo = delete_chat_photo
self.group_chat_created = group_chat_created
# Optionals
self.forward_from = kwargs.get('forward_from')
self.forward_date = kwargs.get('forward_date')
self.reply_to_message = kwargs.get('reply_to_message')
self.text = kwargs.get('text', '')
self.audio = kwargs.get('audio')
self.document = kwargs.get('document')
self.photo = kwargs.get('photo')
self.sticker = kwargs.get('sticker')
self.video = kwargs.get('video')
self.voice = kwargs.get('voice')
self.caption = kwargs.get('caption', '')
self.contact = kwargs.get('contact')
self.location = kwargs.get('location')
self.new_chat_participant = kwargs.get('new_chat_participant')
self.left_chat_participant = kwargs.get('left_chat_participant')
self.new_chat_title = kwargs.get('new_chat_title', '')
self.new_chat_photo = kwargs.get('new_chat_photo')
self.delete_chat_photo = bool(kwargs.get('delete_chat_photo', False))
self.group_chat_created = bool(kwargs.get('group_chat_created', False))
self.supergroup_chat_created = bool(kwargs.get(
'supergroup_chat_created', False))
self.migrate_to_chat_id = int(kwargs.get('migrate_to_chat_id', 0))
self.migrate_from_chat_id = int(kwargs.get('migrate_from_chat_id', 0))
self.channel_chat_created = bool(kwargs.get('channel_chat_created',
False))
@property
def chat_id(self):
"""int: Short for :attr:`Message.chat.id`"""
return self.chat.id
@staticmethod
def de_json(data):
if 'from' in data: # from is a reserved word, use from_user instead.
from telegram import User
from_user = User.de_json(data['from'])
else:
from_user = None
"""
Args:
data (dict):
if 'date' in data:
date = datetime.fromtimestamp(data['date'])
else:
date = None
Returns:
telegram.Message:
"""
if not data:
return None
if 'chat' in data:
if 'first_name' in data['chat']:
from telegram import User
chat = User.de_json(data['chat'])
if 'title' in data['chat']:
from telegram import GroupChat
chat = GroupChat.de_json(data['chat'])
else:
chat = None
data['from_user'] = User.de_json(data.get('from'))
data['date'] = datetime.fromtimestamp(data['date'])
data['chat'] = Chat.de_json(data.get('chat'))
data['forward_from'] = \
User.de_json(data.get('forward_from'))
data['forward_date'] = \
Message._fromtimestamp(data.get('forward_date'))
data['reply_to_message'] = \
Message.de_json(data.get('reply_to_message'))
data['audio'] = \
Audio.de_json(data.get('audio'))
data['document'] = \
Document.de_json(data.get('document'))
data['photo'] = \
PhotoSize.de_list(data.get('photo'))
data['sticker'] = \
Sticker.de_json(data.get('sticker'))
data['video'] = \
Video.de_json(data.get('video'))
data['voice'] = \
Voice.de_json(data.get('voice'))
data['contact'] = \
Contact.de_json(data.get('contact'))
data['location'] = \
Location.de_json(data.get('location'))
data['new_chat_participant'] = \
User.de_json(data.get('new_chat_participant'))
data['left_chat_participant'] = \
User.de_json(data.get('left_chat_participant'))
data['new_chat_photo'] = \
PhotoSize.de_list(data.get('new_chat_photo'))
if 'forward_from' in data:
from telegram import User
forward_from = User.de_json(data['forward_from'])
else:
forward_from = None
return Message(**data)
if 'forward_date' in data:
forward_date = datetime.fromtimestamp(data['forward_date'])
else:
forward_date = None
if 'reply_to_message' in data:
reply_to_message = Message.de_json(data['reply_to_message'])
else:
reply_to_message = None
if 'audio' in data:
from telegram import Audio
audio = Audio.de_json(data['audio'])
else:
audio = None
if 'document' in data:
from telegram import Document
document = Document.de_json(data['document'])
else:
document = None
if 'photo' in data:
from telegram import PhotoSize
photo = [PhotoSize.de_json(x) for x in data['photo']]
else:
photo = None
if 'sticker' in data:
from telegram import Sticker
sticker = Sticker.de_json(data['sticker'])
else:
sticker = None
if 'video' in data:
from telegram import Video
video = Video.de_json(data['video'])
else:
video = None
if 'contact' in data:
from telegram import Contact
contact = Contact.de_json(data['contact'])
else:
contact = None
if 'location' in data:
from telegram import Location
location = Location.de_json(data['location'])
else:
location = None
if 'new_chat_participant' in data:
from telegram import User
new_chat_participant = User.de_json(data['new_chat_participant'])
else:
new_chat_participant = None
if 'left_chat_participant' in data:
from telegram import User
left_chat_participant = User.de_json(data['left_chat_participant'])
else:
left_chat_participant = None
return Message(message_id=data.get('message_id', None),
from_user=from_user,
date=date,
chat=chat,
forward_from=forward_from,
forward_date=forward_date,
reply_to_message=reply_to_message,
text=data.get('text', ''),
audio=audio,
document=document,
photo=photo,
sticker=sticker,
video=video,
caption=data.get('caption', ''),
contact=contact,
location=location,
new_chat_participant=new_chat_participant,
left_chat_participant=left_chat_participant,
new_chat_title=data.get('new_chat_title', None),
new_chat_photo=data.get('new_chat_photo', None),
delete_chat_photo=data.get('delete_chat_photo', None),
group_chat_created=data.get('group_chat_created', None))
def __getitem__(self, item):
if item in self.__dict__.keys():
return self.__dict__[item]
elif item == 'chat_id':
return self.chat.id
def to_dict(self):
data = {'message_id': self.message_id,
'from': self.from_user.to_dict(),
'chat': self.chat.to_dict()}
try:
# Python 3.3+ supports .timestamp()
data['date'] = int(self.date.timestamp())
"""
Returns:
dict:
"""
data = super(Message, self).to_dict()
if self.forward_date:
data['forward_date'] = int(self.forward_date.timestamp())
except AttributeError:
# _totimestamp() for Python 3 (< 3.3) and Python 2
data['date'] = self._totimestamp(self.date)
if self.forward_date:
data['forward_date'] = self._totimestamp(self.forward_date)
if self.forward_from:
data['forward_from'] = self.forward_from
if self.reply_to_message:
data['reply_to_message'] = self.reply_to_message
if self.text:
data['text'] = self.text
if self.audio:
data['audio'] = self.audio.to_dict()
if self.document:
data['document'] = self.document.to_dict()
# Required
data['from'] = data.pop('from_user', None)
data['date'] = self._totimestamp(self.date)
# Optionals
if self.forward_date:
data['forward_date'] = self._totimestamp(self.forward_date)
if self.photo:
data['photo'] = [p.to_dict() for p in self.photo]
if self.sticker:
data['sticker'] = self.sticker.to_dict()
if self.video:
data['video'] = self.video.to_dict()
if self.caption:
data['caption'] = self.caption
if self.contact:
data['contact'] = self.contact.to_dict()
if self.location:
data['location'] = self.location.to_dict()
if self.new_chat_participant:
data['new_chat_participant'] = self.new_chat_participant
if self.left_chat_participant:
data['left_chat_participant'] = self.left_chat_participant
if self.new_chat_title:
data['new_chat_title'] = self.new_chat_title
if self.new_chat_photo:
data['new_chat_photo'] = self.new_chat_photo
if self.delete_chat_photo:
data['delete_chat_photo'] = self.delete_chat_photo
if self.group_chat_created:
data['group_chat_created'] = self.group_chat_created
data['new_chat_photo'] = [p.to_dict() for p in self.new_chat_photo]
return data
@staticmethod
def _totimestamp(dt):
return int(mktime(dt.timetuple()))
def _fromtimestamp(unixtime):
"""
Args:
unixtime (int):
Returns:
datetime.datetime:
"""
if not unixtime:
return None
return datetime.fromtimestamp(unixtime)
@staticmethod
def _totimestamp(dt_obj):
"""
Args:
dt_obj (:class:`datetime.datetime`):
Returns:
int:
"""
if not dt_obj:
return None
try:
# Python 3.3+
return int(dt_obj.timestamp())
except AttributeError:
# Python 3 (< 3.3) and Python 2
return int(mktime(dt_obj.timetuple()))
+9 -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 Lesser Public License as published by
@@ -16,10 +17,17 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a logging NullHandler."""
import logging
class NullHandler(logging.Handler):
"""This object represents a logging NullHandler."""
def emit(self, record):
"""
Args:
record (str):
"""
pass
+10 -11
View File
@@ -1,7 +1,9 @@
#!/usr/bin/env python
# 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
@@ -16,15 +18,12 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
Message Parse Modes."""
import logging
import unittest
from tests.test_bot import BotTest
if __name__ == '__main__':
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
testsuite = unittest.TestLoader().loadTestsFromTestCase(BotTest)
unittest.TextTestRunner(verbosity=1).run(testsuite)
class ParseMode(object):
"""This object represents a Telegram Message Parse Modes."""
MARKDOWN = 'Markdown'
HTML = 'HTML'
+64 -17
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,32 +17,78 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram PhotoSize."""
from telegram import TelegramObject
class PhotoSize(TelegramObject):
"""This object represents a Telegram PhotoSize.
Attributes:
file_id (str):
width (int):
height (int):
file_size (int):
Args:
file_id (str):
width (int):
height (int):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
file_size (Optional[int]):
"""
def __init__(self,
file_id,
width,
height,
file_size=None):
self.file_id = file_id
self.width = width
self.height = height
self.file_size = file_size
**kwargs):
# Required
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):
return PhotoSize(file_id=data.get('file_id', None),
width=data.get('width', None),
height=data.get('height', None),
file_size=data.get('file_size', None))
"""
Args:
data (str):
def to_dict(self):
data = {'file_id': self.file_id,
'width': self.width,
'height': self.height}
if self.file_size:
data['file_size'] = self.file_size
return data
Returns:
telegram.PhotoSize:
"""
if not data:
return None
return PhotoSize(**data)
@staticmethod
def de_list(data):
"""
Args:
data (list):
Returns:
List<telegram.PhotoSize>:
"""
if not data:
return []
photos = list()
for photo in data:
photos.append(PhotoSize.de_json(photo))
return photos
+33 -11
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,24 +17,45 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
ReplyKeyboardHide."""
from telegram import ReplyMarkup
class ReplyKeyboardHide(ReplyMarkup):
"""This object represents a Telegram ReplyKeyboardHide.
Attributes:
hide_keyboard (bool):
selective (bool):
Args:
hide_keyboard (bool):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
selective (Optional[bool]):
"""
def __init__(self,
hide_keyboard=True,
selective=None):
self.hide_keyboard = hide_keyboard
self.selective = selective
**kwargs):
# Required
self.hide_keyboard = bool(hide_keyboard)
# Optionals
self.selective = bool(kwargs.get('selective', False))
@staticmethod
def de_json(data):
return ReplyKeyboardHide(hide_keyboard=data.get('hide_keyboard', None),
selective=data.get('selective', None))
"""
Args:
data (str):
def to_dict(self):
data = {'hide_keyboard': self.hide_keyboard}
if self.selective:
data['selective'] = self.selective
return data
Returns:
telegram.ReplyKeyboardHide:
"""
if not data:
return None
return ReplyKeyboardHide(**data)
+38 -24
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,38 +17,51 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
ReplyKeyboardMarkup."""
from telegram import ReplyMarkup
class ReplyKeyboardMarkup(ReplyMarkup):
"""This object represents a Telegram ReplyKeyboardMarkup.
Attributes:
keyboard (List[List[str]]):
resize_keyboard (bool):
one_time_keyboard (bool):
selective (bool):
Args:
keyboard (List[List[str]]):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
resize_keyboard (Optional[bool]):
one_time_keyboard (Optional[bool]):
selective (Optional[bool]):
"""
def __init__(self,
keyboard,
resize_keyboard=None,
one_time_keyboard=None,
selective=None):
**kwargs):
# Required
self.keyboard = keyboard
self.resize_keyboard = resize_keyboard
self.one_time_keyboard = one_time_keyboard
self.selective = selective
# Optionals
self.resize_keyboard = bool(kwargs.get('resize_keyboard', False))
self.one_time_keyboard = bool(kwargs.get('one_time_keyboard', False))
self.selective = bool(kwargs.get('selective', False))
@staticmethod
def de_json(data):
return ReplyKeyboardMarkup(keyboard=data.get('keyboard', None),
resize_keyboard=data.get(
'resize_keyboard', None
),
one_time_keyboard=data.get(
'one_time_keyboard', None
),
selective=data.get('selective', None))
"""
Args:
data (str):
def to_dict(self):
data = {'keyboard': self.keyboard}
if self.resize_keyboard:
data['resize_keyboard'] = self.resize_keyboard
if self.one_time_keyboard:
data['one_time_keyboard'] = self.one_time_keyboard
if self.selective:
data['selective'] = self.selective
return data
Returns:
telegram.ReplyKeyboardMarkup:
"""
if not data:
return None
return ReplyKeyboardMarkup(**data)
+8 -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,9 +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 ReplyMarkup Objects."""
from telegram import TelegramObject
class ReplyMarkup(TelegramObject):
pass
"""Base class for Telegram ReplyMarkup Objects"""
@staticmethod
def de_json(data):
pass
+43 -27
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,42 +17,57 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Sticker."""
from telegram import TelegramObject
from telegram import PhotoSize, TelegramObject
class Sticker(TelegramObject):
"""This object represents a Telegram Sticker.
Attributes:
file_id (str):
width (int):
height (int):
thumb (:class:`telegram.PhotoSize`):
file_size (int):
Args:
file_id (str):
width (int):
height (int):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
thumb (Optional[:class:`telegram.PhotoSize`]):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
width,
height,
thumb=None,
file_size=None):
self.file_id = file_id
self.width = width
self.height = height
self.thumb = thumb
self.file_size = file_size
**kwargs):
# Required
self.file_id = str(file_id)
self.width = int(width)
self.height = int(height)
# Optionals
self.thumb = kwargs.get('thumb')
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
def de_json(data):
if 'thumb' in data:
from telegram import PhotoSize
thumb = PhotoSize.de_json(data['thumb'])
else:
thumb = None
"""
Args:
data (str):
return Sticker(file_id=data.get('file_id', None),
width=data.get('width', None),
height=data.get('height', None),
thumb=thumb,
file_size=data.get('file_size', None))
Returns:
telegram.Sticker:
"""
if not data:
return None
def to_dict(self):
data = {'file_id': self.file_id,
'width': self.width,
'height': self.height,
'thumb': self.thumb.to_dict()}
if self.file_size:
data['file_size'] = self.file_size
return data
data['thumb'] = PhotoSize.de_json(data.get('thumb'))
return Sticker(**data)
+42 -17
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,30 +17,54 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Update."""
from telegram import TelegramObject
from telegram import Message, TelegramObject, InlineQuery, ChosenInlineResult
class Update(TelegramObject):
"""This object represents a Telegram Update.
Attributes:
update_id (int):
message (:class:`telegram.Message`):
inline_query (:class:`telegram.InlineQuery`):
chosen_inline_result (:class:`telegram.ChosenInlineResult`):
Args:
update_id (int):
**kwargs: Arbitrary keyword arguments.
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,
message=None):
self.update_id = update_id
self.message = message
**kwargs):
# Required
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):
if 'message' in data:
from telegram import Message
message = Message.de_json(data['message'])
else:
message = None
"""
Args:
data (dict):
return Update(update_id=data.get('update_id', None),
message=message)
Returns:
telegram.Update:
"""
if not data:
return None
def to_dict(self):
data = {'update_id': self.update_id}
if self.message:
data['message'] = self.message.to_dict()
return data
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)
+333
View File
@@ -0,0 +1,333 @@
#!/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."""
import logging
import os
import ssl
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, JobQueue, UpdateQueue)
from telegram.utils.webhookhandler import (WebhookServer, WebhookHandler)
H = NullHandler()
logging.getLogger(__name__).addHandler(H)
class Updater:
"""
This class, which employs the Dispatcher class, provides a frontend to
telegram.Bot to the programmer, so they can focus on coding the bot. It's
purpose is to receive the updates from Telegram and to deliver them to said
dispatcher. It also runs in a separate thread, so the user can interact
with the bot, for example on the command line. The dispatcher supports
handlers for different kinds of data: Updates from Telegram, basic text
commands and even arbitrary types.
The updater can be started as a polling service or, for production, use a
webhook to receive updates. This is achieved using the WebhookServer and
WebhookHandler classes.
Attributes:
Args:
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=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')
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=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 0.0
timeout (Optional[float]): Passed to Bot.getUpdates
network_delay (Optional[float]): Passed to Bot.getUpdates
Returns:
Queue: The update queue that can be filled from the main thread
"""
with self.__lock:
if not self.running:
self.running = True
# Create & start threads
self._init_thread(self.dispatcher.start, "dispatcher")
self._init_thread(self._start_polling, "updater",
poll_interval, timeout, network_delay)
# 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',
port=80,
url_path='',
cert=None,
key=None):
"""
Starts a small http server to listen for updates via webhook. If cert
and key are not provided, the webhook will be started directly on
http://listen:port/url_path, so SSL can be handled by another
application. Else, the webhook will be started on
https://listen:port/url_path
Args:
listen (Optional[str]): IP-Address to listen on
port (Optional[int]): Port the bot should be listening on
url_path (Optional[str]): Path inside url
cert (Optional[str]): Path to the SSL certificate file
key (Optional[str]): Path to the SSL key file
Returns:
Queue: The update queue that can be filled from the main thread
"""
with self.__lock:
if not self.running:
self.running = True
# Create & start threads
self._init_thread(self.dispatcher.start, "dispatcher"),
self._init_thread(self._start_webhook, "updater", listen,
port, url_path, cert, key)
# Return the update queue so the main thread can insert updates
return self.update_queue
def _start_polling(self, poll_interval, timeout, network_delay):
"""
Thread target of thread 'updater'. Runs in background, pulls
updates from Telegram and inserts them in the update queue of the
Dispatcher.
"""
cur_interval = poll_interval
self.logger.info('Updater thread started')
# Remove webhook
self.bot.setWebhook(webhook_url=None)
while self.running:
try:
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
if updates:
for update in updates:
self.update_queue.put(update)
self.last_update_id = updates[-1].update_id + 1
cur_interval = poll_interval
sleep(cur_interval)
@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')
use_ssl = cert is not None and key is not None
url_path = "/%s" % url_path
# Create and start server
self.httpd = WebhookServer((listen, port), WebhookHandler,
self.update_queue, url_path)
if use_ssl:
# Check SSL-Certificate with openssl, if possible
try:
exit_code = subprocess.call(["openssl", "x509", "-text",
"-noout", "-in", cert],
stdout=open(os.devnull, 'wb'),
stderr=subprocess.STDOUT)
except OSError:
exit_code = 0
if exit_code is 0:
try:
self.httpd.socket = ssl.wrap_socket(self.httpd.socket,
certfile=cert,
keyfile=key,
server_side=True)
except ssl.SSLError as error:
self.logger.exception('failed to init SSL socket')
raise TelegramError(str(error))
else:
raise TelegramError('SSL Certificate invalid')
self.httpd.serve_forever(poll_interval=1)
def stop(self):
"""
Stops the polling/webhook thread, the dispatcher and the job queue
"""
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.')
self.httpd.shutdown()
self.httpd = None
def _stop_dispatcher(self):
self.logger.debug("Requesting Dispatcher to stop...")
self.dispatcher.stop()
def _join_async_threads(self):
with dispatcher.async_lock:
threads = list(dispatcher.async_threads)
total = len(threads)
for i, thr in enumerate(threads):
self.logger.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
self.stop()
def idle(self, stop_signals=(SIGINT, SIGTERM, SIGABRT)):
"""
Blocks until one of the signals are received and stops the updater
Args:
stop_signals: Iterable containing signals from the signal module
that should be subscribed to. Updater.stop() will be called on
receiving one of those signals. Defaults to (SIGINT, SIGTERM,
SIGABRT)
"""
for sig in stop_signals:
signal(sig, self.signal_handler)
self.is_idle = True
while self.is_idle:
sleep(1)
+42 -18
View File
@@ -1,7 +1,9 @@
#!/usr/bin/env python
# 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
@@ -16,23 +18,47 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram User."""
from telegram import TelegramObject
class User(TelegramObject):
"""This object represents a Telegram User.
Attributes:
id (int):
first_name (str):
last_name (str):
username (str):
type (str):
Args:
id (int):
first_name (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
type (Optional[str]):
last_name (Optional[str]):
username (Optional[str]):
"""
def __init__(self,
id,
first_name,
last_name=None,
username=None):
self.id = id
**kwargs):
# Required
self.id = int(id)
self.first_name = first_name
self.last_name = last_name
self.username = username
# Optionals
self.type = kwargs.get('type', '')
self.last_name = kwargs.get('last_name', '')
self.username = kwargs.get('username', '')
@property
def name(self):
"""str: """
if self.username:
return '@%s' % self.username
if self.last_name:
@@ -41,16 +67,14 @@ class User(TelegramObject):
@staticmethod
def de_json(data):
return User(id=data.get('id', None),
first_name=data.get('first_name', None),
last_name=data.get('last_name', None),
username=data.get('username', None))
"""
Args:
data (str):
def to_dict(self):
data = {'id': self.id,
'first_name': self.first_name}
if self.last_name:
data['last_name'] = self.last_name
if self.username:
data['username'] = self.username
return data
Returns:
telegram.User:
"""
if not data:
return None
return User(**data)
+40 -19
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,36 +17,56 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
UserProfilePhotos."""
from telegram import TelegramObject
from telegram import PhotoSize, TelegramObject
class UserProfilePhotos(TelegramObject):
"""This object represents a Telegram UserProfilePhotos.
Attributes:
total_count (int):
photos (List[List[:class:`telegram.PhotoSize`]]):
Args:
total_count (int):
photos (List[List[:class:`telegram.PhotoSize`]]):
"""
def __init__(self,
total_count,
photos):
self.total_count = total_count
# Required
self.total_count = int(total_count)
self.photos = photos
@staticmethod
def de_json(data):
if 'photos' in data:
from telegram import PhotoSize
photos = []
for photo in data['photos']:
photos.append([PhotoSize.de_json(x) for x in photo])
else:
photos = None
"""
Args:
data (str):
return UserProfilePhotos(total_count=data.get('total_count', None),
photos=photos)
Returns:
telegram.UserProfilePhotos:
"""
if not data:
return None
data['photos'] = [PhotoSize.de_list(photo) for photo in data['photos']]
return UserProfilePhotos(**data)
def to_dict(self):
data = {}
if self.total_count:
data['total_count'] = self.total_count
if self.photos:
data['photos'] = []
for photo in self.photos:
data['photos'].append([x.to_dict() for x in photo])
"""
Returns:
dict:
"""
data = super(UserProfilePhotos, self).to_dict()
data['photos'] = []
for photo in self.photos:
data['photos'].append([x.to_dict() for x in photo])
return data
View File
+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
+189
View File
@@ -0,0 +1,189 @@
#!/usr/bin/env python
# pylint: disable=no-name-in-module,unused-import
#
# 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 methods to make POST and GET requests"""
import functools
import json
import socket
from ssl import SSLError
try:
# 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, URLError
from urllib2 import HTTPError
from telegram import (InputFile, TelegramError)
from telegram.error import Unauthorized, NetworkError, TimedOut
def _parse(json_data):
"""Try and parse the JSON returned from Telegram and return an empty
dictionary if there is any error.
Args:
url:
urllib.urlopen object
Returns:
A JSON parsed as Python dict with results.
"""
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']
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:
url:
The web location we want to retrieve.
Returns:
A JSON object.
"""
result = urlopen(url).read()
return _parse(result)
@_try_except_req
def post(url,
data,
timeout=None,
network_delay=2.):
"""Request an URL.
Args:
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:
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 = {}
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:
data = json.dumps(data)
request = Request(url,
data=data.encode(),
headers={'Content-Type': 'application/json'})
result = urlopen(request, **urlopen_kwargs).read()
return _parse(result)
@_try_except_req
def download(url,
filename):
"""Download a file by its URL.
Args:
url:
The web location we want to retrieve.
filename:
The filename wihtin the path to download the file.
"""
urlretrieve(url, filename)
@@ -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,23 +17,22 @@
# 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"""
from telegram import TelegramObject
try:
type(basestring)
except NameError:
basestring = str
class GroupChat(TelegramObject):
def __init__(self,
id,
title):
self.id = id
self.title = title
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.
@staticmethod
def de_json(data):
return GroupChat(id=data.get('id', None),
title=data.get('title', None))
def to_dict(self):
data = {'id': self.id,
'title': self.title}
return data
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")
+109
View File
@@ -0,0 +1,109 @@
import logging
from telegram import Update, NullHandler
from future.utils import bytes_to_native_str
from threading import Lock
import json
try:
import BaseHTTPServer
except ImportError:
import http.server as BaseHTTPServer
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):
super(WebhookServer, self).__init__(server_address,
RequestHandlerClass)
self.logger = logging.getLogger(__name__)
self.update_queue = update_queue
self.webhook_path = webhook_path
self.is_running = False
self.server_lock = Lock()
self.shutdown_lock = Lock()
def serve_forever(self, poll_interval=0.5):
with self.server_lock:
self.is_running = True
self.logger.info("Webhook Server started.")
super(WebhookServer, self).serve_forever(poll_interval)
self.logger.info("Webhook Server stopped.")
def shutdown(self):
with self.shutdown_lock:
if not self.is_running:
self.logger.warn("Webhook Server already stopped.")
return
else:
super(WebhookServer, self).shutdown()
self.is_running = False
# WebhookHandler, process webhook calls
# Based on: https://github.com/eternnoir/pyTelegramBotAPI/blob/master/
# examples/webhook_examples/webhook_cpython_echo_bot.py
class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler, object):
server_version = "WebhookHandler/1.0"
def __init__(self, request, client_address, server):
self.logger = logging.getLogger(__name__)
super(WebhookHandler, self).__init__(request, client_address, server)
def do_HEAD(self):
self.send_response(200)
self.end_headers()
def do_GET(self):
self.send_response(200)
self.end_headers()
def do_POST(self):
self.logger.debug("Webhook triggered")
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()
self.logger.debug("Webhook received data: " + json_string)
update = Update.de_json(json.loads(json_string))
self.logger.info("Received Update with ID %d on Webhook" %
update.update_id)
self.server.update_queue.put(update)
def _validate_post(self):
if not (self.path == self.server.webhook_path and
'content-type' in self.headers and
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
+49 -36
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,52 +17,64 @@
# 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."""
from telegram import TelegramObject
from telegram import PhotoSize, TelegramObject
class Video(TelegramObject):
"""This object represents a Telegram Video.
Attributes:
file_id (str):
width (int):
height (int):
duration (int):
thumb (:class:`telegram.PhotoSize`):
mime_type (str):
file_size (int):
Args:
file_id (str):
width (int):
height (int):
duration (int):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
thumb (Optional[:class:`telegram.PhotoSize`]):
mime_type (Optional[str]):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
width,
height,
duration,
thumb=None,
mime_type=None,
file_size=None):
self.file_id = file_id
self.width = width
self.height = height
self.duration = duration
self.thumb = thumb
self.mime_type = mime_type
self.file_size = file_size
**kwargs):
# Required
self.file_id = str(file_id)
self.width = int(width)
self.height = int(height)
self.duration = int(duration)
# Optionals
self.thumb = kwargs.get('thumb')
self.mime_type = str(kwargs.get('mime_type', ''))
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
def de_json(data):
if 'thumb' in data:
from telegram import PhotoSize
thumb = PhotoSize.de_json(data['thumb'])
else:
thumb = None
"""
Args:
data (str):
return Video(file_id=data.get('file_id', None),
width=data.get('width', None),
height=data.get('height', None),
duration=data.get('duration', None),
thumb=thumb,
mime_type=data.get('mime_type', None),
file_size=data.get('file_size', None))
Returns:
telegram.Video:
"""
if not data:
return None
def to_dict(self):
data = {'file_id': self.file_id,
'width': self.width,
'height': self.height,
'duration': self.duration}
if self.thumb:
data['thumb'] = self.thumb.to_dict()
if self.mime_type:
data['mime_type'] = self.mime_type
if self.file_size:
data['file_size'] = self.file_size
return data
data['thumb'] = PhotoSize.de_json(data.get('thumb'))
return Video(**data)
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Voice."""
from telegram import TelegramObject
class Voice(TelegramObject):
"""This object represents a Telegram Voice.
Attributes:
file_id (str):
duration (int):
mime_type (str):
file_size (int):
Args:
file_id (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
duration (Optional[int]):
mime_type (Optional[str]):
file_size (Optional[int]):
"""
def __init__(self,
file_id,
**kwargs):
# Required
self.file_id = str(file_id)
# Optionals
self.duration = int(kwargs.get('duration', 0))
self.mime_type = str(kwargs.get('mime_type', ''))
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
def de_json(data):
"""
Args:
data (str):
Returns:
telegram.Voice:
"""
if not data:
return None
return Voice(**data)
+90
View File
@@ -0,0 +1,90 @@
#!/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 a Base class for tests"""
import os
import sys
import signal
import traceback
from nose.tools import make_decorator
sys.path.append('.')
import json
import telegram
class BaseTest(object):
"""This object represents a Base test and its sets of functions."""
def __init__(self, *args, **kwargs):
super(BaseTest, self).__init__(*args, **kwargs)
bot = telegram.Bot(os.environ.get('TOKEN'))
chat_id = os.environ.get('CHAT_ID')
self._bot = bot
self._chat_id = chat_id
@staticmethod
def is_json(string):
try:
json.loads(string)
except ValueError:
return False
return True
@staticmethod
def is_dict(dictionary):
if isinstance(dictionary, dict):
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

+230
View File
@@ -0,0 +1,230 @@
#!/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 Audio"""
import os
import unittest
import sys
from flaky import flaky
sys.path.append('.')
import telegram
from tests.base import BaseTest, timeout
class AudioTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram Audio."""
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'
self.mime_type = 'audio/mpeg'
self.file_size = 28232
self.json_dict = {
'file_id': self.audio_file_id,
'duration': self.duration,
'performer': self.performer,
'title': self.title,
'mime_type': self.mime_type,
'file_size': self.file_size
}
@flaky(3, 1)
@timeout(10)
def test_send_audio_required_args_only(self):
message = self._bot.sendAudio(self._chat_id,
self.audio_file)
audio = message.audio
self.assertTrue(isinstance(audio.file_id, str))
self.assertNotEqual(audio.file_id, '')
# 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):
message = self._bot.sendAudio(self._chat_id,
self.audio_file,
duration=self.duration,
performer=self.performer,
title=self.title,
mime_type=self.mime_type,
file_size=self.file_size)
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_mp3_file(self):
message = self._bot.sendAudio(chat_id=self._chat_id,
audio=self.audio_file,
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_mp3_file_custom_filename(self):
message = self._bot.sendAudio(chat_id=self._chat_id,
audio=self.audio_file,
duration=self.duration,
performer=self.performer,
title=self.title,
filename='telegram_custom.mp3')
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_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,
performer=self.performer,
title=self.title)
audio = message.audio
self.assertEqual(audio.file_id, self.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)
def test_audio_de_json(self):
audio = telegram.Audio.de_json(self.json_dict)
self.assertEqual(audio.file_id, self.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)
def test_audio_to_json(self):
audio = telegram.Audio.de_json(self.json_dict)
self.assertTrue(self.is_json(audio.to_json()))
def test_audio_to_dict(self):
audio = telegram.Audio.de_json(self.json_dict)
self.assertTrue(self.is_dict(audio.to_dict()))
self.assertEqual(audio['file_id'], self.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_error_send_audio_empty_file(self):
json_dict = self.json_dict
del(json_dict['file_id'])
json_dict['audio'] = open(os.devnull, 'rb')
self.assertRaises(telegram.TelegramError,
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):
json_dict = self.json_dict
del(json_dict['file_id'])
json_dict['audio'] = ''
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendAudio(chat_id=self._chat_id,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_audio_without_required_args(self):
json_dict = self.json_dict
del(json_dict['file_id'])
del(json_dict['duration'])
self.assertRaises(TypeError,
lambda: self._bot.sendAudio(chat_id=self._chat_id,
**json_dict))
if __name__ == '__main__':
unittest.main()
+121 -108
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
@@ -17,143 +18,155 @@
# 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 Bot"""
import os
import telegram
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, timeout
class BotTest(unittest.TestCase):
def setUp(self):
bot = telegram.Bot(token=os.environ.get('TOKEN'))
self._bot = bot
print('Testing the Bot API class')
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.assertEqual(120405045, bot.id)
self.assertEqual('Toledo\'s Palace Bot', bot.first_name)
self.assertEqual(None, bot.last_name)
self.assertEqual('ToledosPalaceBot', bot.username)
self.assertTrue(self.is_json(bot.to_json()))
self.assertEqual(bot.id, 133505823)
self.assertEqual(bot.first_name, 'PythonTelegramBot')
self.assertEqual(bot.last_name, '')
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=12173560,
message = self._bot.sendMessage(chat_id=self._chat_id,
text='Моё судно на воздушной подушке полно угрей')
self.assertEqual(u'Моё судно на воздушной подушке полно угрей', message.text)
self.assertIsInstance(message.date, datetime)
self.assertTrue(self.is_json(message.to_json()))
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()
self.assertIsInstance(updates[0], telegram.Update)
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=12173560,
from_chat_id=12173560,
message_id=138)
self.assertEqual('Oi', message.text)
self.assertEqual('leandrotoledo', message.forward_from.username)
self.assertIsInstance(message.forward_date, datetime)
message = self._bot.forwardMessage(chat_id=self._chat_id,
from_chat_id=self._chat_id,
message_id=2398)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.text, 'teste')
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/telegram.png', 'rb'),
message = self._bot.sendPhoto(photo=open('tests/data/telegram.png', 'rb'),
caption='testSendPhoto',
chat_id=12173560)
self.assertEqual(1451, message.photo[0].file_size)
self.assertEqual('testSendPhoto', message.caption)
chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
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=str('AgADAQADr6cxGzU8LQe6q0dMJD2rHYkP2ykABFymiQqJgjxRGGMAAgI'),
chat_id=12173560)
self.assertEqual('AgADAQADr6cxGzU8LQe6q0dMJD2rHYkP2ykABFymiQqJgjxRGGMAAgI', message.photo[0].file_id)
message = self._bot.sendPhoto(photo='AgADAQADyKcxGx8j9Qdp6d-gpUsw4Gja1i8ABEVJsVqQk8LfJ3wAAgI',
chat_id=self._chat_id)
def testSendURLPhoto(self):
'''Test the telegram.Bot sendPhoto method'''
print('Testing sendPhoto - URL')
message = self._bot.sendPhoto(photo=str('http://dummyimage.com/600x400/000/fff.jpg&text=telegram'),
chat_id=12173560)
self.assertEqual(822, message.photo[0].file_size)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.photo[0].file_id, 'AgADAQADyKcxGx8j9Qdp6d-gpUsw4Gja1i8ABEVJsVqQk8LfJ3wAAgI')
def testSendAudio(self):
'''Test the telegram.Bot sendAudio method'''
print('Testing sendAudio - File')
message = self._bot.sendAudio(audio=open('tests/telegram.ogg', 'rb'),
chat_id=12173560)
self.assertEqual(9199, message.audio.file_size)
@flaky(3, 1)
@timeout(10)
def testSendJPGURLPhoto(self):
message = self._bot.sendPhoto(photo='http://dummyimage.com/600x400/000/fff.jpg&text=telegram',
chat_id=self._chat_id)
def testResendAudio(self):
'''Test the telegram.Bot sendAudio method'''
print('Testing sendAudio - Resend')
message = self._bot.sendAudio(audio=str('AwADAQADIQEAAvjAuQABSAXg_GhkhZcC'),
chat_id=12173560)
self.assertEqual('AwADAQADIQEAAvjAuQABSAXg_GhkhZcC', message.audio.file_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.photo[0].file_size, 822)
def testSendDocument(self):
'''Test the telegram.Bot sendDocument method'''
print('Testing sendDocument - File')
message = self._bot.sendDocument(document=open('tests/telegram.png', 'rb'),
chat_id=12173560)
self.assertEqual(12948, message.document.file_size)
@flaky(3, 1)
@timeout(10)
def testSendPNGURLPhoto(self):
message = self._bot.sendPhoto(photo='http://dummyimage.com/600x400/000/fff.png&text=telegram',
chat_id=self._chat_id)
def testResendDocument(self):
'''Test the telegram.Bot sendDocument method'''
print('Testing sendDocument - Resend')
message = self._bot.sendDocument(document=str('BQADAQADHAADNTwtBxZxUGKyxYbYAg'),
chat_id=12173560)
self.assertEqual('BQADAQADHAADNTwtBxZxUGKyxYbYAg', message.document.file_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.photo[0].file_size, 684)
def testSendVideo(self):
'''Test the telegram.Bot sendVideo method'''
print('Testing sendVideo - File')
message = self._bot.sendVideo(video=open('tests/telegram.mp4', 'rb'),
caption='testSendVideo',
chat_id=12173560)
self.assertEqual(326534, message.video.file_size)
self.assertEqual('testSendVideo', message.caption)
@flaky(3, 1)
@timeout(10)
def testSendGIFURLPhoto(self):
message = self._bot.sendPhoto(photo='http://dummyimage.com/600x400/000/fff.gif&text=telegram',
chat_id=self._chat_id)
def testResendVideo(self):
'''Test the telegram.Bot sendVideo method'''
print('Testing sendVideo - Resend')
message = self._bot.sendVideo(video=str('BAADAQADIgEAAvjAuQABOuTB937fPTgC'),
chat_id=12173560)
self.assertEqual(4, message.video.duration)
def testResendSticker(self):
'''Test the telegram.Bot sendSticker method'''
print('Testing sendSticker - Resend')
message = self._bot.sendSticker(sticker=str('BQADAQADHAADyIsGAAFZfq1bphjqlgI'),
chat_id=12173560)
self.assertEqual(39518, message.sticker.file_size)
def testSendLocation(self):
'''Test the telegram.Bot sendLocation method'''
print('Testing sendLocation')
message = self._bot.sendLocation(latitude=-23.558873,
longitude=-46.659732,
chat_id=12173560)
self.assertEqual(-23.558873, message.location.latitude)
self.assertEqual(-46.659732, message.location.longitude)
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=12173560)
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=12173560)
self.assertEqual(6547, upf.photos[0][0].file_size)
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()
+71
View File
@@ -0,0 +1,71 @@
#!/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 Chat"""
import os
import unittest
import sys
sys.path.append('.')
import telegram
from tests.base import BaseTest
class ChatTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram Chat."""
def setUp(self):
self.id = -28767330
self.title = 'ToledosPalaceBot - Group'
self.type = 'group'
self.json_dict = {
'id': self.id,
'title': self.title,
'type': self.type
}
def test_group_chat_de_json_empty_json(self):
group_chat = telegram.Chat.de_json({})
self.assertEqual(group_chat, None)
def test_group_chat_de_json(self):
group_chat = telegram.Chat.de_json(self.json_dict)
self.assertEqual(group_chat.id, self.id)
self.assertEqual(group_chat.title, self.title)
self.assertEqual(group_chat.type, self.type)
def test_group_chat_to_json(self):
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):
group_chat = telegram.Chat.de_json(self.json_dict)
self.assertTrue(self.is_dict(group_chat.to_dict()))
self.assertEqual(group_chat['id'], self.id)
self.assertEqual(group_chat['title'], self.title)
self.assertEqual(group_chat['type'], self.type)
if __name__ == '__main__':
unittest.main()
+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()
+70
View File
@@ -0,0 +1,70 @@
#!/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 Contact"""
import os
import unittest
import sys
sys.path.append('.')
import telegram
from tests.base import BaseTest
class ContactTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram Contact."""
def setUp(self):
self.phone_number = '+11234567890'
self.first_name = 'Leandro Toledo'
self.last_name = ''
self.user_id = 0
self.json_dict = {
'phone_number': self.phone_number,
'first_name': self.first_name,
'last_name': self.last_name,
'user_id': self.user_id
}
def test_contact_de_json(self):
contact = telegram.Contact.de_json(self.json_dict)
self.assertEqual(contact.phone_number, self.phone_number)
self.assertEqual(contact.first_name, self.first_name)
self.assertEqual(contact.last_name, self.last_name)
self.assertEqual(contact.user_id, self.user_id)
def test_contact_to_json(self):
contact = telegram.Contact.de_json(self.json_dict)
self.assertTrue(self.is_json(contact.to_json()))
def test_contact_to_dict(self):
contact = telegram.Contact.de_json(self.json_dict)
self.assertTrue(self.is_dict(contact.to_dict()))
self.assertEqual(contact['phone_number'], self.phone_number)
self.assertEqual(contact['first_name'], self.first_name)
self.assertEqual(contact['last_name'], self.last_name)
self.assertEqual(contact['user_id'], self.user_id)
if __name__ == '__main__':
unittest.main()
+175
View File
@@ -0,0 +1,175 @@
#!/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 Document"""
import os
import unittest
import sys
from flaky import flaky
sys.path.append('.')
import telegram
from tests.base import BaseTest, timeout
class DocumentTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram Document."""
def setUp(self):
self.document_file = open('tests/data/telegram.png', 'rb')
self.document_file_id = 'BQADAQADpAADHyP1B04ipZxJTe2BAg'
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',
'file_size': 2364}
self.file_name = 'telegram.png'
self.mime_type = 'image/png'
self.file_size = 12948
self.json_dict = {
'file_id': self.document_file_id,
'thumb': self.thumb,
'file_name': self.file_name,
'mime_type': self.mime_type,
'file_size': self.file_size
}
@flaky(3, 1)
@timeout(10)
def test_send_document_png_file(self):
message = self._bot.sendDocument(self._chat_id,
self.document_file)
document = message.document
self.assertTrue(isinstance(document.file_id, str))
self.assertNotEqual(document.file_id, '')
self.assertTrue(isinstance(document.thumb, telegram.PhotoSize))
self.assertEqual(document.file_name, self.file_name)
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):
message = self._bot.sendDocument(self._chat_id,
self.document_file,
filename='telegram_custom.png')
document = message.document
self.assertTrue(isinstance(document.file_id, str))
self.assertNotEqual(document.file_id, '')
self.assertTrue(isinstance(document.thumb, telegram.PhotoSize))
self.assertEqual(document.file_name, 'telegram_custom.png')
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):
message = self._bot.sendDocument(self._chat_id,
self.document_file_url)
document = message.document
self.assertTrue(isinstance(document.file_id, str))
self.assertNotEqual(document.file_id, '')
self.assertTrue(isinstance(document.thumb, telegram.PhotoSize))
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):
message = self._bot.sendDocument(chat_id=self._chat_id,
document=self.document_file_id)
document = message.document
self.assertEqual(document.file_id, self.document_file_id)
self.assertTrue(isinstance(document.thumb, telegram.PhotoSize))
self.assertEqual(document.file_name, self.file_name)
self.assertEqual(document.mime_type, self.mime_type)
def test_document_de_json(self):
document = telegram.Document.de_json(self.json_dict)
self.assertEqual(document.file_id, self.document_file_id)
self.assertTrue(isinstance(document.thumb, telegram.PhotoSize))
self.assertEqual(document.file_name, self.file_name)
self.assertEqual(document.mime_type, self.mime_type)
self.assertEqual(document.file_size, self.file_size)
def test_document_to_json(self):
document = telegram.Document.de_json(self.json_dict)
self.assertTrue(self.is_json(document.to_json()))
def test_document_to_dict(self):
document = telegram.Document.de_json(self.json_dict)
self.assertTrue(self.is_dict(document.to_dict()))
self.assertEqual(document['file_id'], self.document_file_id)
self.assertTrue(isinstance(document['thumb'], telegram.PhotoSize))
self.assertEqual(document['file_name'], self.file_name)
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):
json_dict = self.json_dict
del(json_dict['file_id'])
json_dict['document'] = open(os.devnull, 'rb')
self.assertRaises(telegram.TelegramError,
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):
json_dict = self.json_dict
del(json_dict['file_id'])
json_dict['document'] = ''
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendDocument(chat_id=self._chat_id,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_document_without_required_args(self):
json_dict = self.json_dict
del(json_dict['file_id'])
self.assertRaises(TypeError,
lambda: self._bot.sendDocument(chat_id=self._chat_id,
**json_dict))
if __name__ == '__main__':
unittest.main()
+42
View File
@@ -0,0 +1,42 @@
#!/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 Emoji"""
import os
import unittest
import sys
sys.path.append('.')
import telegram
from telegram.emoji import Emoji
from tests.base import BaseTest
class EmojiTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram Emoji."""
def test_emoji(self):
for attr in dir(Emoji):
if attr[0] != '_': # TODO: dirty way to filter out functions
self.assertTrue(type(getattr(Emoji, attr)) is str)
if __name__ == '__main__':
unittest.main()
+143
View File
@@ -0,0 +1,143 @@
#!/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 File"""
import os
import unittest
import sys
sys.path.append('.')
import telegram
from tests.base import BaseTest
class FileTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram File."""
def setUp(self):
self.audio_file_id = 'BQADAQADDwADHyP1B6PSPq2HjX8kAg'
self.document_file_id = 'BQADAQADpAADHyP1B04ipZxJTe2BAg'
self.sticker_file_id = 'BQADAQADHAADyIsGAAFZfq1bphjqlgI'
self.video_file_id = 'BAADAQADXwADHyP1BwJFTcmY2RYCAg'
self.voice_file_id = 'AwADAQADTgADHyP1B_mbw34svXPHAg'
self.json_dict = {
'file_id': self.audio_file_id,
'file_path': 'https://api.telegram.org/file/bot133505823:AAHZFMHno3mzVLErU5b5jJvaeG--qUyLyG0/document/file_3',
'file_size': 28232
}
def test_get_and_download_file_audio(self):
newFile = self._bot.getFile(self.audio_file_id)
self.assertEqual(newFile.file_size, 28232)
self.assertEqual(newFile.file_id, self.audio_file_id)
self.assertTrue(newFile.file_path.startswith('https://'))
newFile.download('telegram.mp3')
self.assertTrue(os.path.isfile('telegram.mp3'))
def test_get_and_download_file_document(self):
newFile = self._bot.getFile(self.document_file_id)
self.assertEqual(newFile.file_size, 12948)
self.assertEqual(newFile.file_id, self.document_file_id)
self.assertTrue(newFile.file_path.startswith('https://'))
newFile.download('telegram.png')
self.assertTrue(os.path.isfile('telegram.png'))
def test_get_and_download_file_sticker(self):
newFile = self._bot.getFile(self.sticker_file_id)
self.assertEqual(newFile.file_size, 39518)
self.assertEqual(newFile.file_id, self.sticker_file_id)
self.assertTrue(newFile.file_path.startswith('https://'))
newFile.download('telegram.webp')
self.assertTrue(os.path.isfile('telegram.webp'))
def test_get_and_download_file_video(self):
newFile = self._bot.getFile(self.video_file_id)
self.assertEqual(newFile.file_size, 326534)
self.assertEqual(newFile.file_id, self.video_file_id)
self.assertTrue(newFile.file_path.startswith('https://'))
newFile.download('telegram.mp4')
self.assertTrue(os.path.isfile('telegram.mp4'))
def test_get_and_download_file_voice(self):
newFile = self._bot.getFile(self.voice_file_id)
self.assertEqual(newFile.file_size, 9199)
self.assertEqual(newFile.file_id, self.voice_file_id)
self.assertTrue(newFile.file_path.startswith('https://'))
newFile.download('telegram.ogg')
self.assertTrue(os.path.isfile('telegram.ogg'))
def test_file_de_json(self):
newFile = telegram.File.de_json(self.json_dict)
self.assertEqual(newFile.file_id, self.json_dict['file_id'])
self.assertEqual(newFile.file_path, self.json_dict['file_path'])
self.assertEqual(newFile.file_size, self.json_dict['file_size'])
def test_file_to_json(self):
newFile = telegram.File.de_json(self.json_dict)
self.assertTrue(self.is_json(newFile.to_json()))
def test_file_to_dict(self):
newFile = telegram.File.de_json(self.json_dict)
self.assertTrue(self.is_dict(newFile.to_dict()))
self.assertEqual(newFile['file_id'], self.json_dict['file_id'])
self.assertEqual(newFile['file_path'], self.json_dict['file_path'])
self.assertEqual(newFile['file_size'], self.json_dict['file_size'])
def test_error_get_empty_file_id(self):
json_dict = self.json_dict
json_dict['file_id'] = ''
del(json_dict['file_path'])
del(json_dict['file_size'])
self.assertRaises(telegram.TelegramError,
lambda: self._bot.getFile(**json_dict))
def test_error_file_without_required_args(self):
json_dict = self.json_dict
del(json_dict['file_id'])
del(json_dict['file_path'])
del(json_dict['file_size'])
self.assertRaises(TypeError,
lambda: self._bot.getFile(**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 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()
+101
View File
@@ -0,0 +1,101 @@
#!/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 Location"""
import os
import unittest
import sys
sys.path.append('.')
import telegram
from tests.base import BaseTest
class LocationTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram Location."""
def setUp(self):
self.latitude = -23.691288
self.longitude = -46.788279
self.json_dict = {
'latitude': self.latitude,
'longitude': self.longitude
}
def test_send_location_implicit_args(self):
message = self._bot.sendLocation(self._chat_id,
self.latitude,
self.longitude)
location = message.location
self.assertEqual(location.latitude, self.latitude)
self.assertEqual(location.longitude, self.longitude)
def test_send_location_explicit_args(self):
message = self._bot.sendLocation(chat_id=self._chat_id,
latitude=self.latitude,
longitude=self.longitude)
location = message.location
self.assertEqual(location.latitude, self.latitude)
self.assertEqual(location.longitude, self.longitude)
def test_location_de_json(self):
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):
location = telegram.Location.de_json(self.json_dict)
self.assertTrue(self.is_json(location.to_json()))
def test_location_to_dict(self):
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):
json_dict = self.json_dict
json_dict['latitude'] = ''
json_dict['longitude'] = ''
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendLocation(chat_id=self._chat_id,
**json_dict))
def test_error_location_without_required_args(self):
json_dict = self.json_dict
del(json_dict['latitude'])
del(json_dict['longitude'])
self.assertRaises(TypeError,
lambda: self._bot.sendLocation(chat_id=self._chat_id,
**json_dict))
if __name__ == '__main__':
unittest.main()
+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()
+132
View File
@@ -0,0 +1,132 @@
#!/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 Sticker"""
import os
import unittest
import sys
from flaky import flaky
sys.path.append('.')
import telegram
from tests.base import BaseTest, timeout
class StickerTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram Sticker."""
def setUp(self):
self.sticker_file_id = 'BQADAQADHAADyIsGAAFZfq1bphjqlgI'
self.width = 510
self.height = 512
self.thumb = {'width': 90,
'height': 90,
'file_id': 'BQADAQADoQADHyP1B0mzJMVyzcB0Ag',
'file_size': 2364}
self.file_size = 39518
self.json_dict = {
'file_id': self.sticker_file_id,
'width': self.width,
'height': self.height,
'thumb': self.thumb,
'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):
message = self._bot.sendSticker(chat_id=self._chat_id,
sticker=self.sticker_file_id)
sticker = message.sticker
self.assertEqual(sticker.file_id, self.sticker_file_id)
self.assertEqual(sticker.width, self.width)
self.assertEqual(sticker.height, self.height)
self.assertTrue(isinstance(sticker.thumb, telegram.PhotoSize))
self.assertEqual(sticker.file_size, self.file_size)
def test_sticker_de_json(self):
sticker = telegram.Sticker.de_json(self.json_dict)
self.assertEqual(sticker.file_id, self.sticker_file_id)
self.assertEqual(sticker.width, self.width)
self.assertEqual(sticker.height, self.height)
self.assertTrue(isinstance(sticker.thumb, telegram.PhotoSize))
self.assertEqual(sticker.file_size, self.file_size)
def test_sticker_to_json(self):
sticker = telegram.Sticker.de_json(self.json_dict)
self.assertTrue(self.is_json(sticker.to_json()))
def test_sticker_to_dict(self):
sticker = telegram.Sticker.de_json(self.json_dict)
self.assertEqual(sticker['file_id'], self.sticker_file_id)
self.assertEqual(sticker['width'], self.width)
self.assertEqual(sticker['height'], self.height)
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):
json_dict = self.json_dict
del(json_dict['file_id'])
json_dict['sticker'] = open(os.devnull, 'rb')
self.assertRaises(telegram.TelegramError,
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):
json_dict = self.json_dict
del(json_dict['file_id'])
json_dict['sticker'] = ''
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendSticker(chat_id=self._chat_id,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_sticker_without_required_args(self):
json_dict = self.json_dict
del(json_dict['file_id'])
self.assertRaises(TypeError,
lambda: self._bot.sendSticker(chat_id=self._chat_id,
**json_dict))
if __name__ == '__main__':
unittest.main()
+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 Update"""
import os
import unittest
import sys
sys.path.append('.')
import telegram
from tests.base import BaseTest
class UpdateTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram Update."""
def setUp(self):
self.update_id = 868573637
self.message = {'message_id': 319,
'from': {'id': 12173560,
'first_name': "Leandro",
'last_name': "S.",
'username': "leandrotoledo"},
'chat': {'id': 12173560,
'type': 'private',
'first_name': "Leandro",
'last_name': "S.",
'username': "leandrotoledo"},
'date': 1441644592,
'text': "Update Test"}
self.json_dict = {
'update_id': self.update_id,
'message': self.message
}
def test_update_de_json(self):
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):
update = telegram.Update.de_json(self.json_dict)
self.assertTrue(self.is_json(update.to_json()))
def test_update_to_dict(self):
update = telegram.Update.de_json(self.json_dict)
self.assertTrue(self.is_dict(update.to_dict()))
self.assertEqual(update['update_id'], self.update_id)
self.assertTrue(isinstance(update['message'], telegram.Message))
if __name__ == '__main__':
unittest.main()

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