1
0
mirror of https://github.com/eternnoir/pyTelegramBotAPI.git synced 2023-08-10 21:12:57 +03:00

Compare commits

...

257 Commits
0.2.0 ... 1.4.2

Author SHA1 Message Date
ddc1a4d66f Version Update.
Change log:
- Add disable_notification parameter.
- Added setters for message/inline/chosen-inline handlers.
2016-02-27 11:47:01 +08:00
3d9e012c40 Merge pull request #120 from eternnoir/feature-disable_notification
Feature disable notification
2016-02-27 11:45:23 +08:00
9b9c0287ec Add test case. 2016-02-27 11:36:53 +08:00
292191038f Add disable_notification 2016-02-27 11:17:35 +08:00
f0e157213c Merge pull request #119 from Kondra007/patch-4
Update README.md
2016-02-26 20:45:54 +08:00
18330275bd Corrected author of Proxy Bot 2016-02-26 15:34:14 +03:00
d173681a7c Update README.md 2016-02-25 20:49:45 +08:00
0830f2a0f9 Update README.md 2016-02-23 21:16:35 +08:00
45d6f8980c Merge pull request #117 from Dejust/add-handler-setters
Added setters for message/inline/chosen-inline handlers.
2016-02-21 10:56:22 +08:00
28417d18af Added ability to set message handler, inline handler, chosen inline handler with method calling 2016-02-20 17:51:14 +05:00
e0ae119512 Merge pull request #116 from idlesign/patch-1
answer_inline_query() made POST
2016-02-15 16:19:55 +08:00
e851f37712 answer_inline_query made POST
The default GET produces `414 Request-URI Too Large` response.
2016-02-14 00:08:37 +05:00
40cf8d6903 Update README.md 2016-02-14 02:00:00 +08:00
e507463741 Update README.md 2016-02-14 01:59:31 +08:00
3a055734f9 Update README.md 2016-02-14 01:52:46 +08:00
16ec573c9e Add more example. 2016-02-11 21:42:07 +08:00
2cd19a1a39 Merge pull request #113 from ratijas/master
__retrieve_updates split into process_new_updates
2016-02-08 23:09:15 +08:00
5f9cf881e6 __retrieve_updates split into process_new_updates 2016-02-07 23:45:54 +08:00
baacafe3c0 Update examples. 2016-02-01 22:50:47 +08:00
734767177d Merge pull request #110 from Kondra007/patch-4
Updated README
2016-01-25 09:50:18 +08:00
1e7f71029c Updated README
Added "supergroup" to message.chat examples.  
via https://core.telegram.org/bots/api-changelog#november-2015
2016-01-24 20:51:52 +03:00
02c22c990b Add chosen_inline_handler doc. 2016-01-19 13:56:44 +08:00
00d6481818 Version Update.
Change log:
- All Message's attributes default to None. Don't need hasattr to check
  attribute.
- Inline mode support.
- Add InlineQueryResultArticle InlineQueryResultPhoto
  InlineQueryResultGif InlineQueryResultMpeg4Gif InlineQueryResultVideo
- Add new method answer_inline_query
- Add new inline_handler
2016-01-06 21:26:28 +08:00
a74ed6e3ab Update some comment. 2016-01-06 16:59:42 +08:00
38eb2ba833 Add comment. 2016-01-06 15:53:35 +08:00
93dc1cd92e Add comment. 2016-01-06 14:31:21 +08:00
a7469e6b14 Merge branch 'master' of github.com:eternnoir/pyTelegramBotAPI 2016-01-05 22:25:02 +08:00
b8f251140d Fix thumb_url must have value. 2016-01-05 22:23:00 +08:00
e2e6b07fdb Update README.md 2016-01-05 22:00:34 +08:00
a2ceffecc8 Update README.md 2016-01-05 21:59:34 +08:00
805e3554de Update readme. 2016-01-05 21:54:47 +08:00
94d34747d1 Merge branch 'inline-mode' into develop 2016-01-05 15:35:45 +08:00
8278eef7f3 Add inline bot example. 2016-01-05 14:58:19 +08:00
94f1bbd402 Almost done. 2016-01-05 14:07:47 +08:00
c706a7aba3 Handler done. 2016-01-05 13:18:32 +08:00
c214f8000e Add InlineQueryResultVideo class. 2016-01-05 11:03:05 +08:00
81a201f19f Add InlineQueryResultMpeg4Gif class. 2016-01-05 10:51:33 +08:00
0f0d76ca82 Add InlineQueryResultGif class. 2016-01-05 10:41:32 +08:00
fa6f16ca5e Add InlineQueryResultPhoto class. 2016-01-05 10:24:21 +08:00
a1267a7670 Add InlineQueryResultArticle class. 2016-01-05 09:57:25 +08:00
79c46bccae inline_hanlder done. 2016-01-04 23:10:32 +08:00
e912546680 Add ChosenInlineResult class. 2016-01-04 22:53:08 +08:00
7eeda3bc4d Update update object. 2016-01-04 22:29:04 +08:00
b5680a1c1f Add InlineQuery class. 2016-01-04 22:24:18 +08:00
7514c617fc Merge branch 'master' into develop 2015-12-24 13:42:02 +08:00
32f3a64720 Update README.md 2015-12-24 11:25:19 +08:00
3a08111f10 Fix hasattr. 2015-12-22 14:26:08 +08:00
eeff4e7bf8 Let message all attribute default be None. #100 2015-12-22 14:18:23 +08:00
c37b3625aa Merge branch 'develop' of github.com:eternnoir/pyTelegramBotAPI into develop 2015-12-22 14:12:51 +08:00
a3519ff539 Merge pull request #97 from askainet/skip_pending_update
Get and discard all pending updates sent before first poll of the bot
2015-12-02 09:16:00 +08:00
43ad415320 Get and discard all pending updates sent before the first poll of the bot 2015-12-01 15:05:52 +01:00
fb7c79fdfb Merge pull request #95 from ratijas/master
style unification for method_url in apihelper.py.
2015-11-09 13:17:36 +08:00
413f081b19 style unification for method_url in apihelper.py. 2015-11-08 10:30:09 +08:00
cea0604ede Merge pull request #94 from Kondra007/patch-4
Added 'voice' to content_types + some small fixes
2015-11-06 22:01:36 +08:00
be639ef4dd Added 'voice' to content_types + some small fixes 2015-11-04 18:37:51 +03:00
528b23770f Update README.md 2015-10-26 22:36:32 +08:00
4e472dda48 Update README.md 2015-10-26 22:32:10 +08:00
17dec34923 Update README.md 2015-10-26 22:31:55 +08:00
a17b301f1c Jump to release version. 2015-10-26 22:26:04 +08:00
cf830a0fb4 Version update:
Change log:
- Code clean up
- Bugs fix
2015-10-26 22:15:03 +08:00
7e9f5b09cf Bug #92 fix. 2015-10-26 21:55:04 +08:00
969c5e76ef Fix bytes not string. 2015-10-18 22:03:29 +08:00
7346326bc3 Fix video's thumb type forgot de_json. 2015-10-17 02:30:22 +08:00
f355796b01 Merge pull request #88 from mabitte/develop
Little code cleanup
2015-10-17 02:08:34 +08:00
5175803d0b * dict.get(key) returns None if key is not in the dictionary
* used the class method's class reference to instantiate type classes
2015-10-15 16:53:59 +02:00
bf9939d40e Fix queue default value. #86 2015-10-13 13:05:38 +08:00
07960fe348 Update new type chat document. 2015-10-12 23:00:25 +08:00
f4be18e082 Update Version.
Change log:
- New type Chat support.
- Bugs fix.
2015-10-12 22:45:01 +08:00
8e9837a587 Merge branch 'develop' 2015-10-12 22:37:17 +08:00
361c043872 Fix test data error. 2015-10-12 11:02:10 +08:00
50432dbef1 Fix test data error. 2015-10-12 10:54:33 +08:00
37277d74cc Fix testing error. 2015-10-12 10:51:29 +08:00
9ee341f5e6 Add Chat test for telebot. 2015-10-12 10:46:20 +08:00
0bd284afc7 Format. 2015-10-12 10:37:41 +08:00
4ffdada427 Add Chat json test. 2015-10-12 10:37:25 +08:00
d8effd3f9f New type Chat supported. 2015-10-12 10:36:58 +08:00
303e15b88d Merge pull request #84 from AndydeCleyre/patch-1
Fix AttributeError on Python 3
2015-10-09 21:57:05 +08:00
3273aa9afa Merge pull request #85 from AndydeCleyre/patch-2
Fix NameError in Python 3
2015-10-09 12:21:23 +08:00
9e8b11051c Fix NameError in Python 3 2015-10-08 16:46:15 -04:00
b9d458e643 Fix AttributeError on Python 3 2015-10-08 16:39:22 -04:00
1e6361dd57 Fix #83
Added a `encode('utf8')` statement to support non-ASCII symbols in error log messages.
2015-10-06 22:36:57 +02:00
ca2019b8f1 Merge pull request #82 from skgsergio/webhook-examples
Add Webhook examples (CPython, Flask, CherryPy)
2015-10-06 13:17:10 +02:00
a230665424 Update README.rst 2015-10-06 12:19:20 +02:00
8eb6e034fe Update README with webhooks information. 2015-10-06 12:11:42 +02:00
d6552eb4c6 Add webhook examples 2015-10-06 12:06:23 +02:00
117c5a1141 Minor CPU optimization 2015-10-03 12:48:56 +02:00
3f335c37ce Update version.
Change log:
- Add webhook support.
- Better error handling.
- Add threaded parameter for telebot constructor. Allowing users to decide telebot to use thread for polling method.
2015-10-03 16:33:17 +08:00
e7e681928d Fix polling stuch problem. 2015-10-02 23:24:54 +08:00
855ff40070 Fix python3 raise exception error. 2015-10-02 17:08:05 +08:00
29a42a398b Restored the non-threaded variant 2015-10-02 00:00:54 +02:00
b801728924 Fix unnecessary raise 2015-10-01 22:43:18 +02:00
d14e9051d4 Better error handling.
Errors now are re-raised in the Thread polling() was called from.
If none_stop is *not* set, ApiExceptions will cause the calling Thread to halt.
2015-10-01 22:03:54 +02:00
60ca1751ca WorkerThreads now log ApiExceptions 2015-10-01 11:33:23 +02:00
941b8ac5d0 Added webhooks support with set_webhook and remove_webhook in the TeleBot class 2015-09-30 18:19:31 +02:00
7a08102fad Merge branch 'master' of https://github.com/eternnoir/pyTelegramBotAPI into develop 2015-09-30 17:51:45 +02:00
325061ee96 Added "Bots using this API" section to docs
Added Channel link to docs
2015-09-30 17:50:40 +02:00
8839f36706 Update version.
Change log:
- Bug fix : fix race-condition for register_for_reply.
- Add timeout parameter for polling method.
2015-09-30 23:34:37 +08:00
1c53955d5a Add timeout para for polling method. 2015-09-30 23:18:26 +08:00
9cd28b88be Merge pull request #79 from huiyiqun/master
fix race-condition for register_for_reply
2015-09-30 22:50:08 +08:00
2fb2cd6f20 fix race-condition for message_subscribers 2015-09-30 20:46:37 +08:00
036d000b95 Remove 3.2/3.5 2015-09-22 20:34:06 +08:00
11d4bb02a8 Add Test python version 2015-09-22 20:25:35 +08:00
f4b0b61b9d Update version.
Change log:
- Support File type.
- New method getFile supported.
2015-09-19 23:06:47 +08:00
4b8989f3d2 Merge pull request #74 from eternnoir/develop
Add getFile method and File type
2015-09-19 10:55:44 +08:00
19b4e35ee5 Attempt to fix failing test case 2015-09-18 21:11:56 +02:00
61b4ca8a37 Added download_file and created an example 2015-09-18 20:54:22 +02:00
1038d4fafa Added download_file 2015-09-18 20:53:10 +02:00
fd1f16598b Added File & getFile, including testing 2015-09-18 20:31:29 +02:00
07c28830db Update version.
Change log:
- Hotfix handler lambda bug.
2015-09-10 21:51:51 +08:00
a8fccdbeb3 Add test. 2015-09-10 21:44:14 +08:00
c213b7732b Add lambda text. 2015-09-10 21:38:09 +08:00
2ca5c0d6f3 Hotfix lambda bug. 2015-09-10 21:27:37 +08:00
2d0c54a7f2 Update version
Change log:
* send_message add parse_mode parameter support.
* Add get_updates method to telebot class.
* Remove time.sleep() from polling method. Use optional
* block parameter
2015-09-09 10:36:25 +08:00
bb0d028750 Add parse_mode markdown test. 2015-09-09 09:43:19 +08:00
e7b4e95fbc Merge remote-tracking branch 'origin/master' into develop 2015-09-09 09:34:13 +08:00
85ffe9936d Merge pull request #71 from eternnoir/implement-parse_mode
Implement parse_mode
2015-09-09 08:22:13 +08:00
99f6829ede Fix failing test case in Py2.6 because of using {} instead of {0..9} 2015-09-08 22:34:55 +02:00
55053fe413 Fixed failing test cases because of iteritems() 2015-09-08 22:31:02 +02:00
f2971c5d6c Merge branch 'redesign-message-handlers' into 'develop' 2015-09-08 22:22:01 +02:00
89288b166e Implement parse_mode 2015-09-08 21:51:45 +02:00
88bd6dcbb0 Updated Logging section in README 2015-09-08 19:56:05 +02:00
710fc273d6 Better log messages 2015-09-08 19:47:55 +02:00
9f04f0ece2 Fix "module has no attribute 'logger'" 2015-09-08 18:53:36 +02:00
cc7ab58ed8 Fixed some typos in the README
Fixed a bug where TeleBot would ignore KeyboardInterrupt events
2015-09-08 17:38:44 +02:00
edf1694606 Fix README message handler table 2015-09-08 11:29:35 +02:00
f9cbcf9b65 Redesigned message handlers 2015-09-08 10:44:31 +02:00
3a148c6e85 Refactor get_update method. 2015-09-05 21:54:54 +08:00
4522b37b9d Merge pull request #68 from eternnoir/refactor-Add-getUpdates-method
Refactor add get updates method
2015-09-05 18:25:53 +08:00
149c54cc31 Update readme.rst. 2015-09-05 18:13:47 +08:00
93aa37768a Add readme & pep8 2015-09-05 18:12:52 +08:00
da729069c2 Add get_updates method. 2015-09-05 18:10:11 +08:00
f7c4b14b77 Add types test. 2015-09-05 18:02:28 +08:00
2d5cb4fd75 Add Update class. 2015-09-05 17:58:53 +08:00
f2f556ed12 Merge pull request #67 from Kondra007/patch-1
Check if field is None
2015-09-03 17:40:34 +02:00
b1e659da28 Check if field is None
A slight change, still not very good though. Something like `{!s}{!s}{!s}.format(....)` seems a better aproach
2015-09-03 18:06:54 +03:00
4526c4f8a7 First TeleBot bot bug fix: wrong group chat id comparison (str and int) 2015-09-01 23:29:01 +02:00
efc98c084f Added TeleBot bot source 2015-09-01 23:20:24 +02:00
cf61577e3e Added a string representation (__str__) for JsonDeserializable 2015-09-01 21:55:02 +02:00
3c8faa155f Created util.py to clean up __init__.py and apihelper.py and updated README accordingly
Fixed failing send_document_by_id and send_photo_by_id
2015-08-31 11:49:03 +02:00
6f34a22c4b Added an incremental sleep when an error is encountered while polling 2015-08-31 02:14:14 +02:00
d118e9edcc Remove the deprecated time.sleep calls.
Reformatted detailed_example to follow Python naming conventions & PEP-8 conventions
2015-08-30 19:31:48 +02:00
c33c116488 Integrated blocking into polling + updated docs 2015-08-30 18:53:18 +02:00
e4bb2ff4f9 Merge branch 'develop' of github.com:eternnoir/pyTelegramBotAPI into develop 2015-08-24 10:43:01 +08:00
92fb982a57 Update travis ci script. 2015-08-24 10:42:32 +08:00
e734e0c974 Merge pull request #64 from pevdh/update-docs
Update docs
2015-08-24 10:39:14 +08:00
bf91d605ea Fix some more typos 2015-08-23 22:23:18 +02:00
e736c59896 Fix echo_bot example error 2015-08-23 22:06:11 +02:00
94844054e4 Added images 2015-08-23 21:56:42 +02:00
00f7c1ddc0 Fixed a typo 2015-08-23 20:07:53 +02:00
7f68bbbdb9 Copied to README.rst 2015-08-23 20:03:46 +02:00
2221b59b6f Updates after some reviews 2015-08-23 20:01:09 +02:00
3b61c7cd8a Update the documentation. 2015-08-23 19:32:23 +02:00
f2664d8232 Update version.
Change log:
- Add logging.basicConfig() to avoid handler not found.
2015-08-23 22:54:30 +08:00
4ed7be19ad Avoid no hnadler found. 2015-08-23 22:52:20 +08:00
93f1d9e6f8 Add readme.rst to repo. 2015-08-23 22:45:59 +08:00
2a103e5bbc Update version.
Change log:
- bug fix #61
- Better error description
2015-08-21 23:58:55 +08:00
645270861c Update readme. 2015-08-21 23:09:35 +08:00
ee4061561e Fix #61 2015-08-21 22:58:48 +08:00
b7a4d3f0b3 Merge pull request #60 from pevdh/better-error-description
Better error description
2015-08-20 15:07:17 +08:00
ee0ff8b1fb Fix test case 2015-08-19 22:15:40 +02:00
8412be1336 (Attempt to) fix failing Travis CI tests. 2015-08-19 22:13:27 +02:00
596df2ef2c Extended error descriptions
They should provide more information about the origin of the failure.
2015-08-19 21:30:44 +02:00
5c6de8aa04 Update version.
Change log:
- New type Voice
- Update audio
- sendAduio
- sendVoice
checkout: https://core.telegram.org/bots/api-changelog 20150815
2015-08-19 18:36:10 +08:00
7dc1c5095a Merge branch 'change20150815' into develop 2015-08-19 18:28:07 +08:00
b17831b726 Update Comment. 2015-08-19 18:27:35 +08:00
1517979bf2 Voice bug fix. 2015-08-19 18:25:08 +08:00
571a0fb320 Fix test case error. 2015-08-19 18:13:40 +08:00
c3300af656 New audio,voice supported. 2015-08-19 18:08:01 +08:00
cb4a58a1e8 Merge branch 'master' into develop 2015-08-14 15:42:30 +08:00
57ed31da71 Updare version.
Change log:
- fix mime_type syntax error.
2015-08-14 15:39:37 +08:00
fb3b63d441 Merge branch 'master' into develop 2015-08-10 09:05:57 +08:00
f06bec1a44 Merge pull request #57 from rjornetc/patch-1
Replaced mine_type by mime_type
2015-08-09 11:17:26 +08:00
b25435f29a Replaced mine_type by mime_type 2015-08-08 23:26:47 +02:00
a12ea63858 Update readme. 2015-08-01 21:03:40 +08:00
98f126d249 Update Version.
Change log:
- Some API method change.
- next_step_handler supported. #56
2015-08-01 10:20:41 +08:00
821a63e3a7 Support new sendVideo API method. 2015-08-01 10:12:15 +08:00
b745088a05 Let thumb option in Sticker. Api chamged. 2015-08-01 09:55:17 +08:00
8abd3fe140 Update text. 2015-07-30 11:40:54 +08:00
004f6917aa Fix test error. 2015-07-30 11:34:23 +08:00
d546a4f1e9 Add step handler example. 2015-07-30 11:30:16 +08:00
9f6a3bfb26 next_step_handler done. 2015-07-30 11:02:08 +08:00
a82fece950 Add method. 2015-07-30 09:23:15 +08:00
8f13b636ed Update version.
Change log:
- new method: register_for_reply
- Added a warning to send_message to not exceed 5000 characters per message.
2015-07-30 09:06:41 +08:00
740090640a PEP8 2015-07-30 09:03:08 +08:00
9219404a9a Merge branch 'sgomez-fix-video-type' into develop 2015-07-26 20:15:57 +08:00
d5c1601bf0 Merge branch 'master' into develop 2015-07-26 20:15:51 +08:00
83ab8a39c9 Fix video type class 2015-07-26 11:19:20 +02:00
3ffdd427dd Merge pull request #54 from pevdh/develop
Added split_string to apihelper.py
2015-07-26 11:25:36 +08:00
f41cc8134a Added split_string to apihelper.py
Added a warning to send_message to not exceed 5000 characters per message.
Changed send_message to use HTTP POST instead of GET, to stretch the maximum character limit a little more.
And added build status to README for fun.
2015-07-25 21:41:19 +02:00
20e3f731f7 Added split_string to apihelper.py
Added a warning to send_message to not exceed 5000 characters per message.
Changed send_message to use HTTP POST instead of GET, to stretch the maximum character limit a little more.
2015-07-25 21:31:58 +02:00
97f2c0c110 Merge pull request #52 from pevdh/develop
Implements #26
2015-07-24 21:24:10 +08:00
b484baea37 Fix doc error. 2015-07-24 13:56:47 +02:00
2647777b2b Implemented register_for_reply. Closes #26 2015-07-24 13:52:48 +02:00
b468e8c943 Moved extract_command and is_command to apihelper.py. 2015-07-24 13:09:27 +02:00
7b919852fa Update Readme. 2015-07-24 09:22:02 +08:00
60a7595b72 Update version.
Change log:
- Fix regexp handler bug.
- Fix python3 Exception message attribute not found.
2015-07-23 09:17:17 +08:00
893b414e33 Merge branch 'develop' 2015-07-23 09:16:44 +08:00
0932e603fb Fix python3 Exception message attribute not found. 2015-07-23 09:14:20 +08:00
2047b04a4c Update vsesion. Bug fix. 2015-07-22 14:49:52 +08:00
f513270971 PEP8. 2015-07-22 14:12:56 +08:00
53769f9e8c Merge branch 'fix_regexp_handler_fail' into develop
fix #48
2015-07-22 14:12:05 +08:00
ba1693dcf0 Bug fix. 2015-07-22 14:02:16 +08:00
f7168770ab Update Version.
Change log:
- Message type support forward_from forward_date reply_to_message
- Add logging.
2015-07-20 10:43:34 +08:00
43e1d26696 Update logger readme. 2015-07-20 10:38:33 +08:00
99b82b6219 Add logger. 2015-07-20 09:56:17 +08:00
968d30e246 Merge branch 'master' into develop 2015-07-18 17:35:46 +08:00
f183575d10 Merge pull request #41 from nasfarley88/master
Changed __stop_polling from bool to threading.Event().
2015-07-18 17:34:52 +08:00
edddab8956 Changed __stop_polling from bool to threading.Event(). 2015-07-18 10:27:16 +01:00
766b45e223 Update README.md 2015-07-17 21:40:33 +08:00
dd94b8624d Fix python3 test cases. 2015-07-17 14:50:56 +08:00
1350e19391 Add telebot tests. 2015-07-17 14:41:26 +08:00
6bdc313fa6 Message add forward_from forward_date reply_to_message support. 2015-07-17 14:40:49 +08:00
4870764b66 Add pypy to CI. 2015-07-17 13:56:34 +08:00
754d0ad558 Add python 3.4 to ci. 2015-07-17 11:31:00 +08:00
dbeec88ddf Update Version.
Change log:
- Bug fix. Python3 can not send file by string bug.
2015-07-17 09:38:28 +08:00
621b191e8d Fix python3 can not send file by string bug.
bug #40
2015-07-17 09:38:28 +08:00
634f88323c Add six package to requires. 2015-07-17 09:38:28 +08:00
55f6844d1f Add use file_id example 2015-07-15 13:14:46 +08:00
d5b7c2b49b Update README.md 2015-07-15 13:09:24 +08:00
1544efbbda Update version.
Change log:
- sendPhoto Document Audio ... file_id supported.
- Add process_new_message method for webhook.
- Fix thumb not in Document json
- Polling interval.
2015-07-15 11:28:21 +08:00
185b3e007e Polling interval support
issue #38
2015-07-15 11:19:29 +08:00
aeeb94d386 Fix api's document change. 2015-07-15 11:02:30 +08:00
56d1dfc045 Add some test. 2015-07-15 10:52:32 +08:00
6c8d30b063 Add process_new_message public method for webhook.
issue #36
2015-07-14 13:28:39 +08:00
c1bb900d91 send file, photo ...etc. by ID support. #35 2015-07-14 13:24:32 +08:00
0ee4fc8528 Updare version.
Change log:
- add none_stop flag to avoid polling stop.
2015-07-13 19:33:07 +08:00
bded9e021d Merge branch 'develop' of github.com:eternnoir/pyTelegramBotAPI into develop 2015-07-13 11:17:52 +08:00
913f01dfe8 Edit confused notice. 2015-07-13 09:07:07 +08:00
7144c79efe Add none_stop flag for polling method. 2015-07-13 09:07:07 +08:00
a9b51e039a Merge pull request #32 from LeoDJ/master
Updated detailed example to use message handlers
2015-07-12 22:49:37 +08:00
e5a17caa9f Updated detailed example to use message handlers
Now using message handlers instead of the listener for almost everything (except console output)
2015-07-12 15:50:50 +02:00
292bb307f0 Edit confused notice. 2015-07-12 19:03:41 +08:00
4a3e989391 Add none_stop flag for polling method. 2015-07-12 15:49:22 +08:00
09a39fcb89 Merge pull request #31 from LeoDJ/master
Created detailed example
2015-07-12 15:31:23 +08:00
efdf35796d Detailed example 2015-07-12 00:41:34 +02:00
bbf8b5d5f9 Added todo comment 2015-07-12 00:36:39 +02:00
30e3fbaa1a Created detailed example
Using almost every command, this API has to offer
2015-07-12 00:29:45 +02:00
8cf8661b7e Update README.md 2015-07-11 13:05:13 +08:00
2e1143e3d8 Update Version. 2015-07-09 10:39:08 +08:00
f6a0037f8d Fix thread pool high cpu usage. 2015-07-08 21:00:46 +08:00
3a574b4596 Update Version.
Change log:
- Thread pool support.
- Add create_thread flag.
2015-07-08 11:36:48 +08:00
45544d7b54 Update readme. 2015-07-07 11:51:11 +08:00
a82f4d780f Support python3 queue 2015-07-04 20:25:02 +08:00
814dbd2666 Remove python3.4 in ci 2015-07-04 20:13:56 +08:00
da176b770c Create ThreadPool when creat_thread flag is True. 2015-07-04 19:51:06 +08:00
b316e699e1 Fix failed testcase. 2015-07-04 17:27:53 +08:00
e095a2ffe9 Message handler support create_threads flag. 2015-07-04 17:20:29 +08:00
3d678707ac Add create_threads flag. 2015-07-04 17:00:42 +08:00
9b3f5f5ba0 Merge pool-impl branch 2015-07-04 02:42:41 +02:00
d71fdc633d Merge commit '0a2d139' into develop
Merge into develop
2015-07-04 02:34:05 +02:00
0a2d1394b2 Created a deep-linking example.
(Source: my answer at http://stackoverflow.com/questions/31042219/how-to-have-authentication-in-a-telegram-bot)
2015-07-04 02:32:43 +02:00
7dae316789 Merge pull request #24 from pevdh/master
Fix TeleBot#extract_command
2015-07-04 03:28:14 +08:00
bc181572d9 Merge commit '666ab76' into develop
Merge master.
2015-07-03 19:38:03 +02:00
e061aa051a Fixed API inconsistency (Message.fromUser -> Message.from_user) 2015-07-03 19:34:02 +02:00
666ab7609d Fix TeleBot#extract_command where the function previously would incorrectly slice /command@botname 2015-07-03 19:28:04 +02:00
0af4051dfd First implementation attempt 2015-07-03 19:22:26 +02:00
645c0b7fb4 Fix example echo bot command list bug. 2015-07-03 15:04:11 +08:00
28 changed files with 3399 additions and 412 deletions

View File

@ -2,6 +2,11 @@ language: python
python:
- "2.6"
- "2.7"
- "3.3"
- "3.4"
- "pypy"
- "pypy3"
install: "pip install -r requirements.txt"
script: cd tests && py.test
script:
- python setup.py install
- cd tests && py.test

513
README.md
View File

@ -1,14 +1,47 @@
# pyTelegramBotAPI
# <p align="center">pyTelegramBotAPI
A Python implementation for the Telegram Bot API.
<p align="center">A simple, but extensible Python implementation for the [Telegram Bot API](https://core.telegram.org/bots/api).
See [https://core.telegram.org/bots/api](https://core.telegram.org/bots/api)
[![Download Month](https://img.shields.io/pypi/v/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI)
[![Build Status](https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master)](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
[![Download Month](https://img.shields.io/pypi/dm/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI)
## How to install
* [Getting started.](#getting-started)
* [Writing your first bot](#writing-your-first-bot)
* [Prerequisites](#prerequisites)
* [A simple echo bot](#a-simple-echo-bot)
* [General API Documentation](#general-api-documentation)
* [Types](#types)
* [Methods](#methods)
* [General use of the API](#general-use-of-the-api)
* [Message handlers](#message-handlers)
* [TeleBot](#telebot)
* [Reply markup](#reply-markup)
* [Inline Mode](#inline-mode)
* [Advanced use of the API](#advanced-use-of-the-api)
* [Asynchronous delivery of messages](#asynchronous-delivery-of-messages)
* [Sending large text messages](#sending-large-text-messages)
* [Controlling the amount of Threads used by TeleBot](#controlling-the-amount-of-threads-used-by-telebot)
* [The listener mechanism](#the-listener-mechanism)
* [Using web hooks](#using-web-hooks)
* [Logging](#logging)
* [F.A.Q.](#faq)
* [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)
* [The Telegram Chat Group](#the-telegram-chat-group)
* [More examples](#more-examples)
* [Bots using this API](#bots-using-this-api)
Python 2 or Python 3 is required.
## Getting started.
* Install from source
This API is tested with Python 2.6, Python 2.7, Python 3.4, Pypy and Pypy 3.
There are two ways to install the library:
* Installation using pip (a Python package manager)*:
```
$ pip install pyTelegramBotAPI
```
* Installation from source (requires git):
```
$ git clone https://github.com/eternnoir/pyTelegramBotAPI.git
@ -16,92 +49,215 @@ $ cd pyTelegramBotAPI
$ python setup.py install
```
* or install with pip
It is generally recommended to use the first option.
```
$ pip install pyTelegramBotAPI
```
**While the API is production-ready, it is still under development and it has regular updates, do not forget to update it regularly by calling `pip install pytelegrambotapi --upgrade`*
## Example
## Writing your first bot
* Sending a message.
### Prerequisites
It is presumed that you [have obtained an API token with @BotFather](https://core.telegram.org/bots#botfather). We will call this token `TOKEN`.
Furthermore, you have basic knowledge of the Python programming language and more importantly [the Telegram Bot API](https://core.telegram.org/bots/api).
### A simple echo bot
The TeleBot class (defined in \__init__.py) encapsulates all API calls in a single class. It provides functions such as `send_xyz` (`send_message`, `send_document` etc.) and several ways to listen for incoming messages.
Create a file called `echo_bot.py`.
Then, open the file and create an instance of the TeleBot class.
```python
import telebot
TOKEN = '<token string>'
tb = telebot.TeleBot(TOKEN)
# tb.send_message(chatid, message)
tb.send_message(281281, 'gogo power ranger')
bot = telebot.TeleBot("TOKEN")
```
*Note: Make sure to actually replace TOKEN with your own API token.*
* Echo Bot
After that declaration, we need to register some so-called message handlers. Message handlers define filters which a message must pass. If a message passes the filter, the decorated function is called and the incoming message is passed as an argument.
Let's define a message handler which handles incoming `/start` and `/help` commands.
```python
@bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
bot.reply_to(message, "Howdy, how are you doing?")
```
A function which is decorated by a message handler __can have an arbitrary name, however, it must have only one parameter (the message)__.
Let's add another handler:
```python
@bot.message_handler(func=lambda m: True)
def echo_all(message):
bot.reply_to(message, message.text)
```
This one echoes all incoming text messages back to the sender. It uses a lambda function to test a message. If the lambda returns True, the message is handled by the decorated function. Since we want all messages to be handled by this function, we simply always return True.
*Note: all handlers are tested in the order in which they were declared*
We now have a basic bot which replies a static message to "/start" and "/help" commands and which echoes the rest of the sent messages. To start the bot, add the following to our source file:
```python
bot.polling()
```
Alright, that's it! Our source file now looks like this:
```python
import telebot
import time
TOKEN = '<token_string>'
bot = telebot.TeleBot("TOKEN")
@bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
bot.reply_to(message, "Howdy, how are you doing?")
def listener(*messages):
"""
When new messages arrive TeleBot will call this function.
"""
for m in messages:
chatid = m.chat.id
if m.content_type == 'text':
text = m.text
tb.send_message(chatid, text)
@bot.message_handler(func=lambda message: True)
def echo_all(message):
bot.reply_to(message, message.text)
bot.polling()
```
To start the bot, simply open up a terminal and enter `python echo_bot.py` to run the bot! Test it by sending commands ('/start' and '/help') and arbitrary text messages.
tb = telebot.TeleBot(TOKEN)
tb.set_update_listener(listener) #register listener
tb.polling()
## General API Documentation
while True: # Don't let the main Thread end.
### Types
All types are defined in types.py. They are all completely in line with the [Telegram API's definition of the types](https://core.telegram.org/bots/api#available-types), except for the Message's `from` field, which is renamed to `from_user` (because `from` is a Python reserved token). Thus, attributes such as `message_id` can be accessed directly with `message.message_id`. Note that `message.chat` can be either an instance of `User` or `GroupChat` (see [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)).
The Message object also has a `content_types`attribute, which defines the type of the Message. `content_types` can be one of the following strings:
'text', 'audio', 'document', 'photo', 'sticker', 'video', 'voice', 'location', 'contact', 'new_chat_participant', 'left_chat_participant', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created'.
### Methods
All [API methods](https://core.telegram.org/bots/api#available-methods) are located in the TeleBot class. They are renamed to follow common Python naming conventions. E.g. `getMe` is renamed to `get_me` and `sendMessage` to `send_message`.
### General use of the API
Outlined below are some general use cases of the API.
#### Message handlers
A message handler is a function that is decorated with the `message_handler` decorator of a TeleBot instance. Message handlers consist of one or multiple filters.
Each filter much return True for a certain message in order for a message handler to become eligible to handle that message. A message handler is declared in the following way (provided `bot` is an instance of TeleBot):
```python
@bot.message_handler(filters)
def function_name(message):
bot.reply_to(message, "This is a message handler")
```
`function_name` is not bound to any restrictions. Any function name is permitted with message handlers. The function must accept at most one argument, which will be the message that the function must handle.
`filters` is a list of keyword arguments.
A filter is declared in the following manner: `name=argument`. One handler may have multiple filters.
TeleBot supports the following filters:
|name|argument(s)|Condition|
|:---:|---| ---|
|content_types|list of strings (default `['text']`)|`True` if message.content_type is in the list of strings.|
|regexp|a regular expression as a string|`True` if `re.search(regexp_arg)` returns `True` and `message.content_type == 'text'` (See [Python Regular Expressions](https://docs.python.org/2/library/re.html)|
|commands|list of strings|`True` if `message.content_type == 'text'` and `message.text` starts with a command that is in the list of strings.|
|func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True`
Here are some examples of using the filters and message handlers:
```python
import telebot
bot = telebot.TeleBot("TOKEN")
# Handles all text messages that contains the commands '/start' or '/help'.
@bot.message_handler(commands=['start', 'help'])
def handle_start_help(message):
pass
# Handles all sent documents and audio files
@bot.message_handler(content_types=['document', 'audio'])
def handle_docs_audio(message):
pass
# Handles all text messages that match the regular expression
@bot.message_handler(regexp="SOME_REGEXP")
def handle_message(message):
pass
#Handles all messages for which the lambda returns True
@bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document'])
def handle_text_doc(message):
pass
#Which could also be defined as:
def test_message(message):
return message.document.mime_type == 'text/plan'
@bot.message_handler(func=test_message, content_types=['document'])
def handle_text_doc(message)
pass
# Handlers can be stacked to create a function which will be called if either message_handler is eligible
# This handler will be called if the message starts with '/hello' OR is some emoji
@bot.message_handler(commands=['hello'])
@bot.message_handler(func=lambda msg: msg.text.encode("utf-8") == SOME_FANCY_EMOJI)
def send_something(message):
pass
```
## TeleBot API usage
**Important: all handlers are tested in the order in which they were declared**
#### TeleBot
```python
import telebot
import time
TOKEN = '<token_string>'
tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object
# Upon calling this function, TeleBot starts polling the Telegram servers for new messages.
# - none_stop: True/False (default False) - Don't stop polling when receiving an error from the Telegram servers
# - interval: True/False (default False) - The interval between polling requests
# Note: Editing this parameter harms the bot's response time
# - block: True/False (default True) - Blocks upon calling this function
tb.polling(none_stop=False, interval=0, block=True)
# getMe
user = tb.get_me()
# setWebhook
tb.set_webhook(url="http://example.com", certificate=open('mycert.pem'))
# unset webhook
tb.remove_webhook()
# getUpdates
updates = tb.get_updates()
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
# sendMessage
tb.send_message(chatid, text)
# forwardMessage
# tb.forward_message(10894,926,3)
tb.forward_message(to_chat_id, from_chat_id, message_id)
# All send_xyz functions which can take a file as an argument, can also take a file_id instead of a file.
# sendPhoto
photo = open('/tmp/photo.png', 'rb')
tb.send_photo(chat_id, photo)
tb.send_photo(chat_id, "FILEID")
# sendAudio
audio = open('/tmp/audio.ogg', 'rb')
audio = open('/tmp/audio.mp3', 'rb')
tb.send_audio(chat_id, audio)
tb.send_audio(chat_id, "FILEID")
## sendAudio with duration, performer and title.
tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram')
# sendVoice
voice = open('/tmp/voice.ogg', 'rb')
tb.send_voice(chat_id, voice)
tb.send_voice(chat_id, "FILEID")
# sendDocument
doc = open('/tmp/file.txt', 'rb')
tb.send_document(chat_id, doc)
tb.send_document(chat_id, "FILEID")
# sendSticker
sti = open('/tmp/sti.webp', 'rb')
tb.send_sticker(chat_id, sti)
tb.send_sticker(chat_id, "FILEID")
# sendVideo
video = open('/tmp/video.mp4', 'rb')
tb.send_video(chat_id, video)
tb.send_video(chat_id, "FILEID")
# sendLocation
tb.send_location(chat_id, lat, lon)
@ -111,123 +267,218 @@ tb.send_location(chat_id, lat, lon)
# 'record_audio', 'upload_audio', 'upload_document' or 'find_location'.
tb.send_chat_action(chat_id, action_string)
# Use the ReplyKeyboardMarkup class.
# Thanks pevdh.
# getFile
# Downloading a file is straightforward
# Returns a File object
import requests
file_info = tb.get_file(file_id)
file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(API_TOKEN, file_info.file_path))
```
#### Reply markup
All `send_xyz` functions of TeleBot take an optional `reply_markup` argument. This argument must be an instance of `ReplyKeyboardMarkup`, `ReplyKeyboardHide` or `ForceReply`, which are defined in types.py.
```python
from telebot import types
markup = types.ReplyKeyboardMarkup()
# Using the ReplyKeyboardMarkup class
# It's constructor can take the following optional arguments:
# - resize_keyboard: True/False (default False)
# - one_time_keyboard: True/False (default False)
# - selective: True/False (default False)
# - row_width: integer (default 3)
# row_width is used in combination with the add() function.
# It defines how many buttons are fit on each row before continuing on the next row.
markup = types.ReplyKeyboardMarkup(row_width=2)
markup.add('a', 'v', 'd')
tb.send_message(chat_id, message, reply_markup=markup)
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
# or add strings one row at a time:
markup = types.ReplyKeyboardMarkup()
markup.row('a', 'v')
markup.row('c', 'd', 'e')
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
```
The last example yields this result:
![ReplyKeyboardMarkup](https://pp.vk.me/c624430/v624430512/473e5/_mxxW7FPe4U.jpg "ReplyKeyboardMarkup")
```python
# ReplyKeyboardHide: hides a previously sent ReplyKeyboardMarkup
# Takes an optional selective argument (True/False, default False)
markup = types.ReplyKeyboardHide(selective=False)
tb.send_message(chat_id, message, reply_markup=markup)
```
```python
# ForceReply: forces a user to reply to a message
# Takes an optional selective argument (True/False, default False)
markup = types.ForceReply(selective=False)
tb.send_message(chat_id, "Send me another word:", reply_markup=markup)
```
ForceReply:
![ForceReply](https://pp.vk.me/c624430/v624430512/473ec/602byyWUHcs.jpg "ForceReply")
### Inline Mode
More information about [Inline mode](https://core.telegram.org/bots/inline).
#### inline_handler
Now, you can use inline_handler to get inline_query in telebot.
```python
@bot.inline_handler(lambda query: query.query == 'text')
def query_text(inline_query):
# Query message is text
```
#### chosen_inline_handler
Use chosen_inline_handler to get chosen_inline_result in telebot. Don't forgot add the /setinlinefeedback
command for @Botfather.
More information : [collecting-feedback](https://core.telegram.org/bots/inline#collecting-feedback)
```python
@bot.chosen_inline_handler(func=lambda chosen_inline_result: True)
def test_chosen(chosen_inline_result):
# Process all chosen_inline_result.
```
#### answer_inline_query
```python
@bot.inline_handler(lambda query: query.query == 'text')
def query_text(inline_query):
try:
r = types.InlineQueryResultArticle('1', 'Result', 'Result message.')
r2 = types.InlineQueryResultArticle('2', 'Result2', 'Result message2.')
bot.answer_inline_query(inline_query.id, [r, r2])
except Exception as e:
print(e)
```
## Creating a Telegram bot with the pyTelegramBotAPI
There are two ways to define a Telegram Bot with the pyTelegramBotAPI.
## Advanced use of the API
### Asynchronous delivery of messages
There exists an implementation of TeleBot which executes all `send_xyz` and the `get_me` functions asynchronously. This can speed up you bot __significantly__, but it has unwanted side effects if used without caution.
To enable this behaviour, create an instance of AsyncTeleBot instead of TeleBot.
```python
tb = telebot.AsyncTeleBot("TOKEN")
```
Now, every function that calls the Telegram API is executed in a separate Thread. The functions are modified to return an AsyncTask instance (defined in util.py). Using AsyncTeleBot allows you to do the following:
```python
import telebot
tb = telebot.AsyncTeleBot("TOKEN")
task = tb.get_me() # Execute an API call
# Do some other operations...
a = 0
for a in range(100):
a += 10
result = task.wait() # Get the result of the execution
```
*Note: if you execute send_xyz functions after eachother without calling wait(), the order in which messages are delivered might be wrong.*
### Sending large text messages
Sometimes you must send messages that exceed 5000 characters. The Telegram API can not handle that many characters in one request, so we need to split the message in multiples. Here is how to do that using the API:
```python
from telebot import util
large_text = open("large_text.txt", "rb").read()
# Split the text each 3000 characters.
# split_string returns a list with the splitted text.
splitted_text = util.split_string(large_text, 3000)
for text in splitted_text:
tb.send_message(chat_id, text)
```
### Controlling the amount of Threads used by TeleBot
The TeleBot constructor takes the following optional arguments:
- create_threads: True/False (default True). A flag to indicate whether
TeleBot should execute message handlers on it's polling Thread.
- num_threads: integer (default 4). Controls the amount of WorkerThreads created for the internal thread pool that TeleBot uses to execute message handlers. Is not used when create_threads is False.
### The listener mechanism
* First, create a TeleBot instance.
As an alternative to the message handlers, one can also register a function as a listener to TeleBot. Example:
```python
import telebot
def handle_messages(messages):
for message in messsages:
# Do something with the message
bot.reply_to(message, 'Hi')
TOKEN = '<token string>'
bot = telebot.TeleBot(TOKEN)
```
* Then, define a listener function.
```python
def echo_messages(*messages):
"""
Echoes all incoming messages of content_type 'text'.
"""
for m in messages:
chatid = m.chat.id
if m.content_type == 'text':
text = m.text
bot.send_message(chatid, text)
```
* Now, register your listener with the TeleBot instance and call TeleBot#polling()
```python
bot.set_update_listener(echo_messages)
bot.set_update_listener(handle_messages)
bot.polling()
while True: # Don't let the main Thread end.
pass
```
* use Message's content_type attribute to check the type of Message. Now Message supports content types:
* text
* audio
* document
* photo
* sticker
* video
* location
* contact
* new_chat_participant
* left_chat_participant
* new_chat_title
* new_chat_photo
* delete_chat_photo
* group_chat_created
* That's it!
### The decorator mechanism
* First, create a TeleBot instance.
### Using webhooks
When using webhooks telegram sends one Update per call, for processing it you should call process_new_messages([update.message]) when you recieve it.
There are some examples using webhooks in the *examples/webhook_examples* directory.
### Logging
You can use the Telebot module logger to log debug info about Telebot. Use `telebot.logger` to get the logger of the TeleBot module.
It is possible to add custom logging Handlers to the logger. Refer to the [Python logging module page](https://docs.python.org/2/library/logging.html) for more info.
```python
import telebot
import logging
TOKEN = '<token string>'
bot = telebot.TeleBot(TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
```
* Next, define all of your so-called message handlers and decorate them with @bot.message_handler
## F.A.Q.
### How can I distinguish a User and a GroupChat in message.chat?
Telegram Bot API support new type Chat for message.chat.
- Check the ```type``` attribute in ```Chat``` object:
```python
# Handle /start and /help
@bot.message_handler(commands=['start', 'help'])
def command_help(message):
bot.reply_to(message, "Hello, did someone call for help?")
# Handles all messages which text matches the regex regexp.
# See https://en.wikipedia.org/wiki/Regular_expression
# This regex matches all sent url's.
@bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
def command_url(message):
bot.reply_to(message, "I shouldn't open that url, should I?")
if message.chat.type == private:
# private chat message
# Handle all sent documents of type 'text/plain'.
@bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document'])
def command_handle_document(message):
bot.reply_to(message, "Document received, sir!")
if message.chat.type == group:
# group chat message
if message.chat.type == supergroup:
# supergroup chat message
if message.chat.type == channel:
# channel message
# Default command handler. A lambda expression which always returns True is used for this purpose.
@bot.message_handler(func=lambda message: True, content_types=['audio', 'video', 'document', 'text', 'location', 'contact', 'sticker'])
def default_command(message):
bot.reply_to(message, "This is the default command handler.")
```
* And finally, call bot.polling()
```python
bot.polling()
while True: # Don't end the main thread.
pass
```
Use whichever mechanism fits your purpose! It is even possible to mix and match.
## The Telegram Chat Group
## TODO
Get help. Discuss. Chat.
- [x] getMe
- [x] sendMessage
- [x] forwardMessage
- [x] sendPhoto
- [x] sendAudio
- [x] sendDocument
- [x] sendSticker
- [x] sendVideo
- [x] sendLocation
- [x] sendChatAction
- [x] getUserProfilePhotos
- [x] getUpdate
* Join the pyTelegramBotAPI Telegram Chat Group
* Messge to @eternnoir by telegram for Invitation.
* We now have a Telegram Channel as well! Keep yourself up to date with API changes, and [join it](https://telegram.me/pytelegrambotapi).
## More examples
* [Echo Bot](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/echo_bot.py)
* [Deep Linking](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/deep_linking.py)
* [next_step_handler Example](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py)
## Bots using this API
* [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes
* [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger*
* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF*
* [Telegram UrlProBot](https://github.com/GabrielRF/telegram-urlprobot) by *GabrielRF*
* [Telegram Proxy Bot](https://bitbucket.org/master_groosha/telegram-proxy-bot) by *Groosha* - A simple BITM (bot-in-the-middle) for Telegram acting as some kind of "proxy".
* [RadRetroRobot](https://github.com/Tronikart/RadRetroRobot) by *Tronikart* - Multifunctional Telegram Bot RadRetroRobot.
* [League of Legends bot](https://telegram.me/League_of_Legends_bot) ([source](https://github.com/i32ropie/lol)) by *i32ropie*
* [NeoBot](https://github.com/neoranger/NeoBot) by *neoranger*
Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh.

592
README.rst Normal file
View File

@ -0,0 +1,592 @@
#
.. raw:: html
<p align="center">
pyTelegramBotAPI
.. raw:: html
<p align="center">
A simple, but extensible Python implementation for the `Telegram Bot
API <https://core.telegram.org/bots/api>`__.
.. raw:: html
<p align="center">
|Build Status|
- `Getting started. <#getting-started>`__
- `Writing your first bot <#writing-your-first-bot>`__
- `Prerequisites <#prerequisites>`__
- `A simple echo bot <#a-simple-echo-bot>`__
- `General API Documentation <#general-api-documentation>`__
- `Types <#types>`__
- `Methods <#methods>`__
- `General use of the API <#general-use-of-the-api>`__
- `Message handlers <#message-handlers>`__
- `TeleBot <#telebot>`__
- `Reply markup <#reply-markup>`__
- `Advanced use of the API <#advanced-use-of-the-api>`__
- `Asynchronous delivery of
messages <#asynchronous-delivery-of-messages>`__
- `Sending large text messages <#sending-large-text-messages>`__
- `Controlling the amount of Threads used by
TeleBot <#controlling-the-amount-of-threads-used-by-telebot>`__
- `The listener mechanism <#the-listener-mechanism>`__
- `Using web hooks <#using-web-hooks>`__
- `Logging <#logging>`__
- `F.A.Q. <#faq>`__
- `How can I distinguish a User and a GroupChat in
message.chat? <#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat>`__
- `The Telegram Chat Group <#the-telegram-chat-group>`__
- `More examples <#more-examples>`__
- `Bots using this API <#bots-using-this-api>`__
Getting started.
================
This API is tested with Python 2.6, Python 2.7, Python 3.4, Pypy and
Pypy 3. There are two ways to install the library:
- Installation using pip (a Python package manager)\*:
::
$ pip install pyTelegramBotAPI
- Installation from source (requires git):
::
$ git clone https://github.com/eternnoir/pyTelegramBotAPI.git
$ cd pyTelegramBotAPI
$ python setup.py install
It is generally recommended to use the first option.
\*\*While the API is production-ready, it is still under development and
it has regular updates, do not forget to update it regularly by calling
``pip install pytelegrambotapi --upgrade``\ \*
Writing your first bot
======================
Prerequisites
-------------
It is presumed that you [have obtained an API token with
@BotFather](https://core.telegram.org/bots#botfather). We will call this
token ``TOKEN``. Furthermore, you have basic knowledge of the Python
programming language and more importantly `the Telegram Bot
API <https://core.telegram.org/bots/api>`__.
A simple echo bot
-----------------
The TeleBot class (defined in \_\_init\_\_.py) encapsulates all API
calls in a single class. It provides functions such as ``send_xyz``
(``send_message``, ``send_document`` etc.) and several ways to listen
for incoming messages.
Create a file called ``echo_bot.py``. Then, open the file and create an
instance of the TeleBot class.
.. code:: python
import telebot
bot = telebot.TeleBot("TOKEN")
*Note: Make sure to actually replace TOKEN with your own API token.*
After that declaration, we need to register some so-called message
handlers. Message handlers define filters which a message must pass. If
a message passes the filter, the decorated function is called and the
incoming message is passed as an argument.
Let's define a message handler which handles incoming ``/start`` and
``/help`` commands.
.. code:: python
@bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
bot.reply_to(message, "Howdy, how are you doing?")
A function which is decorated by a message handler **can have an
arbitrary name, however, it must have only one parameter (the
message)**.
Let's add another handler:
.. code:: python
@bot.message_handler(func=lambda m: True)
def echo_all(message):
bot.reply_to(message, message.text)
This one echoes all incoming text messages back to the sender. It uses a
lambda function to test a message. If the lambda returns True, the
message is handled by the decorated function. Since we want all messages
to be handled by this function, we simply always return True.
*Note: all handlers are tested in the order in which they were declared*
We now have a basic bot which replies a static message to "/start" and
"/help" commands and which echoes the rest of the sent messages. To
start the bot, add the following to our source file:
.. code:: python
bot.polling()
Alright, that's it! Our source file now looks like this:
.. code:: python
import telebot
bot = telebot.TeleBot("TOKEN")
@bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
bot.reply_to(message, "Howdy, how are you doing?")
@bot.message_handler(func=lambda message: True)
def echo_all(message):
bot.reply_to(message, message.text)
bot.polling()
To start the bot, simply open up a terminal and enter
``python echo_bot.py`` to run the bot! Test it by sending commands
('/start' and '/help') and arbitrary text messages.
General API Documentation
=========================
Types
-----
All types are defined in types.py. They are all completely in line with
the `Telegram API's definition of the
types <https://core.telegram.org/bots/api#available-types>`__, except
for the Message's ``from`` field, which is renamed to ``from_user``
(because ``from`` is a Python reserved token). Thus, attributes such as
``message_id`` can be accessed directly with ``message.message_id``.
Note that ``message.chat`` can be either an instance of ``User`` or
``GroupChat`` (see `How can I distinguish a User and a GroupChat in
message.chat? <#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat>`__).
The Message object also has a ``content_type``\ attribute, which defines
the type of the Message. ``content_type`` can be one of the following
strings: 'text', 'audio', 'document', 'photo', 'sticker', 'video',
'location', 'contact', 'new\_chat\_participant',
'left\_chat\_participant', 'new\_chat\_title', 'new\_chat\_photo',
'delete\_chat\_photo', 'group\_chat\_created'.
Methods
-------
All `API
methods <https://core.telegram.org/bots/api#available-methods>`__ are
located in the TeleBot class. They are renamed to follow common Python
naming conventions. E.g. ``getMe`` is renamed to ``get_me`` and
``sendMessage`` to ``send_message``.
General use of the API
----------------------
Outlined below are some general use cases of the API.
Message handlers
~~~~~~~~~~~~~~~~
A message handler is a function that is decorated with the
``message_handler`` decorator of a TeleBot instance. Message handlers
consist of one or multiple filters. Each filter much return True for a
certain message in order for a message handler to become eligible to
handle that message. A message handler is declared in the following way
(provided ``bot`` is an instance of TeleBot):
.. code:: python
@bot.message_handler(filters)
def function_name(message):
bot.reply_to(message, "This is a message handler")
``function_name`` is not bound to any restrictions. Any function name is
permitted with message handlers. The function must accept at most one
argument, which will be the message that the function must handle.
``filters`` is a list of keyword arguments. A filter is declared in the
following manner: ``name=argument``. One handler may have multiple
filters. TeleBot supports the following filters:
+------------------+---------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| name | argument(s) | Condition |
+==================+=============================================+=================================================================================================================================================================================+
| content\_types | list of strings (default ``['text']``) | ``True`` if message.content\_type is in the list of strings. |
+------------------+---------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| regexp | a regular expression as a string | ``True`` if ``re.search(regexp_arg)`` returns ``True`` and ``message.content_type == 'text'`` (See `Python Regular Expressions <https://docs.python.org/2/library/re.html>`__ |
+------------------+---------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| commands | list of strings | ``True`` if ``message.content_type == 'text'`` and ``message.text`` starts with a command that is in the list of strings. |
+------------------+---------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| func | a function (lambda or function reference) | ``True`` if the lambda or function reference returns ``True`` |
+------------------+---------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Here are some examples of using the filters and message handlers:
.. code:: python
import telebot
bot = telebot.TeleBot("TOKEN")
# Handles all text messages that contains the commands '/start' or '/help'.
@bot.message_handler(commands=['start', 'help'])
def handle_start_help(message):
pass
# Handles all sent documents and audio files
@bot.message_handler(content_types=['document', 'audio'])
def handle_docs_audio(message):
pass
# Handles all text messages that match the regular expression
@bot.message_handler(regexp="SOME_REGEXP")
def handle_message(message):
pass
#Handles all messages for which the lambda returns True
@bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document'])
def handle_text_doc(message):
pass
#Which could also be defined as:
def test_message(message):
return message.document.mime_type == 'text/plan'
@bot.message_handler(func=test_message, content_types=['document'])
def handle_text_doc(message)
pass
# Handlers can be stacked to create a function which will be called if either message_handler is eligible
# This handler will be called if the message starts with '/hello' OR is some emoji
@bot.message_handler(commands=['hello'])
@bot.message_handler(func=lambda msg: msg.text.encode("utf-8") == SOME_FANCY_EMOJI)
def send_something(message):
pass
**Important: all handlers are tested in the order in which they were
declared** #### TeleBot
.. code:: python
import telebot
TOKEN = '<token_string>'
tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object
# Upon calling this function, TeleBot starts polling the Telegram servers for new messages.
# - none_stop: True/False (default False) - Don't stop polling when receiving an error from the Telegram servers
# - interval: True/False (default False) - The interval between polling requests
# Note: Editing this parameter harms the bot's response time
# - block: True/False (default True) - Blocks upon calling this function
tb.polling(none_stop=False, interval=0, block=True)
# getMe
user = tb.get_me()
# setWebhook
tb.set_webhook(url="http://example.com", certificate=open('mycert.pem'))
# unset webhook
tb.remove_webhook()
# getUpdates
updates = tb.get_updates()
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
# sendMessage
tb.send_message(chatid, text)
# forwardMessage
tb.forward_message(to_chat_id, from_chat_id, message_id)
# All send_xyz functions which can take a file as an argument, can also take a file_id instead of a file.
# sendPhoto
photo = open('/tmp/photo.png', 'rb')
tb.send_photo(chat_id, photo)
tb.send_photo(chat_id, "FILEID")
# sendAudio
audio = open('/tmp/audio.mp3', 'rb')
tb.send_audio(chat_id, audio)
tb.send_audio(chat_id, "FILEID")
## sendAudio with duration, performer and title.
tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram')
# sendVoice
voice = open('/tmp/voice.ogg', 'rb')
tb.send_voice(chat_id, voice)
tb.send_voice(chat_id, "FILEID")
# sendDocument
doc = open('/tmp/file.txt', 'rb')
tb.send_document(chat_id, doc)
tb.send_document(chat_id, "FILEID")
# sendSticker
sti = open('/tmp/sti.webp', 'rb')
tb.send_sticker(chat_id, sti)
tb.send_sticker(chat_id, "FILEID")
# sendVideo
video = open('/tmp/video.mp4', 'rb')
tb.send_video(chat_id, video)
tb.send_video(chat_id, "FILEID")
# sendLocation
tb.send_location(chat_id, lat, lon)
# sendChatAction
# action_string can be one of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video',
# 'record_audio', 'upload_audio', 'upload_document' or 'find_location'.
tb.send_chat_action(chat_id, action_string)
# getFile
# Downloading a file is straightforward
# Returns a File object
import requests
file_info = tb.get_file(file_id)
file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(API_TOKEN, file_info.file_path))
Reply markup
~~~~~~~~~~~~
All ``send_xyz`` functions of TeleBot take an optional ``reply_markup``
argument. This argument must be an instance of ``ReplyKeyboardMarkup``,
``ReplyKeyboardHide`` or ``ForceReply``, which are defined in types.py.
.. code:: python
from telebot import types
# Using the ReplyKeyboardMarkup class
# It's constructor can take the following optional arguments:
# - resize_keyboard: True/False (default False)
# - one_time_keyboard: True/False (default False)
# - selective: True/False (default False)
# - row_width: integer (default 3)
# row_width is used in combination with the add() function.
# It defines how many buttons are fit on each row before continuing on the next row.
markup = types.ReplyKeyboardMarkup(row_width=2)
markup.add('a', 'v', 'd')
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
# or add strings one row at a time:
markup = types.ReplyKeyboardMarkup()
markup.row('a', 'v')
markup.row('c', 'd', 'e')
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
The last example yields this result:
.. figure:: https://pp.vk.me/c624430/v624430512/473e5/_mxxW7FPe4U.jpg
:alt: ReplyKeyboardMarkup
ReplyKeyboardMarkup
.. code:: python
# ReplyKeyboardHide: hides a previously sent ReplyKeyboardMarkup
# Takes an optional selective argument (True/False, default False)
markup = types.ReplyKeyboardHide(selective=False)
tb.send_message(chat_id, message, reply_markup=markup)
.. code:: python
# ForceReply: forces a user to reply to a message
# Takes an optional selective argument (True/False, default False)
markup = types.ForceReply(selective=False)
tb.send_message(chat_id, "Send me another word:", reply_markup=markup)
ForceReply:
.. figure:: https://pp.vk.me/c624430/v624430512/473ec/602byyWUHcs.jpg
:alt: ForceReply
ForceReply
Advanced use of the API
=======================
Asynchronous delivery of messages
---------------------------------
There exists an implementation of TeleBot which executes all
``send_xyz`` and the ``get_me`` functions asynchronously. This can speed
up you bot **significantly**, but it has unwanted side effects if used
without caution. To enable this behaviour, create an instance of
AsyncTeleBot instead of TeleBot.
.. code:: python
tb = telebot.AsyncTeleBot("TOKEN")
Now, every function that calls the Telegram API is executed in a
separate Thread. The functions are modified to return an AsyncTask
instance (defined in util.py). Using AsyncTeleBot allows you to do the
following:
.. code:: python
import telebot
tb = telebot.AsyncTeleBot("TOKEN")
task = tb.get_me() # Execute an API call
# Do some other operations...
a = 0
for a in range(100):
a += 10
result = task.wait() # Get the result of the execution
*Note: if you execute send\_xyz functions after eachother without
calling wait(), the order in which messages are delivered might be
wrong.*
Sending large text messages
---------------------------
Sometimes you must send messages that exceed 5000 characters. The
Telegram API can not handle that many characters in one request, so we
need to split the message in multiples. Here is how to do that using the
API:
.. code:: python
from telebot import util
large_text = open("large_text.txt", "rb").read()
# Split the text each 3000 characters.
# split_string returns a list with the splitted text.
splitted_text = util.split_string(large_text, 3000)
for text in splitted_text:
tb.send_message(chat_id, text)
Controlling the amount of Threads used by TeleBot
-------------------------------------------------
The TeleBot constructor takes the following optional arguments:
- create\_threads: True/False (default True). A flag to indicate
whether TeleBot should execute message handlers on it's polling
Thread.
- num\_threads: integer (default 4). Controls the amount of
WorkerThreads created for the internal thread pool that TeleBot uses
to execute message handlers. Is not used when create\_threads is
False.
The listener mechanism
----------------------
As an alternative to the message handlers, one can also register a
function as a listener to TeleBot. Example:
.. code:: python
def handle_messages(messages):
for message in messsages:
# Do something with the message
bot.reply_to(message, 'Hi')
bot.set_update_listener(handle_messages)
bot.polling()
Using webhooks
--------------
When using webhooks telegram sends one Update per call, for processing
it you should call process\_new\_messages([update.message]) when you
recieve it.
There are some examples using webhooks in the
*examples/webhook\_examples* directory.
Logging
-------
You can use the Telebot module logger to log debug info about Telebot.
Use ``telebot.logger`` to get the logger of the TeleBot module. It is
possible to add custom logging Handlers to the logger. Refer to the
`Python logging module
page <https://docs.python.org/2/library/logging.html>`__ for more info.
.. code:: python
import logging
logger = telebot.logger
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
F.A.Q.
======
How can I distinguish a User and a GroupChat in message.chat?
-------------------------------------------------------------
There are two ways to do this:
- Checking the instance of message.chat with ``isinstance``:
\`\`\`python def is\_user(chat): return isinstance(chat, types.User)
print is\_user(message.chat) # True or False
``- Checking whether the chat id is negative or positive. If the chat id is negative, the chat is a GroupChat, if it is positive, it is a User. Example:``\ python
def is\_user(chat): return chat.id > 0
print is\_user(message.chat) # True or False \`\`\`
The Telegram Chat Group
=======================
Get help. Discuss. Chat.
Join the `pyTelegramBotAPI Telegram Chat
Group <https://telegram.me/joinchat/067e22c60035523fda8f6025ee87e30b>`__.
We now have a Telegram Channel as well! Keep yourself up to date with
API changes, and `join it <https://telegram.me/pytelegrambotapi>`__.
More examples
=============
- `Echo
Bot <https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/echo_bot.py>`__
- `Deep
Linking <https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/deep_linking.py>`__
- `next\_step\_handler
Example <https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py>`__
Bots using this API
===================
- `SiteAlert bot <https://telegram.me/SiteAlert_bot>`__
(`source <https://github.com/ilteoood/SiteAlert-Python>`__) by
*ilteoood* - Monitors websites and sends a notification on changes
Want to have your bot listed here? Send a Telegram message to
@eternnoir or @pevdh.
.. |Build Status| image:: https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master
:target: https://travis-ci.org/eternnoir/pyTelegramBotAPI

70
examples/deep_linking.py Normal file
View File

@ -0,0 +1,70 @@
# This example shows how to implement deep linking (https://core.telegram.org/bots#deep-linking)
# with the pyTelegramBotAPI.
# Note: This is not a working, production-ready sample.
#
# In this example we are connecting a user account on a website with a Telegram bot.
# Implementing this will enable you to push notifications (and other content) to your users' Telegram account.
# In this explanation the word 'database' can refer to any form of key-value storage.
# The deep linking explained:
#
# 1. Let the user log in on an actual website with actual username-password authentication.
#
# 2. Generate a unique hashcode (we will call it unique_code)
#
# 3. Save unique_code->username to the database.
#
# 4. Show the user the URL https://telegram.me/YOURBOTNAME?start=unique_code
#
# 5. Now as soon as the user opens this URL in Telegram and presses 'Start',
# your bot will receive a text message containing '/start unique_code',
# where unique_code is of course replaced by the actual hashcode.
#
# 6. Let the bot retrieve the username by querying the database for unique_code.
#
# 7. Save chat_id->username to the database.
#
# 8. Now when your bot receives another message, it can query message.chat.id in the database
# to check if the message is from this specific user. (And handle accordingly) or
# you can push messages to the user using his chat id.
#
# Steps 1 to 4 will have to be implemented in a web server, using a language such as PHP, Python, C# or Java. These
# steps are not shown here. Only steps 5 to 7 are illustrated, some in pseudo-code, with this example.
import telebot
import time
bot = telebot.TeleBot('TOKEN')
def extract_unique_code(text):
# Extracts the unique_code from the sent /start command.
return text.split()[1] if len(text.split()) > 1 else None
def in_storage(unique_code):
# (pseudo-code) Should check if a unique code exists in storage
return True
def get_username_from_storage(unique_code):
# (pseudo-code) Does a query to the storage, retrieving the associated username
# Should be replaced by a real database-lookup.
return "ABC" if in_storage(unique_code) else None
def save_chat_id(chat_id, username):
# (pseudo-code) Save the chat_id->username to storage
# Should be replaced by a real database query.
pass
@bot.message_handler(commands=['start'])
def send_welcome(message):
unique_code = extract_unique_code(message.text)
if unique_code: # if the '/start' command contains a unique_code
username = get_username_from_storage(unique_code)
if username: # if the username exists in our database
save_chat_id(message.chat.id, username)
reply = "Hello {0}, how are you?".format(username)
else:
reply = "I have no clue who you are..."
else:
reply = "Please visit me via a provided URL from the website."
bot.reply_to(message, reply)
bot.polling()

View File

@ -0,0 +1,131 @@
"""
This is a detailed example using almost every command of the API
"""
import telebot
from telebot import types
import time
TOKEN = '<token_string>'
knownUsers = [] # todo: save these in a file,
userStep = {} # so they won't reset every time the bot restarts
commands = { # command description used in the "help" command
'start': 'Get used to the bot',
'help': 'Gives you information about the available commands',
'sendLongText': 'A test using the \'send_chat_action\' command',
'getImage': 'A test using multi-stage messages, custom keyboard, and media sending'
}
imageSelect = types.ReplyKeyboardMarkup(one_time_keyboard=True) # create the image selection keyboard
imageSelect.add('cock', 'pussy')
hideBoard = types.ReplyKeyboardHide() # if sent as reply_markup, will hide the keyboard
# error handling if user isn't known yet
# (obsolete once known users are saved to file, because all users
# had to use the /start command and are therefore known to the bot)
def get_user_step(uid):
if uid in userStep:
return userStep[uid]
else:
knownUsers.append(uid)
userStep[uid] = 0
print "New user detected, who hasn't used \"/start\" yet"
return 0
# only used for console output now
def listener(messages):
"""
When new messages arrive TeleBot will call this function.
"""
for m in messages:
if m.content_type == 'text':
# print the sent message to the console
print str(m.chat.first_name) + " [" + str(m.chat.id) + "]: " + m.text
bot = telebot.TeleBot(TOKEN)
bot.set_update_listener(listener) # register listener
# handle the "/start" command
@bot.message_handler(commands=['start'])
def command_start(m):
cid = m.chat.id
if cid not in knownUsers: # if user hasn't used the "/start" command yet:
knownUsers.append(cid) # save user id, so you could brodcast messages to all users of this bot later
userStep[cid] = 0 # save user id and his current "command level", so he can use the "/getImage" command
bot.send_message(cid, "Hello, stranger, let me scan you...")
bot.send_message(cid, "Scanning complete, I know you now")
command_help(m) # show the new user the help page
else:
bot.send_message(cid, "I already know you, no need for me to scan you again!")
# help page
@bot.message_handler(commands=['help'])
def command_help(m):
cid = m.chat.id
help_text = "The following commands are available: \n"
for key in commands: # generate help text out of the commands dictionary defined at the top
help_text += "/" + key + ": "
help_text += commands[key] + "\n"
bot.send_message(cid, help_text) # send the generated help page
# chat_action example (not a good one...)
@bot.message_handler(commands=['sendLongText'])
def command_long_text(m):
cid = m.chat.id
bot.send_message(cid, "If you think so...")
bot.send_chat_action(cid, 'typing') # show the bot "typing" (max. 5 secs)
time.sleep(3)
bot.send_message(cid, ".")
# user can chose an image (multi-stage command example)
@bot.message_handler(commands=['getImage'])
def command_image(m):
cid = m.chat.id
bot.send_message(cid, "Please choose your image now", reply_markup=imageSelect) # show the keyboard
userStep[cid] = 1 # set the user to the next step (expecting a reply in the listener now)
# if the user has issued the "/getImage" command, process the answer
@bot.message_handler(func=lambda message: get_user_step(message.chat.id) == 1)
def msg_image_select(m):
cid = m.chat.id
text = m.text
# for some reason the 'upload_photo' status isn't quite working (doesn't show at all)
bot.send_chat_action(cid, 'typing')
if text == "cock": # send the appropriate image based on the reply to the "/getImage" command
bot.send_photo(cid, open('rooster.jpg', 'rb'),
reply_markup=hideBoard) # send file and hide keyboard, after image is sent
userStep[cid] = 0 # reset the users step back to 0
elif text == "pussy":
bot.send_photo(cid, open('kitten.jpg', 'rb'), reply_markup=hideBoard)
userStep[cid] = 0
else:
bot.send_message(cid, "Don't type bullsh*t, if I give you a predefined keyboard!")
bot.send_message(cid, "Please try again")
# filter on a specific message
@bot.message_handler(func=lambda message: message.text == "hi")
def command_text_hi(m):
bot.send_message(m.chat.id, "I love you too!")
# default handler for every other text
@bot.message_handler(func=lambda message: True, content_types=['text'])
def command_default(m):
# this is the standard reply to a normal message
bot.send_message(m.chat.id, "I don't understand \"" + m.text + "\"\nMaybe try the help page at /help")
bot.polling()

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -0,0 +1,15 @@
import telebot
TOKEN = 'YOUR BOT TOKEN'
CHAT_ID = 'YOUR CHAT ID'
bot = telebot.TeleBot(TOKEN)
ret_msg = bot.send_voice(CHAT_ID, open('tests/test_data/record.ogg'))
file_info = bot.get_file(ret_msg.voice.file_id)
downloaded_file = bot.download_file(file_info.file_path)
with open('new_file.ogg', 'wb') as new_file:
new_file.write(downloaded_file)

View File

@ -7,9 +7,8 @@ API_TOKEN = '<api_token>'
bot = telebot.TeleBot(API_TOKEN)
# Handle '/start' and '/help'
@bot.message_handler(commands=['help, start'])
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
bot.reply_to(message, """\
Hi there, I am EchoBot.
@ -23,6 +22,3 @@ def echo_message(message):
bot.reply_to(message, message.text)
bot.polling()
while True:
pass

View File

@ -0,0 +1,72 @@
# This example show how to write an inline mode telegramt bot use pyTelegramBotAPI.
import telebot
import time
import sys
import logging
from telebot import types
API_TOKEN = '<api_token>'
bot = telebot.TeleBot(API_TOKEN)
telebot.logger.setLevel(logging.DEBUG)
@bot.inline_handler(lambda query: query.query == 'text')
def query_text(inline_query):
try:
r = types.InlineQueryResultArticle('1', 'Result', inline_query.query)
r2 = types.InlineQueryResultArticle('2', 'Result2', inline_query.query)
bot.answer_inline_query(inline_query.id, [r, r2])
except Exception as e:
print(e)
@bot.inline_handler(lambda query: query.query == 'photo')
def query_photo(inline_query):
try:
r = types.InlineQueryResultPhoto('1',
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/kitten.jpg',
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/kitten.jpg')
r2 = types.InlineQueryResultPhoto('2',
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/rooster.jpg',
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/rooster.jpg')
bot.answer_inline_query(inline_query.id, [r, r2])
except Exception as e:
print(e)
@bot.inline_handler(lambda query: query.query == 'video')
def query_video(inline_query):
try:
r = types.InlineQueryResultVideo('1',
'https://github.com/eternnoir/pyTelegramBotAPI/blob/master/tests/test_data/test_video.mp4?raw=true',
'video/mp4', 'Video',
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/rooster.jpg',
'Title'
)
bot.answer_inline_query(inline_query.id, [r])
except Exception as e:
print(e)
@bot.inline_handler(lambda query: len(query.query) is 0)
def default_query(inline_query):
try:
r = types.InlineQueryResultArticle('1', 'default', 'default')
bot.answer_inline_query(inline_query.id, [r])
except Exception as e:
print(e)
def main_loop():
bot.polling(True)
while 1:
time.sleep(3)
if __name__ == '__main__':
try:
main_loop()
except KeyboardInterrupt:
print >> sys.stderr, '\nExiting by user request.\n'
sys.exit(0)

78
examples/step_example.py Normal file
View File

@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
"""
This Example will show you how to use register_next_step handler.
"""
import time
import telebot
from telebot import types
API_TOKEN = '<api_token>'
bot = telebot.TeleBot(API_TOKEN)
user_dict = {}
class User:
def __init__(self, name):
self.name = name
self.age = None
self.sex = None
# Handle '/start' and '/help'
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
msg = bot.reply_to(message, """\
Hi there, I am Example bot.
What's your name?
""")
bot.register_next_step_handler(msg, process_name_step)
def process_name_step(message):
try:
chat_id = message.chat.id
name = message.text
user = User(name)
user_dict[chat_id] = user
msg = bot.reply_to(message, 'How old are you?')
bot.register_next_step_handler(msg, process_age_step)
except Exception as e:
bot.reply_to(message, 'oooops')
def process_age_step(message):
try:
chat_id = message.chat.id
age = message.text
if not age.isdigit():
msg = bot.reply_to(message, 'Age should be a number. How old are you?')
bot.register_next_step_handler(msg, process_age_step)
return
user = user_dict[chat_id]
user.age = age
markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
markup.add('Male', 'Female')
msg = bot.reply_to(message, 'What is your gender', reply_markup=markup)
bot.register_next_step_handler(msg, process_sex_step)
except Exception as e:
bot.reply_to(message, 'oooops')
def process_sex_step(message):
try:
chat_id = message.chat.id
sex = message.text
user = user_dict[chat_id]
if (sex == u'Male') or (sex == u'Female'):
user.sex = sex
else:
raise Exception()
bot.send_message(chat_id, 'Nice to meet you ' + user.name + '\n Age:' + str(user.age) + '\n Sex:' + user.sex)
except Exception as e:
bot.reply_to(message, 'oooops')
bot.polling()

View File

@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
# This bot was made specifically for the pyTelegramAPI Telegram chat,
# and goes by the name 'TeleBot (@pyTeleBot)'. Join our group to talk to him!
# WARNING: Tested with Python 2.7
import telebot
import os
text_messages = {
'welcome':
u'Please welcome {name}!\n\n'
u'This chat is intended for questions about and discussion of the pyTelegramBotAPI.\n'
u'To enable group members to answer your questions fast and accurately, please make sure to study the '
u'project\'s documentation (https://github.com/eternnoir/pyTelegramBotAPI/blob/master/README.md) and the '
u'examples (https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples) first.\n\n'
u'I hope you enjoy your stay here!',
'info':
u'My name is TeleBot,\n'
u'I am a bot that assists these wonderful bot-creating people of this bot library group chat.\n'
u'Also, I am still under development. Please improve my functionality by making a pull request! '
u'Suggestions are also welcome, just drop them in this group chat!',
'wrong_chat':
u'Hi there!\nThanks for trying me out. However, this bot can only be used in the pyTelegramAPI group chat.\n'
u'Join us!\n\n'
u'https://telegram.me/joinchat/067e22c60035523fda8f6025ee87e30b'
}
if "TELEBOT_BOT_TOKEN" not in os.environ or "GROUP_CHAT_ID" not in os.environ:
raise AssertionError("Please configure TELEBOT_BOT_TOKEN and GROUP_CHAT_ID as environment variables")
bot = telebot.AsyncTeleBot(os.environ["TELEBOT_BOT_TOKEN"])
GROUP_CHAT_ID = int(os.environ["GROUP_CHAT_ID"])
def is_api_group(chat_id):
return chat_id== GROUP_CHAT_ID
@bot.message_handler(func=lambda m: True, content_types=['new_chat_participant'])
def on_user_joins(message):
if not is_api_group(message.chat.id):
return
name = message.new_chat_participant.first_name
if hasattr(message.new_chat_participant, 'last_name') and message.new_chat_participant.last_name is not None:
name += u" {}".format(message.new_chat_participant.last_name)
if hasattr(message.new_chat_participant, 'username') and message.new_chat_participant.username is not None:
name += u" (@{})".format(message.new_chat_participant.username)
bot.reply_to(message, text_messages['welcome'].format(name=name))
@bot.message_handler(commands=['info', 'help'])
def on_info(message):
if not is_api_group(message.chat.id):
bot.reply_to(message, text_messages['wrong_chat'])
return
bot.reply_to(message, text_messages['info'])
@bot.message_handler(commands=["ping"])
def on_ping(message):
bot.reply_to(message, "Still alive and kicking!")
@bot.message_handler(commands=['start'])
def on_start(message):
if not is_api_group(message.chat.id):
bot.reply_to(message, text_messages['wrong_chat'])
return
def listener(messages):
for m in messages:
print str(m)
bot.set_update_listener(listener)
bot.polling()

View File

@ -0,0 +1,36 @@
# Webhook examples using pyTelegramBotAPI
There are 3 examples in this directory using different libraries:
* **Python (CPython):** *webhook_cpython_echo_bot.py*
* **Pros:**
* Official python libraries, it works out of the box (doesn't require to
install anything).
* Works with Python 2 and Python 3 (need to be converted with 2to3).
* **Cons:**
* Ugly code.
* Many things to handle yourself, this can lead to errors.
* Not powerful, do the trick but the performance is low.
* **CherryPy (3.8.0):** *webhook_cherrypy_echo_bot.py*
* **Pros:**
* It's a web application framework, cleaner code, uses objects for defining
the web application.
* Very good performance.
* The project seems to be active, latest version is recent.
* Works with Python 2 and Python 3.
* **Cons:**
* Some things are not very intuitive, reading the doc is a must.
* **Flask (0.10.1):** *webhook_flask_echo_bot.py*
* **Pros:**
* It's a web application framework, cleaner code, uses decorator which can
be nice.
* Good performance.
* It's intuitive if you know how web application works.
* **Cons:**
* The project seems not to be very active, latest version dates 2013.
* They don't recommend to use it with Python 3, but may work.
* May be a oversized for just handling webhook petitions.
*Latest update of this document: 2015-10-06*

View File

@ -0,0 +1,85 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This is a simple echo bot using decorators and webhook with CherryPy
# It echoes any incoming text messages and does not use the polling method.
import cherrypy
import telebot
import logging
API_TOKEN = '<api_token>'
WEBHOOK_HOST = '<ip/host where the bot is running>'
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
# Quick'n'dirty SSL certificate generation:
#
# openssl genrsa -out webhook_pkey.pem 2048
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
#
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
# with the same value in you put in WEBHOOK_HOST
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.INFO)
bot = telebot.TeleBot(API_TOKEN)
# WebhookServer, process webhook calls
class WebhookServer(object):
@cherrypy.expose
def index(self):
if 'content-length' in cherrypy.request.headers and \
'content-type' in cherrypy.request.headers and \
cherrypy.request.headers['content-type'] == 'application/json':
length = int(cherrypy.request.headers['content-length'])
json_string = cherrypy.request.body.read(length).decode("utf-8")
update = telebot.types.Update.de_json(json_string)
bot.process_new_messages([update.message])
return ''
else:
raise cherrypy.HTTPError(403)
# Handle '/start' and '/help'
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
bot.reply_to(message,
("Hi there, I am EchoBot.\n"
"I am here to echo your kind words back to you."))
# Handle all other messages
@bot.message_handler(func=lambda message: True, content_types=['text'])
def echo_message(message):
bot.reply_to(message, message.text)
# Remove webhook, it fails sometimes the set if there is a previous webhook
bot.remove_webhook()
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
# Start cherrypy server
cherrypy.config.update({
'server.socket_host': WEBHOOK_LISTEN,
'server.socket_port': WEBHOOK_PORT,
'server.ssl_module': 'builtin',
'server.ssl_certificate': WEBHOOK_SSL_CERT,
'server.ssl_private_key': WEBHOOK_SSL_PRIV
})
cherrypy.quickstart(WebhookServer(), WEBHOOK_URL_PATH, {'/': {}})

View File

@ -0,0 +1,99 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This is a simple echo bot using decorators and webhook with BaseHTTPServer
# It echoes any incoming text messages and does not use the polling method.
import BaseHTTPServer
import ssl
import telebot
import logging
API_TOKEN = '<api_token>'
WEBHOOK_HOST = '<ip/host where the bot is running>'
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
# Quick'n'dirty SSL certificate generation:
#
# openssl genrsa -out webhook_pkey.pem 2048
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
#
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
# with the same value in you put in WEBHOOK_HOST
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.INFO)
bot = telebot.TeleBot(API_TOKEN)
# WebhookHandler, process webhook calls
class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler):
server_version = "WebhookHandler/1.0"
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):
if self.path == WEBHOOK_URL_PATH and \
'content-type' in self.headers and \
'content-length' in self.headers and \
self.headers['content-type'] == 'application/json':
json_string = self.rfile.read(int(self.headers['content-length']))
self.send_response(200)
self.end_headers()
update = telebot.types.Update.de_json(json_string)
bot.process_new_messages([update.message])
else:
self.send_error(403)
self.end_headers()
# Handle '/start' and '/help'
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
bot.reply_to(message,
("Hi there, I am EchoBot.\n"
"I am here to echo your kind words back to you."))
# Handle all other messages
@bot.message_handler(func=lambda message: True, content_types=['text'])
def echo_message(message):
bot.reply_to(message, message.text)
# Remove webhook, it fails sometimes the set if there is a previous webhook
bot.remove_webhook()
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
# Start server
httpd = BaseHTTPServer.HTTPServer((WEBHOOK_LISTEN, WEBHOOK_PORT),
WebhookHandler)
httpd.socket = ssl.wrap_socket(httpd.socket,
certfile=WEBHOOK_SSL_CERT,
keyfile=WEBHOOK_SSL_PRIV,
server_side=True)
httpd.serve_forever()

View File

@ -0,0 +1,84 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This is a simple echo bot using decorators and webhook with flask
# It echoes any incoming text messages and does not use the polling method.
import flask
import telebot
import logging
API_TOKEN = '<api_token>'
WEBHOOK_HOST = '<ip/host where the bot is running>'
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
# Quick'n'dirty SSL certificate generation:
#
# openssl genrsa -out webhook_pkey.pem 2048
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
#
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
# with the same value in you put in WEBHOOK_HOST
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.INFO)
bot = telebot.TeleBot(API_TOKEN)
app = flask.Flask(__name__)
# Empty webserver index, return nothing, just http 200
@app.route('/', methods=['GET', 'HEAD'])
def index():
return ''
# Process webhook calls
@app.route(WEBHOOK_URL_PATH, methods=['POST'])
def webhook():
if flask.request.headers.get('content-type') == 'application/json':
json_string = flask.request.get_data()
update = telebot.types.Update.de_json(json_string)
bot.process_new_messages([update.message])
return ''
else:
flask.abort(403)
# Handle '/start' and '/help'
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
bot.reply_to(message,
("Hi there, I am EchoBot.\n"
"I am here to echo your kind words back to you."))
# Handle all other messages
@bot.message_handler(func=lambda message: True, content_types=['text'])
def echo_message(message):
bot.reply_to(message, message.text)
# Remove webhook, it fails sometimes the set if there is a previous webhook
bot.remove_webhook()
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
# Start flask server
app.run(host=WEBHOOK_LISTEN,
port=WEBHOOK_PORT,
ssl_context=(WEBHOOK_SSL_CERT, WEBHOOK_SSL_PRIV),
debug=True)

View File

@ -1,4 +1,5 @@
py==1.4.29
pytest==2.7.2
requests==2.7.0
six==1.9.0
wheel==0.24.0

View File

@ -1,18 +1,23 @@
#!/usr/bin/env python
from setuptools import setup
def readme():
with open('README.rst') as f:
return f.read()
setup(name='pyTelegramBotAPI',
version='0.2.0',
version='1.4.2',
description='Python Telegram bot api. ',
long_description=readme(),
author='eternnoir',
author_email='eternnoir@gmail.com',
url='https://github.com/eternnoir/pyTelegramBotAPI',
packages=['telebot'],
license='GPL2',
keywords='tools',
install_requires=['pytest', 'requests'],
keywords='telegram bot api tools',
install_requires=['pytest', 'requests', 'six'],
classifiers=[
'Development Status :: 4 - Beta',
'Development Status :: 5 - Production/Stable',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',

View File

@ -2,16 +2,30 @@
from __future__ import print_function
import threading
import time
import re
from telebot import apihelper, types
import sys
import six
import logging
logger = logging.getLogger('TeleBot')
formatter = logging.Formatter(
'%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"'
)
console_output_handler = logging.StreamHandler(sys.stderr)
console_output_handler.setFormatter(formatter)
logger.addHandler(console_output_handler)
logger.setLevel(logging.ERROR)
from telebot import apihelper, types, util
"""
Module : telebot
"""
API_URL = r"https://api.telegram.org/"
class TeleBot:
""" This is TeleBot Class
@ -30,69 +44,212 @@ class TeleBot:
getUpdates
"""
def __init__(self, token):
def __init__(self, token, threaded=True, skip_pending=False):
"""
:param token: bot API token
:return: Telebot object.
"""
self.token = token
self.update_listener = []
self.polling_thread = None
self.__stop_polling = False
self.interval = 3
self.skip_pending = skip_pending
self.__stop_polling = threading.Event()
self.last_update_id = 0
self.exc_info = None
self.message_subscribers_messages = []
self.message_subscribers_callbacks = []
self.message_subscribers_lock = threading.Lock()
# key: chat_id, value: handler list
self.message_subscribers_next_step = {}
self.pre_message_subscribers_next_step = {}
self.message_handlers = []
self.inline_handlers = []
self.chosen_inline_handlers = []
def get_update(self):
self.threaded = threaded
if self.threaded:
self.worker_pool = util.ThreadPool()
def set_webhook(self, url=None, certificate=None):
return apihelper.set_webhook(self.token, url, certificate)
def remove_webhook(self):
return self.set_webhook() # No params resets webhook
def get_updates(self, offset=None, limit=None, timeout=20):
"""
Use this method to receive incoming updates using long polling (wiki). An Array of Update objects is returned.
:param offset: Integer. Identifier of the first update to be returned.
:param limit: Integer. Limits the number of updates to be retrieved.
:param timeout: Integer. Timeout in seconds for long polling.
:return: array of Updates
"""
json_updates = apihelper.get_updates(self.token, offset, limit, timeout)
ret = []
for ju in json_updates:
ret.append(types.Update.de_json(ju))
return ret
def __skip_updates(self):
"""
Get and discard all pending updates before first poll of the bot
:return: total updates skipped
"""
total = 0
updates = self.get_updates(offset=self.last_update_id, timeout=1)
while updates:
total += len(updates)
for update in updates:
if update.update_id > self.last_update_id:
self.last_update_id = update.update_id
updates = self.get_updates(offset=self.last_update_id + 1, timeout=1)
return total
def __retrieve_updates(self, timeout=20):
"""
Retrieves any updates from the Telegram API.
Registered listeners and applicable message handlers will be notified when a new message arrives.
:raises ApiException when a call has failed.
"""
updates = apihelper.get_updates(self.token, offset=(self.last_update_id + 1), timeout=20)
new_messages = []
for update in updates:
if update['update_id'] > self.last_update_id:
self.last_update_id = update['update_id']
msg = types.Message.de_json(update['message'])
new_messages.append(msg)
if self.skip_pending:
logger.debug('Skipped {0} pending messages'.format(self.__skip_updates()))
self.skip_pending = False
updates = self.get_updates(offset=(self.last_update_id + 1), timeout=timeout)
self.process_new_updates(updates)
def process_new_updates(self, updates):
new_messages = []
new_inline_querys = []
new_chosen_inline_results = []
for update in updates:
if update.update_id > self.last_update_id:
self.last_update_id = update.update_id
if update.message:
new_messages.append(update.message)
if update.inline_query:
new_inline_querys.append(update.inline_query)
if update.chosen_inline_result:
new_chosen_inline_results.append(update.chosen_inline_result)
logger.debug('Received {0} new updates'.format(len(updates)))
if len(new_messages) > 0:
self.__notify_update(new_messages)
self._notify_command_handlers(new_messages)
self.process_new_messages(new_messages)
if len(new_inline_querys) > 0:
self.process_new_inline_query(new_inline_querys)
if len(new_chosen_inline_results) > 0:
self.process_new_chosen_inline_query(new_chosen_inline_results)
def process_new_messages(self, new_messages):
self._append_pre_next_step_handler()
self.__notify_update(new_messages)
self._notify_command_handlers(self.message_handlers, new_messages)
self._notify_message_subscribers(new_messages)
self._notify_message_next_handler(new_messages)
def process_new_inline_query(self, new_inline_querys):
self._notify_command_handlers(self.inline_handlers, new_inline_querys)
def process_new_chosen_inline_query(self, new_chosen_inline_querys):
self._notify_command_handlers(self.chosen_inline_handlers, new_chosen_inline_querys)
def __notify_update(self, new_messages):
for listener in self.update_listener:
t = threading.Thread(target=listener, args=new_messages)
t.start()
self.__exec_task(listener, new_messages)
def polling(self):
def polling(self, none_stop=False, interval=0, timeout=20):
"""
This function creates a new Thread that calls an internal __polling function.
This function creates a new Thread that calls an internal __retrieve_updates function.
This allows the bot to retrieve Updates automagically and notify listeners and message handlers accordingly.
Do not call this function more than once!
Warning: Do not call this function more than once!
Always get updates.
:param none_stop: Do not stop polling when an ApiException occurs.
:param timeout: Timeout in seconds for long polling.
:return:
"""
self.__stop_polling = False
self.polling_thread = threading.Thread(target=self.__polling, args=())
self.polling_thread.daemon = True
self.polling_thread.start()
if self.threaded:
self.__threaded_polling(none_stop, interval, timeout)
else:
self.__non_threaded_polling(none_stop, interval, timeout)
def __polling(self):
print('TeleBot: Started polling.')
while not self.__stop_polling:
def __threaded_polling(self, none_stop=False, interval=0, timeout=3):
logger.info('Started polling.')
self.__stop_polling.clear()
error_interval = .25
polling_thread = util.WorkerThread(name="PollingThread")
or_event = util.OrEvent(
polling_thread.done_event,
polling_thread.exception_event,
self.worker_pool.exception_event
)
while not self.__stop_polling.wait(interval):
or_event.clear()
try:
self.get_update()
except Exception as e:
print("TeleBot: Exception occurred. Stopping.")
self.__stop_polling = True
print(e)
polling_thread.put(self.__retrieve_updates, timeout)
print('TeleBot: Stopped polling.')
or_event.wait() # wait for polling thread finish, polling thread error or thread pool error
polling_thread.raise_exceptions()
self.worker_pool.raise_exceptions()
error_interval = .25
except apihelper.ApiException as e:
logger.error(e)
if not none_stop:
self.__stop_polling.set()
logger.info("Exception occurred. Stopping.")
else:
polling_thread.clear_exceptions()
self.worker_pool.clear_exceptions()
logger.info("Waiting for {0} seconds until retry".format(error_interval))
time.sleep(error_interval)
error_interval *= 2
except KeyboardInterrupt:
logger.info("KeyboardInterrupt received.")
self.__stop_polling.set()
polling_thread.stop()
break
logger.info('Stopped polling.')
def __non_threaded_polling(self, none_stop=False, interval=0, timeout=3):
logger.info('Started polling.')
self.__stop_polling.clear()
error_interval = .25
while not self.__stop_polling.wait(interval):
try:
self.__retrieve_updates(timeout)
error_interval = .25
except apihelper.ApiException as e:
logger.error(e)
if not none_stop:
self.__stop_polling.set()
logger.info("Exception occurred. Stopping.")
else:
logger.info("Waiting for {0} seconds until retry".format(error_interval))
time.sleep(error_interval)
error_interval *= 2
except KeyboardInterrupt:
logger.info("KeyboardInterrupt received.")
self.__stop_polling.set()
break
logger.info('Stopped polling.')
def __exec_task(self, task, *args, **kwargs):
if self.threaded:
self.worker_pool.put(task, *args, **kwargs)
else:
task(*args, **kwargs)
def stop_polling(self):
self.__stop_polling = True
self.__stop_polling.set()
def set_update_listener(self, listener):
self.update_listener.append(listener)
@ -101,6 +258,12 @@ class TeleBot:
result = apihelper.get_me(self.token)
return types.User.de_json(result)
def get_file(self, file_id):
return types.File.de_json(apihelper.get_file(self.token, file_id))
def download_file(self, file_path):
return apihelper.download_file(self.token, file_path)
def get_user_profile_photos(self, user_id, offset=None, limit=None):
"""
Retrieves the user profile photos of the person with 'user_id'
@ -113,31 +276,41 @@ class TeleBot:
result = apihelper.get_user_profile_photos(self.token, user_id, offset, limit)
return types.UserProfilePhotos.de_json(result)
def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None):
def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None):
"""
Use this method to send text messages.
Warning: Do not send more than about 5000 characters each message, otherwise you'll risk an HTTP 414 error.
If you must send more than 5000 characters, use the split_string function in apihelper.py.
:param chat_id:
:param text:
:param disable_web_page_preview:
:param reply_to_message_id:
:param reply_markup:
:param parse_mode:
:param disable_notification: Boolean, Optional. Sends the message silently.
:return: API reply.
"""
return types.Message.de_json(
apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id,
reply_markup))
reply_markup, parse_mode, disable_notification))
def forward_message(self, chat_id, from_chat_id, message_id):
def forward_message(self, chat_id, from_chat_id, message_id, disable_notification=None):
"""
Use this method to forward messages of any kind.
:param disable_notification:
:param chat_id: which chat to forward
:param from_chat_id: which chat message from
:param message_id: message id
:return: API reply.
"""
return types.Message.de_json(apihelper.forward_message(self.token, chat_id, from_chat_id, message_id))
return types.Message.de_json(
apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification))
def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None):
def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
"""
Use this method to send photos.
:param chat_id:
@ -148,22 +321,42 @@ class TeleBot:
:return: API reply.
"""
return types.Message.de_json(
apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup))
apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup,
disable_notification))
def send_audio(self, chat_id, data, reply_to_message_id=None, reply_markup=None):
def send_audio(self, chat_id, audio, duration=None, performer=None, title=None, reply_to_message_id=None,
reply_markup=None, disable_notification=None):
"""
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
:param chat_id:
:param data:
:param reply_to_message_id:
Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format.
:param chat_id:Unique identifier for the message recipient
:param audio:Audio file to send.
:param duration:Duration of the audio in seconds
:param performer:Performer
:param title:Track name
:param reply_to_message_id:If the message is a reply, ID of the original message
:param reply_markup:
:return: API reply.
:return: Message
"""
return types.Message.de_json(
apihelper.send_data(self.token, chat_id, data, 'audio', reply_to_message_id, reply_markup))
apihelper.send_audio(self.token, chat_id, audio, duration, performer, title, reply_to_message_id,
reply_markup, disable_notification))
def send_document(self, chat_id, data, reply_to_message_id=None, reply_markup=None):
def send_voice(self, chat_id, voice, duration=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
"""
Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message.
:param chat_id:Unique identifier for the message recipient.
:param voice:
:param duration:Duration of sent audio in seconds
:param reply_to_message_id:
:param reply_markup:
:return: Message
"""
return types.Message.de_json(
apihelper.send_voice(self.token, chat_id, voice, duration, reply_to_message_id, reply_markup,
disable_notification))
def send_document(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None):
"""
Use this method to send general files.
:param chat_id:
@ -173,9 +366,10 @@ class TeleBot:
:return: API reply.
"""
return types.Message.de_json(
apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup))
apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup,
disable_notification))
def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None):
def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None):
"""
Use this method to send .webp stickers.
:param chat_id:
@ -185,21 +379,27 @@ class TeleBot:
:return: API reply.
"""
return types.Message.de_json(
apihelper.send_data(self.token, chat_id, data, 'sticker', reply_to_message_id, reply_markup))
apihelper.send_data(self.token, chat_id, data, 'sticker', reply_to_message_id, reply_markup,
disable_notification))
def send_video(self, chat_id, data, reply_to_message_id=None, reply_markup=None):
def send_video(self, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
"""
Use this method to send video files, Telegram clients support mp4 videos.
:param chat_id:
:param data:
:param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id
:param data: InputFile or String : Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram server
:param duration: Integer : Duration of sent video in seconds
:param caption: String : Video caption (may also be used when resending videos by file_id).
:param reply_to_message_id:
:param reply_markup:
:return: API reply.
:return:
"""
return types.Message.de_json(
apihelper.send_data(self.token, chat_id, data, 'video', reply_to_message_id, reply_markup))
apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup,
disable_notification))
def send_location(self, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None):
def send_location(self, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
"""
Use this method to send point on the map.
:param chat_id:
@ -210,7 +410,8 @@ class TeleBot:
:return: API reply.
"""
return types.Message.de_json(
apihelper.send_location(self.token, chat_id, latitude, longitude, reply_to_message_id, reply_markup))
apihelper.send_location(self.token, chat_id, latitude, longitude, reply_to_message_id, reply_markup,
disable_notification))
def send_chat_action(self, chat_id, action):
"""
@ -225,8 +426,87 @@ class TeleBot:
return apihelper.send_chat_action(self.token, chat_id, action)
def reply_to(self, message, text, **kwargs):
"""
Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)`
"""
return self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)
def answer_inline_query(self, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None):
"""
Use this method to send answers to an inline query. On success, True is returned.
No more than 50 results per query are allowed.
:param inline_query_id: Unique identifier for the answered query
:param results: Array of results for the inline query
:param cache_time: The maximum amount of time in seconds that the result of the inline query may be cached on the server.
:param is_personal: Pass True, if results may be cached on the server side only for the user that sent the query.
:param next_offset: Pass the offset that a client should send in the next query with the same text to receive more results.
:return: True means success.
"""
return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset)
def register_for_reply(self, message, callback):
"""
Registers a callback function to be notified when a reply to `message` arrives.
Warning: `message` must be sent with reply_markup=types.ForceReply(), otherwise TeleBot will not be able to see
the difference between a reply to `message` and an ordinary message.
:param message: The message for which we are awaiting a reply.
:param callback: The callback function to be called when a reply arrives. Must accept one `message`
parameter, which will contain the replied message.
"""
with self.message_subscribers_lock:
self.message_subscribers_messages.insert(0, message.message_id)
self.message_subscribers_callbacks.insert(0, callback)
if len(self.message_subscribers_messages) > 10000:
self.message_subscribers_messages.pop()
self.message_subscribers_callbacks.pop()
def _notify_message_subscribers(self, new_messages):
for message in new_messages:
if not message.reply_to_message:
continue
reply_msg_id = message.reply_to_message.message_id
if reply_msg_id in self.message_subscribers_messages:
index = self.message_subscribers_messages.index(reply_msg_id)
self.message_subscribers_callbacks[index](message)
with self.message_subscribers_lock:
index = self.message_subscribers_messages.index(reply_msg_id)
del self.message_subscribers_messages[index]
del self.message_subscribers_callbacks[index]
def register_next_step_handler(self, message, callback):
"""
Registers a callback function to be notified when new message arrives after `message`.
:param message: The message for which we want to handle new message after that in same chat.
:param callback: The callback function which next new message arrives.
"""
chat_id = message.chat.id
if chat_id in self.pre_message_subscribers_next_step:
self.pre_message_subscribers_next_step[chat_id].append(callback)
else:
self.pre_message_subscribers_next_step[chat_id] = [callback]
def _notify_message_next_handler(self, new_messages):
for message in new_messages:
chat_id = message.chat.id
if chat_id in self.message_subscribers_next_step:
handlers = self.message_subscribers_next_step[chat_id]
for handler in handlers:
self.__exec_task(handler, message)
self.message_subscribers_next_step.pop(chat_id, None)
def _append_pre_next_step_handler(self):
for k in self.pre_message_subscribers_next_step.keys():
if k in self.message_subscribers_next_step:
self.message_subscribers_next_step[k].extend(self.pre_message_subscribers_next_step[k])
else:
self.message_subscribers_next_step[k] = self.pre_message_subscribers_next_step[k]
self.pre_message_subscribers_next_step = {}
def message_handler(self, commands=None, regexp=None, func=None, content_types=['text']):
"""
Message handler decorator.
@ -257,146 +537,136 @@ class TeleBot:
:param content_types: This commands' supported content types. Must be a list. Defaults to ['text'].
"""
def decorator(fn):
func_dict = {'function': fn, 'content_types': content_types}
if regexp:
func_dict['regexp'] = regexp if 'text' in content_types else None
if func:
func_dict['lambda'] = func
if commands:
func_dict['commands'] = commands if 'text' in content_types else None
self.message_handlers.append(func_dict)
return fn
def decorator(handler):
self.add_message_handler(handler, commands, regexp, func, content_types)
return handler
return decorator
@staticmethod
def is_command(text):
"""
Checks if `text` is a command. Telegram chat commands start with the '/' character.
:param text: Text to check.
:return: True if `text` is a command, else False.
"""
return text.startswith('/')
def add_message_handler(self, handler, commands=None, regexp=None, func=None, content_types=None):
if content_types is None:
content_types = ['text']
@staticmethod
def extract_command(text):
"""
Extracts the command from `text` (minus the '/') if `text` is a command (see is_command).
If `text` is not a command, this function returns None.
filters = {'content_types': content_types}
if regexp:
filters['regexp'] = regexp
if func:
filters['lambda'] = func
if commands:
filters['commands'] = commands
Examples:
extract_command('/help'): 'help'
extract_command('/search black eyed peas'): 'search'
extract_command('Good day to you'): None
handler_dict = {
'function': handler,
'filters': filters
}
:param text: String to extract the command from
:return: the command if `text` is a command, else None.
"""
return text.split()[0][1:] if TeleBot.is_command(text) else None
self.message_handlers.append(handler_dict)
def inline_handler(self, func):
def decorator(handler):
self.add_inline_handler(handler, func)
return handler
return decorator
def add_inline_handler(self, handler, func):
filters = {'lambda': func}
handler_dict = {
'function': handler,
'filters': filters
}
self.inline_handlers.append(handler_dict)
def chosen_inline_handler(self, func):
def decorator(handler):
self.add_chosen_inline_handler(handler, func)
return handler
return decorator
def add_chosen_inline_handler(self, handler, func):
filters = {'lambda': func}
handler_dict = {
'function': handler,
'filters': filters
}
self.chosen_inline_handlers.append(handler_dict)
@staticmethod
def _test_message_handler(message_handler, message):
if message.content_type not in message_handler['content_types']:
return False
if 'commands' in message_handler and message.content_type == 'text':
return TeleBot.extract_command(message.text) in message_handler['commands']
if 'regexp' in message_handler and message.content_type == 'text' and re.search(message_handler['regexp'],
message.text):
return False
if 'lambda' in message_handler:
return message_handler['lambda'](message)
for filter, filter_value in six.iteritems(message_handler['filters']):
if not TeleBot._test_filter(filter, filter_value, message):
return False
return True
@staticmethod
def _test_filter(filter, filter_value, message):
if filter == 'content_types':
return message.content_type in filter_value
if filter == 'regexp':
return message.content_type == 'text' and re.search(filter_value, message.text)
if filter == 'commands':
return message.content_type == 'text' and util.extract_command(message.text) in filter_value
if filter == 'lambda':
return filter_value(message)
return False
def _notify_command_handlers(self, new_messages):
def _notify_command_handlers(self, handlers, new_messages):
for message in new_messages:
for message_handler in self.message_handlers:
for message_handler in handlers:
if self._test_message_handler(message_handler, message):
t = threading.Thread(target=message_handler['function'], args=(message,))
t.start()
self.__exec_task(message_handler['function'], message)
break
class AsyncTask:
def __init__(self, target, *args, **kwargs):
self.target = target
self.args = args
self.kwargs = kwargs
self.done = False
self.thread = threading.Thread(target=self._run)
self.thread.start()
def _run(self):
try:
self.result = self.target(*self.args, **self.kwargs)
except Exception as e:
self.result = e
self.done = True
def wait(self):
if not self.done:
self.thread.join()
if isinstance(self.result, Exception):
raise self.result
else:
return self.result
def async():
def decorator(fn):
def wrapper(*args, **kwargs):
return AsyncTask(fn, *args, **kwargs)
return wrapper
return decorator
class AsyncTeleBot(TeleBot):
def __init__(self, *args, **kwargs):
TeleBot.__init__(self, *args, **kwargs)
@async()
@util.async()
def get_me(self):
return TeleBot.get_me(self)
@async()
@util.async()
def get_user_profile_photos(self, *args, **kwargs):
return TeleBot.get_user_profile_photos(self, *args, **kwargs)
@async()
@util.async()
def send_message(self, *args, **kwargs):
return TeleBot.send_message(self, *args, **kwargs)
@async()
@util.async()
def forward_message(self, *args, **kwargs):
return TeleBot.forward_message(self, *args, **kwargs)
@async()
@util.async()
def send_photo(self, *args, **kwargs):
return TeleBot.send_photo(self, *args, **kwargs)
@async()
@util.async()
def send_audio(self, *args, **kwargs):
return TeleBot.send_audio(self, *args, **kwargs)
@async()
@util.async()
def send_document(self, *args, **kwargs):
return TeleBot.send_document(self, *args, **kwargs)
@async()
@util.async()
def send_sticker(self, *args, **kwargs):
return TeleBot.send_sticker(self, *args, **kwargs)
@async()
@util.async()
def send_video(self, *args, **kwargs):
return TeleBot.send_video(self, *args, **kwargs)
@async()
@util.async()
def send_location(self, *args, **kwargs):
return TeleBot.send_location(self, *args, **kwargs)
@async()
@util.async()
def send_chat_action(self, *args, **kwargs):
return TeleBot.send_chat_action(self, *args, **kwargs)

View File

@ -1,12 +1,17 @@
# -*- coding: utf-8 -*-
import requests
import telebot
from telebot import types
from telebot import util
logger = telebot.logger
API_URL = "https://api.telegram.org/bot{0}/{1}"
FILE_URL = "https://api.telegram.org/file/bot{0}/{1}"
def _make_request(token, method_name, method='get', params=None, files=None):
def _make_request(token, method_name, method='get', params=None, files=None, base_url=API_URL):
"""
Makes a request to the Telegram API.
:param token: The bot's API token. (Created with @BotFather)
@ -14,27 +19,69 @@ def _make_request(token, method_name, method='get', params=None, files=None):
:param method: HTTP method to be used. Defaults to 'get'.
:param params: Optional parameters. Should be a dictionary with key-value pairs.
:param files: Optional files.
:return:
:return: The result parsed to a JSON dictionary.
"""
request_url = telebot.API_URL + 'bot' + token + '/' + method_name
request_url = base_url.format(token, method_name)
logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files))
result = requests.request(method, request_url, params=params, files=files)
logger.debug("The server returned: '{0}'".format(result.text.encode('utf8')))
return _check_result(method_name, result)['result']
def _check_result(method_name, result):
"""
Checks whether `result` is a valid API response.
A result is considered invalid if:
- The server returned an HTTP response code other than 200
- The content of the result is invalid JSON.
- The method call was unsuccessful (The JSON 'ok' field equals False)
:raises ApiException: if one of the above listed cases is applicable
:param method_name: The name of the method called
:param result: The returned result of the method request
:return: The result parsed to a JSON dictionary.
"""
if result.status_code != 200:
raise ApiException(method_name, result)
msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \
.format(result.status_code, result.reason, result.text.encode('utf8'))
raise ApiException(msg, method_name, result)
try:
result_json = result.json()
if not result_json['ok']:
raise Exception()
except:
raise ApiException(method_name, result)
return result_json['result']
msg = 'The server returned an invalid JSON response. Response body:\n[{0}]' \
.format(result.text.encode('utf8'))
raise ApiException(msg, method_name, result)
if not result_json['ok']:
msg = 'Error code: {0} Description: {1}' \
.format(result_json['error_code'], result_json['description'])
raise ApiException(msg, method_name, result)
return result_json
def get_me(token):
method_url = 'getMe'
method_url = r'getMe'
return _make_request(token, method_url)
def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None):
def get_file(token, file_id):
method_url = r'getFile'
return _make_request(token, method_url, params={'file_id': file_id})
def download_file(token, file_path):
url = FILE_URL.format(token, file_path)
result = requests.get(url)
if result.status_code != 200:
msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \
.format(result.status_code, result.reason, result.text)
raise ApiException(msg, 'Download file', result)
return result.content
def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None):
"""
Use this method to send text messages. On success, the sent Message is returned.
:param token:
@ -53,7 +100,23 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload)
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
return _make_request(token, method_url, params=payload, method='post')
def set_webhook(token, url=None, certificate=None):
method_url = r'setWebhook'
payload = {
'url': url if url else "",
}
files = None
if certificate:
files = {'certificate': certificate}
return _make_request(token, method_url, params=payload, files=files)
def get_updates(token, offset=None, limit=None, timeout=None):
@ -78,32 +141,44 @@ def get_user_profile_photos(token, user_id, offset=None, limit=None):
return _make_request(token, method_url, params=payload)
def forward_message(token, chat_id, from_chat_id, message_id):
def forward_message(token, chat_id, from_chat_id, message_id, disable_notification=None):
method_url = r'forwardMessage'
payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id}
if disable_notification:
payload['disable_notification'] = disable_notification
return _make_request(token, method_url, params=payload)
def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None):
def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
method_url = r'sendPhoto'
payload = {'chat_id': chat_id}
files = {'photo': photo}
files = None
if not util.is_string(photo):
files = {'photo': photo}
else:
payload['photo'] = photo
if caption:
payload['caption'] = caption
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if disable_notification:
payload['disable_notification'] = disable_notification
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_location(token, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None):
def send_location(token, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
method_url = r'sendLocation'
payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude}
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if disable_notification:
payload['disable_notification'] = disable_notification
return _make_request(token, method_url, params=payload)
@ -113,26 +188,116 @@ def send_chat_action(token, chat_id, action):
return _make_request(token, method_url, params=payload)
def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None):
method_url = get_method_by_type(data_type)
def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
method_url = r'sendVideo'
payload = {'chat_id': chat_id}
files = {data_type: data}
files = None
if not util.is_string(data):
files = {'video': data}
else:
payload['video'] = data
if duration:
payload['duration'] = duration
if caption:
payload['caption'] = caption
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if disable_notification:
payload['disable_notification'] = disable_notification
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_voice(token, chat_id, voice, duration=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
method_url = r'sendVoice'
payload = {'chat_id': chat_id}
files = None
if not util.is_string(voice):
files = {'voice': voice}
else:
payload['voice'] = voice
if duration:
payload['duration'] = duration
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if disable_notification:
payload['disable_notification'] = disable_notification
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_audio(token, chat_id, audio, duration=None, performer=None, title=None, reply_to_message_id=None,
reply_markup=None, disable_notification=None):
method_url = r'sendAudio'
payload = {'chat_id': chat_id}
files = None
if not util.is_string(audio):
files = {'audio': audio}
else:
payload['audio'] = audio
if duration:
payload['duration'] = duration
if performer:
payload['performer'] = performer
if title:
payload['title'] = title
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if disable_notification:
payload['disable_notification'] = disable_notification
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, disable_notification=None):
method_url = get_method_by_type(data_type)
payload = {'chat_id': chat_id}
files = None
if not util.is_string(data):
files = {data_type: data}
else:
payload[data_type] = data
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if disable_notification:
payload['disable_notification'] = disable_notification
return _make_request(token, method_url, params=payload, files=files, method='post')
def get_method_by_type(data_type):
if data_type == 'audio':
return 'sendAudio'
if data_type == 'document':
return 'sendDocument'
return r'sendDocument'
if data_type == 'sticker':
return 'sendSticker'
if data_type == 'video':
return 'sendVideo'
return r'sendSticker'
def answer_inline_query(token, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None):
method_url = 'answerInlineQuery'
payload = {'inline_query_id': inline_query_id, 'results': _convert_inline_results(results)}
if cache_time:
payload['cache_time'] = cache_time
if is_personal:
payload['is_personal'] = is_personal
if next_offset:
payload['next_offset'] = next_offset
return _make_request(token, method_url, params=payload, method='post')
def _convert_inline_results(results):
ret = ''
for r in results:
if isinstance(r, types.JsonSerializable):
ret = ret + r.to_json() + ','
if len(ret) > 0:
ret = ret[:-1]
return '[' + ret + ']'
def _convert_markup(markup):
@ -144,8 +309,12 @@ def _convert_markup(markup):
class ApiException(Exception):
"""
This class represents an Exception thrown when a call to the Telegram API fails.
In addition to an informative message, it has a `function_name` and a `result` attribute, which respectively
contain the name of the failed function and the returned result that made the function to be considered as
failed.
"""
def __init__(self, function_name, result):
super(ApiException, self).__init__('{0} failed. Returned result: {1}'.format(function_name, result))
def __init__(self, msg, function_name, result):
super(ApiException, self).__init__("A request to the Telegram API was unsuccessful. {0}".format(msg))
self.function_name = function_name
self.result = result

View File

@ -1,26 +1,7 @@
# -*- coding: utf-8 -*-
"""
Available types
User
GroupChat
Message
PhotoSize
Audio
Document
Sticker
Video
Contact
Location
Update
InputFile
UserProfilePhotos
ReplyKeyboardMarkup
ReplyKeyboardHide
ForceReply
"""
import json
import six
class JsonSerializable:
@ -44,6 +25,7 @@ class JsonDeserializable:
Subclasses of this class are guaranteed to be able to be created from a json-style dict or json formatted string.
All subclasses of this class must override de_json.
"""
@classmethod
def de_json(cls, json_type):
"""
@ -69,6 +51,39 @@ class JsonDeserializable:
else:
raise ValueError("json_type should be a json dict or string.")
def __str__(self):
d = {}
for x, y in six.iteritems(self.__dict__):
if hasattr(y, '__dict__'):
d[x] = y.__dict__
else:
d[x] = y
return six.text_type(d)
class Update(JsonDeserializable):
@classmethod
def de_json(cls, json_type):
obj = cls.check_json(json_type)
update_id = obj['update_id']
message = None
inline_query = None
chosen_inline_result = None
if 'message' in obj:
message = Message.de_json(obj['message'])
if 'inline_query' in obj:
inline_query = InlineQuery.de_json(obj['inline_query'])
if 'chosen_inline_result' in obj:
chosen_inline_result = ChosenInlineResult.de_json(obj['chosen_inline_result'])
return cls(update_id, message, inline_query, chosen_inline_result)
def __init__(self, update_id, message, inline_query, chosen_inline_result):
self.update_id = update_id
self.message = message
self.inline_query = inline_query
self.chosen_inline_result = chosen_inline_result
class User(JsonDeserializable):
@classmethod
@ -76,13 +91,9 @@ class User(JsonDeserializable):
obj = cls.check_json(json_string)
id = obj['id']
first_name = obj['first_name']
last_name = None
username = None
if 'last_name' in obj:
last_name = obj['last_name']
if 'username' in obj:
username = obj['username']
return User(id, first_name, last_name, username)
last_name = obj.get('last_name')
username = obj.get('username')
return cls(id, first_name, last_name, username)
def __init__(self, id, first_name, last_name=None, username=None):
self.id = id
@ -97,29 +108,61 @@ class GroupChat(JsonDeserializable):
obj = cls.check_json(json_string)
id = obj['id']
title = obj['title']
return GroupChat(id, title)
return cls(id, title)
def __init__(self, id, title):
self.id = id
self.title = title
class Chat(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
id = obj['id']
type = obj['type']
title = obj.get('title')
username = obj.get('username')
first_name = obj.get('first_name')
last_name = obj.get('last_name')
return cls(id, type, title, username, first_name, last_name)
def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None):
self.type = type
self.last_name = last_name
self.first_name = first_name
self.username = username
self.id = id
self.title = title
class Message(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
message_id = obj['message_id']
from_user = User.de_json(obj['from'])
chat = Message.parse_chat(obj['chat'])
from_user = None
if 'from' in obj:
from_user = User.de_json(obj['from'])
chat = Chat.de_json(obj['chat'])
date = obj['date']
content_type = None
opts = {}
if 'forward_from' in obj:
opts['forward_from'] = User.de_json(obj['forward_from'])
if 'forward_date' in obj:
opts['forward_date'] = obj['forward_date']
if 'reply_to_message' in obj:
opts['reply_to_message'] = Message.de_json(obj['reply_to_message'])
if 'text' in obj:
opts['text'] = obj['text']
content_type = 'text'
if 'audio' in obj:
opts['audio'] = Audio.de_json(obj['audio'])
content_type = 'audio'
if 'voice' in obj:
opts['voice'] = Audio.de_json(obj['voice'])
content_type = 'voice'
if 'document' in obj:
opts['document'] = Document.de_json(obj['document'])
content_type = 'document'
@ -156,7 +199,9 @@ class Message(JsonDeserializable):
if 'group_chat_created' in obj:
opts['group_chat_created'] = obj['group_chat_created']
content_type = 'group_chat_created'
return Message(message_id, from_user, date, chat, content_type, opts)
if 'caption' in obj:
opts['caption'] = obj['caption']
return cls(message_id, from_user, date, chat, content_type, opts)
@classmethod
def parse_chat(cls, chat):
@ -175,9 +220,28 @@ class Message(JsonDeserializable):
def __init__(self, message_id, from_user, date, chat, content_type, options):
self.chat = chat
self.date = date
self.fromUser = from_user
self.from_user = from_user
self.message_id = message_id
self.content_type = content_type
self.forward_from = None
self.forward_date = None
self.reply_to_message = None
self.text = None
self.audio = None
self.voice = None
self.document = None
self.photo = None
self.sticker = None
self.video = None
self.location = None
self.contact = None
self.new_chat_participant = None
self.left_chat_participant = None
self.new_chat_title = None
self.new_chat_photo = None
self.delete_chat_photo = None
self.group_chat_created = None
self.caption = None
for key in options:
setattr(self, key, options[key])
@ -189,10 +253,8 @@ class PhotoSize(JsonDeserializable):
file_id = obj['file_id']
width = obj['width']
height = obj['height']
file_size = None
if 'file_size' in obj:
file_size = obj['file_size']
return PhotoSize(file_id, width, height, file_size)
file_size = obj.get('file_size')
return cls(file_id, width, height, file_size)
def __init__(self, file_id, width, height, file_size=None):
self.file_size = file_size
@ -207,13 +269,30 @@ class Audio(JsonDeserializable):
obj = cls.check_json(json_string)
file_id = obj['file_id']
duration = obj['duration']
mime_type = None
file_size = None
if 'mime_type' in obj:
mime_type = obj['mime_type']
if 'file_size' in obj:
file_size = obj['file_size']
return Audio(file_id, duration, mime_type, file_size)
performer = obj.get('performer')
title = obj.get('title')
mime_type = obj.get('mime_type')
file_size = obj.get('file_size')
return cls(file_id, duration, performer, title, mime_type, file_size)
def __init__(self, file_id, duration, performer=None, title=None, mime_type=None, file_size=None):
self.file_id = file_id
self.duration = duration
self.performer = performer
self.title = title
self.mime_type = mime_type
self.file_size = file_size
class Voice(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
file_id = obj['file_id']
duration = obj['duration']
mime_type = obj.get('mime_type')
file_size = obj.get('file_size')
return cls(file_id, duration, mime_type, file_size)
def __init__(self, file_id, duration, mime_type=None, file_size=None):
self.file_id = file_id
@ -228,18 +307,12 @@ class Document(JsonDeserializable):
obj = cls.check_json(json_string)
file_id = obj['file_id']
thumb = None
if 'file_id' in obj['thumb']:
if 'thumb' in obj and 'file_id' in obj['thumb']:
thumb = PhotoSize.de_json(obj['thumb'])
file_name = None
mime_type = None
file_size = None
if 'file_name' in obj:
file_name = obj['file_name']
if 'mine_type' in obj:
mime_type = obj['mime_type']
if 'file_size' in obj:
file_size = obj['file_size']
return Document(file_id, thumb, file_name, mime_type, file_size)
file_name = obj.get('file_name')
mime_type = obj.get('mime_type')
file_size = obj.get('file_size')
return cls(file_id, thumb, file_name, mime_type, file_size)
def __init__(self, file_id, thumb, file_name=None, mime_type=None, file_size=None):
self.file_id = file_id
@ -256,11 +329,11 @@ class Sticker(JsonDeserializable):
file_id = obj['file_id']
width = obj['width']
height = obj['height']
thumb = PhotoSize.de_json(obj['thumb'])
file_size = None
if 'file_size' in obj:
file_size = obj['file_size']
return Sticker(file_id, width, height, thumb, file_size)
thumb = None
if 'thumb' in obj:
thumb = PhotoSize.de_json(obj['thumb'])
file_size = obj.get('file_size')
return cls(file_id, width, height, thumb, file_size)
def __init__(self, file_id, width, height, thumb, file_size=None):
self.file_id = file_id
@ -278,19 +351,14 @@ class Video(JsonDeserializable):
width = obj['width']
height = obj['height']
duration = obj['duration']
thumb = PhotoSize.de_json(obj['thumb'])
caption = None
mime_type = None
file_size = None
if 'caption' in obj:
caption = obj['caption']
if 'mine_type' in obj:
mime_type = obj['mime_type']
if 'file_size' in obj:
file_size = obj['file_size']
return Video(file_id, width, height, duration, thumb, mime_type, file_size, caption)
thumb = None
if 'thumb' in obj:
thumb = PhotoSize.de_json(obj['thumb'])
mime_type = obj.get('mime_type')
file_size = obj.get('file_size')
return cls(file_id, width, height, duration, thumb, mime_type, file_size)
def __init__(self, file_id, width, height, duration, thumb, mime_type=None, file_size=None, caption=None):
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
@ -298,7 +366,6 @@ class Video(JsonDeserializable):
self.thumb = thumb
self.mime_type = mime_type
self.file_size = file_size
self.caption = caption
class Contact(JsonDeserializable):
@ -307,13 +374,9 @@ class Contact(JsonDeserializable):
obj = cls.check_json(json_string)
phone_number = obj['phone_number']
first_name = obj['first_name']
last_name = None
user_id = None
if 'last_name' in obj:
last_name = obj['last_name']
if 'user_id' in obj:
user_id = obj['user_id']
return Contact(phone_number, first_name, last_name, user_id)
last_name = obj.get('last_name')
user_id = obj.get('user_id')
return cls(phone_number, first_name, last_name, user_id)
def __init__(self, phone_number, first_name, last_name=None, user_id=None):
self.phone_number = phone_number
@ -328,7 +391,7 @@ class Location(JsonDeserializable):
obj = cls.check_json(json_string)
longitude = obj['longitude']
latitude = obj['latitude']
return Location(longitude, latitude)
return cls(longitude, latitude)
def __init__(self, longitude, latitude):
self.longitude = longitude
@ -341,13 +404,28 @@ class UserProfilePhotos(JsonDeserializable):
obj = cls.check_json(json_string)
total_count = obj['total_count']
photos = [[PhotoSize.de_json(y) for y in x] for x in obj['photos']]
return UserProfilePhotos(total_count, photos)
return cls(total_count, photos)
def __init__(self, total_count, photos):
self.total_count = total_count
self.photos = photos
class File(JsonDeserializable):
@classmethod
def de_json(cls, json_type):
obj = cls.check_json(json_type)
file_id = obj['file_id']
file_size = obj.get('file_size')
file_path = obj.get('file_path')
return cls(file_id, file_size, file_path)
def __init__(self, file_id, file_size, file_path):
self.file_id = file_id
self.file_size = file_size
self.file_path = file_path
class ForceReply(JsonSerializable):
def __init__(self, selective=None):
self.selective = selective
@ -427,3 +505,306 @@ class ReplyKeyboardMarkup(JsonSerializable):
json_dict['selective'] = True
return json.dumps(json_dict)
class InlineQuery(JsonDeserializable):
@classmethod
def de_json(cls, json_type):
obj = cls.check_json(json_type)
id = obj['id']
from_user = User.de_json(obj['from'])
query = obj['query']
offset = obj['offset']
return cls(id, from_user, query, offset)
def __init__(self, id, from_user, query, offset):
"""
This object represents an incoming inline query.
When the user sends an empty query, your bot could
return some default or trending results.
:param id: string Unique identifier for this query
:param from_user: User Sender
:param query: String Text of the query
:param offset: String Offset of the results to be returned, can be controlled by the bot
:return: InlineQuery Object
"""
self.id = id
self.from_user = from_user
self.query = query
self.offset = offset
class ChosenInlineResult(JsonDeserializable):
@classmethod
def de_json(cls, json_type):
obj = cls.check_json(json_type)
result_id = obj['result_id']
from_user = User.de_json(obj['from'])
query = obj['query']
return cls(result_id, from_user, query)
def __init__(self, result_id, from_user, query):
"""
This object represents a result of an inline query
that was chosen by the user and sent to their chat partner.
:param result_id: string The unique identifier for the result that was chosen.
:param from_user: User The user that chose the result.
:param query: String The query that was used to obtain the result.
:return: ChosenInlineResult Object.
"""
self.result_id = result_id
self.from_user = from_user
self.query = query
class InlineQueryResultArticle(JsonSerializable):
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):
"""
Represents a link to an article or web page.
:param id: Unique identifier for this result, 1-64 Bytes.
:param title: Title of the result.
:param message_text: Text of the message to be sent.
:param parse_mode: Send “Markdown”, if you want Telegram apps to show bold, italic and inline URLs in your bot's message.
:param disable_web_page_preview: Disables link previews for links in the sent message.
:param url: URL of the result.
:param hide_url: Pass True, if you don't want the URL to be shown in the message.
:param description: Short description of the result.
:param thumb_url: Url of the thumbnail for the result.
:param thumb_width: Thumbnail width.
:param thumb_height: Thumbnail height
:return:
"""
self.type = 'article'
self.id = id
self.title = title
self.message_text = message_text
self.parse_mode = parse_mode
self.disable_web_page_preview = disable_web_page_preview
self.url = url
self.hide_url = hide_url
self.description = description
self.thumb_url = thumb_url
self.thumb_width = thumb_width
self.thumb_height = thumb_height
def to_json(self):
json_dict = {'type': self.type, 'id': self.id, 'title': self.title, 'message_text': self.message_text}
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.disable_web_page_preview:
json_dict['disable_web_page_preview'] = self.disable_web_page_preview
if self.url:
json_dict['url'] = self.url
if self.hide_url:
json_dict['hide_url'] = self.hide_url
if self.description:
json_dict['description'] = self.description
if self.thumb_url:
json_dict['thumb_url'] = self.thumb_url
if self.thumb_width:
json_dict['thumb_width'] = self.thumb_width
if self.thumb_height:
json_dict['thumb_height'] = self.thumb_height
return json.dumps(json_dict)
class InlineQueryResultPhoto(JsonSerializable):
def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None, title=None,
description=None, caption=None, message_text=None, parse_mode=None, disable_web_page_preview=None):
"""
Represents a link to a photo.
:param id: Unique identifier for this result, 1-64 bytes
:param photo_url: A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB
:param thumb_url: URL of the thumbnail for the photo
:param photo_width: Width of the photo.
:param photo_height: Height of the photo.
:param title: Title for the result.
:param description: Short description of the result.
:param caption: Caption of the photo to be sent, 0-200 characters.
:param message_text: Text of a message to be sent instead of the photo, 1-512 characters.
:param parse_mode: Send “Markdown”, if you want Telegram apps to show bold, italic and inline URLs in your bot's message.
:param disable_web_page_preview: Disables link previews for links in the sent message.
:return:
"""
self.type = 'photo'
self.id = id
self.photo_url = photo_url
self.photo_width = photo_width
self.photo_height = photo_height
self.thumb_url = thumb_url
self.title = title
self.description = description
self.caption = caption
self.message_text = message_text
self.parse_mode = parse_mode
self.disable_web_page_preview = disable_web_page_preview
def to_json(self):
json_dict = {'type': self.type, 'id': self.id, 'photo_url': self.photo_url, 'thumb_url': self.thumb_url}
if self.photo_width:
json_dict['photo_width'] = self.photo_width
if self.photo_height:
json_dict['photo_height'] = self.photo_height
if self.title:
json_dict['title'] = self.title
if self.description:
json_dict['description'] = self.description
if self.caption:
json_dict['caption'] = self.caption
if self.message_text:
json_dict['message_text'] = self.message_text
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.disable_web_page_preview:
json_dict['disable_web_page_preview'] = self.disable_web_page_preview
return json.dumps(json_dict)
class InlineQueryResultGif(JsonSerializable):
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):
"""
Represents a link to an animated GIF file.
:param id: Unique identifier for this result, 1-64 bytes.
:param gif_url: A valid URL for the GIF file. File size must not exceed 1MB
:param thumb_url: URL of the static thumbnail (jpeg or gif) for the result.
:param gif_width: Width of the GIF.
:param gif_height: Height of the GIF.
:param title: Title for the result.
:param caption: Caption of the GIF file to be sent, 0-200 characters
:param message_text: Text of a message to be sent instead of the animation, 1-512 characters.
:param parse_mode: Send “Markdown”, if you want Telegram apps to show bold, italic and inline URLs in your bot's message.
:param disable_web_page_preview: Disables link previews for links in the sent message.
:return:
"""
self.type = 'gif'
self.id = id
self.gif_url = gif_url
self.gif_width = gif_width
self.gif_height = gif_height
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 = disable_web_page_preview
def to_json(self):
json_dict = {'type': self.type, 'id': self.id, 'gif_url': self.gif_url, 'thumb_url': self.thumb_url}
if self.gif_height:
json_dict['gif_height'] = self.gif_height
if self.gif_width:
json_dict['gif_width'] = self.gif_width
if self.title:
json_dict['title'] = self.title
if self.caption:
json_dict['caption'] = self.caption
if self.message_text:
json_dict['message_text'] = self.message_text
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.disable_web_page_preview:
json_dict['disable_web_page_preview'] = self.disable_web_page_preview
return json.dumps(json_dict)
class InlineQueryResultMpeg4Gif(JsonSerializable):
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):
"""
Represents a link to a video animation (H.264/MPEG-4 AVC video without sound).
:param id: Unique identifier for this result, 1-64 bytes
:param mpeg4_url: A valid URL for the MP4 file. File size must not exceed 1MB
:param thumb_url: URL of the static thumbnail (jpeg or gif) for the result
:param mpeg4_width: Video width
:param mpeg4_height: Video height
:param title: Title for the result
:param caption: Caption of the MPEG-4 file to be sent, 0-200 characters
:param message_text: Text of a message to be sent instead of the animation, 1-512 characters
:param parse_mode: end “Markdown”, if you want Telegram apps to show bold, italic and inline URLs in your bot's message.
:param disable_web_page_preview: Disables link previews for links in the sent message
:return:
"""
self.type = 'mpeg4_gif'
self.id = id
self.mpeg4_url = mpeg4_url
self.mpeg4_width = mpeg4_width
self.mpeg4_height = mpeg4_height
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 = disable_web_page_preview
def to_json(self):
json_dict = {'type': self.type, 'id': self.id, 'mpeg4_url': self.mpeg4_url, 'thumb_url': self.thumb_url}
if self.mpeg4_width:
json_dict['mpeg4_width'] = self.mpeg4_width
if self.mpeg4_height:
json_dict['mpeg4_height'] = self.mpeg4_height
if self.title:
json_dict['title'] = self.title
if self.caption:
json_dict['caption'] = self.caption
if self.message_text:
json_dict['message_text'] = self.message_text
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.disable_web_page_preview:
json_dict['disable_web_page_preview'] = self.disable_web_page_preview
return json.dumps(json_dict)
class InlineQueryResultVideo(JsonSerializable):
def __init__(self, id, video_url, mime_type, message_text, thumb_url, title, parse_mode=None,
disable_web_page_preview=None,
video_width=None, video_height=None, video_duration=None,
description=None):
"""
Represents link to a page containing an embedded video player or a video file.
:param id: Unique identifier for this result, 1-64 bytes
:param video_url: A valid URL for the embedded video player or video file
:param mime_type: Mime type of the content of video url, “text/html” or “video/mp4”
:param message_text: Text of the message to be sent with the video, 1-512 characters
:param thumb_url: URL of the thumbnail (jpeg only) for the video
:param title: Title for the result
:param parse_mode: Send “Markdown”, if you want Telegram apps to show bold, italic and inline URLs in your bot's message.
:param disable_web_page_preview: Disables link previews for links in the sent message
:param video_width: Video width
:param video_height: Video height
:param video_duration: Video duration in seconds
:param description: Short description of the result
:return:
"""
self.type = 'video'
self.id = id
self.video_url = video_url
self.mime_type = mime_type
self.message_text = message_text
self.parse_mode = parse_mode
self.disable_web_page_preview = disable_web_page_preview
self.video_width = video_width
self.video_height = video_height
self.video_duration = video_duration
self.thumb_url = thumb_url
self.title = title
self.description = description
def to_json(self):
json_dict = {'type': self.type, 'id': self.id, 'video_url': self.video_url, 'mime_type': self.mime_type,
'message_text': self.message_text, 'thumb_url': self.thumb_url, 'title': self.title}
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.disable_web_page_preview:
json_dict['disable_web_page_preview'] = self.disable_web_page_preview
if self.video_width:
json_dict['video_width'] = self.video_width
if self.video_height:
json_dict['video_height'] = self.video_height
if self.video_duration:
json_dict['video_duration'] = self.video_duration
if self.description:
json_dict['description'] = self.description
return json.dumps(json_dict)

226
telebot/util.py Normal file
View File

@ -0,0 +1,226 @@
# -*- coding: utf-8 -*-
import threading
import sys
import six
from six import string_types
# Python3 queue support.
try:
import Queue
except ImportError:
import queue as Queue
from telebot import logger
class WorkerThread(threading.Thread):
count = 0
def __init__(self, exception_callback=None, queue=None, name=None):
if not name:
name = "WorkerThread{0}".format(self.__class__.count + 1)
self.__class__.count += 1
if not queue:
queue = Queue.Queue()
threading.Thread.__init__(self, name=name)
self.queue = queue
self.daemon = True
self.received_task_event = threading.Event()
self.done_event = threading.Event()
self.exception_event = threading.Event()
self.continue_event = threading.Event()
self.exception_callback = exception_callback
self.exc_info = None
self._running = True
self.start()
def run(self):
while self._running:
try:
task, args, kwargs = self.queue.get(block=True, timeout=.5)
self.continue_event.clear()
self.received_task_event.clear()
self.done_event.clear()
self.exception_event.clear()
logger.debug("Received task")
self.received_task_event.set()
task(*args, **kwargs)
logger.debug("Task complete")
self.done_event.set()
except Queue.Empty:
pass
except:
logger.debug("Exception occurred")
self.exc_info = sys.exc_info()
self.exception_event.set()
if self.exception_callback:
self.exception_callback(self, self.exc_info)
self.continue_event.wait()
def put(self, task, *args, **kwargs):
self.queue.put((task, args, kwargs))
def raise_exceptions(self):
if self.exception_event.is_set():
six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
def clear_exceptions(self):
self.exception_event.clear()
self.continue_event.set()
def stop(self):
self._running = False
class ThreadPool:
def __init__(self, num_threads=2):
self.tasks = Queue.Queue()
self.workers = [WorkerThread(self.on_exception, self.tasks) for _ in range(num_threads)]
self.num_threads = num_threads
self.exception_event = threading.Event()
self.exc_info = None
def put(self, func, *args, **kwargs):
self.tasks.put((func, args, kwargs))
def on_exception(self, worker_thread, exc_info):
self.exc_info = exc_info
self.exception_event.set()
worker_thread.continue_event.set()
def raise_exceptions(self):
if self.exception_event.is_set():
six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
def clear_exceptions(self):
self.exception_event.clear()
def close(self):
for worker in self.workers:
worker.stop()
for worker in self.workers:
worker.join()
class AsyncTask:
def __init__(self, target, *args, **kwargs):
self.target = target
self.args = args
self.kwargs = kwargs
self.done = False
self.thread = threading.Thread(target=self._run)
self.thread.start()
def _run(self):
try:
self.result = self.target(*self.args, **self.kwargs)
except:
self.result = sys.exc_info()
self.done = True
def wait(self):
if not self.done:
self.thread.join()
if isinstance(self.result, BaseException):
six.reraise(self.result[0], self.result[1], self.result[2])
else:
return self.result
def async():
def decorator(fn):
def wrapper(*args, **kwargs):
return AsyncTask(fn, *args, **kwargs)
return wrapper
return decorator
def is_string(var):
return isinstance(var, string_types)
def is_command(text):
"""
Checks if `text` is a command. Telegram chat commands start with the '/' character.
:param text: Text to check.
:return: True if `text` is a command, else False.
"""
return text.startswith('/')
def extract_command(text):
"""
Extracts the command from `text` (minus the '/') if `text` is a command (see is_command).
If `text` is not a command, this function returns None.
Examples:
extract_command('/help'): 'help'
extract_command('/help@BotName'): 'help'
extract_command('/search black eyed peas'): 'search'
extract_command('Good day to you'): None
:param text: String to extract the command from
:return: the command if `text` is a command (according to is_command), else None.
"""
return text.split()[0].split('@')[0][1:] if is_command(text) else None
def split_string(text, chars_per_string):
"""
Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string.
This is very useful for splitting one giant message into multiples.
:param text: The text to split
:param chars_per_string: The number of characters per line the text is split into.
:return: The splitted text as a list of strings.
"""
return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)]
# CREDITS TO http://stackoverflow.com/questions/12317940#answer-12320352
def or_set(self):
self._set()
self.changed()
def or_clear(self):
self._clear()
self.changed()
def orify(e, changed_callback):
e._set = e.set
e._clear = e.clear
e.changed = changed_callback
e.set = lambda: or_set(e)
e.clear = lambda: or_clear(e)
def OrEvent(*events):
or_event = threading.Event()
def changed():
bools = [e.is_set() for e in events]
if any(bools):
or_event.set()
else:
or_event.clear()
def busy_wait():
while not or_event.is_set():
or_event._wait(3)
for e in events:
orify(e, changed)
or_event._wait = or_event.wait
or_event.wait = busy_wait
changed()
return or_event

BIN
tests/test_data/record.mp3 Normal file

Binary file not shown.

BIN
tests/test_data/record.ogg Normal file

Binary file not shown.

BIN
tests/test_data/record.wav Normal file

Binary file not shown.

Binary file not shown.

311
tests/test_telebot.py Normal file
View File

@ -0,0 +1,311 @@
# -*- coding: utf-8 -*-
import sys
sys.path.append('../')
import time
import pytest
import os
import telebot
from telebot import types
from telebot import util
should_skip = 'TOKEN' and 'CHAT_ID' not in os.environ
if not should_skip:
TOKEN = os.environ['TOKEN']
CHAT_ID = os.environ['CHAT_ID']
@pytest.mark.skipif(should_skip, reason="No environment variables configured")
class TestTeleBot:
def test_message_listener(self):
msg_list = []
for x in range(100):
msg_list.append(self.create_text_message('Message ' + str(x)))
def listener(messages):
assert len(messages) == 100
tb = telebot.TeleBot('')
tb.set_update_listener(listener)
def test_message_handler(self):
tb = telebot.TeleBot('')
msg = self.create_text_message('/help')
@tb.message_handler(commands=['help', 'start'])
def command_handler(message):
message.text = 'got'
tb.process_new_messages([msg])
time.sleep(1)
assert msg.text == 'got'
def test_message_handler_reg(self):
bot = telebot.TeleBot('')
msg = self.create_text_message(r'https://web.telegram.org/')
@bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
def command_url(message):
msg.text = 'got'
bot.process_new_messages([msg])
time.sleep(1)
assert msg.text == 'got'
def test_message_handler_lambda(self):
bot = telebot.TeleBot('')
msg = self.create_text_message(r'lambda_text')
@bot.message_handler(func=lambda message: r'lambda' in message.text)
def command_url(message):
msg.text = 'got'
bot.process_new_messages([msg])
time.sleep(1)
assert msg.text == 'got'
def test_message_handler_lambda_fail(self):
bot = telebot.TeleBot('')
msg = self.create_text_message(r'text')
@bot.message_handler(func=lambda message: r'lambda' in message.text)
def command_url(message):
msg.text = 'got'
bot.process_new_messages([msg])
time.sleep(1)
assert not msg.text == 'got'
def test_message_handler_reg_fail(self):
bot = telebot.TeleBot('')
msg = self.create_text_message(r'web.telegram.org/')
@bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
def command_url(message):
msg.text = 'got'
bot.process_new_messages([msg])
time.sleep(1)
assert not msg.text == 'got'
def test_send_message_with_markdown(self):
tb = telebot.TeleBot(TOKEN)
markdown = """
*bold text*
_italic text_
[text](URL)
"""
ret_msg = tb.send_message(CHAT_ID, markdown, parse_mode="Markdown")
assert ret_msg.message_id
def test_send_message_with_disable_notification(self):
tb = telebot.TeleBot(TOKEN)
markdown = """
*bold text*
_italic text_
[text](URL)
"""
ret_msg = tb.send_message(CHAT_ID, markdown, parse_mode="Markdown", disable_notification=True)
assert ret_msg.message_id
def test_send_file(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_document(CHAT_ID, file_data)
assert ret_msg.message_id
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
assert ret_msg.message_id
def test_send_file_dis_noti(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_document(CHAT_ID, file_data, disable_notification=True)
assert ret_msg.message_id
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
assert ret_msg.message_id
def test_send_video(self):
file_data = open('./test_data/test_video.mp4', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_video(CHAT_ID, file_data)
assert ret_msg.message_id
def test_send_video_dis_noti(self):
file_data = open('./test_data/test_video.mp4', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_video(CHAT_ID, file_data, disable_notification=True)
assert ret_msg.message_id
def test_send_video_more_params(self):
file_data = open('./test_data/test_video.mp4', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_video(CHAT_ID, file_data, 1)
assert ret_msg.message_id
def test_send_video_more_params_dis_noti(self):
file_data = open('./test_data/test_video.mp4', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_video(CHAT_ID, file_data, 1, disable_notification=True)
assert ret_msg.message_id
def test_send_file_exception(self):
tb = telebot.TeleBot(TOKEN)
try:
tb.send_document(CHAT_ID, None)
assert False
except Exception as e:
print(e)
assert True
def test_send_photo(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_photo(CHAT_ID, file_data)
assert ret_msg.message_id
ret_msg = tb.send_photo(CHAT_ID, ret_msg.photo[0].file_id)
assert ret_msg.message_id
def test_send_photo_dis_noti(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_photo(CHAT_ID, file_data)
assert ret_msg.message_id
ret_msg = tb.send_photo(CHAT_ID, ret_msg.photo[0].file_id, disable_notification=True)
assert ret_msg.message_id
def test_send_audio(self):
file_data = open('./test_data/record.mp3', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram')
assert ret_msg.content_type == 'audio'
assert ret_msg.audio.performer == 'eternnoir'
assert ret_msg.audio.title == 'pyTelegram'
def test_send_audio_dis_noti(self):
file_data = open('./test_data/record.mp3', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram', disable_notification=True)
assert ret_msg.content_type == 'audio'
assert ret_msg.audio.performer == 'eternnoir'
assert ret_msg.audio.title == 'pyTelegram'
def test_send_voice(self):
file_data = open('./test_data/record.ogg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_voice(CHAT_ID, file_data)
assert ret_msg.voice.mime_type == 'audio/ogg'
def test_send_voice_dis_noti(self):
file_data = open('./test_data/record.ogg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_voice(CHAT_ID, file_data, disable_notification=True)
assert ret_msg.voice.mime_type == 'audio/ogg'
def test_get_file(self):
file_data = open('./test_data/record.ogg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_voice(CHAT_ID, file_data)
file_id = ret_msg.voice.file_id
file_info = tb.get_file(file_id)
assert file_info.file_id == file_id
def test_get_file_dis_noti(self):
file_data = open('./test_data/record.ogg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_voice(CHAT_ID, file_data, disable_notification=True)
file_id = ret_msg.voice.file_id
file_info = tb.get_file(file_id)
assert file_info.file_id == file_id
def test_send_message(self):
text = 'CI Test Message'
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_message(CHAT_ID, text)
assert ret_msg.message_id
def test_send_message_dis_noti(self):
text = 'CI Test Message'
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True)
assert ret_msg.message_id
def test_forward_message(self):
text = 'CI forward_message Test Message'
tb = telebot.TeleBot(TOKEN)
msg = tb.send_message(CHAT_ID, text)
ret_msg = tb.forward_message(CHAT_ID, CHAT_ID, msg.message_id)
assert ret_msg.forward_from
def test_forward_message_dis_noti(self):
text = 'CI forward_message Test Message'
tb = telebot.TeleBot(TOKEN)
msg = tb.send_message(CHAT_ID, text)
ret_msg = tb.forward_message(CHAT_ID, CHAT_ID, msg.message_id, disable_notification=True)
assert ret_msg.forward_from
def test_reply_to(self):
text = 'CI reply_to Test Message'
tb = telebot.TeleBot(TOKEN)
msg = tb.send_message(CHAT_ID, text)
ret_msg = tb.reply_to(msg, text + ' REPLY')
assert ret_msg.reply_to_message.message_id == msg.message_id
def test_register_for_reply(self):
text = 'CI reply_to Test Message'
tb = telebot.TeleBot(TOKEN)
msg = tb.send_message(CHAT_ID, text, reply_markup=types.ForceReply())
reply_msg = tb.reply_to(msg, text + ' REPLY')
def process_reply(message):
assert msg.message_id == message.reply_to_message.message_id
tb.register_for_reply(msg, process_reply)
tb.process_new_messages([reply_msg])
def test_send_location(self):
tb = telebot.TeleBot(TOKEN)
lat = 26.3875591
lon = -161.2901042
ret_msg = tb.send_location(CHAT_ID, lat, lon)
assert int(ret_msg.location.longitude) == int(lon)
assert int(ret_msg.location.latitude) == int(lat)
def test_send_location_dis_noti(self):
tb = telebot.TeleBot(TOKEN)
lat = 26.3875591
lon = -161.2901042
ret_msg = tb.send_location(CHAT_ID, lat, lon, disable_notification=True)
assert int(ret_msg.location.longitude) == int(lon)
assert int(ret_msg.location.latitude) == int(lat)
def test_Chat(self):
tb = telebot.TeleBot(TOKEN)
me = tb.get_me()
msg = tb.send_message(CHAT_ID, 'Test')
assert me.id == msg.from_user.id
assert msg.chat.id == int(CHAT_ID)
def create_text_message(self, text):
params = {'text': text}
chat = types.User(11, 'test')
return types.Message(1, None, None, chat, 'text', params)
def test_is_string_unicode(self):
s1 = u'string'
assert util.is_string(s1)
def test_is_string_string(self):
s1 = 'string'
assert util.is_string(s1)
def test_not_string(self):
i1 = 10
assert not util.is_string(i1)

View File

@ -12,17 +12,17 @@ def test_json_user():
def test_json_message():
jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir"},"chat":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir"},"date":1435296025,"text":"HIHI"}'
jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir"},"chat":{"id":1734,"first_name":"F","type":"private","last_name":"Wa","username":"oir"},"date":1435296025,"text":"HIHI"}'
msg = types.Message.de_json(jsonstring)
assert msg.text == 'HIHI'
def test_json_message_group():
json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG"},"chat":{"id":-866,"title":"\u4ea4"},"date":1435303157,"text":"HIHI"}'
json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG"},"chat":{"id":-866,"type":"private","title":"\u4ea4"},"date":1435303157,"text":"HIHI"}'
msg = types.Message.de_json(json_string)
assert msg.text == 'HIHI'
assert len(msg.chat.title) != 0
assert msg.fromUser.username == 'GG'
assert msg.from_user.username == 'GG'
def test_json_GroupChat():
@ -39,36 +39,46 @@ def test_json_Document():
def test_json_Message_Audio():
json_string = r'{"message_id":100,"from":{"id":10734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":10734,"first_name":"dd","last_name":"dd","username":"dd"},"date":1435481343,"audio":{"duration":3,"mime_type":"audio\/ogg","file_id":"ddg","file_size":8249}}'
json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd"},"chat":{"id":10834,"first_name":"dd","type":"private","type":"private","last_name":"dd","username":"dd"},"date":1439978364,"audio":{"duration":1,"mime_type":"audio\/mpeg","title":"pyTelegram","performer":"eternnoir","file_id":"BQADBQADDH1JaB8-1KyWUss2-Ag","file_size":20096}}'
msg = types.Message.de_json(json_string)
assert msg.audio.duration == 3
assert msg.audio.duration == 1
assert msg.content_type == 'audio'
assert msg.audio.performer == 'eternnoir'
assert msg.audio.title == 'pyTelegram'
def test_json_Message_Sticker():
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
msg = types.Message.de_json(json_string)
assert msg.sticker.height == 368
assert msg.sticker.thumb.height == 60
assert msg.content_type == 'sticker'
def test_json_Message_Sticker_without_thumb():
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
msg = types.Message.de_json(json_string)
assert msg.sticker.height == 368
assert msg.sticker.thumb == None
assert msg.content_type == 'sticker'
def test_json_Message_Document():
json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10,"first_name":"Fd","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}}'
json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}}'
msg = types.Message.de_json(json_string)
assert msg.document.file_name == 'Text File'
assert msg.content_type == 'document'
def test_json_Message_Photo():
json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_size":53013,"width":759,"height":570}]}'
json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_size":53013,"width":759,"height":570}]}'
msg = types.Message.de_json(json_string)
assert len(msg.photo) == 3
assert msg.content_type == 'photo'
def test_json_Message_Video():
json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_size":260699}}'
json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":109734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_size":260699}}'
msg = types.Message.de_json(json_string)
assert msg.video
assert msg.video.duration == 3
@ -77,7 +87,7 @@ def test_json_Message_Video():
def test_json_Message_Location():
json_string = r'{"message_id":102,"from":{"id":108734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":1089734,"first_name":"dd","last_name":"dd","username":"dd"},"date":1535482469,"location":{"longitude":127.479471,"latitude":26.090577}}'
json_string = r'{"message_id":102,"from":{"id":108734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":1089734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1535482469,"location":{"longitude":127.479471,"latitude":26.090577}}'
msg = types.Message.de_json(json_string)
assert msg.location.latitude == 26.090577
assert msg.content_type == 'location'
@ -95,3 +105,25 @@ def test_json_contact():
contact = types.Contact.de_json(json_string)
assert contact.first_name == 'dd'
assert contact.last_name == 'ddl'
def test_json_voice():
json_string = r'{"duration": 0,"mime_type": "audio/ogg","file_id": "AwcccccccDH1JaB7w_gyFjYQxVAg","file_size": 10481}'
voice = types.Voice.de_json(json_string)
assert voice.duration == 0
assert voice.file_size == 10481
def test_json_update():
json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"id":9734,"first_name":"Fk","last_name":"Wg","username":"nir"},"chat":{"id":1111,"first_name":"Fk","type":"private","last_name":"Wg","username":"oir"},"date":1441447009,"text":"HIHI"}}'
update = types.Update.de_json(json_string)
assert update.update_id == 938203
assert update.message.message_id == 241
assert update.message.from_user.id == 9734
def test_json_chat():
json_string = r'{"id": -111111,"title": "Test Title","type": "group"}'
chat = types.Chat.de_json(json_string)
assert chat.id == -111111
assert chat.type == 'group'
assert chat.title == 'Test Title'