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

Compare commits

..

307 Commits
2.0.5 ... 3.6.1

Author SHA1 Message Date
48e6757686 Fix import logger problem. 2018-03-10 14:41:34 +08:00
8495229ce1 Update version 3.6.0 2018-03-02 19:28:34 +08:00
3b60f7ca67 Merge pull request #462 from heyyyoyy/Bot_Api_3.6
Bot Api 3.6
2018-02-22 22:11:46 +08:00
a1930e05c2 Merge pull request #463 from dmytrostriletskyi/master
Make the Heroku example actual
2018-02-22 20:52:53 +08:00
bc067662dc Make the Heroku example actual 2018-02-16 17:52:37 +02:00
518c49f23a fixing formatting of caption in the method send_document 2018-02-16 18:29:29 +03:00
903b1dfd50 added parse_mode in edit_message_caption 2018-02-16 14:19:35 +00:00
2e199a5684 Bot Api 3.6 2018-02-14 20:27:55 +00:00
55302cb972 Merge pull request #445 from heyyyoyy/update_send_media_group
Added support for local files in the sendMediaGroup method
2018-02-01 19:24:50 +08:00
f47653d2e4 Update README.md 2018-01-24 19:13:29 +08:00
94b4a25980 Update version. 2018-01-24 19:11:03 +08:00
afac177d7d Fix missing media_group_id in message. 2018-01-24 19:05:38 +08:00
2637e29dbe Updated sendMediaGroup method 2018-01-15 16:08:50 +03:00
6d180e30f0 Merge pull request #443 from reacheight/master
fixed example of usage sendVideoNote method
2018-01-15 10:25:06 +08:00
e2ed4cf065 Merge pull request #387 from fumycat/patch-1
Fix optional parameter
2018-01-15 10:23:26 +08:00
8b2dea1d56 fixed example of usage sendVideoNote method 2018-01-10 00:26:44 +03:00
41e31de034 Merge pull request #429 from JekaFST/master
Fix for SendLocation with live period
2017-12-06 15:50:49 +08:00
ae074fd5c9 Merge pull request #2 from JekaFST/JekaFST-live_location_fixes
Edit and stop live location fixes
2017-12-05 01:32:06 +03:00
60596a95b8 Edit and stop live location fixes
.token was missed in apihelper's methods calls
2017-12-05 01:31:47 +03:00
44531bcedf Merge pull request #1 from JekaFST/JekaFST-FixLiveLocation
Fix for SendLocation with live period
2017-12-05 00:23:32 +03:00
8aa8fa5986 Fix for SendLocation with live period
Fix for payload['live_perion'] typo -> payload['live_period']
2017-12-05 00:21:05 +03:00
f0e64b3653 Merge pull request #428 from 0xVK/patch-1
Added SmartySBot to README
2017-12-04 16:21:41 +08:00
8444ea588a Added SmartySBot to README
Added SmartySBot to README
2017-12-03 20:34:37 +02:00
b2f376a906 Remove debug message. Add content_type 2017-11-30 23:34:07 +08:00
d0b4bb7c69 Bump version. 2017-11-29 14:09:57 +08:00
c300195b49 Add provider_data . 2017-11-29 13:59:47 +08:00
2493b200a4 Add provider_data to sendInvoice. 2017-11-29 13:53:39 +08:00
8528ca9e4e Add some message content type. Fix #426 2017-11-29 13:48:34 +08:00
e1a3ccadb7 Add sendMediaGroup method. 2017-11-29 13:45:25 +08:00
a43f037bc9 Version update. 2017-11-15 00:42:50 +08:00
7ac246b801 Fix inline_query_handler not work. 2017-11-15 00:42:27 +08:00
47624a556e Update version. 2017-11-13 10:26:27 +08:00
8bdbc24014 Fix ShippingOption to_json. #414 2017-11-13 10:25:39 +08:00
e45ced958a Update version to support 3.4. 2017-11-13 10:19:13 +08:00
46c803bf55 Fix shipping_options bug. #414
Update version.
2017-11-13 10:14:10 +08:00
3986f33d3a Fix caption_entities without default value. 2017-11-06 08:42:57 +08:00
c327be5a03 Merge pull request #410 from 0xVK/fix-exec-handlers
#405 - Message has callback function but it exec command handlers too.
2017-11-04 21:28:21 +08:00
d8587419e1 Fixed bug when message has next step handler and exec command handlers. 2017-11-04 15:09:29 +02:00
35d7293ebd Merge pull request #402 from MasterGroosha/master
Bot API 3.4: new methods for live locations, new objects.
2017-10-31 09:51:23 +08:00
8e71a612a6 Added missing methods definitions to __init__.py and Async Telebot 2017-10-22 20:07:51 +03:00
5f8d99664e Bot API 3.4: new methods for live locations, new objects. 2017-10-22 19:50:51 +03:00
aaa968c27f Merge pull request #396 from MasterGroosha/patch-1
Added missing author_signature field to Message object
2017-09-25 16:42:38 +08:00
600c014515 Added missing author_signature field to Message object
`author_signature` field was checked, but never added to `Message` object.
2017-09-22 01:08:54 +03:00
0a80fafd76 Update README.md 2017-09-09 21:32:13 +08:00
4a11bb60b4 Update new_chat_member to new_chat_members 2017-09-08 15:47:16 +08:00
7d37374667 Fix test case. 2017-08-28 12:13:26 +08:00
6ad56eb30f Merge pull request #392 from MasterGroosha/master
Bot API 3.3
2017-08-28 11:50:25 +08:00
211f1c607d Bot API 3.3:
- Added the new field pinned_message to the Chat object.
- Added the new fields author_signature and forward_signature to the Message object.
- Added the new field is_bot to the User object.
2017-08-23 10:30:32 +03:00
be786021dc Merge pull request #389 from mostafaqanbaryan/patch-1
Update __init__.py
2017-08-22 04:06:08 +07:00
15d287919d Update __init__.py
Add clear_step_handler() for resetting bot
2017-08-21 14:40:47 +04:30
af991ea76e Fix optional parameter 2017-08-20 01:36:08 +07:00
064b84ad3d Merge pull request #377 from GabrielRF/master
Update README.md
2017-08-13 00:40:50 +08:00
39b4f0a068 Merge branch 'master' into master 2017-08-10 15:03:39 -03:00
246e7e31d7 Update README.md 2017-08-08 21:50:54 +08:00
48bfb7b84f Update version. 2017-08-06 15:47:59 +08:00
dcddedcd24 Fix file dic. 2017-08-06 15:35:43 +08:00
2e743b4b86 Add v3.2 method. 2017-08-06 15:22:23 +08:00
af70313721 New method for v3.2 2017-08-06 14:25:25 +08:00
aefd666062 Update sticker set. 2017-08-06 12:00:26 +08:00
23d66afbb0 Merge pull request #381 from noirgif/master
change the documentation of TeleBot.polling in readme
2017-08-01 09:31:01 +08:00
ed29f9316f change the documentation of TeleBot.polling in readme 2017-07-30 23:24:30 -07:00
e92dc3717e Update README.md 2017-07-24 14:28:27 -03:00
c91ce6036b Merge pull request #372 from Kylmakalle/master
Async Methods for API v3.1
2017-07-23 15:50:02 +08:00
cb60a1256f Merge pull request #374 from the31k/per_thread_requests_sessions
Per-thread requests sessions
2017-07-23 15:49:42 +08:00
96569cbdac Fix typo 2017-07-19 15:44:10 +03:00
feec19b7f4 Use per-thread requests sessions
Reason is requests.Session is not thread-safe
See: https://github.com/requests/requests/issues/2766
2017-07-19 01:50:12 +03:00
1a80fc5a0e Per-thread singletons 2017-07-19 01:50:04 +03:00
488fb745b7 Merge pull request #359 from Yolley/master
Informative exception message for better exception handling
2017-07-07 10:45:23 +08:00
08d6ab549d Aync Methods for v3.1 2017-07-07 00:54:18 +03:00
f718c36ea7 Merge remote-tracking branch 'refs/remotes/eternnoir/master' 2017-07-07 00:29:40 +03:00
ed88939110 Merge remote-tracking branch 'refs/remotes/eternnoir/master' 2017-07-07 00:25:13 +03:00
0632cfb9b0 Fix new chat members. 2017-07-02 21:24:19 +08:00
4979589faf Fix not require args. #369 2017-07-02 21:08:36 +08:00
d2f694516a Update version. 2017-07-01 11:24:29 +08:00
5f8ed347a1 Add missing arg until_date for kickChatMember. 2017-07-01 11:11:25 +08:00
514880fe22 Merge remote-tracking branch 'origin/proxySupport' 2017-07-01 11:06:05 +08:00
f97bb2f615 FIx missing declare 2017-07-01 11:05:14 +08:00
38af4f441b Merge pull request #368 from MasterGroosha/botapi_v3.1
Bot API v3.1
2017-07-01 10:33:35 +08:00
662a834138 Added missing arguments to restrict_chat_member method 2017-06-30 20:16:51 +03:00
25a37db2bb Bot API v3.1 2017-06-30 19:47:09 +03:00
3e04df7080 Update README.md 2017-06-28 18:01:07 +08:00
6af3067a12 Add proxy to readme. 2017-06-28 17:44:07 +08:00
32c2178b29 Merge pull request #362 from BoberMod/patch-1
Using some content types in one function
2017-06-26 08:28:28 +08:00
6786f87d66 Using some content types in one function 2017-06-23 17:01:05 +03:00
ebdd7d107e Merge pull request #361 from ihoru/master
json.dumps(allowed_updates) before sending request
2017-06-22 14:55:35 +08:00
3713b093b6 json.dumps(allowed_updates) before sending request 2017-06-22 10:35:13 +07:00
242456d92b Update util.py 2017-06-20 15:45:18 +03:00
328cabead6 Update util.py 2017-06-20 15:45:01 +03:00
e834903bc2 Merge pull request #357 from gabolaev/fixDownloadingExample
Added file opening mode
2017-06-16 10:39:39 +08:00
556a04ca8b Added file opening mode 2017-06-16 02:57:12 +03:00
4b165ba3f1 Merge pull request #305 from MonsterDeveloper/patch-2
Added option to disable CherryPy logging
2017-06-02 16:13:56 +08:00
4e1d5b83b6 Merge pull request #349 from MasterGroosha/patch-2
Fixed wrong method call
2017-06-02 16:13:18 +08:00
cb4521f497 Fixed wrong method call
Should be called `apihelper.answer_shipping_query` instead of `apihelper.answer_shippingQuery`
2017-06-02 11:07:35 +03:00
52e50f1286 Merge pull request #347 from denkorzh/patch-1
Update README.md
2017-06-01 17:23:08 +08:00
3f626d37ba Update README.md
Fixed hyperlink formatting in the subtitle
2017-06-01 10:44:46 +03:00
d6aaf0716a Update version.
Change log:
- Fix #314
2017-05-30 17:19:03 +08:00
777a3afaaa Fix #314 2017-05-30 17:18:03 +08:00
0b1ae6ad8b Update version. 2017-05-30 17:18:03 +08:00
7aca24a18b Merge pull request #346 from Kylmakalle/master
Added payments example
2017-05-26 11:05:40 +08:00
754ca77394 Add payments bot example 2017-05-25 21:00:48 +03:00
5ffd9c5755 Merge remote-tracking branch 'refs/remotes/eternnoir/master' 2017-05-25 20:46:10 +03:00
639218b3bf New fields gif_duration in InlineQueryResultGif and mpeg4_duration in
InlineQueryResultMpeg4Gif.
2017-05-25 13:27:13 +08:00
b2449e64c2 Add pay in inline keyboard btn, 2017-05-25 11:48:16 +08:00
708635e420 Fix handler. 2017-05-25 11:45:44 +08:00
84b1aca939 Fix method name. 2017-05-25 11:23:37 +08:00
9025be0ef2 Add handlers. 2017-05-25 11:22:40 +08:00
a8e60b28e0 Fix requirement params. 2017-05-25 11:14:08 +08:00
cf287af549 Add payment method. PEP8. Refactor. 2017-05-25 10:56:58 +08:00
2d10793686 Merge pull request #343 from Kylmakalle/feature-BotAPIV3
Payments methods
2017-05-24 14:31:11 +08:00
3a10c90799 Payments methods 2017-05-24 01:23:52 +03:00
f8fed5c942 Merge remote-tracking branch 'refs/remotes/eternnoir/feature-BotAPIV3' into feature-BotAPIV3 2017-05-23 23:37:33 +03:00
12791e1366 Add payments type to update and message. 2017-05-21 21:52:56 +08:00
5ed333492b All payment type done. 2017-05-21 21:45:12 +08:00
9134e8dd1a Add send video note test case. 2017-05-21 19:58:00 +08:00
43a30e7777 Merge pull request #338 from Kylmakalle/feature-BotAPIV3
Video Note support.
2017-05-21 19:47:30 +08:00
3f5596ddce new_chat_members content type and new send_action actions 2017-05-21 14:27:31 +03:00
443d81d4db FIX: Can't edit file bytes 2017-05-19 18:08:07 +03:00
6cda8d052c VideoNote support
Send and recieve round video messages.
Support for send_video_note metod and video_note content type.
2017-05-19 17:19:15 +03:00
5969a6644c Merge pull request #1 from eternnoir/master
Merging to master
2017-05-19 16:22:07 +03:00
791a183af5 Merge pull request #336 from i32ropie/master
Added language code for users
2017-05-19 10:04:30 +08:00
35214b1270 Added language code for users 2017-05-18 23:40:10 +02:00
50ea288c9e Merge pull request #333 from Kurbezz/master
Add methods to AsyncTeleBot
2017-05-18 21:52:18 +08:00
34047c0121 Add methods to AsyncTeleBot 2017-05-18 13:55:55 +04:00
1a70c2d613 Merge pull request #319 from Artom-Kozincev/master
Add more accurate control over threads count
2017-05-17 14:48:18 +08:00
6d18a2c22f Merge pull request #325 from Kylmakalle/master
Added option to delete messages.
2017-05-16 16:54:34 +08:00
89f515b120 deleteMessage returns Ok on success, not Message type 2017-05-12 01:13:40 +03:00
11f0733974 Merge pull request #326 from jiwidi/master
Update readme
2017-05-08 10:25:54 +08:00
b91eeb2752 Update readme 2017-05-07 18:27:04 +02:00
f7cfb98b60 Added option to delete messages.
Added option to delete messages.

Some bots do not support this method now, waiting for an official api
release.
2017-05-07 17:37:03 +03:00
8bf226e6bf Bump version. 2017-04-30 20:37:55 +08:00
450ef42a83 Fix typo. 2017-04-30 19:40:27 +08:00
4fc83a85ee Merge pull request #321 from bryant1410/master
Fix broken headings in Markdown files
2017-04-18 13:31:55 +08:00
8dca85b1f2 Fix broken Markdown headings 2017-04-18 01:38:53 -03:00
fc65b30e3a Update README.md 2017-04-15 23:27:07 +08:00
e138d2e1ef Add more accurate control over threads count 2017-04-06 22:12:17 +03:00
d29c816b79 Merge pull request #318 from ihoru/master
RecursionError fix during sending files
2017-04-05 14:13:22 +08:00
f220a68c00 Merge pull request #312 from MonsterDeveloper/patch-3
Added webhook example with Tornado
2017-04-05 14:12:45 +08:00
94d723cf7b Merge pull request #317 from MonsterDeveloper/patch-4
Added IGNORECASE flag to message_handler
2017-04-05 14:12:07 +08:00
662c69e09c RecursionError fix during sending files 2017-04-02 14:56:53 +07:00
43f026dc64 Added IGNORECASE flag to message_handler
Added re.IGNORECASE flag to message_handler, so it matches without chars case.
2017-03-28 16:24:28 +03:00
2c631b2973 Added webhook example with Tornado 2017-03-23 15:34:33 +03:00
401a848927 Added option to disable CherryPy logging 2017-03-07 21:43:14 +03:00
7f31f8dde8 Merge pull request #294 from bace1996/patch-1
Fixed README.MD
2017-03-05 20:18:07 +08:00
19b8b4d2bf Fix readme image url link 2017-03-05 20:17:46 +08:00
6515c7c494 Update version. 2017-03-04 21:30:35 +08:00
76a48ffe82 Bug fix for edited_channel_post_handler. 2017-03-04 21:30:07 +08:00
f8f0e0c343 Update Bots using this API list 2017-03-03 11:00:03 +08:00
8129b95118 Fixed README.MD
Fixed a problem that could not jump to "Using web hooks" correctly
2017-02-15 21:31:52 +08:00
2b7e9a6180 Merge pull request #293 from MonsterDeveloper/patch-1
Fixed typo: from plan to plain
2017-02-10 15:33:03 +08:00
a84c0b984b Fixed typo: from plan to plain
Line 184: from `return message.document.mime_type == 'text/plan'` to `return message.document.mime_type == 'text/plain'`
2017-02-09 16:49:15 +03:00
a356c9a325 Merge pull request #283 from roboxv/master
Extend examples with aiohttp
2017-02-02 10:45:49 +08:00
4c4e8deaee Merge pull request #284 from Kuznitsin/master
Non-ASCII chars for filename.
2017-02-02 10:45:19 +08:00
f7fc538bd8 Non-ASCII chars for filename. Telegram doesn't accept rfc2231 styled filename. Using utf-8 directly. 2017-01-30 17:40:18 +03:00
6c770d81f9 Extend examples with aiohttp 2017-01-30 01:54:49 +06:00
d6af33fef7 Merge pull request #277 from i32ropie/master
Updated from ReplyKeyboardHide() methode to ReplyKeyboardRemove()
2017-01-23 11:21:10 +08:00
b6fee07089 Updated from ReplyKeyboardHide() methode to ReplyKeyboardRemove() 2017-01-20 10:55:34 +01:00
a5d6b541a5 Update README.md 2017-01-15 00:57:05 +08:00
ecad88ad00 Merge pull request #275 from mefuckin/patch-1
Fix error in webhook flask example
2017-01-05 11:02:10 +08:00
be87e4b2b9 Fix error in webhook flask example 2017-01-04 21:50:09 +03:00
1215eee167 Merge pull request #272 from mymedia2/persistent-connection
Use session to ensure persistent connection to api.telegram.org
2016-12-20 00:57:22 +08:00
9fe8565d53 Use session to ensure persistent connection to api.telegram.org 2016-12-17 09:03:00 +03:00
1058822f85 Merge pull request #266 from Yolley/patch-1
Added isinstance for bytes to function 'add' in ReplyKeyboardMarkup
2016-12-13 10:02:58 +08:00
57a57c8aca Merge pull request #268 from MasterGroosha/patch-1
Added max_connections and allowed updates to WebhookInfo
2016-12-13 09:45:55 +08:00
12e7879325 Added max_connections and allowed updates to WebhookInfo 2016-12-12 19:29:57 +03:00
d14bd9a36b Add isinstance for bytes to function 'add' in ReplyKeyboardMarkup
All explanation is here https://github.com/eternnoir/pyTelegramBotAPI/issues/265
2016-12-06 17:12:28 +03:00
c168feea32 Version update:
Change log:
- Telegram bot api 20161121 new feature.
- Telegram bot api 20161204 new feature.
2016-12-06 12:09:28 +08:00
a06551daaf Add delete webhook. 2016-12-06 11:52:16 +08:00
eadff07f79 Add allowed_updates to get_updates. 2016-12-06 11:44:30 +08:00
b5e27d0fea Add max_connections and allowed_updates to set_webhook. 2016-12-06 11:42:15 +08:00
509fae6792 Bug fix. 2016-12-03 15:17:06 +08:00
b0bc49c803 Update readme. 2016-12-03 13:56:22 +08:00
e555da86dd Add cache_time to answerCallbackQuery 2016-12-03 13:38:30 +08:00
9a5e8302be Add force params in setGameScore method. 2016-12-03 13:31:55 +08:00
30ed6e37d3 Add channel_post, edited_channel_post support. 2016-12-03 13:28:22 +08:00
27a79c4be5 Merge pull request #254 from Kondra007/patch-2
Bot API November updates
2016-11-25 08:27:15 +08:00
c99bb16619 Updated set_game_score
• New field `force`
• Changed `edit_message` to `disable_edit_message`
2016-11-21 09:28:32 +03:00
856af72599 Added cache_time to answer_callback_query 2016-11-21 09:19:59 +03:00
8c8be81bb9 Added optional forward_from_message_id
And changed `forward_date` to optional (as it should be)
2016-11-21 09:10:51 +03:00
b2cd3c9716 Added channel_post and edited_channel_post to Update object 2016-11-21 09:06:36 +03:00
1c9a9b9622 hide_keyboard -> remove_keyboard 2016-11-21 08:57:38 +03:00
f413ccf3fb Merge pull request #257 from rmed/wat-bridge
Added wat-bridge to the list of bots
2016-11-21 10:37:01 +08:00
9aaa00c8fd Added wat-bridge to the list of bots 2016-11-20 19:04:21 +00:00
11c2505d50 Update readme. 2016-11-18 19:37:29 +08:00
91076d0b59 Add dailypepebot to list. 2016-11-18 19:37:00 +08:00
1691e84d01 ReplyKeyboardHide -> ReplyKeyboardRemove
Since Telegram changed object name in API docs: https://core.telegram.org/bots/api#replykeyboardremove
2016-11-16 14:18:39 +03:00
903de2a72c Merge pull request #252 from DmytryiStriletskyi/striletskyi
Update README.md
2016-11-11 15:03:15 +08:00
9ddc529b28 Update README.md 2016-11-09 20:24:27 +02:00
d9ca776e2c Update README.md 2016-11-09 20:03:28 +02:00
2c497edca6 Update README.md 2016-11-03 16:43:46 +08:00
bf6634bc36 Version update.
Chnage log:
- Fix InlineQueryResultGame's reply_makrup bug.
- Remove type param from InlineQueryResultGame contractor.
2016-11-01 01:23:51 +08:00
2455d7013c Merge branch 'bug-fixInlineQueryResultGame' 2016-11-01 01:23:21 +08:00
7a6bb4dcc8 Remove type in InlineQueryResultGame. 2016-11-01 01:14:28 +08:00
e342b9fa6b Fix InlineQueryResultGame replymarkup do not to_dic. 2016-11-01 01:10:06 +08:00
2e8151cb7d Version update. 2016-10-31 00:35:07 +08:00
2af9209005 Fixed KeyError when data field is None in CallbackQuery
obj['data'] raises KeyError when `data` is None, while obj.get('data') returns None
2016-10-30 18:23:57 +08:00
34b0a2404e Version change.
Change log:
- bug fix.
2016-10-30 07:05:15 +08:00
f6d5358d1a Merge pull request #245 from Kondra007/patch-11
Fixed API object type mismatch
2016-10-30 07:03:53 +08:00
d2e1acde6a Fixed API object type mismatch 2016-10-29 23:23:39 +03:00
aad9251d48 Version update.
Change log:
* Gaming platform new methods.
* Other change in October 3, 2016 update.
(https://core.telegram.org/bots/api/#recent-changes)
2016-10-29 21:44:02 +08:00
54ed2038aa New methods setGameScore and getGameHighScores. 2016-10-29 21:22:46 +08:00
1b767215b5 Merge pull request #243 from Kondra007/patch-10
Added setGameScore and getGameHighScores
2016-10-27 10:41:47 +08:00
6f8ebbae89 Added setGameScore and getGameHighScores
1. https://core.telegram.org/bots/api#setgamescore
2. https://core.telegram.org/bots/api#getgamehighscores
2016-10-26 16:19:04 +03:00
d1498979d4 Merge branch 'JrooTJunior-master' into feature-20161003update 2016-10-20 20:09:07 +08:00
702763edd6 Get webhook info
https://core.telegram.org/bots/api#getwebhookinfo
2016-10-20 10:52:38 +03:00
ffa0ea449b Fix test case. 2016-10-12 15:52:34 +08:00
d53a881ac4 Merge pull request #238 from Kondra007/patch-6
Added "all_members_are_administrators" field to Chat object
2016-10-12 12:01:46 +08:00
67583d3639 Merge pull request #240 from Kondra007/patch-9
Added caption field to several objects
2016-10-12 12:00:32 +08:00
0f3398ed2f Merge pull request #239 from Kondra007/patch-7
Added Caption field to sendAudio & sendVoice
2016-10-12 11:59:15 +08:00
08dd7d1593 Added caption field to several objects
InlineQueryResultAudio, InlineQueryResultVoice, InlineQueryResultCachedAudio, InlineQueryResultCachedVoice
2016-10-11 22:57:16 +03:00
7e94810ece Added Caption field to sendAudio & sendVoice 2016-10-11 22:51:20 +03:00
11aa5fcb85 Added "all_members_are_administrators" field to Chat object 2016-10-11 22:43:44 +03:00
8d65856dec New field callback_game in InlineKeyboardButton, new fields
game_short_name and chat_instance in CallbackQuery.
2016-10-08 22:04:44 +08:00
740d7f44cf Add url param in answer inline query. 2016-10-08 21:55:28 +08:00
b8e5c43598 Add send game method. 2016-10-08 20:36:48 +08:00
795a00f92c Add game in Message. InlineQueryResultGame 2016-10-08 20:06:08 +08:00
de740be506 Add class Game,Animation,GameHighScore. For Game feature. 2016-10-08 19:50:29 +08:00
cd89de5a9a Update README.md 2016-09-29 13:35:42 +08:00
4dc2d5f1db Update README.md 2016-09-29 13:25:01 +08:00
1b0a872619 return statement added to callback_query_handler in decorator 2016-09-28 20:07:15 +08:00
82cf5535dd Merge pull request #230 from Kondra007/patch-6
Update Readme.md
2016-09-19 10:59:01 +08:00
48002f280b Update Readme.md
Added new content_types names in documentation.
2016-09-18 20:36:40 +03:00
057d130baa Update README.md 2016-09-17 20:32:54 +08:00
88e49bdaef Update test case. 2016-09-17 07:55:25 +08:00
acdc2058c5 Version change.
Change log:
- Fix venue's location not dejson to Location object.
2016-09-17 07:49:05 +08:00
a5ed76018d Fix venue Loacation dejson. 2016-09-17 07:38:18 +08:00
bac269d48a Update pytest. 2016-09-12 17:13:48 +08:00
e69790a8fc CI support py3.5. 2016-09-12 16:53:48 +08:00
590b27ca8a Add ujson support. 2016-09-12 16:38:54 +08:00
404f81fd43 Version update.
Change log:
- bugs fix.
2016-08-29 21:50:26 +08:00
89cf2658ae Fix edit reply markup return bool. 2016-08-29 20:50:27 +08:00
ea92e8696e Fix test case. 2016-08-29 20:48:09 +08:00
6da88c9751 FIx #225 2016-08-29 20:21:56 +08:00
ff5f6f727a Fix document #220 2016-08-11 22:46:40 +08:00
315400de47 Merge pull request #219 from olshevskiy87/t1
fix relative link for "callback query handlers" in readme
2016-08-10 10:37:36 +08:00
ca33801565 fix relative link for "callback query handlers" in readme 2016-08-09 16:17:45 +03:00
c61b82ace6 Update create_threads to threads. #198 2016-07-17 23:38:37 +08:00
8a4d2000d2 Update message's content_type doc. #199 2016-07-17 23:36:43 +08:00
f9d7f6905b Merge pull request #209 from QtRoS/master
Example of inline bot fixed
2016-07-17 23:15:34 +08:00
124b550de5 Merge pull request #1 from QtRoS/QtRoS-inline-bot-fix
Example of inline bot fixed
2016-07-17 15:55:11 +03:00
653c892b33 Example of inline bot fixed 2016-07-17 15:54:26 +03:00
b79c3165a1 Add issue templaate. 2016-07-11 21:45:52 +08:00
303406020b Update version. 2016-07-11 21:45:52 +08:00
3dcd59c22e Fix example 2016-07-11 21:29:25 +08:00
d847cfb9fa Merge branch 'bug-fix#202' 2016-07-06 10:22:55 +08:00
4205e46608 Fix missing location object in InlineQuery. 2016-07-06 10:13:42 +08:00
2044fba29a Update version. 2016-06-17 17:55:53 +08:00
29ef0e74af Fix typo. 2016-06-17 17:50:06 +08:00
46fc8da79e Merge pull request #197 from aivel/master
+ More flexibility for different handlers(callback_query_handler and etc.)
2016-06-17 01:22:07 +08:00
Max
b9a0c3e511 + More flexibility for different handlers(callback_query_handler and
etc.): now they are not limited in input arguments so can be easily
extended by subclassing

arguments
2016-06-16 11:49:51 +03:00
a4ac83aec2 Merge pull request #196 from auveele/patch-1
Update README.md
2016-06-15 20:03:22 +08:00
70fa5b405a Update README.md 2016-06-15 13:30:57 +02:00
c7d2731559 Merge pull request #195 from aivel/master
Some changes to make the library more flexible in terms of extensibility
2016-06-13 22:33:42 +08:00
Max
527351385b + Don't check filters against NoneType values;
+ More flexibility for subclassing:
	- __exec_task is protected now(was private)
	- _test_message_handler and _test_filter are class members
	  now(used to be static methods)
+ More flexibility on extention of message_handler(**kwargs for
additional parameters)
2016-06-13 16:47:15 +03:00
Max
1b47e5cc62 code duplication lessened 2016-06-13 14:24:27 +03:00
Max
eb4d58bec1 little optimization on handler dictionary building: code duplication lessened 2016-06-13 14:15:15 +03:00
57486b18cd Remove download count from readme. 2016-06-07 20:04:19 +08:00
25ad3f055f Update version.
Change log:
- Added new methods: getChat, leaveChat, getChatAdministrators,
  getChatMember, getChatMembersCount.
- New fields: edited_message in Update, edit_date in Message, user in
  MessageEntity. New value text_mention for the type field in
MessageEntity.
2016-06-07 19:57:40 +08:00
cdb6d6760d Add some comment. 2016-06-07 19:44:30 +08:00
5bd8e9d3f5 Update readme. 2016-06-07 19:40:35 +08:00
fab2f324d0 Fix test case. 2016-06-07 19:34:46 +08:00
9bf4be2caf fix test case. 2016-06-07 19:32:12 +08:00
d701fd6e1d Merge remote-tracking branch 'origin/feature-changes20160522' into api2.1 2016-06-07 19:30:01 +08:00
8e3c9d8d24 Add edited message handler. 2016-06-07 19:29:12 +08:00
0b9f91c6fb Add new method test case. 2016-06-07 19:16:17 +08:00
468a535257 Fix de_json. 2016-06-07 19:08:52 +08:00
d2e7f4d8f2 Add 2.1 new method. 2016-06-07 19:00:44 +08:00
ddd3664329 Merge pull request #192 from alejandrocq/comedoresugrbot
Add ComedoresUGRbot to bots list.
2016-06-06 01:32:45 +08:00
9e397c41cf ComedoresUGRbot added to bots list. 2016-06-05 19:20:17 +02:00
b528b0bcf0 Merge pull request #191 from Ledokol/uni-json
unicode strings for check_json
2016-06-05 18:36:36 +08:00
2f20d70e89 unicode strings for check_json 2016-06-04 21:18:09 +05:00
ac740e4755 Change public link to invite link. 2016-06-03 17:21:33 +08:00
2cf8ffcbc0 Update group link. 2016-06-03 14:45:25 +08:00
87ad4a62ff Merge pull request #187 from GabrielRF/master
Added Send2KindleBot
2016-06-03 00:06:10 +08:00
6b255deed7 Added Send2KindleBot
Also added description of urlprobot and lmgtfy_bot
2016-06-02 12:04:04 -03:00
ce24aa25f2 Fix #179. 2016-06-02 13:25:50 +08:00
a0f25089e0 Merge pull request #177 from qwidim/master
heroku webhook example
2016-06-02 13:17:12 +08:00
1bfdf7cf35 Merge pull request #173 from dmcelectrico/master
Added optional connection (aka sending) timeouts to methods that may …
2016-05-31 02:11:31 +08:00
5b1de9f339 Merge pull request #181 from PowerInside/patch-1
Fix inline demo code in readme.md
2016-05-29 22:29:21 +08:00
e956800e34 Fix inline demo code in readme.md 2016-05-29 19:25:36 +05:30
b9a57f630a Merge pull request #178 from Kondra007/patch-5
Update Readme.md
2016-05-26 11:02:30 +08:00
851d76888b Update Readme.md
Added `user` to attributes list
2016-05-26 00:44:39 +03:00
05f1c87c7d Update webhook_flask_heroku_echo.py 2016-05-25 19:25:46 +06:00
59b19fbbc1 Update webhook_flask_heroku_echo.py 2016-05-25 19:15:13 +06:00
e87907f0b8 Update webhook_flask_heroku_echo.py 2016-05-25 01:44:09 +06:00
d84aa796c0 Add files via upload 2016-05-25 01:42:25 +06:00
8c20f63022 Add user to MessageEntity. 2016-05-23 11:12:20 +08:00
f9c7497c5c Merge branch 'Kondra007-patch-3' into feature-changes20160522 2016-05-23 11:05:14 +08:00
3413669a23 Added optional connection (aka sending) timeouts to methods that may upload big chunks of data: send_audio, send_voice, send_document, send_sticker and send_video. 2016-05-22 18:35:20 +02:00
1a45b4844a Fixed Travis build (again)
Yeah, I'm really sorry for all this errors, maybe tired a little bit.
2016-05-22 15:47:44 +03:00
8a9d89591b Fixed Travis build errors 2016-05-22 15:44:04 +03:00
234dd8cf9f Edited edit_date field
It's integer, not `Message`
2016-05-22 13:55:54 +03:00
6a98d27f1a Added edited_message to Update object
As of Bot API update 2.1, Update object now has optional edited_message field.
2016-05-22 13:35:21 +03:00
04df139efb Added edit_date to Message object
As of Bot API update 2.1, `Message` object now has optional `edit_date` field.
2016-05-22 13:32:53 +03:00
747f14216b Merge pull request #166 from Kondra007/patch-1
Changed "Process_new_message" to "Process_new_update"
2016-05-20 23:23:10 +08:00
aee9255568 Changed "Process_new_message" to "Process_new_update"
This way all types of queries (Message, Inline query, Callback query) are supported.
2016-05-20 17:40:22 +03:00
fbaf88c237 Update version. 2016-05-20 14:41:00 +08:00
3ebc47de8b Add missing title in InlineQueryResultVenue. #165 2016-05-20 14:39:34 +08:00
8017c8d919 Fix failing build for python 3.3, 3.4 and pypy3 2016-05-19 10:18:00 +02:00
2545724a6f Fix #164 2016-05-19 10:02:59 +02:00
b8770e2c04 Merge pull request #163 from agentik-007/patch-1
Update webhook_flask_echo_bot.py
2016-05-17 08:41:34 +08:00
1da5d1e0e2 Update webhook_flask_echo_bot.py 2016-05-16 14:31:36 +03:00
bc73f345b2 Update README.md 2016-05-15 09:40:56 +08:00
a824ff967b Version update. 2016-05-12 11:39:50 +08:00
0c420ee5e4 Add missing title to InlineQueryResultLocation to_json.
Fix #160
2016-05-12 11:09:21 +08:00
16838c30b6 Update version.
Change log:
- Added the field emoji to the Sticker.
- Added the field forward_from_chat to the Message object for messages
  forwarded from channels.
2016-05-08 18:45:21 +08:00
022cdada49 Merge pull request #157 from Kondra007/patch-6
Added 06.05.16 updates
2016-05-08 10:45:28 +08:00
fb1052824c Added 06.05.16 updates
* Added the field emoji to the Sticker object. Your bot can now know the emoji a sticker corresponds to.
* Added the field forwarded_from_chat to the Message object for messages forwarded from channels.
2016-05-07 20:24:16 +03:00
22 changed files with 2689 additions and 222 deletions

7
.github/ISSUE_TEMPLATE vendored Normal file
View File

@ -0,0 +1,7 @@
Please answer these questions before submitting your issue. Thanks!
1. What version of pyTelegramBotAPI are you using?
2. What OS are you using?
3. What version of python are you using?

View File

@ -4,6 +4,7 @@ python:
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "pypy"
- "pypy3"
install: "pip install -r requirements.txt"

129
README.md
View File

@ -1,10 +1,9 @@
# <p align="center">pyTelegramBotAPI
<p align="center">A simple, but extensible Python implementation for the [Telegram Bot API](https://core.telegram.org/bots/api).
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.
[![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)
* [Getting started.](#getting-started)
* [Writing your first bot](#writing-your-first-bot)
@ -15,7 +14,7 @@
* [Methods](#methods)
* [General use of the API](#general-use-of-the-api)
* [Message handlers](#message-handlers)
* [Callback Query handlers](#message-handlers)
* [Callback Query handlers](#callback-query-handler)
* [TeleBot](#telebot)
* [Reply markup](#reply-markup)
* [Inline Mode](#inline-mode)
@ -26,6 +25,7 @@
* [The listener mechanism](#the-listener-mechanism)
* [Using web hooks](#using-web-hooks)
* [Logging](#logging)
* [Proxy](#proxy)
* [F.A.Q.](#faq)
* [Bot 2.0](#bot-20)
* [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)
@ -123,8 +123,12 @@ To start the bot, simply open up a terminal and enter `python echo_bot.py` to ru
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'.
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`, `video_note`, `voice`, `location`, `contact`, `new_chat_members`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`.
You can use some types in one function. Example:
```content_types=["text", "sticker", "pinned_message", "photo", "audio"]```
### Methods
@ -182,7 +186,7 @@ def handle_text_doc(message):
#Which could also be defined as:
def test_message(message):
return message.document.mime_type == 'text/plan'
return message.document.mime_type == 'text/plain'
@bot.message_handler(func=test_message, content_types=['document'])
def handle_text_doc(message)
@ -197,6 +201,18 @@ def send_something(message):
```
**Important: all handlers are tested in the order in which they were declared**
#### Edited Message handlers
Same as Message handlers
#### channel_post_handler
Same as Message handlers
#### edited_channel_post_handler
Same as Message handlers
#### Callback Query Handler
In bot2.0 update. You can get `callback_query` in update object. In telebot use `callback_query_handler` to process callback_querys.
@ -205,7 +221,7 @@ In bot2.0 update. You can get `callback_query` in update object. In telebot use
@bot.callback_query_handler(func=lambda call: True)
def test_callback(call):
logger.info(call)
```
```
#### TeleBot
```python
@ -218,8 +234,8 @@ tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object
# - 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)
# - timeout: integer (default 20) - Timeout in seconds for long polling.
tb.polling(none_stop=False, interval=0, timeout=20)
# getMe
user = tb.get_me()
@ -273,6 +289,11 @@ video = open('/tmp/video.mp4', 'rb')
tb.send_video(chat_id, video)
tb.send_video(chat_id, "FILEID")
# sendVideoNote
videonote = open('/tmp/videonote.mp4', 'rb')
tb.send_video_note(chat_id, videonote)
tb.send_video_note(chat_id, "FILEID")
# sendLocation
tb.send_location(chat_id, lat, lon)
@ -292,7 +313,7 @@ file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(API_TOKEN,
```
#### 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.
All `send_xyz` functions of TeleBot take an optional `reply_markup` argument. This argument must be an instance of `ReplyKeyboardMarkup`, `ReplyKeyboardRemove` or `ForceReply`, which are defined in types.py.
```python
from telebot import types
@ -325,12 +346,12 @@ 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")
![ReplyKeyboardMarkup](https://farm3.staticflickr.com/2933/32418726704_9ef76093cf_o_d.jpg "ReplyKeyboardMarkup")
```python
# ReplyKeyboardHide: hides a previously sent ReplyKeyboardMarkup
# ReplyKeyboardRemove: hides a previously sent ReplyKeyboardMarkup
# Takes an optional selective argument (True/False, default False)
markup = types.ReplyKeyboardHide(selective=False)
markup = types.ReplyKeyboardRemove(selective=False)
tb.send_message(chat_id, message, reply_markup=markup)
```
@ -342,7 +363,7 @@ tb.send_message(chat_id, "Send me another word:", reply_markup=markup)
```
ForceReply:
![ForceReply](https://pp.vk.me/c624430/v624430512/473ec/602byyWUHcs.jpg "ForceReply")
![ForceReply](https://farm4.staticflickr.com/3809/32418726814_d1baec0fc2_o_d.jpg "ForceReply")
### Inline Mode
@ -379,13 +400,28 @@ def test_chosen(chosen_inline_result):
@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.')
r = types.InlineQueryResultArticle('1', 'Result', types.InputTextMessageContent('Result message.'))
r2 = types.InlineQueryResultArticle('2', 'Result2', types.InputTextMessageContent('Result message2.'))
bot.answer_inline_query(inline_query.id, [r, r2])
except Exception as e:
print(e)
```
### Working with entities:
This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.
Attributes:
* `type`
* `url`
* `offset`
* `length`
* `user`
**Here's an Example:**`message.entities[num].<attribute>`<br>
Here `num` is the entity number or order of entity in a reply, for if incase there are multiple entities in the reply/message.<br>
`message.entities` returns a list of entities object. <br>
`message.entities[0].type` would give the type of the first entity<br>
Refer [Bot Api](https://core.telegram.org/bots/api#messageentity) for extra details
## Advanced use of the API
@ -425,15 +461,14 @@ for text in splitted_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
- threaded: 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:
```python
def handle_messages(messages):
for message in messsages:
for message in messages:
# Do something with the message
bot.reply_to(message, 'Hi')
@ -441,7 +476,7 @@ bot.set_update_listener(handle_messages)
bot.polling()
```
### Using webhooks
### Using web hooks
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.
@ -458,6 +493,26 @@ logger = telebot.logger
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
```
### Proxy
You can use proxy for request. `apihelper.proxy` object will use by call `requests` proxies argument.
```python
from telebot import apihelper
apihelper.proxy = {'http', 'http://10.10.1.10:3128'}
```
If you want to use socket5 proxy you need install dependency `pip install requests[socks]`.
```python
proxies = {
'http': 'socks5://user:pass@host:port',
'https': 'socks5://user:pass@host:port'
}
```
## F.A.Q.
### Bot 2.0
@ -470,14 +525,14 @@ April 9,2016 Telegram release new bot 2.0 API, which has a drastic revision espe
Telegram Bot API support new type Chat for message.chat.
- Check the ```type``` attribute in ```Chat``` object:
-
-
```python
if message.chat.type == “private”:
# private chat message
if message.chat.type == “group”:
# group chat message
if message.chat.type == “supergroup”:
# supergroup chat message
@ -490,8 +545,7 @@ if message.chat.type == “channel”:
Get help. Discuss. Chat.
* Join the pyTelegramBotAPI Telegram Chat Group
* Messge to @eternnoir by telegram for Invitation.
* Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/joinchat/Bn4ixj84FIZVkwhk2jag6A)
* 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
@ -503,13 +557,34 @@ Get help. Discuss. Chat.
## 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*
* [Send to Kindle Bot](https://telegram.me/Send2KindleBot) by *GabrielRF* - Send to Kindle files or links to files.
* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) ([source](https://github.com/GabrielRF/telegram-lmgtfy_bot)) by *GabrielRF* - Let me Google that for you.
* [Telegram UrlProBot](https://github.com/GabrielRF/telegram-urlprobot) ([source](https://github.com/GabrielRF/telegram-urlprobot)) by *GabrielRF* - URL shortener and URL expander.
* [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".
* [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte* - `Credits for the original version of this bot goes to` **Groosha** `, simply added certain features which I thought were needed`.
* [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*
* [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*
* [TagAlertBot](https://github.com/pitasi/TagAlertBot) by *pitasi*
* [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall.
* [picpingbot](https://web.telegram.org/#/im?p=%40picpingbot) - Fun anonymous photo exchange by Boogie Muffin.
* [TheZigZagProject](https://github.com/WebShark025/TheZigZagProject) - The 'All In One' bot for Telegram! by WebShark025
* [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash
* [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot)- DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch
* [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev.
* [dailypepebot](https://telegram.me/dailypepebot) by [*Jaime*](https://github.com/jiwidi/Dailypepe) - Get's you random pepe images and gives you their id, then you can call this image with the number.
* [DailyQwertee](https://t.me/DailyQwertee) by [*Jaime*](https://github.com/jiwidi/DailyQwertee) - Bot that manages a channel that sends qwertee daily tshirts every day at 00:00
* [wat-bridge](https://github.com/rmed/wat-bridge) by [*rmed*](https://github.com/rmed) - Send and receive messages to/from WhatsApp through Telegram
* [flibusta_bot](https://github.com/Kurbezz/flibusta_bot) by [*Kurbezz*](https://github.com/Kurbezz)
* [EmaProject](https://github.com/halkliff/emaproject) by [*halkliff*](https://github.com/halkliff) - Ema - Eastern Media Assistant was made thinking on the ease-to-use feature. Coding here is simple, as much as is fast and powerful.
* [filmratingbot](http://t.me/filmratingbot)([source](https://github.com/jcolladosp/film-rating-bot)) by [*jcolladosp*](https://github.com/jcolladosp) - Telegram bot using the Python API that gets films rating from IMDb and metacritic
* [you2mp3bot](http://t.me/you2mp3bot)([link](https://storebot.me/bot/you2mp3bot)) - This bot can convert a Youtube video to Mp3. All you need is send the URL video.
* [areajugonesbot](http://t.me/areajugonesbot)([link](http://t.me/areajugonesbot)) - The areajugonesbot sends news published on the videogames blog Areajugones to Telegram.
* [Send2Kindlebot](http://t.me/Send2KindleBot) ([source](https://github.com/GabrielRF/Send2KindleBot)) by *GabrielRF* - Send to Kindle service.
* [RastreioBot](http://t.me/RastreioBot) ([source](https://github.com/GabrielRF/RastreioBot)) by *GabrielRF* - Bot used to track packages on the Brazilian Mail Service.
* [filex_bot](http://t.me/filex_bot)([link](https://github.com/victor141516/FileXbot-telegram))
* [Spbu4UBot](http://t.me/Spbu4UBot)([link](https://github.com/EeOneDown/spbu4u)) by *EeOneDown* - Bot with timetables for SPbU students.
* [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students.
* [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free.
Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh.

View File

@ -27,7 +27,7 @@ API <https://core.telegram.org/bots/api>`__.
- `Methods <#methods>`__
- `General use of the API <#general-use-of-the-api>`__
- `Message handlers <#message-handlers>`__
- `Callback Query handlers <#message-handlers>`__
- `Callback Query handlers <#callback-query-handler>`__
- `TeleBot <#telebot>`__
- `Reply markup <#reply-markup>`__
- `Inline Mode <#inline-mode>`__
@ -421,8 +421,8 @@ TeleBot
# - 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)
# - timeout: integer (default 20) - Timeout in seconds for long polling.
tb.polling(none_stop=False, interval=0, timeout=20)
# getMe
user = tb.get_me()
@ -497,7 +497,7 @@ 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.
``ReplyKeyboardRemove`` or ``ForceReply``, which are defined in types.py.
.. code:: python
@ -538,9 +538,9 @@ The last example yields this result:
.. code:: python
# ReplyKeyboardHide: hides a previously sent ReplyKeyboardMarkup
# ReplyKeyboardRemove: hides a previously sent ReplyKeyboardMarkup
# Takes an optional selective argument (True/False, default False)
markup = types.ReplyKeyboardHide(selective=False)
markup = types.ReplyKeyboardRemove(selective=False)
tb.send_message(chat_id, message, reply_markup=markup)
.. code:: python

View File

@ -21,7 +21,7 @@ commands = { # command description used in the "help" command
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
hideBoard = types.ReplyKeyboardRemove() # if sent as reply_markup, will hide the keyboard
# error handling if user isn't known yet

View File

@ -5,7 +5,7 @@ CHAT_ID = 'YOUR CHAT ID'
bot = telebot.TeleBot(TOKEN)
ret_msg = bot.send_voice(CHAT_ID, open('tests/test_data/record.ogg'))
ret_msg = bot.send_voice(CHAT_ID, open('tests/test_data/record.ogg', 'rb'))
file_info = bot.get_file(ret_msg.voice.file_id)

View File

@ -53,7 +53,7 @@ def query_video(inline_query):
@bot.inline_handler(lambda query: len(query.query) is 0)
def default_query(inline_query):
try:
r = types.InlineQueryResultArticle('1', 'default', 'default')
r = types.InlineQueryResultArticle('1', 'default', types.InputTextMessageContent('default'))
bot.answer_inline_query(inline_query.id, [r])
except Exception as e:
print(e)

View File

@ -0,0 +1,83 @@
import telebot
from telebot.types import LabeledPrice
from telebot.types import ShippingOption
token = '1234567890:AAAABBBBCCCCDDDDeeeeFFFFgggGHHHH'
provider_token = '1234567890:TEST:AAAABBBBCCCCDDDD' # @BotFather -> Bot Settings -> Payments
bot = telebot.TeleBot(token)
# More about Payments: https://core.telegram.org/bots/payments
prices = [LabeledPrice(label='Working Time Machine', amount=5750), LabeledPrice('Gift wrapping', 500)]
shipping_options = [
ShippingOption(id='instant', title='WorldWide Teleporter').add_price(LabeledPrice('Teleporter', 1000)),
ShippingOption(id='pickup', title='Local pickup').add_price(LabeledPrice('Pickup', 300))]
@bot.message_handler(commands=['start'])
def command_start(message):
bot.send_message(message.chat.id,
"Hello, I'm the demo merchant bot."
" I can sell you a Time Machine."
" Use /buy to order one, /terms for Terms and Conditions")
@bot.message_handler(commands=['terms'])
def command_terms(message):
bot.send_message(message.chat.id,
'Thank you for shopping with our demo bot. We hope you like your new time machine!\n'
'1. If your time machine was not delivered on time, please rethink your concept of time and try again.\n'
'2. If you find that your time machine is not working, kindly contact our future service workshops on Trappist-1e.'
' They will be accessible anywhere between May 2075 and November 4000 C.E.\n'
'3. If you would like a refund, kindly apply for one yesterday and we will have sent it to you immediately.')
@bot.message_handler(commands=['buy'])
def command_pay(message):
bot.send_message(message.chat.id,
"Real cards won't work with me, no money will be debited from your account."
" Use this test card number to pay for your Time Machine: `4242 4242 4242 4242`"
"\n\nThis is your demo invoice:", parse_mode='Markdown')
bot.send_invoice(message.chat.id, title='Working Time Machine',
description='Want to visit your great-great-great-grandparents?'
' Make a fortune at the races?'
' Shake hands with Hammurabi and take a stroll in the Hanging Gardens?'
' Order our Working Time Machine today!',
provider_token=provider_token,
currency='usd',
photo_url='http://erkelzaar.tsudao.com/models/perrotta/TIME_MACHINE.jpg',
photo_height=512, # !=0/None or picture won't be shown
photo_width=512,
photo_size=512,
is_flexible=False, # True If you need to set up Shipping Fee
prices=prices,
start_parameter='time-machine-example',
invoice_payload='HAPPY FRIDAYS COUPON')
@bot.shipping_query_handler(func=lambda query: True)
def shipping(shipping_query):
print(shipping_query)
bot.answer_shipping_query(shipping_query.id, ok=True, shipping_options=shipping_options,
error_message='Oh, seems like our Dog couriers are having a lunch right now. Try again later!')
@bot.pre_checkout_query_handler(func=lambda query: True)
def checkout(pre_checkout_query):
bot.answer_pre_checkout_query(pre_checkout_query.id, ok=True,
error_message="Aliens tried to steal your card's CVV, but we successfully protected your credentials,"
" try to pay again in a few minutes, we need a small rest.")
@bot.message_handler(content_types=['successful_payment'])
def got_payment(message):
bot.send_message(message.chat.id,
'Hoooooray! Thanks for payment! We will proceed your order for `{} {}` as fast as possible! '
'Stay in touch.\n\nUse /buy again to get a Time Machine for your friend!'.format(
message.successful_payment.total_amount / 100, message.successful_payment.currency),
parse_mode='Markdown')
bot.skip_pending = True
bot.polling(none_stop=True, interval=0)

View File

@ -1,6 +1,6 @@
# Webhook examples using pyTelegramBotAPI
There are 3 examples in this directory using different libraries:
There are 4 examples in this directory using different libraries:
* **Python (CPython):** *webhook_cpython_echo_bot.py*
* **Pros:**
@ -32,5 +32,14 @@ There are 3 examples in this directory using different libraries:
* 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.
* **aiohttp (1.2.0):** *webhook_aiohttp_echo_bot.py*
* **Pros:**
* It's a web application framework
* Python 3 compatible
* Asynchronous, excellent perfomance
* Utilizes new async/await syntax
* **Cons:**
* Requires Python 3.4.2+, don't work with Python 2
*Latest update of this document: 2015-10-06*
*Latest update of this document: 2017-01-30*

View File

@ -0,0 +1,88 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This is a simple echo bot using decorators and webhook with aiohttp
# It echoes any incoming text messages and does not use the polling method.
import logging
import ssl
from aiohttp import web
import telebot
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://{}:{}".format(WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/{}/".format(API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.INFO)
bot = telebot.TeleBot(API_TOKEN)
app = web.Application()
# Process webhook calls
async def handle(request):
if request.match_info.get('token') == bot.token:
request_body_dict = await request.json()
update = telebot.types.Update.de_json(request_body_dict)
bot.process_new_updates([update])
return web.Response()
else:
return web.Response(status=403)
app.router.add_post('/{token}/', handle)
# 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'))
# Build ssl context
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_cert_chain(WEBHOOK_SSL_CERT, WEBHOOK_SSL_PRIV)
# Start aiohttp server
web.run_app(
app,
host=WEBHOOK_LISTEN,
port=WEBHOOK_PORT,
ssl_context=context,
)

View File

@ -46,7 +46,7 @@ class WebhookServer(object):
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])
bot.process_new_updates([update])
return ''
else:
raise cherrypy.HTTPError(403)
@ -73,6 +73,11 @@ bot.remove_webhook()
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
# Disable CherryPy requests log
access_log = cherrypy.log.access_log
for handler in tuple(access_log.handlers):
access_log.removeHandler(handler)
# Start cherrypy server
cherrypy.config.update({
'server.socket_host': WEBHOOK_LISTEN,

View File

@ -50,7 +50,7 @@ def webhook():
if flask.request.headers.get('content-type') == 'application/json':
json_string = flask.request.get_data().decode('utf-8')
update = telebot.types.Update.de_json(json_string)
bot.process_new_messages([update.message])
bot.process_new_updates([update])
return ''
else:
flask.abort(403)

View File

@ -0,0 +1,35 @@
import os
import telebot
from flask import Flask, request
TOKEN = '<api_token>'
bot = telebot.TeleBot(TOKEN)
server = Flask(__name__)
@bot.message_handler(commands=['start'])
def start(message):
bot.reply_to(message, 'Hello, ' + message.from_user.first_name)
@bot.message_handler(func=lambda message: True, content_types=['text'])
def echo_message(message):
bot.reply_to(message, message.text)
@server.route('/' + TOKEN, methods=['POST'])
def getMessage():
bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))])
return "!", 200
@server.route("/")
def webhook():
bot.remove_webhook()
bot.set_webhook(url='https://your_heroku_project.com/' + TOKEN)
return "!", 200
if __name__ == "__main__":
server.run(host="0.0.0.0", port=int(os.environ.get('PORT', 5000)))

View File

@ -0,0 +1,94 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This example shows webhook echo bot with Tornado web framework
# Documenation to Tornado: http://tornadoweb.org
import telebot
import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
import signal
API_TOKEN = '<api_token>'
WEBHOOK_CERT = "./cert.pem"
WEBHOOK_PKEY = "./pkey.pem"
WEBHOOK_HOST = "<domain_or_ip>"
WEBHOOK_SECRET = "<secret_uri_for_updates"
WEBHOOK_PORT = 88
WEBHOOK_URL_BASE = "https://{0}:{1}/{2}".format(WEBHOOK_HOST, str(WEBHOOK_PORT), WEBHOOK_SECRET)
# Quick'n'dirty SSL certificate generation:
#
# openssl genrsa -out pkey.pem 2048
# openssl req -new -x509 -days 3650 -key pkey.pem -out 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
bot = telebot.TeleBot(API_TOKEN)
class Root(tornado.web.RequestHandler):
def get(self):
self.write("Hi! This is webhook example!")
self.finish()
class webhook_serv(tornado.web.RequestHandler):
def get(self):
self.write("What are you doing here?")
self.finish()
def post(self):
if "Content-Length" in self.request.headers and \
"Content-Type" in self.request.headers and \
self.request.headers['Content-Type'] == "application/json":
# length = int(self.request.headers['Content-Length'])
json_data = self.request.body.decode("utf-8")
update = telebot.types.Update.de_json(json_data)
bot.process_new_updates([update])
self.write("")
self.finish()
else:
self.write("What are you doing here?")
self.finish()
tornado.options.define("port", default=WEBHOOK_PORT, help="run on the given port", type=int)
is_closing = False
def signal_handler(signum, frame):
global is_closing
print("Exiting...")
is_closing = True
def try_exit():
global is_closing
if is_closing:
# clean up here
tornado.ioloop.IOLoop.instance().stop()
print("Exit success!")
# 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."))
bot.remove_webhook()
bot.set_webhook(url=WEBHOOK_URL_BASE,
certificate=open(WEBHOOK_CERT, 'r'))
tornado.options.options.logging = None
tornado.options.parse_command_line()
signal.signal(signal.SIGINT, signal_handler)
application = tornado.web.Application([
(r"/", Root),
(r"/" + WEBHOOK_SECRET, webhook_serv)
])
http_server = tornado.httpserver.HTTPServer(application, ssl_options={
"certfile": WEBHOOK_CERT,
"keyfile": WEBHOOK_PKEY,
})
http_server.listen(tornado.options.options.port)
tornado.ioloop.PeriodicCallback(try_exit, 100).start()
tornado.ioloop.IOLoop.instance().start()

View File

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

View File

@ -7,7 +7,7 @@ def readme():
return f.read()
setup(name='pyTelegramBotAPI',
version='2.0.5',
version='3.6.0',
description='Python Telegram bot api. ',
long_description=readme(),
author='eternnoir',
@ -17,6 +17,9 @@ setup(name='pyTelegramBotAPI',
license='GPL2',
keywords='telegram bot api tools',
install_requires=['requests', 'six'],
extras_require={
'json': 'ujson',
},
classifiers=[
'Development Status :: 5 - Production/Stable',
'Programming Language :: Python :: 2',

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,24 @@
# -*- coding: utf-8 -*-
try:
import ujson as json
except ImportError:
import json
import requests
try:
from requests.packages.urllib3 import fields
format_header_param = fields.format_header_param
except ImportError:
format_header_param = None
import telebot
from telebot import types
from telebot import util
logger = telebot.logger
proxy = None
API_URL = "https://api.telegram.org/bot{0}/{1}"
FILE_URL = "https://api.telegram.org/file/bot{0}/{1}"
@ -14,6 +27,10 @@ CONNECT_TIMEOUT = 3.5
READ_TIMEOUT = 9999
def _get_req_session():
return util.per_thread('req_session', lambda: requests.session())
def _make_request(token, method_name, method='get', params=None, files=None, base_url=API_URL):
"""
Makes a request to the Telegram API.
@ -27,9 +44,14 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas
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))
read_timeout = READ_TIMEOUT
connect_timeout = CONNECT_TIMEOUT
if files and format_header_param:
fields.format_header_param = _no_encode(format_header_param)
if params:
if 'timeout' in params: read_timeout = params['timeout'] + 10
result = requests.request(method, request_url, params=params, files=files, timeout=(CONNECT_TIMEOUT, read_timeout))
if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10
result = _get_req_session().request(method, request_url, params=params, files=files,
timeout=(connect_timeout, read_timeout), proxies=proxy)
logger.debug("The server returned: '{0}'".format(result.text.encode('utf8')))
return _check_result(method_name, result)['result']
@ -78,7 +100,7 @@ def get_file(token, file_id):
def download_file(token, file_path):
url = FILE_URL.format(token, file_path)
result = requests.get(url)
result = _get_req_session().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)
@ -113,7 +135,7 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m
return _make_request(token, method_url, params=payload, method='post')
def set_webhook(token, url=None, certificate=None):
def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None):
method_url = r'setWebhook'
payload = {
'url': url if url else "",
@ -121,11 +143,25 @@ def set_webhook(token, url=None, certificate=None):
files = None
if certificate:
files = {'certificate': certificate}
if max_connections:
payload['max_connections'] = max_connections
if allowed_updates:
payload['allowed_updates'] = json.dumps(allowed_updates)
return _make_request(token, method_url, params=payload, files=files)
def get_updates(token, offset=None, limit=None, timeout=None):
def delete_webhook(token):
method_url = r'deleteWebhook'
return _make_request(token, method_url)
def get_webhook_info(token):
method_url = r'getWebhookInfo'
payload = {}
return _make_request(token, method_url, params=payload)
def get_updates(token, offset=None, limit=None, timeout=None, allowed_updates=None):
method_url = r'getUpdates'
payload = {}
if offset:
@ -134,6 +170,8 @@ def get_updates(token, offset=None, limit=None, timeout=None):
payload['limit'] = limit
if timeout:
payload['timeout'] = timeout
if allowed_updates:
payload['allowed_updates'] = json.dumps(allowed_updates)
return _make_request(token, method_url, params=payload)
@ -147,6 +185,48 @@ def get_user_profile_photos(token, user_id, offset=None, limit=None):
return _make_request(token, method_url, params=payload)
def get_chat(token, chat_id):
method_url = r'getChat'
payload = {'chat_id': chat_id}
return _make_request(token, method_url, params=payload)
def leave_chat(token, chat_id):
method_url = r'leaveChat'
payload = {'chat_id': chat_id}
return _make_request(token, method_url, params=payload)
def get_chat_administrators(token, chat_id):
method_url = r'getChatAdministrators'
payload = {'chat_id': chat_id}
return _make_request(token, method_url, params=payload)
def get_chat_members_count(token, chat_id):
method_url = r'getChatMembersCount'
payload = {'chat_id': chat_id}
return _make_request(token, method_url, params=payload)
def set_chat_sticker_set(token, chat_id, sticker_set_name):
method_url = r'setChatStickerSet'
payload = {'chat_id': chat_id, 'sticker_set_name': sticker_set_name}
return _make_request(token, method_url, params=payload)
def delete_chat_sticker_set(token, chat_id):
method_url = r'deleteChatStickerSet'
payload = {'chat_id': chat_id}
return _make_request(token, method_url, params=payload)
def get_chat_member(token, chat_id, user_id):
method_url = r'getChatMember'
payload = {'chat_id': chat_id, 'user_id': user_id}
return _make_request(token, method_url, params=payload)
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}
@ -156,7 +236,7 @@ def forward_message(token, chat_id, from_chat_id, message_id, disable_notificati
def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
parse_mode=None, disable_notification=None):
method_url = r'sendPhoto'
payload = {'chat_id': chat_id}
files = None
@ -170,15 +250,31 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
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_media_group(token, chat_id, media, disable_notification=None, reply_to_message_id=None):
method_url = r'sendMediaGroup'
media_json, files = _convert_input_media(media)
payload = {'chat_id': chat_id, 'media': media_json}
if disable_notification:
payload['disable_notification'] = disable_notification
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
return _make_request(token, method_url, params=payload, method='post' if files else 'get',
files=files if files else None)
def send_location(token, chat_id, latitude, longitude, live_period=None, 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 live_period:
payload['live_period'] = live_period
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
@ -188,6 +284,36 @@ def send_location(token, chat_id, latitude, longitude, reply_to_message_id=None,
return _make_request(token, method_url, params=payload)
def edit_message_live_location(token, latitude, longitude, chat_id=None, message_id=None,
inline_message_id=None, reply_markup=None):
method_url = r'editMessageLiveLocation'
payload = {'latitude': latitude, 'longitude': longitude}
if chat_id:
payload['chat_id'] = chat_id
if message_id:
payload['message_id'] = message_id
if inline_message_id:
payload['inline_message_id'] = inline_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload)
def stop_message_live_location(token, chat_id=None, message_id=None,
inline_message_id=None, reply_markup=None):
method_url = r'stopMessageLiveLocation'
payload = {}
if chat_id:
payload['chat_id'] = chat_id
if message_id:
payload['message_id'] = message_id
if inline_message_id:
payload['inline_message_id'] = inline_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload)
def send_venue(token, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None,
reply_to_message_id=None, reply_markup=None):
method_url = r'sendVenue'
@ -225,7 +351,7 @@ def send_chat_action(token, chat_id, action):
def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None):
method_url = r'sendVideo'
payload = {'chat_id': chat_id}
files = None
@ -241,13 +367,19 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if supports_streaming:
payload['supports_streaming'] = supports_streaming
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
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):
def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None, timeout=None):
method_url = r'sendVoice'
payload = {'chat_id': chat_id}
files = None
@ -255,19 +387,51 @@ def send_voice(token, chat_id, voice, duration=None, reply_to_message_id=None, r
files = {'voice': voice}
else:
payload['voice'] = voice
if caption:
payload['caption'] = caption
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 parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
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):
def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
method_url = r'sendVideoNote'
payload = {'chat_id': chat_id}
files = None
if not util.is_string(data):
files = {'video_note': data}
else:
payload['video_note'] = data
if duration:
payload['duration'] = duration
if length:
payload['length'] = length
else:
payload['length'] = 639 # seems like it is MAX length size
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
if timeout:
payload['connect-timeout'] = timeout
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None,
reply_markup=None, parse_mode=None, disable_notification=None, timeout=None):
method_url = r'sendAudio'
payload = {'chat_id': chat_id}
files = None
@ -275,6 +439,8 @@ def send_audio(token, chat_id, audio, duration=None, performer=None, title=None,
files = {'audio': audio}
else:
payload['audio'] = audio
if caption:
payload['caption'] = caption
if duration:
payload['duration'] = duration
if performer:
@ -285,12 +451,17 @@ def send_audio(token, chat_id, audio, duration=None, performer=None, title=None,
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
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):
def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None,
disable_notification=None, timeout=None, caption=None):
method_url = get_method_by_type(data_type)
payload = {'chat_id': chat_id}
files = None
@ -302,8 +473,14 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode and data_type == 'document':
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
if caption:
payload['caption'] = caption
return _make_request(token, method_url, params=payload, files=files, method='post')
@ -314,9 +491,11 @@ def get_method_by_type(data_type):
return r'sendSticker'
def kick_chat_member(token, chat_id, user_id):
def kick_chat_member(token, chat_id, user_id, until_date=None):
method_url = 'kickChatMember'
payload = {'chat_id': chat_id, 'user_id': user_id}
if until_date:
payload['until_date'] = until_date
return _make_request(token, method_url, params=payload, method='post')
@ -326,6 +505,96 @@ def unban_chat_member(token, chat_id, user_id):
return _make_request(token, method_url, params=payload, method='post')
def restrict_chat_member(token, chat_id, user_id, until_date=None, can_send_messages=None,
can_send_media_messages=None, can_send_other_messages=None,
can_add_web_page_previews=None):
method_url = 'restrictChatMember'
payload = {'chat_id': chat_id, 'user_id': user_id}
if until_date:
payload['until_date'] = until_date
if can_send_messages:
payload['can_send_messages'] = can_send_messages
if can_send_media_messages:
payload['can_send_media_messages'] = can_send_media_messages
if can_send_other_messages:
payload['can_send_other_messages'] = can_send_other_messages
if can_add_web_page_previews:
payload['can_add_web_page_previews'] = can_add_web_page_previews
return _make_request(token, method_url, params=payload, method='post')
def promote_chat_member(token, chat_id, user_id, can_change_info=None, can_post_messages=None,
can_edit_messages=None, can_delete_messages=None, can_invite_users=None,
can_restrict_members=None, can_pin_messages=None, can_promote_members=None):
method_url = 'promoteChatMember'
payload = {'chat_id': chat_id, 'user_id': user_id}
if can_change_info:
payload['can_change_info'] = can_change_info
if can_post_messages:
payload['can_post_messages'] = can_post_messages
if can_edit_messages:
payload['can_edit_messages'] = can_edit_messages
if can_delete_messages:
payload['can_delete_messages'] = can_delete_messages
if can_invite_users:
payload['can_invite_users'] = can_invite_users
if can_restrict_members:
payload['can_restrict_members'] = can_restrict_members
if can_pin_messages:
payload['can_pin_messages'] = can_pin_messages
if can_promote_members:
payload['can_promote_members'] = can_promote_members
return _make_request(token, method_url, params=payload, method='post')
def export_chat_invite_link(token, chat_id):
method_url = 'exportChatInviteLink'
payload = {'chat_id': chat_id}
return _make_request(token, method_url, params=payload, method='post')
def set_chat_photo(token, chat_id, photo):
method_url = 'setChatPhoto'
payload = {'chat_id': chat_id}
files = None
if not util.is_string(photo):
files = {'photo': photo}
else:
payload['photo'] = photo
return _make_request(token, method_url, params=payload, files=files, method='post')
def delete_chat_photo(token, chat_id):
method_url = 'deleteChatPhoto'
payload = {'chat_id': chat_id}
return _make_request(token, method_url, params=payload, method='post')
def set_chat_title(token, chat_id, title):
method_url = 'setChatTitle'
payload = {'chat_id': chat_id, 'title': title}
return _make_request(token, method_url, params=payload, method='post')
def set_chat_description(token, chat_id, description):
method_url = 'setChatDescription'
payload = {'chat_id': chat_id, 'description': description}
return _make_request(token, method_url, params=payload, method='post')
def pin_chat_message(token, chat_id, message_id, disable_notification=False):
method_url = 'pinChatMessage'
payload = {'chat_id': chat_id, 'message_id': message_id, 'disable_notification': disable_notification}
return _make_request(token, method_url, params=payload, method='post')
def unpin_chat_message(token, chat_id):
method_url = 'unpinChatMessage'
payload = {'chat_id': chat_id}
return _make_request(token, method_url, params=payload, method='post')
# Updating messages
def edit_message_text(token, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None,
@ -347,7 +616,8 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message
return _make_request(token, method_url, params=payload)
def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None,
parse_mode=None, reply_markup=None):
method_url = r'editMessageCaption'
payload = {'caption': caption}
if chat_id:
@ -356,6 +626,8 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m
payload['message_id'] = message_id
if inline_message_id:
payload['inline_message_id'] = inline_message_id
if parse_mode:
payload['parse_mode'] = parse_mode
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload)
@ -375,22 +647,210 @@ def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_messa
return _make_request(token, method_url, params=payload)
def delete_message(token, chat_id, message_id):
method_url = r'deleteMessage'
payload = {'chat_id': chat_id, 'message_id': message_id}
return _make_request(token, method_url, params=payload)
# Game
def send_game(token, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, reply_markup=None):
method_url = r'sendGame'
payload = {'chat_id': chat_id, 'game_short_name': game_short_name}
if disable_notification:
payload['disable_notification'] = disable_notification
if reply_to_message_id:
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)
# https://core.telegram.org/bots/api#setgamescore
def set_game_score(token, user_id, score, force=None, disable_edit_message=None, chat_id=None, message_id=None,
inline_message_id=None):
"""
Use this method to set the score of the specified user in a game. On success, if the message was sent by the bot, returns the edited Message, otherwise returns True. Returns an error, if the new score is not greater than the user's current score in the chat.
:param token: Bot's token (you don't need to fill this)
:param user_id: User identifier
:param score: New score, must be non-negative
:param force: (Optional) Pass True, if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters
:param disable_edit_message: (Optional) Pass True, if the game message should not be automatically edited to include the current scoreboard
:param chat_id: (Optional, required if inline_message_id is not specified) Unique identifier for the target chat (or username of the target channel in the format @channelusername)
:param message_id: (Optional, required if inline_message_id is not specified) Unique identifier of the sent message
:param inline_message_id: (Optional, required if chat_id and message_id are not specified) Identifier of the inline message
:return:
"""
method_url = r'setGameScore'
payload = {'user_id': user_id, 'score': score}
if force:
payload['force'] = force
if chat_id:
payload['chat_id'] = chat_id
if message_id:
payload['message_id'] = message_id
if inline_message_id:
payload['inline_message_id'] = inline_message_id
if disable_edit_message:
payload['disable_edit_message'] = disable_edit_message
return _make_request(token, method_url, params=payload)
# https://core.telegram.org/bots/api#getgamehighscores
def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_message_id=None):
"""
Use this method to get data for high score tables. Will return the score of the specified user and several of his neighbors in a game. On success, returns an Array of GameHighScore objects.
This method will currently return scores for the target user, plus two of his closest neighbors on each side. Will also return the top three users if the user and his neighbors are not among them. Please note that this behavior is subject to change.
:param token: Bot's token (you don't need to fill this)
:param user_id: Target user id
:param chat_id: (Optional, required if inline_message_id is not specified) Unique identifier for the target chat (or username of the target channel in the format @channelusername)
:param message_id: (Optional, required if inline_message_id is not specified) Unique identifier of the sent message
:param inline_message_id: (Optional, required if chat_id and message_id are not specified) Identifier of the inline message
:return:
"""
method_url = r'getGameHighScores'
payload = {'user_id': user_id}
if chat_id:
payload['chat_id'] = chat_id
if message_id:
payload['message_id'] = message_id
if inline_message_id:
payload['inline_message_id'] = inline_message_id
return _make_request(token, method_url, params=payload)
# Payments (https://core.telegram.org/bots/api#payments)
def send_invoice(token, chat_id, title, description, invoice_payload, provider_token, currency, prices,
start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None,
need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None,
disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None):
"""
Use this method to send invoices. On success, the sent Message is returned.
:param token: Bot's token (you don't need to fill this)
:param chat_id: Unique identifier for the target private chat
:param title: Product name
:param description: Product description
:param invoice_payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.
:param provider_token: Payments provider token, obtained via @Botfather
:param currency: Three-letter ISO 4217 currency code, see https://core.telegram.org/bots/payments#supported-currencies
:param prices: Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
:param start_parameter: Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter
:param photo_url: URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.
:param photo_size: Photo size
:param photo_width: Photo width
:param photo_height: Photo height
:param need_name: Pass True, if you require the user's full name to complete the order
:param need_phone_number: Pass True, if you require the user's phone number to complete the order
:param need_email: Pass True, if you require the user's email to complete the order
:param need_shipping_address: Pass True, if you require the user's shipping address to complete the order
:param is_flexible: Pass True, if the final price depends on the shipping method
:param disable_notification: Sends the message silently. Users will receive a notification with no sound.
:param reply_to_message_id: If the message is a reply, ID of the original message
:param reply_markup: A JSON-serialized object for an inline keyboard. If empty, one 'Pay total price' button will be shown. If not empty, the first button must be a Pay button
:return:
"""
method_url = r'sendInvoice'
payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload,
'provider_token': provider_token, 'start_parameter': start_parameter, 'currency': currency,
'prices': _convert_list_json_serializable(prices)}
if photo_url:
payload['photo_url'] = photo_url
if photo_size:
payload['photo_size'] = photo_size
if photo_width:
payload['photo_width'] = photo_width
if photo_height:
payload['photo_height'] = photo_height
if need_name:
payload['need_name'] = need_name
if need_phone_number:
payload['need_phone_number'] = need_phone_number
if need_email:
payload['need_email'] = need_email
if need_shipping_address:
payload['need_shipping_address'] = need_shipping_address
if is_flexible:
payload['is_flexible'] = is_flexible
if disable_notification:
payload['disable_notification'] = disable_notification
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 provider_data:
payload['provider_data'] = provider_data
return _make_request(token, method_url, params=payload)
def answer_shipping_query(token, shipping_query_id, ok, shipping_options=None, error_message=None):
"""
If you sent an invoice requesting a shipping address and the parameter is_flexible was specified, the Bot API will send an Update with a shipping_query field to the bot. Use this method to reply to shipping queries. On success, True is returned.
:param token: Bot's token (you don't need to fill this)
:param shipping_query_id: Unique identifier for the query to be answered
:param ok: Specify True if delivery to the specified address is possible and False if there are any problems (for example, if delivery to the specified address is not possible)
:param shipping_options: Required if ok is True. A JSON-serialized array of available shipping options.
:param error_message: Required if ok is False. Error message in human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user.
:return:
"""
method_url = 'answerShippingQuery'
payload = {'shipping_query_id': shipping_query_id, 'ok': ok}
if shipping_options:
payload['shipping_options'] = _convert_list_json_serializable(shipping_options)
if error_message:
payload['error_message'] = error_message
return _make_request(token, method_url, params=payload)
def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_message=None):
"""
Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the field pre_checkout_query. Use this method to respond to such pre-checkout queries. On success, True is returned. Note: The Bot API must receive an answer within 10 seconds after the pre-checkout query was sent.
:param token: Bot's token (you don't need to fill this)
:param pre_checkout_query_id: Unique identifier for the query to be answered
:param ok: Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems.
:param error_message: Required if ok is False. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user.
:return:
"""
method_url = 'answerPreCheckoutQuery'
payload = {'pre_checkout_query_id': pre_checkout_query_id, 'ok': ok}
if error_message:
payload['error_message'] = error_message
return _make_request(token, method_url, params=payload)
# InlineQuery
def answer_callback_query(token, callback_query_id, text=None, show_alert=None):
def answer_callback_query(token, callback_query_id, text=None, show_alert=None, url=None, cache_time=None):
"""
Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. On success, True is returned.
Alternatively, the user can be redirected to the specified Game URL. For this option to work, you must first create a game for your bot via BotFather and accept the terms. Otherwise, you may use links like telegram.me/your_bot?start=XXXX that open your bot with a parameter.
:param token: Bot's token (you don't need to fill this)
:param callback_query_id: Unique identifier for the query to be answered
:param text: (Optional) Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters
:param show_alert: (Optional) If true, an alert will be shown by the client instead of a notification at the top of the chat screen. Defaults to false.
:param url: (Optional) URL that will be opened by the user's client. If you have created a Game and accepted the conditions via @Botfather, specify the URL that opens your game note that this will only work if the query comes from a callback_game button.
Otherwise, you may use links like telegram.me/your_bot?start=XXXX that open your bot with a parameter.
:param cache_time: (Optional) The maximum amount of time in seconds that the result of the callback query may be cached client-side. Telegram apps will support caching starting in version 3.14. Defaults to 0.
:return:
"""
method_url = 'answerCallbackQuery'
payload = {'callback_query_id': callback_query_id}
if text:
payload['text'] = text
if show_alert:
payload['show_alert'] = show_alert
if url:
payload['url'] = url
if cache_time:
payload['cache_time'] = cache_time
return _make_request(token, method_url, params=payload, method='post')
def answer_inline_query(token, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None,
switch_pm_text=None, switch_pm_parameter=None):
method_url = 'answerInlineQuery'
payload = {'inline_query_id': inline_query_id, 'results': _convert_inline_results(results)}
payload = {'inline_query_id': inline_query_id, 'results': _convert_list_json_serializable(results)}
if cache_time:
payload['cache_time'] = cache_time
if is_personal:
@ -404,7 +864,59 @@ def answer_inline_query(token, inline_query_id, results, cache_time=None, is_per
return _make_request(token, method_url, params=payload, method='post')
def _convert_inline_results(results):
def get_sticker_set(token, name):
method_url = 'getStickerSet'
return _make_request(token, method_url, params={'name': name})
def upload_sticker_file(token, user_id, png_sticker):
method_url = 'uploadStickerFile'
payload = {'user_id': user_id}
files = {'png_sticker': png_sticker}
return _make_request(token, method_url, params=payload, files=files, method='post')
def create_new_sticker_set(token, user_id, name, title, png_sticker, emojis, contains_masks=None, mask_position=None):
method_url = 'createNewStickerSet'
payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis}
files = None
if not util.is_string(png_sticker):
files = {'png_sticker': png_sticker}
else:
payload['png_sticker'] = png_sticker
if contains_masks:
payload['contains_masks'] = contains_masks
if mask_position:
payload['mask_position'] = mask_position.to_json()
return _make_request(token, method_url, params=payload, files=files, method='post')
def add_sticker_to_set(token, user_id, name, png_sticker, emojis, mask_position):
method_url = 'addStickerToSet'
payload = {'user_id': user_id, 'name': name, 'emojis': emojis}
files = None
if not util.is_string(png_sticker):
files = {'png_sticker': png_sticker}
else:
payload['png_sticker'] = png_sticker
if mask_position:
payload['mask_position'] = mask_position.to_json()
return _make_request(token, method_url, params=payload, files=files, method='post')
def set_sticker_position_in_set(token, sticker, position):
method_url = 'setStickerPositionInSet'
payload = {'sticker': sticker, 'position': position}
return _make_request(token, method_url, params=payload, method='post')
def delete_sticker_from_set(token, sticker):
method_url = 'deleteStickerFromSet'
payload = {'sticker': sticker}
return _make_request(token, method_url, params=payload, method='post')
def _convert_list_json_serializable(results):
ret = ''
for r in results:
if isinstance(r, types.JsonSerializable):
@ -420,6 +932,29 @@ def _convert_markup(markup):
return markup
def _convert_input_media(array):
media = []
files = {}
for input_media in array:
if isinstance(input_media, types.JsonSerializable):
media_dict = input_media.to_dic()
if media_dict['media'].startswith('attach://'):
key = media_dict['media'].replace('attach://', '')
files[key] = input_media.media
media.append(media_dict)
return json.dumps(media), files
def _no_encode(func):
def wrapper(key, val):
if key == 'filename':
return '{0}={1}'.format(key, val)
else:
return func(key, val)
return wrapper
class ApiException(Exception):
"""
This class represents an Exception thrown when a call to the Telegram API fails.

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
import random
import string
import threading
import traceback
import re
import sys
import six
@ -12,8 +14,11 @@ try:
import Queue
except ImportError:
import queue as Queue
import logging
from telebot import logger
logger = logging.getLogger('TeleBot')
thread_local = threading.local()
class WorkerThread(threading.Thread):
@ -56,8 +61,8 @@ class WorkerThread(threading.Thread):
self.done_event.set()
except Queue.Empty:
pass
except:
logger.debug("Exception occurred")
except Exception as e:
logger.error(type(e).__name__ + " occurred, args=" + str(e.args) + "\n" + traceback.format_exc())
self.exc_info = sys.exc_info()
self.exception_event.set()
@ -241,3 +246,16 @@ def extract_arguments(text):
regexp = re.compile("\/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE)
result = regexp.match(text)
return result.group(2) if is_command(text) else None
def per_thread(key, construct_value):
try:
return getattr(thread_local, key)
except AttributeError:
value = construct_value()
setattr(thread_local, key, value)
return value
def generate_random_token():
return ''.join(random.sample(string.ascii_letters, 16))

View File

@ -16,6 +16,7 @@ should_skip = 'TOKEN' and 'CHAT_ID' not in os.environ
if not should_skip:
TOKEN = os.environ['TOKEN']
CHAT_ID = os.environ['CHAT_ID']
GROUP_ID = os.environ['GROUP_ID']
@pytest.mark.skipif(should_skip, reason="No environment variables configured")
@ -129,6 +130,15 @@ class TestTeleBot:
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
assert ret_msg.message_id
def test_send_file_caption(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_document(CHAT_ID, file_data, caption="Test")
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)
@ -183,7 +193,7 @@ class TestTeleBot:
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')
ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram')
assert ret_msg.content_type == 'audio'
assert ret_msg.audio.performer == 'eternnoir'
assert ret_msg.audio.title == 'pyTelegram'
@ -191,7 +201,8 @@ class TestTeleBot:
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)
ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram',
disable_notification=True)
assert ret_msg.content_type == 'audio'
assert ret_msg.audio.performer == 'eternnoir'
assert ret_msg.audio.title == 'pyTelegram'
@ -321,6 +332,7 @@ class TestTeleBot:
lon = -161.2901042
ret_msg = tb.send_venue(CHAT_ID, lat, lon, "Test Venue", "1123 Test Venue address")
assert ret_msg.venue.title == "Test Venue"
assert int(lat) == int(ret_msg.venue.location.latitude)
def test_send_venue_dis_noti(self):
tb = telebot.TeleBot(TOKEN)
@ -342,6 +354,28 @@ class TestTeleBot:
new_msg = tb.edit_message_text('Edit test', chat_id=CHAT_ID, message_id=msg.message_id)
assert new_msg.text == 'Edit test'
def test_edit_message_caption(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
msg = tb.send_document(CHAT_ID, file_data, caption="Test")
new_msg = tb.edit_message_caption(caption='Edit test', chat_id=CHAT_ID, message_id=msg.message_id)
assert new_msg.caption == 'Edit test'
def test_get_chat(self):
tb = telebot.TeleBot(TOKEN)
ch = tb.get_chat(GROUP_ID)
assert str(ch.id) == GROUP_ID
def test_get_chat_administrators(self):
tb = telebot.TeleBot(TOKEN)
cas = tb.get_chat_administrators(GROUP_ID)
assert len(cas) > 0
def test_get_chat_members_count(self):
tb = telebot.TeleBot(TOKEN)
cn = tb.get_chat_members_count(GROUP_ID)
assert cn > 1
def test_edit_markup(self):
text = 'CI Test Message'
tb = telebot.TeleBot(TOKEN)
@ -356,7 +390,7 @@ class TestTeleBot:
def create_text_message(self, text):
params = {'text': text}
chat = types.User(11, 'test')
chat = types.User(11, False, 'test')
return types.Message(1, None, None, chat, 'text', params)
def test_is_string_unicode(self):
@ -370,3 +404,73 @@ class TestTeleBot:
def test_not_string(self):
i1 = 10
assert not util.is_string(i1)
def test_send_video_note(self):
file_data = open('./test_data/test_video.mp4', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_video_note(CHAT_ID, file_data)
assert ret_msg.message_id
def test_send_media_group(self):
tb = telebot.TeleBot(TOKEN)
img1 = 'https://i.imgur.com/CjXjcnU.png'
img2 = 'https://i.imgur.com/CjXjcnU.png'
medias = [types.InputMediaPhoto(img1, "View"), types.InputMediaPhoto(img2, "Dog")]
result = tb.send_media_group(CHAT_ID, medias)
assert len(result) == 2
assert result[0].media_group_id is not None
assert result[0].media_group_id == result[1].media_group_id
def test_send_media_group_local_files(self):
photo = open('../examples/detailed_example/kitten.jpg', 'rb')
video = open('./test_data/test_video.mp4', 'rb')
tb = telebot.TeleBot(TOKEN)
medias = [types.InputMediaPhoto(photo, "View"),
types.InputMediaVideo(video)]
result = tb.send_media_group(CHAT_ID, medias)
assert len(result) == 2
assert result[0].media_group_id is not None
assert result[1].media_group_id is not None
def test_send_photo_formating_caption(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_photo(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
assert ret_msg.caption_entities[0].type == 'italic'
def test_send_video_formatting_caption(self):
file_data = open('./test_data/test_video.mp4', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_video(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
assert ret_msg.caption_entities[0].type == 'italic'
def test_send_audio_formatting_caption(self):
file_data = open('./test_data/record.mp3', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_audio(CHAT_ID, file_data, caption='<b>bold</b>', parse_mode='HTML')
assert ret_msg.caption_entities[0].type == 'bold'
def test_send_voice_formatting_caprion(self):
file_data = open('./test_data/record.ogg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_voice(CHAT_ID, file_data, caption='<b>bold</b>', parse_mode='HTML')
assert ret_msg.caption_entities[0].type == 'bold'
assert ret_msg.voice.mime_type == 'audio/ogg'
def test_send_media_group_formatting_caption(self):
tb = telebot.TeleBot(TOKEN)
img1 = 'https://i.imgur.com/CjXjcnU.png'
img2 = 'https://i.imgur.com/CjXjcnU.png'
medias = [types.InputMediaPhoto(img1, "*View*", parse_mode='Markdown'),
types.InputMediaPhoto(img2, "_Dog_", parse_mode='Markdown')]
result = tb.send_media_group(CHAT_ID, medias)
assert len(result) == 2
assert result[0].media_group_id is not None
assert result[0].caption_entities[0].type == 'bold'
assert result[1].caption_entities[0].type == 'italic'
def test_send_document_formating_caption(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_document(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
assert ret_msg.caption_entities[0].type == 'italic'

View File

@ -6,19 +6,19 @@ from telebot import types
def test_json_user():
jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","username":"rdss_bot"}'
jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","username":"rdss_bot","is_bot":true}'
u = types.User.de_json(jsonstring)
assert u.id == 101176298
def test_json_message():
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"}'
jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir","is_bot":true},"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,"type":"private","title":"\u4ea4"},"date":1435303157,"text":"HIHI"}'
json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG","is_bot":true},"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
@ -39,7 +39,7 @@ def test_json_Document():
def test_json_Message_Audio():
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}}'
json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd","is_bot":true },"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 == 1
assert msg.content_type == 'audio'
@ -48,7 +48,7 @@ def test_json_Message_Audio():
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","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}}'
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"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
@ -56,7 +56,7 @@ def test_json_Message_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}}'
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"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
@ -64,21 +64,21 @@ def test_json_Message_Sticker_without_thumb():
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","type":"private","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","is_bot":true },"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","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}]}'
json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"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","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}}'
json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd","is_bot":true },"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
@ -87,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","type":"private","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","is_bot":true },"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'
@ -114,7 +114,7 @@ def test_json_voice():
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"}}'
json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"is_bot":true,"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