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

Compare commits

..

187 Commits
2.1.5 ... 3.2.0

Author SHA1 Message Date
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
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
21 changed files with 1975 additions and 100 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"

View File

@ -1,6 +1,6 @@
# <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)
@ -14,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)
@ -25,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)
@ -122,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_member`, `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
@ -181,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)
@ -200,6 +205,14 @@ def send_something(message):
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.
@ -208,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
@ -221,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()
@ -276,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(chat_id, videonote)
tb.send_video(chat_id, "FILEID")
# sendLocation
tb.send_location(chat_id, lat, lon)
@ -295,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
@ -328,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)
```
@ -345,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
@ -389,8 +407,8 @@ def query_text(inline_query):
print(e)
```
###Working with entities:
This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.
### Working with entities:
This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.
Attributes:
* `type`
* `url`
@ -443,9 +461,8 @@ 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:
@ -459,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.
@ -476,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
@ -488,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
@ -526,9 +563,22 @@ Get help. Discuss. Chat.
* [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.
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

@ -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

@ -48,9 +48,9 @@ def index():
@app.route(WEBHOOK_URL_PATH, methods=['POST'])
def webhook():
if flask.request.headers.get('content-type') == 'application/json':
json_string = flask.request.get_data().encode('utf-8')
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

@ -16,8 +16,7 @@ def echo_message(message):
@server.route("/bot", methods=['POST'])
def getMessage():
bot.process_new_messages(
[telebot.types.Update.de_json(request.stream.read().decode("utf-8")).message])
bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))])
return "!", 200
@server.route("/")

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.1.4',
version='3.1.1',
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',

View File

@ -33,18 +33,39 @@ class TeleBot:
getMe
sendMessage
forwardMessage
deleteMessage
sendPhoto
sendAudio
sendDocument
sendSticker
sendVideo
sendVideoNote
sendLocation
sendChatAction
getUserProfilePhotos
getUpdates
"""
getFile
kickChatMember
unbanChatMember
restrictChatMember
promoteChatMember
exportChatInviteLink
setChatPhoto
deleteChatPhoto
setChatTitle
setChatDescription
pinChatMessage
unpinChatMessage
leaveChat
getChat
getChatAdministrators
getChatMembersCount
getChatMember
answerCallbackQuery
answerInlineQuery
"""
def __init__(self, token, threaded=True, skip_pending=False):
def __init__(self, token, threaded=True, skip_pending=False, num_threads=2):
"""
:param token: bot API token
:return: Telebot object.
@ -67,29 +88,45 @@ class TeleBot:
self.message_handlers = []
self.edited_message_handlers = []
self.channel_post_handlers = []
self.edited_channel_post_handlers = []
self.inline_handlers = []
self.chosen_inline_handlers = []
self.callback_query_handlers = []
self.shipping_query_handlers = []
self.pre_checkout_query_handlers = []
self.threaded = threaded
if self.threaded:
self.worker_pool = util.ThreadPool()
self.worker_pool = util.ThreadPool(num_threads=num_threads)
def set_webhook(self, url=None, certificate=None):
return apihelper.set_webhook(self.token, url, certificate)
def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None):
return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates)
def delete_webhook(self):
"""
Use this method to remove webhook integration if you decide to switch back to getUpdates.
:return: bool
"""
return apihelper.delete_webhook(self.token)
def get_webhook_info(self):
result = apihelper.get_webhook_info(self.token)
return types.WebhookInfo.de_json(result)
def remove_webhook(self):
return self.set_webhook() # No params resets webhook
def get_updates(self, offset=None, limit=None, timeout=20):
def get_updates(self, offset=None, limit=None, timeout=20, allowed_updates=None):
"""
Use this method to receive incoming updates using long polling (wiki). An Array of Update objects is returned.
:param allowed_updates: Array of string. List the types of updates you want your bot to receive.
:param offset: Integer. Identifier of the first update to be returned.
:param limit: Integer. Limits the number of updates to be retrieved.
:param timeout: Integer. Timeout in seconds for long polling.
:return: array of Updates
"""
json_updates = apihelper.get_updates(self.token, offset, limit, timeout)
json_updates = apihelper.get_updates(self.token, offset, limit, timeout, allowed_updates)
ret = []
for ju in json_updates:
ret.append(types.Update.de_json(ju))
@ -125,9 +162,14 @@ class TeleBot:
def process_new_updates(self, updates):
new_messages = []
edited_new_messages = []
new_channel_posts = []
new_edited_channel_posts = []
new_inline_querys = []
new_chosen_inline_results = []
new_callback_querys = []
new_shipping_querys = []
new_pre_checkout_querys = []
for update in updates:
if update.update_id > self.last_update_id:
self.last_update_id = update.update_id
@ -135,23 +177,40 @@ class TeleBot:
new_messages.append(update.message)
if update.edited_message:
edited_new_messages.append(update.edited_message)
if update.channel_post:
new_channel_posts.append(update.channel_post)
if update.edited_channel_post:
new_edited_channel_posts.append(update.edited_channel_post)
if update.inline_query:
new_inline_querys.append(update.inline_query)
if update.chosen_inline_result:
new_chosen_inline_results.append(update.chosen_inline_result)
if update.callback_query:
new_callback_querys.append(update.callback_query)
if update.shipping_query:
new_shipping_querys.append(update.shipping_query)
if update.pre_checkout_query:
new_pre_checkout_querys.append(update.pre_checkout_query)
logger.debug('Received {0} new updates'.format(len(updates)))
if len(new_messages) > 0:
self.process_new_messages(new_messages)
if len(edited_new_messages) > 0:
self.process_new_edited_messages(edited_new_messages)
if len(new_channel_posts) > 0:
self.process_new_channel_posts(new_channel_posts)
if len(new_edited_channel_posts) > 0:
self.process_new_edited_channel_posts(new_edited_channel_posts)
if len(new_inline_querys) > 0:
self.process_new_inline_query(new_inline_querys)
if len(new_chosen_inline_results) > 0:
self.process_new_chosen_inline_query(new_chosen_inline_results)
if len(new_callback_querys) > 0:
self.process_new_callback_query(new_callback_querys)
if len(new_pre_checkout_querys) > 0:
self.process_new_pre_checkout_query(new_pre_checkout_querys)
if len(new_shipping_querys) > 0:
self.process_new_shipping_query(new_shipping_querys)
def process_new_messages(self, new_messages):
self._append_pre_next_step_handler()
@ -163,6 +222,12 @@ class TeleBot:
def process_new_edited_messages(self, edited_message):
self._notify_command_handlers(self.edited_message_handlers, edited_message)
def process_new_channel_posts(self, channel_post):
self._notify_command_handlers(self.channel_post_handlers, channel_post)
def process_new_edited_channel_posts(self, edited_channel_post):
self._notify_command_handlers(self.edited_channel_post_handlers, edited_channel_post)
def process_new_inline_query(self, new_inline_querys):
self._notify_command_handlers(self.inline_handlers, new_inline_querys)
@ -172,6 +237,12 @@ class TeleBot:
def process_new_callback_query(self, new_callback_querys):
self._notify_command_handlers(self.callback_query_handlers, new_callback_querys)
def process_new_shipping_query(self, new_shipping_querys):
self._notify_command_handlers(self.shipping_query_handlers, new_shipping_querys)
def process_new_pre_checkout_query(self, pre_checkout_querys):
self._notify_command_handlers(self.pre_checkout_query_handlers, pre_checkout_querys)
def __notify_update(self, new_messages):
for listener in self.update_listener:
self._exec_task(listener, new_messages)
@ -184,6 +255,7 @@ class TeleBot:
Warning: Do not call this function more than once!
Always get updates.
:param interval:
:param none_stop: Do not stop polling when an ApiException occurs.
:param timeout: Timeout in seconds for long polling.
:return:
@ -378,10 +450,20 @@ class TeleBot:
return types.Message.de_json(
apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification))
def delete_message(self, chat_id, message_id):
"""
Use this method to delete message. Returns True on success.
:param chat_id: in which chat to delete
:param message_id: which message to delete
:return: API reply.
"""
return apihelper.delete_message(self.token, chat_id, message_id)
def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
"""
Use this method to send photos.
:param disable_notification:
:param chat_id:
:param photo:
:param caption:
@ -393,7 +475,8 @@ class TeleBot:
apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup,
disable_notification))
def send_audio(self, chat_id, audio, duration=None, performer=None, title=None, reply_to_message_id=None,
def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None, title=None,
reply_to_message_id=None,
reply_markup=None, disable_notification=None, timeout=None):
"""
Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format.
@ -407,10 +490,10 @@ class TeleBot:
:return: Message
"""
return types.Message.de_json(
apihelper.send_audio(self.token, chat_id, audio, duration, performer, title, reply_to_message_id,
apihelper.send_audio(self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id,
reply_markup, disable_notification, timeout))
def send_voice(self, chat_id, voice, duration=None, reply_to_message_id=None, reply_markup=None,
def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
"""
Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message.
@ -422,7 +505,7 @@ class TeleBot:
:return: Message
"""
return types.Message.de_json(
apihelper.send_voice(self.token, chat_id, voice, duration, reply_to_message_id, reply_markup,
apihelper.send_voice(self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup,
disable_notification, timeout))
def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, reply_markup=None,
@ -469,6 +552,22 @@ class TeleBot:
apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup,
disable_notification, timeout))
def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
"""
Use this method to send video files, Telegram clients support mp4 videos.
:param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id
:param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend a video that is already on the Telegram server
:param duration: Integer : Duration of sent video in seconds
:param length: Integer : Video width and height, Can't be None and should be in range of (0, 640)
:param reply_to_message_id:
:param reply_markup:
:return:
"""
return types.Message.de_json(
apihelper.send_video_note(self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup,
disable_notification, timeout))
def send_location(self, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
"""
@ -518,23 +617,167 @@ class TeleBot:
its typing status).
:param chat_id:
:param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video',
'record_audio', 'upload_audio', 'upload_document', 'find_location'.
'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', 'upload_video_note'.
:return: API reply. :type: boolean
"""
return apihelper.send_chat_action(self.token, chat_id, action)
def kick_chat_member(self, chat_id, user_id):
def kick_chat_member(self, chat_id, user_id, until_date=None):
"""
Use this method to kick a user from a group or a supergroup.
:param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup
:param user_id: Int : Unique identifier of the target user
:param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or
less than 30 seconds from the current time they are considered to be banned forever
:return: types.Message
"""
return apihelper.kick_chat_member(self.token, chat_id, user_id)
return apihelper.kick_chat_member(self.token, chat_id, user_id, until_date)
def unban_chat_member(self, chat_id, user_id):
return apihelper.unban_chat_member(self.token, chat_id, user_id)
def restrict_chat_member(self, 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):
"""
Use this method to restrict a user in a supergroup.
The bot must be an administrator in the supergroup for this to work and must have
the appropriate admin rights. Pass True for all boolean parameters to lift restrictions from a user.
Returns True on success.
:param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup
or channel (in the format @channelusername)
:param user_id: Int : Unique identifier of the target user
:param until_date: Date when restrictions will be lifted for the user, unix time.
If user is restricted for more than 366 days or less than 30 seconds from the current time,
they are considered to be restricted forever
:param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues
:param can_send_media_messages Pass True, if the user can send audios, documents, photos, videos, video notes
and voice notes, implies can_send_messages
:param can_send_other_messages: Pass True, if the user can send animations, games, stickers and
use inline bots, implies can_send_media_messages
:param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages,
implies can_send_media_messages
:return: types.Message
"""
return apihelper.restrict_chat_member(self.token, chat_id, user_id, until_date, can_send_messages,
can_send_media_messages, can_send_other_messages,
can_add_web_page_previews)
def promote_chat_member(self, 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):
"""
Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator
in the chat for this to work and must have the appropriate admin rights.
Pass False for all boolean parameters to demote a user. Returns True on success.
:param chat_id: Unique identifier for the target chat or username of the target channel (
in the format @channelusername)
:param user_id: Int : Unique identifier of the target user
:param can_change_info: Bool: Pass True, if the administrator can change chat title, photo and other settings
:param can_post_messages: Bool : Pass True, if the administrator can create channel posts, channels only
:param can_edit_messages: Bool : Pass True, if the administrator can edit messages of other users, channels only
:param can_delete_messages: Bool : Pass True, if the administrator can delete messages of other users
:param can_invite_users: Bool : Pass True, if the administrator can invite new users to the chat
:param can_restrict_members: Bool: Pass True, if the administrator can restrict, ban or unban chat members
:param can_pin_messages: Bool: Pass True, if the administrator can pin messages, supergroups only
:param can_promote_members: Bool: Pass True, if the administrator can add new administrators with a subset
of his own privileges or demote administrators that he has promoted, directly or indirectly
(promoted by administrators that were appointed by him)
:return:
"""
return apihelper.promote_chat_member(self.token, chat_id, user_id, can_change_info, can_post_messages,
can_edit_messages, can_delete_messages, can_invite_users,
can_restrict_members, can_pin_messages, can_promote_members)
def export_chat_invite_link(self, chat_id):
"""
Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator
in the chat for this to work and must have the appropriate admin rights.
Returns exported invite link as String on success.
:param chat_id: Id: Unique identifier for the target chat or username of the target channel
(in the format @channelusername)
:return:
"""
return apihelper.export_chat_invite_link(self.token, chat_id)
def set_chat_photo(self, chat_id, photo):
"""
Use this method to set a new profile photo for the chat. Photos can't be changed for private chats.
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
Returns True on success.
Note: In regular groups (non-supergroups), this method will only work if the All Members Are Admins
setting is off in the target group.
:param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel
(in the format @channelusername)
:param photo: InputFile: New chat photo, uploaded using multipart/form-data
:return:
"""
return apihelper.set_chat_photo(self.token, chat_id, photo)
def delete_chat_photo(self, chat_id):
"""
Use this method to delete a chat photo. Photos can't be changed for private chats.
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
Returns True on success.
Note: In regular groups (non-supergroups), this method will only work if the All Members Are Admins
setting is off in the target group.
:param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel
(in the format @channelusername)
:return:
"""
return apihelper.delete_chat_photo(self.token, chat_id)
def set_chat_title(self, chat_id, title):
"""
Use this method to change the title of a chat. Titles can't be changed for private chats.
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
Returns True on success.
Note: In regular groups (non-supergroups), this method will only work if the All Members Are Admins
setting is off in the target group.
:param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel
(in the format @channelusername)
:param title: New chat title, 1-255 characters
:return:
"""
return apihelper.set_chat_title(self.token, chat_id, title)
def set_chat_description(self, chat_id, description):
"""
Use this method to change the description of a supergroup or a channel.
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
Returns True on success.
:param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel
(in the format @channelusername)
:param description: Str: New chat description, 0-255 characters
:return:
"""
return apihelper.set_chat_description(self.token, chat_id, description)
def pin_chat_message(self, chat_id, message_id, disable_notification=False):
"""
Use this method to pin a message in a supergroup.
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
Returns True on success.
:param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel
(in the format @channelusername)
:param message_id: Int: Identifier of a message to pin
:param disable_notification: Bool: Pass True, if it is not necessary to send a notification
to all group members about the new pinned message
:return:
"""
return apihelper.pin_chat_message(self.token, chat_id, message_id, disable_notification)
def unpin_chat_message(self, chat_id):
"""
Use this method to unpin a message in a supergroup chat.
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
Returns True on success.
:param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel
(in the format @channelusername)
:return:
"""
return apihelper.unpin_chat_message(self.token, chat_id)
def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None,
disable_web_page_preview=None, reply_markup=None):
result = apihelper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode,
@ -544,14 +787,56 @@ class TeleBot:
return types.Message.de_json(result)
def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
return types.Message.de_json(
apihelper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup)
)
result = apihelper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup)
if type(result) == bool:
return result
return types.Message.de_json(result)
def send_game(self, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None,
reply_markup=None):
result = apihelper.send_game(self.token, chat_id, game_short_name, disable_notification, reply_to_message_id,
reply_markup)
return types.Message.de_json(result)
def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=None, inline_message_id=None,
edit_message=None):
result = apihelper.set_game_score(self.token, user_id, score, force, chat_id, message_id, inline_message_id,
edit_message)
if type(result) == bool:
return result
return types.Message.de_json(result)
def get_game_high_scores(self, user_id, chat_id=None, message_id=None, inline_message_id=None):
result = apihelper.get_game_high_scores(self.token, user_id, chat_id, message_id, inline_message_id)
ret = []
for r in result:
ret.append(types.GameHighScore.de_json(r))
return ret
def send_invoice(self, 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):
result = apihelper.send_invoice(self.token, chat_id, title, description, invoice_payload, provider_token,
currency, prices, start_parameter, photo_url, photo_size, photo_width,
photo_height,
need_name, need_phone_number, need_email, need_shipping_address, is_flexible,
disable_notification, reply_to_message_id, reply_markup)
return types.Message.de_json(result)
def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, error_message=None):
return apihelper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message)
def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=None):
return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message)
def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
return types.Message.de_json(
apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, reply_markup)
)
result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id,
reply_markup)
if type(result) == bool:
return result
return types.Message.de_json(result)
def reply_to(self, message, text, **kwargs):
"""
@ -577,7 +862,7 @@ class TeleBot:
return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset,
switch_pm_text, switch_pm_parameter)
def answer_callback_query(self, callback_query_id, text=None, show_alert=None):
def answer_callback_query(self, 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.
@ -586,7 +871,87 @@ class TeleBot:
:param show_alert:
:return:
"""
return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert)
return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time)
# def send_sticker(self, chat_id, sticker, disable_notification=None, reply_to_message_id=None, reply_markup=None):
# """
# Use this method to send .webp stickers. On success, the sent Message is returned.
# :param chat_id:
# :param sticker:
# :param disable_notification:
# :param reply_to_message_id:
# :param reply_markup:
# :return:
# """
# result = apihelper.send_sticker(self.token, chat_id, sticker, disable_notification, reply_markup, reply_markup)
# return types.Message.de_json(result)
def get_sticker_set(self, name):
"""
Use this method to get a sticker set. On success, a StickerSet object is returned.
:param token:
:param name:
:return:
"""
result = apihelper.get_sticker_set(self.token, name)
return types.StickerSet.de_json(result)
def upload_sticker_file(self, user_id, png_sticker):
"""
Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet
methods (can be used multiple times). Returns the uploaded File on success.
:param user_id:
:param png_sticker:
:return:
"""
result = apihelper.upload_sticker_file(self.token, user_id, png_sticker)
return types.File.de_json(result)
def create_new_sticker_set(self, user_id, name, title, png_sticker, emojis, contains_masks=None,
mask_position=None):
"""
Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set.
Returns True on success.
:param user_id:
:param name:
:param title:
:param png_sticker:
:param emojis:
:param contains_masks:
:param mask_position:
:return:
"""
return apihelper.create_new_sticker_set(self.token, user_id, name, title, png_sticker, emojis, contains_masks,
mask_position)
def add_sticker_to_set(self, user_id, name, png_sticker, emojis, mask_position):
"""
Use this method to add a new sticker to a set created by the bot. Returns True on success.
:param user_id:
:param name:
:param png_sticker:
:param emojis:
:param mask_position:
:return:
"""
return apihelper.add_sticker_to_set(self.token, user_id, name, png_sticker, emojis, mask_position)
def set_sticker_position_in_set(self, sticker, position):
"""
Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success.
:param sticker:
:param position:
:return:
"""
return apihelper.set_sticker_position_in_set(self.token, sticker, position)
def delete_sticker_from_set(self, sticker):
"""
Use this method to delete a sticker from a set created by the bot. Returns True on success.
:param sticker:
:return:
"""
return apihelper.delete_sticker_from_set(self.token, sticker)
def register_for_reply(self, message, callback):
"""
@ -720,6 +1085,38 @@ class TeleBot:
def add_edited_message_handler(self, handler_dict):
self.edited_message_handlers.append(handler_dict)
def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=['text'], **kwargs):
def decorator(handler):
handler_dict = self._build_handler_dict(handler,
commands=commands,
regexp=regexp,
func=func,
content_types=content_types,
**kwargs)
self.add_channel_post_handler(handler_dict)
return handler
return decorator
def add_channel_post_handler(self, handler_dict):
self.channel_post_handlers.append(handler_dict)
def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=['text'], **kwargs):
def decorator(handler):
handler_dict = self._build_handler_dict(handler,
commands=commands,
regexp=regexp,
func=func,
content_types=content_types,
**kwargs)
self.add_edited_channel_post_handler(handler_dict)
return handler
return decorator
def add_edited_channel_post_handler(self, handler_dict):
self.edited_channel_post_handlers.append(handler_dict)
def inline_handler(self, func, **kwargs):
def decorator(handler):
handler_dict = self._build_handler_dict(handler, func=func, **kwargs)
@ -746,12 +1143,35 @@ class TeleBot:
def decorator(handler):
handler_dict = self._build_handler_dict(handler, func=func, **kwargs)
self.add_callback_query_handler(handler_dict)
return handler
return decorator
def add_callback_query_handler(self, handler_dict):
self.callback_query_handlers.append(handler_dict)
def shipping_query_handler(self, func, **kwargs):
def decorator(handler):
handler_dict = self._build_handler_dict(handler, func=func, **kwargs)
self.add_shipping_query_handler(handler_dict)
return handler
return decorator
def add_shipping_query_handler(self, handler_dict):
self.shipping_query_handlers.append(handler_dict)
def pre_checkout_query_handler(self, func, **kwargs):
def decorator(handler):
handler_dict = self._build_handler_dict(handler, func=func, **kwargs)
self.add_pre_checkout_query_handler(handler_dict)
return handler
return decorator
def add_pre_checkout_query_handler(self, handler_dict):
self.pre_checkout_query_handlers.append(handler_dict)
def _test_message_handler(self, message_handler, message):
for filter, filter_value in six.iteritems(message_handler['filters']):
if filter_value is None:
@ -765,7 +1185,7 @@ class TeleBot:
def _test_filter(self, filter, filter_value, message):
test_cases = {
'content_types': lambda msg: msg.content_type in filter_value,
'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text),
'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE),
'commands': lambda msg: msg.content_type == 'text' and util.extract_command(msg.text) in filter_value,
'func': lambda msg: filter_value(msg)
}
@ -788,10 +1208,38 @@ class AsyncTeleBot(TeleBot):
def get_me(self):
return TeleBot.get_me(self)
@util.async()
def get_file(self, *args):
return TeleBot.get_file(self, *args)
@util.async()
def download_file(self, *args):
return TeleBot.download_file(self, *args)
@util.async()
def get_user_profile_photos(self, *args, **kwargs):
return TeleBot.get_user_profile_photos(self, *args, **kwargs)
@util.async()
def get_chat(self, *args):
return TeleBot.get_chat(self, *args)
@util.async()
def leave_chat(self, *args):
return TeleBot.leave_chat(self, *args)
@util.async()
def get_chat_administrators(self, *args):
return TeleBot.get_chat_administrators(self, *args)
@util.async()
def get_chat_members_count(self, *args):
return TeleBot.get_chat_members_count(self, *args)
@util.async()
def get_chat_member(self, *args):
return TeleBot.get_chat_member(self, *args)
@util.async()
def send_message(self, *args, **kwargs):
return TeleBot.send_message(self, *args, **kwargs)
@ -800,6 +1248,10 @@ class AsyncTeleBot(TeleBot):
def forward_message(self, *args, **kwargs):
return TeleBot.forward_message(self, *args, **kwargs)
@util.async()
def delete_message(self, *args):
return TeleBot.delete_message(self, *args)
@util.async()
def send_photo(self, *args, **kwargs):
return TeleBot.send_photo(self, *args, **kwargs)
@ -808,6 +1260,10 @@ class AsyncTeleBot(TeleBot):
def send_audio(self, *args, **kwargs):
return TeleBot.send_audio(self, *args, **kwargs)
@util.async()
def send_voice(self, *args, **kwargs):
return TeleBot.send_voice(self, *args, **kwargs)
@util.async()
def send_document(self, *args, **kwargs):
return TeleBot.send_document(self, *args, **kwargs)
@ -820,10 +1276,138 @@ class AsyncTeleBot(TeleBot):
def send_video(self, *args, **kwargs):
return TeleBot.send_video(self, *args, **kwargs)
@util.async()
def send_video_note(self, *args, **kwargs):
return TeleBot.send_video_note(self, *args, **kwargs)
@util.async()
def send_location(self, *args, **kwargs):
return TeleBot.send_location(self, *args, **kwargs)
@util.async()
def send_venue(self, *args, **kwargs):
return TeleBot.send_venue(self, *args, **kwargs)
@util.async()
def send_contact(self, *args, **kwargs):
return TeleBot.send_contact(self, *args, **kwargs)
@util.async()
def send_chat_action(self, *args, **kwargs):
return TeleBot.send_chat_action(self, *args, **kwargs)
@util.async()
def kick_chat_member(self, *args, **kwargs):
return TeleBot.kick_chat_member(self, *args, **kwargs)
@util.async()
def unban_chat_member(self, *args):
return TeleBot.unban_chat_member(self, *args)
@util.async()
def restrict_chat_member(self, *args, **kwargs):
return TeleBot.restrict_chat_member(self, *args, **kwargs)
@util.async()
def promote_chat_member(self, *args, **kwargs):
return TeleBot.promote_chat_member(self, *args, **kwargs)
@util.async()
def export_chat_invite_link(self, *args):
return TeleBot.export_chat_invite_link(self, *args)
@util.async()
def set_chat_photo(self, *args):
return TeleBot.set_chat_photo(self, *args)
@util.async()
def delete_chat_photo(self, *args):
return TeleBot.delete_chat_photo(self, *args)
@util.async()
def set_chat_title(self, *args):
return TeleBot.set_chat_title(self, *args)
@util.async()
def set_chat_description(self, *args):
return TeleBot.set_chat_description(self, *args)
@util.async()
def pin_chat_message(self, *args, **kwargs):
return TeleBot.pin_chat_message(self, *args, **kwargs)
@util.async()
def unpin_chat_message(self, *args):
return TeleBot.unpin_chat_message(self, *args)
@util.async()
def edit_message_text(self, *args, **kwargs):
return TeleBot.edit_message_text(self, *args, **kwargs)
@util.async()
def edit_message_reply_markup(self, *args, **kwargs):
return TeleBot.edit_message_reply_markup(self, *args, **kwargs)
@util.async()
def send_game(self, *args, **kwargs):
return TeleBot.send_game(self, *args, **kwargs)
@util.async()
def set_game_score(self, *args, **kwargs):
return TeleBot.set_game_score(self, *args, **kwargs)
@util.async()
def get_game_high_scores(self, *args, **kwargs):
return TeleBot.get_game_high_scores(self, *args, **kwargs)
@util.async()
def send_invoice(self, *args, **kwargs):
return TeleBot.send_invoice(self, *args, **kwargs)
@util.async()
def answer_shipping_query(self, *args, **kwargs):
return TeleBot.answer_shipping_query(self, *args, **kwargs)
@util.async()
def answer_pre_checkout_query(self, *args, **kwargs):
return TeleBot.answer_pre_checkout_query(self, *args, **kwargs)
@util.async()
def edit_message_caption(self, *args, **kwargs):
return TeleBot.edit_message_caption(self, *args, **kwargs)
@util.async()
def answer_inline_query(self, *args, **kwargs):
return TeleBot.answer_inline_query(self, *args, **kwargs)
@util.async()
def answer_callback_query(self, *args, **kwargs):
return TeleBot.answer_callback_query(self, *args, **kwargs)
@util.async()
def send_sticker(self, *args, **kwargs):
return TeleBot.send_sticker(self, *args, **kwargs)
@util.async()
def get_sticker_set(self, *args, **kwargs):
return TeleBot.get_sticker_set(self, *args, **kwargs)
@util.async()
def upload_sticker_file(self, *args, **kwargs):
return TeleBot.upload_sticker_file(self, *args, **kwargs)
@util.async()
def create_new_sticker_set(self, *args, **kwargs):
return TeleBot.create_new_sticker_set(self, *args, **kwargs)
@util.async()
def add_sticker_to_set(self, *args, **kwargs):
return TeleBot.add_sticker_to_set(self, *args, **kwargs)
@util.async()
def set_sticker_position_in_set(self, *args, **kwargs):
return TeleBot.set_sticker_position_in_set(self, *args, **kwargs)
@util.async()
def delete_sticker_from_set(self, *args, **kwargs):
return TeleBot.delete_sticker_from_set(self, *args, **kwargs)

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.
@ -28,10 +45,13 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas
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
if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10
result = requests.request(method, request_url, params=params, files=files, timeout=(connect_timeout, read_timeout))
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']
@ -80,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)
@ -115,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 "",
@ -123,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:
@ -136,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)
@ -280,7 +316,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
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,
def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
method_url = r'sendVoice'
payload = {'chat_id': chat_id}
@ -289,6 +325,8 @@ 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:
@ -302,7 +340,33 @@ def send_voice(token, chat_id, voice, duration=None, reply_to_message_id=None, r
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,
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, disable_notification=None, timeout=None):
method_url = r'sendAudio'
payload = {'chat_id': chat_id}
@ -311,6 +375,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:
@ -357,9 +423,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')
@ -369,6 +437,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,
@ -418,22 +576,208 @@ 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):
"""
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)
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['reply_markup'] = _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:
@ -447,7 +791,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):
@ -463,6 +859,16 @@ def _convert_markup(markup):
return markup
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.

View File

@ -1,6 +1,10 @@
# -*- coding: utf-8 -*-
import json
try:
import ujson as json
except ImportError:
import json
import six
from telebot import util
@ -92,29 +96,80 @@ class Update(JsonDeserializable):
update_id = obj['update_id']
message = None
edited_message = None
channel_post = None
edited_channel_post = None
inline_query = None
chosen_inline_result = None
callback_query = None
shipping_query = None
pre_checkout_query = None
if 'message' in obj:
message = Message.de_json(obj['message'])
if 'edited_message' in obj:
edited_message = Message.de_json(obj['edited_message'])
if 'channel_post' in obj:
channel_post = Message.de_json(obj['channel_post'])
if 'edited_channel_post' in obj:
edited_channel_post = Message.de_json(obj['edited_channel_post'])
if 'inline_query' in obj:
inline_query = InlineQuery.de_json(obj['inline_query'])
if 'chosen_inline_result' in obj:
chosen_inline_result = ChosenInlineResult.de_json(obj['chosen_inline_result'])
if 'callback_query' in obj:
callback_query = CallbackQuery.de_json(obj['callback_query'])
return cls(update_id, message, edited_message, inline_query, chosen_inline_result, callback_query)
if 'shipping_query' in obj:
shipping_query = ShippingQuery.de_json(obj['shipping_query'])
if 'pre_checkout_query' in obj:
pre_checkout_query = PreCheckoutQuery.de_json(obj['pre_checkout_query'])
return cls(update_id, message, edited_message, channel_post, edited_channel_post, inline_query,
chosen_inline_result, callback_query, shipping_query, pre_checkout_query)
def __init__(self, update_id, message, edited_message, inline_query, chosen_inline_result, callback_query):
def __init__(self, update_id, message, edited_message, channel_post, edited_channel_post, inline_query,
chosen_inline_result, callback_query, shipping_query, pre_checkout_query):
self.update_id = update_id
self.edited_message = edited_message
self.message = message
self.edited_message = edited_message
self.channel_post = channel_post
self.edited_channel_post = edited_channel_post
self.inline_query = inline_query
self.chosen_inline_result = chosen_inline_result
self.callback_query = callback_query
self.shipping_query = shipping_query
self.pre_checkout_query = pre_checkout_query
class WebhookInfo(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
url = obj['url']
has_custom_certificate = obj['has_custom_certificate']
pending_update_count = obj['pending_update_count']
last_error_date = None
last_error_message = None
max_connections = None
allowed_updates = None
if 'last_error_message' in obj:
last_error_date = obj['last_error_date']
if 'last_error_message' in obj:
last_error_message = obj['last_error_message']
if 'max_connections' in obj:
max_connections = obj['max_connections']
if 'allowed_updates' in obj:
allowed_updates = obj['allowed_updates']
return cls(url, has_custom_certificate, pending_update_count, last_error_date, last_error_message,
max_connections, allowed_updates)
def __init__(self, url, has_custom_certificate, pending_update_count, last_error_date, last_error_message,
max_connections, allowed_updates):
self.url = url
self.has_custom_certificate = has_custom_certificate
self.pending_update_count = pending_update_count
self.last_error_date = last_error_date
self.last_error_message = last_error_message
self.max_connections = max_connections
self.allowed_updates = allowed_updates
class User(JsonDeserializable):
@ -125,13 +180,15 @@ class User(JsonDeserializable):
first_name = obj['first_name']
last_name = obj.get('last_name')
username = obj.get('username')
return cls(id, first_name, last_name, username)
language_code = obj.get('language_code')
return cls(id, first_name, last_name, username, language_code)
def __init__(self, id, first_name, last_name=None, username=None):
def __init__(self, id, first_name, last_name=None, username=None, language_code=None):
self.id = id
self.first_name = first_name
self.username = username
self.last_name = last_name
self.language_code = language_code
class GroupChat(JsonDeserializable):
@ -157,15 +214,27 @@ class Chat(JsonDeserializable):
username = obj.get('username')
first_name = obj.get('first_name')
last_name = obj.get('last_name')
return cls(id, type, title, username, first_name, last_name)
all_members_are_administrators = obj.get('all_members_are_administrators')
photo = None
if 'photo' in obj:
photo = ChatPhoto.de_json(obj['photo'])
description = obj.get('description')
invite_link = obj.get('invite_link')
return cls(id, type, title, username, first_name, last_name, all_members_are_administrators,
photo, description, invite_link)
def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None):
def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None,
all_members_are_administrators=None, photo=None, description=None, invite_link=None):
self.type = type
self.last_name = last_name
self.first_name = first_name
self.username = username
self.id = id
self.title = title
self.all_members_are_administrators = all_members_are_administrators
self.photo = photo
self.description = description
self.invite_link = invite_link
class Message(JsonDeserializable):
@ -184,8 +253,10 @@ class Message(JsonDeserializable):
opts['forward_from'] = User.de_json(obj['forward_from'])
if 'forward_from_chat' in obj:
opts['forward_from_chat'] = Chat.de_json(obj['forward_from_chat'])
if 'forward_from_message_id' in obj:
opts['forward_from_message_id'] = obj.get('forward_from_message_id')
if 'forward_date' in obj:
opts['forward_date'] = obj['forward_date']
opts['forward_date'] = obj.get('forward_date')
if 'reply_to_message' in obj:
opts['reply_to_message'] = Message.de_json(obj['reply_to_message'])
if 'edit_date' in obj:
@ -201,6 +272,9 @@ class Message(JsonDeserializable):
if 'document' in obj:
opts['document'] = Document.de_json(obj['document'])
content_type = 'document'
if 'game' in obj:
opts['game'] = Game.de_json(obj['game'])
content_type = 'game'
if 'photo' in obj:
opts['photo'] = Message.parse_photo(obj['photo'])
content_type = 'photo'
@ -210,6 +284,9 @@ class Message(JsonDeserializable):
if 'video' in obj:
opts['video'] = Video.de_json(obj['video'])
content_type = 'video'
if 'video_note' in obj:
opts['video_note'] = VideoNote.de_json(obj['video_note'])
content_type = 'video_note'
if 'voice' in obj:
opts['voice'] = Audio.de_json(obj['voice'])
content_type = 'voice'
@ -227,6 +304,13 @@ class Message(JsonDeserializable):
if 'new_chat_member' in obj:
opts['new_chat_member'] = User.de_json(obj['new_chat_member'])
content_type = 'new_chat_member'
if 'new_chat_members' in obj:
chat_members = obj['new_chat_members']
nms = []
for m in chat_members:
nms.append(User.de_json(m))
opts['new_chat_members'] = nms
content_type = 'new_chat_members'
if 'left_chat_member' in obj:
opts['left_chat_member'] = User.de_json(obj['left_chat_member'])
content_type = 'left_chat_member'
@ -248,6 +332,12 @@ class Message(JsonDeserializable):
opts['migrate_from_chat_id'] = obj['migrate_from_chat_id']
if 'pinned_message' in obj:
opts['pinned_message'] = Message.de_json(obj['pinned_message'])
if 'invoice' in obj:
opts['invoice'] = Invoice.de_json(obj['invoice'])
content_type = 'invoice'
if 'successful_payment' in obj:
opts['successful_payment'] = SuccessfulPayment.de_json(obj['successful_payment'])
content_type = 'successful_payment'
return cls(message_id, from_user, date, chat, content_type, opts)
@classmethod
@ -289,12 +379,14 @@ class Message(JsonDeserializable):
self.photo = None
self.sticker = None
self.video = None
self.video_note = None
self.voice = None
self.caption = None
self.contact = None
self.location = None
self.venue = None
self.new_chat_member = None
self.new_chat_members = None
self.left_chat_member = None
self.new_chat_title = None
self.new_chat_photo = None
@ -305,6 +397,8 @@ class Message(JsonDeserializable):
self.migrate_to_chat_id = None
self.migrate_from_chat_id = None
self.pinned_message = None
self.invoice = None
self.successful_payment = None
for key in options:
setattr(self, key, options[key])
@ -454,6 +548,27 @@ class Video(JsonDeserializable):
self.file_size = file_size
class VideoNote(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
file_id = obj['file_id']
length = obj['length']
duration = obj['duration']
thumb = None
if 'thumb' in obj:
thumb = PhotoSize.de_json(obj['thumb'])
file_size = obj.get('file_size')
return cls(file_id, length, duration, thumb, file_size)
def __init__(self, file_id, length, duration, thumb=None, file_size=None):
self.file_id = file_id
self.length = length
self.duration = duration
self.thumb = thumb
self.file_size = file_size
class Contact(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
@ -488,7 +603,7 @@ class Venue(JsonDeserializable):
@classmethod
def de_json(cls, json_type):
obj = cls.check_json(json_type)
location = obj['location']
location = Location.de_json(obj['location'])
title = obj['title']
address = obj['address']
foursquare_id = obj.get('foursquare_id')
@ -540,12 +655,12 @@ class ForceReply(JsonSerializable):
return json.dumps(json_dict)
class ReplyKeyboardHide(JsonSerializable):
class ReplyKeyboardRemove(JsonSerializable):
def __init__(self, selective=None):
self.selective = selective
def to_json(self):
json_dict = {'hide_keyboard': True}
json_dict = {'remove_keyboard': True}
if self.selective:
json_dict['selective'] = True
return json.dumps(json_dict)
@ -574,6 +689,8 @@ class ReplyKeyboardMarkup(JsonSerializable):
for button in args:
if util.is_string(button):
row.append({'text': button})
elif isinstance(button, bytes):
row.append({'text': button.decode('utf-8')})
else:
row.append(button.to_dic())
if i % self.row_width == 0:
@ -692,11 +809,15 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable):
class InlineKeyboardButton(JsonSerializable):
def __init__(self, text, url=None, callback_data=None, switch_inline_query=None):
def __init__(self, text, url=None, callback_data=None, switch_inline_query=None,
switch_inline_query_current_chat=None, callback_game=None, pay=None):
self.text = text
self.url = url
self.callback_data = callback_data
self.switch_inline_query = switch_inline_query
self.switch_inline_query_current_chat = switch_inline_query_current_chat
self.callback_game = callback_game
self.pay = pay
def to_json(self):
return json.dumps(self.to_dic())
@ -709,6 +830,12 @@ class InlineKeyboardButton(JsonSerializable):
json_dic['callback_data'] = self.callback_data
if self.switch_inline_query is not None:
json_dic['switch_inline_query'] = self.switch_inline_query
if self.switch_inline_query_current_chat is not None:
json_dic['switch_inline_query_current_chat'] = self.switch_inline_query_current_chat
if self.callback_game is not None:
json_dic['callback_game'] = self.callback_game
if self.pay is not None:
json_dic['pay'] = self.pay
return json_dic
@ -722,10 +849,14 @@ class CallbackQuery(JsonDeserializable):
if 'message' in obj:
message = Message.de_json(obj['message'])
inline_message_id = obj.get('inline_message_id')
data = obj['data']
return cls(id, from_user, data, message, inline_message_id)
chat_instance = obj['chat_instance']
data = obj.get('data')
game_short_name = obj.get('game_short_name')
return cls(id, from_user, data, chat_instance, message, inline_message_id, game_short_name)
def __init__(self, id, from_user, data, message=None, inline_message_id=None):
def __init__(self, id, from_user, data, chat_instance, message=None, inline_message_id=None, game_short_name=None):
self.game_short_name = game_short_name
self.chat_instance = chat_instance
self.id = id
self.from_user = from_user
self.message = message
@ -733,17 +864,62 @@ class CallbackQuery(JsonDeserializable):
self.inline_message_id = inline_message_id
class ChatPhoto(JsonDeserializable):
@classmethod
def de_json(cls, json_type):
obj = cls.check_json(json_type)
small_file_id = obj['small_file_id']
big_file_id = obj['big_file_id']
return cls(small_file_id, big_file_id)
def __init__(self, small_file_id, big_file_id):
self.small_file_id = small_file_id
self.big_file_id = big_file_id
class ChatMember(JsonDeserializable):
@classmethod
def de_json(cls, json_type):
obj = cls.check_json(json_type)
user = User.de_json(obj['user'])
status = obj['status']
return cls(user, status)
until_date = obj.get('until_date')
can_be_edited = obj.get('can_be_edited')
can_change_info = obj.get('can_change_info')
can_post_messages = obj.get('can_post_messages')
can_edit_messages = obj.get('can_edit_messages')
can_delete_messages = obj.get('can_delete_messages')
can_invite_users = obj.get('can_invite_users')
can_restrict_members = obj.get('can_restrict_members')
can_pin_messages = obj.get('can_pin_messages')
can_promote_members = obj.get('can_promote_members')
can_send_messages = obj.get('can_send_messages')
can_send_media_messages = obj.get('can_send_media_messages')
can_send_other_messages = obj.get('can_send_other_messages')
can_add_web_page_previews = obj.get('can_add_web_page_previews')
return cls(user, status, until_date, can_be_edited, can_change_info, can_post_messages, can_edit_messages,
can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members,
can_send_messages, can_send_media_messages, can_send_other_messages, can_add_web_page_previews)
def __init__(self, user, status):
def __init__(self, user, status, until_date, can_be_edited, can_change_info, can_post_messages, can_edit_messages,
can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members,
can_send_messages, can_send_media_messages, can_send_other_messages, can_add_web_page_previews):
self.user = user
self.status = status
self.until_date = until_date
self.can_be_edited = can_be_edited
self.can_change_info = can_change_info
self.can_post_messages = can_post_messages
self.can_edit_messages = can_edit_messages
self.can_delete_messages = can_delete_messages
self.can_invite_users = can_invite_users
self.can_restrict_members = can_restrict_members
self.can_pin_messages = can_pin_messages
self.can_promote_members = can_promote_members
self.can_send_messages = can_send_messages
self.can_send_media_messages = can_send_media_messages
self.can_send_other_messages = can_send_other_messages
self.can_add_web_page_previews = can_add_web_page_previews
# InlineQuery
@ -801,7 +977,7 @@ class InputLocationMessageContent(Dictionaryable):
self.longitude = longitude
def to_dic(self):
json_dic = {'latitude': self.latitudet, 'longitude': self.longitude}
json_dic = {'latitude': self.latitude, 'longitude': self.longitude}
return json_dic
@ -962,7 +1138,7 @@ class InlineQueryResultPhoto(JsonSerializable):
class InlineQueryResultGif(JsonSerializable):
def __init__(self, id, gif_url, thumb_url, gif_width=None, gif_height=None, title=None, caption=None,
reply_markup=None, input_message_content=None):
reply_markup=None, input_message_content=None, gif_duration=None):
"""
Represents a link to an animated GIF file.
:param id: Unique identifier for this result, 1-64 bytes.
@ -986,6 +1162,7 @@ class InlineQueryResultGif(JsonSerializable):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.gif_duration = gif_duration
def to_json(self):
json_dict = {'type': self.type, 'id': self.id, 'gif_url': self.gif_url, 'thumb_url': self.thumb_url}
@ -1001,12 +1178,14 @@ class InlineQueryResultGif(JsonSerializable):
json_dict['reply_markup'] = self.reply_markup.to_dic()
if self.input_message_content:
json_dict['input_message_content'] = self.input_message_content.to_dic()
if self.gif_duration:
json_dict['gif_duration'] = self.gif_duration
return json.dumps(json_dict)
class InlineQueryResultMpeg4Gif(JsonSerializable):
def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None, title=None, caption=None,
reply_markup=None, input_message_content=None):
reply_markup=None, input_message_content=None, mpeg4_duration=None):
"""
Represents a link to a video animation (H.264/MPEG-4 AVC video without sound).
:param id: Unique identifier for this result, 1-64 bytes
@ -1030,6 +1209,7 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.mpeg4_duration = mpeg4_duration
def to_json(self):
json_dict = {'type': self.type, 'id': self.id, 'mpeg4_url': self.mpeg4_url, 'thumb_url': self.thumb_url}
@ -1045,6 +1225,8 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
json_dict['reply_markup'] = self.reply_markup.to_dic()
if self.input_message_content:
json_dict['input_message_content'] = self.input_message_content.to_dic()
if self.mpeg4_duration:
json_dict['mpeg4_duration '] = self.mpeg4_duration
return json.dumps(json_dict)
@ -1100,12 +1282,13 @@ class InlineQueryResultVideo(JsonSerializable):
class InlineQueryResultAudio(JsonSerializable):
def __init__(self, id, audio_url, title, performer=None, audio_duration=None, reply_markup=None,
def __init__(self, id, audio_url, title, caption=None, performer=None, audio_duration=None, reply_markup=None,
input_message_content=None):
self.type = 'audio'
self.id = id
self.audio_url = audio_url
self.title = title
self.caption = caption
self.performer = performer
self.audio_duration = audio_duration
self.reply_markup = reply_markup
@ -1113,6 +1296,8 @@ class InlineQueryResultAudio(JsonSerializable):
def to_json(self):
json_dict = {'type': self.type, 'id': self.id, 'audio_url': self.audio_url, 'title': self.title}
if self.caption:
json_dict['caption'] = self.caption
if self.performer:
json_dict['performer'] = self.performer
if self.audio_duration:
@ -1125,12 +1310,13 @@ class InlineQueryResultAudio(JsonSerializable):
class InlineQueryResultVoice(JsonSerializable):
def __init__(self, id, voice_url, title, performer=None, voice_duration=None, reply_markup=None,
def __init__(self, id, voice_url, title, caption=None, performer=None, voice_duration=None, reply_markup=None,
input_message_content=None):
self.type = 'voice'
self.id = id
self.voice_url = voice_url
self.title = title
self.caption = caption
self.performer = performer
self.voice_duration = voice_duration
self.reply_markup = reply_markup
@ -1138,6 +1324,8 @@ class InlineQueryResultVoice(JsonSerializable):
def to_json(self):
json_dict = {'type': self.type, 'id': self.id, 'voice_url': self.voice_url, 'title': self.title}
if self.caption:
json_dict['caption'] = self.caption
if self.performer:
json_dict['performer'] = self.performer
if self.voice_duration:
@ -1395,23 +1583,354 @@ class InlineQueryResultCachedVideo(BaseInlineQueryResultCached):
class InlineQueryResultCachedVoice(BaseInlineQueryResultCached):
def __init__(self, id, voice_file_id, title, reply_markup=None, input_message_content=None):
def __init__(self, id, voice_file_id, title, caption=None, reply_markup=None, input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'voice'
self.id = id
self.voice_file_id = voice_file_id
self.title = title
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.payload_dic['voice_file_id'] = voice_file_id
class InlineQueryResultCachedAudio(BaseInlineQueryResultCached):
def __init__(self, id, audio_file_id, reply_markup=None, input_message_content=None):
def __init__(self, id, audio_file_id, caption=None, reply_markup=None, input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'audio'
self.id = id
self.audio_file_id = audio_file_id
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.payload_dic['audio_file_id'] = audio_file_id
# Games
class InlineQueryResultGame(JsonSerializable):
def __init__(self, id, game_short_name, reply_markup=None):
self.type = 'game'
self.id = id
self.game_short_name = game_short_name
self.reply_markup = reply_markup
def to_json(self):
json_dic = {'type': self.type, 'id': self.id, 'game_short_name': self.game_short_name}
if self.reply_markup:
json_dic['reply_markup'] = self.reply_markup.to_dic()
return json.dumps(json_dic)
class Game(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
title = obj['title']
description = obj['description']
photo = Game.parse_photo(obj['photo'])
text = obj.get('text')
text_entities = None
if 'text_entities' in obj:
text_entities = Game.parse_entities(obj['text_entities'])
animation = None
if 'animation' in obj:
animation = Animation.de_json(obj['animation'])
return cls(title, description, photo, text, text_entities, animation)
@classmethod
def parse_photo(cls, photo_size_array):
ret = []
for ps in photo_size_array:
ret.append(PhotoSize.de_json(ps))
return ret
@classmethod
def parse_entities(cls, message_entity_array):
ret = []
for me in message_entity_array:
ret.append(MessageEntity.de_json(me))
return ret
def __init__(self, title, description, photo, text=None, text_entities=None, animation=None):
self.title = title
self.description = description
self.photo = photo
self.text = text
self.text_entities = text_entities
self.animation = animation
class Animation(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
file_id = obj['file_id']
thumb = None
if 'thumb' in obj:
thumb = PhotoSize.de_json(obj['thumb'])
file_name = obj.get('file_name')
mime_type = obj.get('mime_type')
file_size = obj.get('file_size')
return cls(file_id, thumb, file_name, mime_type, file_size)
def __init__(self, file_id, thumb=None, file_name=None, mime_type=None, file_size=None):
self.file_id = file_id
self.thumb = thumb
self.file_name = file_name
self.mime_type = mime_type
self.file_size = file_size
class GameHighScore(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
position = obj['position']
user = User.de_json(obj['user'])
score = obj['score']
return cls(position, user, score)
def __init__(self, position, user, score):
self.position = position
self.user = user
self.score = score
# Payments
class LabeledPrice(JsonSerializable):
def __init__(self, label, amount):
self.label = label
self.amount = amount
def to_json(self):
return json.dumps(self.to_dic())
def to_dic(self):
return {'label': self.label, 'amount': self.amount}
class Invoice(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
title = obj['title']
description = obj['description']
start_parameter = obj['start_parameter']
currency = obj['currency']
total_amount = obj['total_amount']
return cls(title, description, start_parameter, currency, total_amount)
def __init__(self, title, description, start_parameter, currency, total_amount):
self.title = title
self.description = description
self.start_parameter = start_parameter
self.currency = currency
self.total_amount = total_amount
class ShippingAddress(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
country_code = obj['country_code']
state = obj['state']
city = obj['city']
street_line1 = obj['street_line1']
street_line2 = obj['street_line2']
post_code = obj['post_code']
return cls(country_code, state, city, street_line1, street_line2, post_code)
def __init__(self, country_code, state, city, street_line1, street_line2, post_code):
self.country_code = country_code
self.state = state
self.city = city
self.street_line1 = street_line1
self.street_line2 = street_line2
self.post_code = post_code
class OrderInfo(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
name = obj.get('name')
phone_number = obj.get('phone_number')
email = obj.get('email')
shipping_address = None
if 'shipping_address' in obj:
shipping_address = ShippingAddress.de_json(obj['shipping_address'])
return cls(name, phone_number, email, shipping_address)
def __init__(self, name, phone_number, email, shipping_address):
self.name = name
self.phone_number = phone_number
self.email = email
self.shipping_address = shipping_address
class ShippingOption(JsonSerializable):
def __init__(self, id, title):
self.id = id
self.title = title
self.prices = []
def add_price(self, *args):
"""
Add LabeledPrice to ShippingOption
:param args: LabeledPrices
"""
for price in args:
self.prices.append(price)
def to_json(self):
price_list = []
for p in self.prices:
price_list.append(p.to_dic())
json_dict = {'id': self.id, 'title': self.title, 'prices': price_list}
return json_dict
class SuccessfulPayment(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
currency = obj['currency']
total_amount = obj['total_amount']
invoice_payload = obj['invoice_payload']
shipping_option_id = obj.get('shipping_option_id')
order_info = None
if 'order_info' in obj:
order_info = OrderInfo.de_json(obj['order_info'])
telegram_payment_charge_id = obj['telegram_payment_charge_id']
provider_payment_charge_id = obj['provider_payment_charge_id']
return cls(currency, total_amount, invoice_payload, shipping_option_id, order_info,
telegram_payment_charge_id, provider_payment_charge_id)
def __init__(self, currency, total_amount, invoice_payload, shipping_option_id, order_info,
telegram_payment_charge_id, provider_payment_charge_id):
self.currency = currency
self.total_amount = total_amount
self.invoice_payload = invoice_payload
self.shipping_option_id = shipping_option_id
self.order_info = order_info
self.telegram_payment_charge_id = telegram_payment_charge_id
self.provider_payment_charge_id = provider_payment_charge_id
class ShippingQuery(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
id = obj['id']
from_user = User.de_json(obj['from'])
invoice_payload = obj['invoice_payload']
shipping_address = ShippingAddress.de_json(obj['shipping_address'])
return cls(id, from_user, invoice_payload, shipping_address)
def __init__(self, id, from_user, invoice_payload, shipping_address):
self.id = id
self.from_user = from_user
self.invoice_payload = invoice_payload
self.shipping_address = shipping_address
class PreCheckoutQuery(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
id = obj['id']
from_user = User.de_json(obj['from'])
currency = obj['currency']
total_amount = obj['total_amount']
invoice_payload = obj['invoice_payload']
shipping_option_id = obj.get('shipping_option_id')
order_info = None
if 'order_info' in obj:
order_info = OrderInfo.de_json(obj['order_info'])
return cls(id, from_user, currency, total_amount, invoice_payload, shipping_option_id, order_info)
def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipping_option_id, order_info):
self.id = id
self.from_user = from_user
self.currency = currency
self.total_amount = total_amount
self.invoice_payload = invoice_payload
self.shipping_option_id = shipping_option_id
self.order_info = order_info
# Stickers
class StickerSet(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
name = obj['name']
title = obj['title']
contains_masks = obj['contains_masks']
stickers = []
for s in obj['stickers']:
stickers.append(Sticker.de_json(s))
return cls(name, title, contains_masks, stickers)
def __init__(self, name, title, contains_masks, stickers):
self.stickers = stickers
self.contains_masks = contains_masks
self.title = title
self.name = name
class Sticker(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
file_id = obj['file_id']
width = obj['width']
height = obj['height']
thumb = None
if 'thumb' in obj:
thumb = PhotoSize.de_json(obj['thumb'])
emoji = obj.get('emoji')
set_name = obj.get('set_name')
mask_position = None
if 'mask_position' in obj:
mask_position = MaskPosition.de_json(obj['mask_position'])
file_size = obj.get('file_size')
return cls(file_id, width, height, thumb, emoji, set_name, mask_position, file_size)
def __init__(self, file_id, width, height, thumb, emoji, set_name, mask_position, file_size):
self.file_id = file_id
self.width = width
self.height = height
self.thumb = thumb
self.emoji = emoji
self.set_name = set_name
self.mask_position = mask_position
self.file_size = file_size
class MaskPosition(JsonDeserializable, JsonSerializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
point = obj['point']
x_shift = obj['x_shift']
y_shift = obj['y_shift']
scale = obj['scale']
return cls(point, x_shift, y_shift, scale)
def __init__(self, point, x_shift, y_shift, scale):
self.point = point
self.x_shift = x_shift
self.y_shift = y_shift
self.scale = scale
def to_json(self):
return json.dumps(self.to_dic())
def to_dic(self):
return {'point': self.point, 'x_shift': self.x_shift, 'y_shift': self.y_shift, 'scale': self.scale}

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import threading
import traceback
import re
import sys
import six
@ -16,6 +17,9 @@ except ImportError:
from telebot import logger
thread_local = threading.local()
class WorkerThread(threading.Thread):
count = 0
@ -56,8 +60,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 +245,12 @@ 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

View File

@ -193,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'
@ -201,7 +201,7 @@ 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'
@ -331,6 +331,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)
@ -352,6 +353,13 @@ 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)
@ -395,3 +403,9 @@ 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