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

Compare commits

..

261 Commits
3.2.0 ... 3.6.7

Author SHA1 Message Date
b1e5d00821 Merge pull request #728 from Badiboy/master
Design updates from #711
2020-01-08 20:29:12 +03:00
aa02ddb573 TAB fix
TAB fix
2020-01-08 20:17:25 +03:00
760ea5a2f0 Design updates from #711
Significant design updated from
https://github.com/eternnoir/pyTelegramBotAPI/pull/711
2020-01-08 20:06:40 +03:00
9b279dc562 Merge pull request #726 from Badiboy/master
Update test_types.py
2020-01-03 19:31:39 +03:00
5cd97ebc96 Update detailed_example.py
Vulgarity replaced.
2020-01-03 19:24:10 +03:00
b5ba2445d3 Update test_types.py
Updated test vectors for stickers.
2020-01-03 19:18:18 +03:00
7adec8bd90 Merge pull request #673 from TahaPY/master
Added Animation and is_animated for stickers
2020-01-03 17:51:53 +03:00
0603a0df4c Update types.py
Animation is moved before document to save backward compatibility. content_type = 'document' should override content_type = 'animation' to save previous behaviour.
2020-01-03 17:51:05 +03:00
59810b5e2a Merge pull request #620 from nev3rfail/send_animation
Added reduced version of sendAnimation (no width/height/thumb).
2020-01-03 17:12:29 +03:00
b999fea2ac Merge pull request #703 from voskresla/patch-1
Using Azure Functions for serverless bots.
2020-01-03 16:59:09 +03:00
a41dabf73c Merge pull request #725 from cclauss/patch-1
Travis CI: Drop EOL Py34 and add current Py38
2020-01-03 16:53:18 +03:00
5407801f62 Merge pull request #708 from xxsokolov/patch-1
Remove unused base_url from _make_request. Fixed misc error.
2020-01-03 16:52:47 +03:00
2efb33fc29 Merge branch 'master' into patch-1 2020-01-03 16:45:43 +03:00
620ea5dee0 Travis CI: Drop EOL Py34 and add current Py38 2020-01-03 14:36:08 +01:00
eaf44f1a6b Merge pull request #724 from Badiboy/master
Update version list in travis.yml
2020-01-03 02:08:00 +03:00
8c62b99057 Update .travis.yml
Travis: removed 2.6 and 3.3 (not supported). Added 3.7. Also added 3.8 (for experiment)
2020-01-03 02:05:32 +03:00
e3b126807e Merge pull request #713 from dtalkachou/patch-1
Correct work with empty base_url in make_request
2020-01-03 01:33:27 +03:00
769ff8008e Merge pull request #599 from KanerL/KanerL-patch-shipping-option
ShippingOption.add_price returns "self" (for chaining etc.).
2020-01-03 01:28:37 +03:00
0e0e2d97c0 Merge pull request #715 from Pablohn26/patch-1
Change chatid to chat_id to be the same as other examples
2020-01-03 01:19:47 +03:00
bb199024fd Merge pull request #593 from neoranger/patch-2
Update README.md
2020-01-03 01:02:35 +03:00
86644c05f7 Merge pull request #539 from Badiboy/master
"timeout" parameter for send_message
Fix kick_chat_member return type
HTML symbols not replaced
2020-01-03 00:50:24 +03:00
3a3bab5b92 Merge pull request #480 from SkymanOne/patch-1
create field forward_from_message_id in Message
2020-01-03 00:46:27 +03:00
bf844ed202 HTML symbols not replaced
HTML symbols not replaced because return is before replace.
2020-01-01 13:46:18 +03:00
fefb9d4555 Merge pull request #723 from LeoSvalov/master
Update README
2019-12-28 12:34:14 +08:00
a413a51221 Update README
Adding another bot that user pyTelegramBotAPI.
2019-12-24 16:25:58 +05:00
a71030dcdd Change chatid to chat_id to be the same as other examples
Change in README.md chatid to chat_id to be the same as other examples
2019-11-30 05:51:56 +01:00
68db599790 Delete duplicate string 2019-11-23 21:25:29 +03:00
a749acde15 Update apihelper.py #2
Merge Fixes # 684
2019-11-05 17:37:53 +03:00
5935a378ca Merge pull request #685 from cclauss/patch-1
Use ==/!= to compare str, bytes, and int literals
2019-10-30 21:26:00 +08:00
1dd94d6e6d Merge pull request #667 from desexcile/patch-2
Update Readme
2019-10-30 21:25:11 +08:00
2fb0f3fb4b Merge pull request #684 from xxsokolov/patch-1
Update apihelper.py
2019-10-30 21:24:51 +08:00
575fb9da7f Merge branch 'master' into patch-1 2019-10-30 21:24:43 +08:00
c6358f35d2 Merge pull request #688 from vryazanov/master
New content type related to Telegram Passport
2019-10-30 21:22:36 +08:00
20b87f2242 Merge pull request #701 from eternnoir/dependabot/pip/requests-2.20.0
Bump requests from 2.7.0 to 2.20.0
2019-10-30 21:22:16 +08:00
f4c215b0b8 Merge pull request #705 from keshamin/fix-578
Fixed #578
2019-10-30 21:21:49 +08:00
1a30a9a249 Fixed #578 2019-10-30 14:02:00 +03:00
e644ed910a Using Azure Functions for serverless bots.
Simple echo bot using Azure Functions as webhook.
2019-10-13 23:49:43 +03:00
8cb2da3775 Bump requests from 2.7.0 to 2.20.0
Bumps [requests](https://github.com/requests/requests) from 2.7.0 to 2.20.0.
- [Release notes](https://github.com/requests/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/master/HISTORY.md)
- [Commits](https://github.com/requests/requests/compare/v2.7.0...v2.20.0)

Signed-off-by: dependabot[bot] <support@github.com>
2019-10-05 11:55:18 +00:00
f8e7c0f819 Merge branch 'master' into patch-2 2019-09-23 10:25:18 -03:00
f241ef1eac passport_data content type added 2019-08-27 11:55:14 +03:00
8f8276314e Merge pull request #1 from cmd410/master
Support for animated stickers
2019-08-16 20:42:08 +00:00
9e30cfbda6 Use ==/!= to compare str, bytes, and int literals
Identity is not the same thing as equality in Python.
2019-08-12 22:47:59 +02:00
6fb9e18385 Update apihelper.py
Hi, to indicate a third-party api-url (reverse proxy) added conditions.
Perhaps not the most elegant solution, but this functionality is very lacking.


apihelper.API_URL = "http://reverseproxy.example.com/bot{0}/{1}"
2019-08-12 17:09:52 +03:00
f0835a1a14 Support for animated stickers 2019-07-30 12:46:39 +03:00
be3b6f88e8 Added Animation 2019-07-14 18:53:59 +04:30
87e811ce3e Update Readme
Fixed source link
2019-06-29 09:39:14 +03:00
151880f391 Merge pull request #659 from OslikAi/master
Add Poll
2019-06-28 21:02:23 +08:00
bf91829088 Merge pull request #664 from vovawed/master
Added LoginUrl to types
2019-06-28 21:02:04 +08:00
56f0b0a0d4 Merge pull request #603 from nailerNAS/master
infinity_polling fix
2019-06-28 20:57:28 +08:00
2b8e77f749 Merge pull request #616 from painca/patch-1
edit message_handler doc
2019-06-28 20:56:47 +08:00
fba425265e Merge pull request #645 from setazer/patch-1
remove unnecessary f-strings
2019-06-28 20:56:29 +08:00
23069ac729 Merge pull request #644 from David-Lor/master
update bot list
2019-06-28 20:56:15 +08:00
7ab93f55a6 Merge pull request #666 from desexcile/patch-1
Update Readme
2019-06-28 20:55:46 +08:00
ba2705dc82 Merge remote-tracking branch 'origin/master' 2019-06-27 15:07:54 +03:00
3a1bdc2899 add Poll, sendPoll, stopPoll 2019-06-27 15:07:41 +03:00
4e57adbcb6 Update Readme
added @asadov_bot to Bot list using this Api
2019-06-26 10:54:59 +03:00
600002e158 Fixed bug with LoginUrl 2019-06-15 23:09:59 +03:00
3c62e9d391 Added LoginUrl to types 2019-06-15 22:59:41 +03:00
63df69aeb8 Delete my_tests.py 2019-06-06 22:23:11 +03:00
a8cf9f4ae5 Update README.md 2019-06-06 21:54:06 +03:00
b10e45f714 add Poll, sendPoll, stopPoll 2019-06-06 21:49:06 +03:00
9624b45314 add Poll, sendPoll, stopPoll 2019-06-06 21:47:08 +03:00
e26ad07965 Merge pull request #658 from FacuM/add_bot_patch
Update README.md
2019-05-31 11:24:14 +08:00
55c7b6373c Update README.md 2019-05-30 00:35:57 -03:00
ceceeb7d8c Update README.md 2019-05-24 15:05:20 +08:00
b76a69e036 Merge pull request #624 from kor0p/editMessageText
Add 'method' parameter to methods that edit message
2019-05-18 07:58:56 +08:00
e5700380bd Merge branch 'master' into master 2019-05-18 07:56:53 +08:00
47b53b8812 Merge pull request #649 from karipov/master
added creationdatebot to bots api list
2019-04-24 17:41:43 +08:00
2d0ebde481 added creationdatebot to bots api list 2019-04-24 11:29:52 +02:00
271b7e0642 Merge pull request #646 from airatk/master
Update README.md
2019-04-23 10:15:02 +08:00
f516438360 Update README.md 2019-04-21 18:34:10 +03:00
7dc9abffc6 remove unnecessary f-strings 2019-04-16 07:38:50 +07:00
2285d0466e update bot list
Remove areajugonesbot; add vigobustelegrambot
2019-04-09 11:32:15 +02:00
31dbe30489 Merge pull request #629 from iv8/patch-1
fix errors
2019-04-07 21:43:41 +08:00
iv8
c77307881d fix errors 2019-03-04 15:24:55 +08:00
1a58731fb7 Add 'method' parameter to methods that edit message 2019-02-23 16:15:20 +02:00
99df992a66 Added the method sendAnimation, which can be used instead of sendDocument to send animations, specifying their duration. 2019-02-15 18:46:18 +00:00
79e6a3166d edit message_handler doc 2019-01-20 23:04:11 +05:00
8005ca2f6c Update example for api server check webhook alive. 2018-12-29 23:49:10 +08:00
b82ed70ec9 fix syntax errors 2018-11-17 13:19:09 +02:00
18e37f3d20 sleep time timeout time instead of 5 seconds always 2018-11-17 12:58:56 +02:00
ceea457cf1 Update shipping option
Setting list of ShippingOptions like in payments_example.py
shipping_options = [
    ShippingOption(id='instant', title='WorldWide Teleporter').add_price(LabeledPrice('Teleporter', 1000)),
    ShippingOption(id='pickup', title='Local pickup').add_price(LabeledPrice('Pickup', 300))]
gives us [None,None],so It's better add_price to return self
2018-11-12 01:43:00 +02:00
4131b05733 Update README.md
Adding another bot that user pyTelegramBotAPI.
2018-11-05 15:11:31 -03:00
ad4be5c0ae Merge branch 'master' of https://github.com/eternnoir/pyTelegramBotAPI 2018-10-19 13:40:14 +03:00
a946b79839 Merge pull request #586 from khode-mohsen/patch-2
Update deep_linking.py
2018-10-19 10:08:05 +08:00
4eeca78f2f Merge pull request #585 from khode-mohsen/patch-1
Update echo_bot.py
2018-10-19 10:07:55 +08:00
2d6c2a345f Merge pull request #577 from rmed/master
Add check for parse_mode in BaseInlineQueryResultCached
2018-10-19 10:07:40 +08:00
e62eeb7ff2 Merge pull request #565 from uburuntu/analyzer-fixes
Some analyzer fixes
2018-10-19 10:06:37 +08:00
76fc8fbe5e Update deep_linking.py
add '#!/usr/bin/python' for direct execute
2018-10-19 03:47:19 +03:30
584955962e Update echo_bot.py
add '#!/usr/bin/env' for direct execute
2018-10-19 03:38:03 +03:30
b8f442d06b Update Bots using this API 2018-10-09 17:32:27 +08:00
891988be93 Added check for parse_mode in BaseInlineQueryResultCached. Should fix #571 2018-09-15 20:25:06 +02:00
8636b282d7 Merge branch 'master' into analyzer-fixes 2018-09-07 18:07:37 +03:00
2c57c5c01c Update version. 2018-09-07 16:02:03 +08:00
07b82dc9b0 Merge pull request #575 from uburuntu/reset_session
Reset requests.Session feature
2018-09-07 15:56:59 +08:00
a850a0d94f Merge pull request #554 from uburuntu/patch-1
Pretty formatted docs for PyPI
2018-09-07 15:55:23 +08:00
d4f1444503 Merge pull request #574 from setazer/master
fix typo + add inline keyboard example
2018-09-07 15:54:11 +08:00
bab9f7bbb9 enh: reset requests.Session feature
Need for proxy changing and other reconnection stuff
2018-09-06 12:48:08 +03:00
d9ace2adc8 fix typo + add inline keyboard example 2018-09-05 12:34:19 +07:00
36621bb22a fix: some intendation 2018-08-17 13:01:03 +03:00
99466017c5 enh: optimize imports 2018-08-17 12:54:26 +03:00
feec1dde56 fix: little style fixes 2018-08-17 12:49:37 +03:00
54eba946be fix: wrong arguments usage (fix fa038c2) 2018-08-17 12:48:59 +03:00
65a272b901 fix: python 2/3 compatibility in examples 2018-08-17 12:47:44 +03:00
6a4c7e731b fix: delete doubled Sticker class (left a new one) 2018-08-17 12:46:40 +03:00
2b3c86b647 Merge pull request #562 from uburuntu/InputMedia
fix: python2 positional argument specifiers
2018-08-14 22:37:39 +08:00
e419214b49 fix: python2 positional argument specifiers 2018-08-14 17:29:35 +03:00
fe6959c38e Merge pull request #561 from uburuntu/InputMedia
fix: support python2 super()
2018-08-14 21:58:48 +08:00
7dd53b1396 fix: support python2 super() 2018-08-14 12:23:15 +03:00
421118d9d8 Merge pull request #558 from uburuntu/InputMedia
v.4.0: InputMediaAnimation, InputMediaAudio, InputMediaDocument, editMessageMedia
2018-08-13 23:47:03 +08:00
cf69a06ab8 enh: make code better and enhance test case 2018-08-10 16:48:09 +03:00
8ac6e664c5 new: InputMediaAnimation, InputMediaAudio, InputMediaDocument, editMessageMedia
Added support for editing the media content of messages: added the method editMessageMedia and new types InputMediaAnimation, InputMediaAudio, and InputMediaDocument.
2018-08-09 19:16:38 +03:00
12dbcb56d3 Update README.md 2018-08-09 08:45:07 +08:00
a46975d038 Merge pull request #556 from Andru1999/patch-2
Fix issue When you use threading mode
2018-08-08 10:06:49 +08:00
494b535a91 Fix issue When you use threading mode 2018-08-08 10:46:23 +10:00
74f75884f3 Delete now unused deprecated README.rst 2018-08-06 13:56:38 +03:00
4eae469528 Use last version of README.md for PyPI docs with pretty formatting
https://packaging.python.org/specifications/core-metadata/#description-content-type
https://pypi.org/project/pyTelegramBotAPI/
2018-08-06 13:54:46 +03:00
41f7c07959 Update version. 2018-08-03 08:35:04 +08:00
35ea2a2b7e Fix #548 2018-08-03 08:34:48 +08:00
522b2b487b Merge pull request #551 from heyyyoyy/origin/Bot_Api_3.6
Added parse mode for objects in Inline mode
2018-08-03 08:30:41 +08:00
5035e0ce80 Added parse mode for objects in Inline mode 2018-08-02 21:15:33 +03:00
7061091c1c Update version.
* Fix python 3.7 async
2018-07-31 08:58:04 +08:00
5c199bd246 Update README.md 2018-07-26 22:17:55 +08:00
44dd89881d Update README.md 2018-07-26 21:41:08 +08:00
8634e65249 Fix kick_chat_member decription
Fix kick_chat_member return value type description (should be boolean according to API and is boolean by fact).
2018-07-25 12:44:18 +03:00
76dbb05259 Merge pull request #537 from Andru1999/patch-1
Update __init__.py
2018-07-24 13:05:02 +08:00
578a9383b2 Merge branch 'master' into patch-1 2018-07-24 10:42:04 +08:00
85093bded5 Merge pull request #515 from WaffleWafflerov/master
Saving next step and reply handlers
2018-07-24 10:39:35 +08:00
27d442fabf timeout for send_message
Add optional "timeout" parameter to send_message (the same as exists in all other send_*).

Equal rights for all send functions! :)
2018-07-24 00:33:13 +03:00
f251def304 Merge branch 'master' into master 2018-07-22 04:43:53 +03:00
2b822f782d Update __init__.py
I find bug when I use your library without threading. If call bot.register_next_step_handler in function that register next_handler in next_step_handlers but in function _notify_next_handlers this delete and bot don`t have handler, but in threading mode function self.next_step_handlers.pop(chat_id, None) has time to eval self.next_step_handlers.pop(chat_id, None) and bug disappear. Sorry for my English
2018-07-22 00:31:02 +10:00
8bc5b74495 Remove 3.7 stable. Travis ci not support now. 2018-07-03 22:34:28 +08:00
70426ac274 Merge pull request #526 from skar404/master
rename async -> async_dec
2018-07-03 11:19:29 +08:00
a3a2bd5793 Add python 3.7 in travis ci.
#527
2018-07-03 11:17:44 +08:00
c3b6ee9dc0 bug in travis-ci 2018-07-02 23:41:37 +03:00
4079772fd3 Update .travis.yml 2018-07-02 23:29:07 +03:00
9547a8d7b1 test python 3.7 2018-07-02 23:23:48 +03:00
c8b2b14157 rename async -> async_dec 2018-07-02 18:13:11 +03:00
3d5ef5b1d8 Merge pull request #516 from Badiboy/master
_notify_next_handlers drops messages if empty handler list
2018-05-31 14:04:45 +08:00
776a699a8d _notify_next_handlers skips sequential messages
Is there are several sequential messages and next_step_handlers are set, the _notify_next_handlers will process only every even message dew to execute both .pop(i) and i+=1
2018-05-29 18:55:41 +03:00
78afd045d8 _notify_next_handlers drops messages if empty handler list
After calling
clear_step_handler(...)
code:
self.next_step_handlers[chat_id] = []
left the key in next_step_handlers.
When a next message arrives, the old handler executes nothing (no handlers), but still remove message from message queue:
new_messages.pop(i).

Updated to pop message only when there are real handlers in the list.
2018-05-27 23:24:37 +03:00
06faed887c Merge pull request #509 from Badiboy/master
html_text fix and html_caption
2018-05-27 23:14:37 +08:00
bc855f7610 Fix register_for_reply_by_message_id and register_for_reply doc strings. 2018-05-27 17:05:01 +03:00
893d5386c5 Fix register_for_reply_by_message_id doc strings. 2018-05-27 17:02:04 +03:00
909d570dca Add warning about lambda functions in callbacks 2018-05-27 17:01:07 +03:00
424c77fd2c Remove type hint for 2.x and PyPy python compatibility 2018-05-27 16:54:56 +03:00
333949683f Add doc strings to new TeleBot methods
Update telebot/__init__.py
2018-05-27 01:37:06 +03:00
fa038c2e42 Move del_file_after_loading param to right methods :face_palm:
Update: telebot/__init__.py
2018-05-27 01:30:14 +03:00
d61de35a32 Remove rudiment json things, again!
Update: telebot/__init__.py
2018-05-27 01:23:20 +03:00
13df7b5908 Add enable_save_next_step_handlers and load_next_step_handlers methods to step_example/
Update: examples/step_example.py
2018-05-26 17:21:39 +03:00
1de356dcc3 Change default save directory to "./.handler-saves/".
Add del_file_after_loading param to load methods.

Update: telebot/__init__.py
2018-05-26 17:10:00 +03:00
47e6dfd6bc Remove rudiment json things
Update: telebot/__init__.py
2018-05-26 16:52:30 +03:00
3c890a7846 Remove 2 spaces up to TeleBot class.
Update: telebot/__init__.py
2018-05-26 16:37:25 +03:00
17971ff48b Move from json to pickle.
Update: relebot/__init__.py
2018-05-26 12:19:01 +03:00
b989b7601b Add new class: Handler
Change type of (next step|reply) handlers from dict to Handler [WIP]

update: telebot/__init__.py
2018-05-25 20:57:22 +03:00
8c574a786a Merge branch 'master' of https://github.com/WaffleWafflerov/pyTelegramBotAPI-1 2018-05-25 20:37:36 +03:00
7e5f51e4ab Remove old thing.
Update telebot/__init__.py
2018-05-25 09:44:43 +03:00
018e4597a2 Add del_file_after_loading param to Saver.return_load_handlers and Saver.load_handlers methods.
Update telebot/__init__.py
2018-05-25 09:40:29 +03:00
7df6b3d4c9 Fix situation where delay <= 0.
Update telebot/__init__.py
2018-05-25 09:35:39 +03:00
4facc5f7d7 fix unenabled saving handlers.
Updated telebot/__init__.py
2018-05-25 09:30:10 +03:00
4bcfc34a50 Update _notify_next_handlers and _notify_reply_handlers methods:
Now if there wasn't any handler updates, timer willn't start.
2018-05-25 09:07:59 +03:00
b1d5cb2129 Rewrite.
Add class 'Saver' that provides methods for saving (next step|reply) handlers.
Add methods
enable_save_next_step_handlers,
enable_save_reply_handlers,
disable_save_next_step_handlers,
disable_save_reply_handlers,
load_next_step_handlers,
load_reply_handlers
to Telebot and AsyncTelebot.

update telebot/__init__.py
2018-05-25 08:57:48 +03:00
00c8dcc19b Add async methods 2018-05-25 03:10:40 +03:00
ed7e33b4c6 Fix loadings funcs 2018-05-25 02:48:46 +03:00
74a952846c Merge branch 'master' of https://github.com/CoconutWaffle/pyTelegramBotAPI-1 2018-05-24 18:32:25 +03:00
e99fb8f84f Add methods to save (reply|next step) handlers [WIP] 2018-05-20 23:40:25 +03:00
49aee14fca Make _test_filter method static and a bit clear doc strings 2018-05-19 00:42:06 +03:00
9267da205d Merge pull request #1 from eternnoir/master
Updating fork
2018-05-19 00:35:58 +03:00
9c79ba2f87 html_text fix and html_caption
html_text now works with text_link
html_caption now works for caption/caption_entities
2018-05-14 13:29:34 +03:00
3be21ae361 Update version. 2018-05-14 10:15:05 +08:00
42343c3a7f Merge pull request #490 from CoconutWaffle/master
Fixing and upgrading next step and reply handlers. + minor fixes
2018-05-04 07:20:18 +08:00
5a102ed8fa Merge pull request #492 from sviat9440/master
Bugfixes (message.html_text offset)
2018-05-04 07:19:28 +08:00
e1e109bef1 Merge pull request #502 from fojetin/patch-1
Fix #253 #231 #485
2018-05-04 07:18:57 +08:00
b5a217013a Fix #253 #231 #485 2018-04-30 15:41:12 +03:00
3ba9799b98 Renaming back pytelegrambotapi module to telebot 2018-04-28 13:50:59 +03:00
91f213ff34 Fix #501 2018-04-27 15:47:03 +08:00
8f55460924 Fix cache time is zero. 2018-04-26 09:53:55 +08:00
f6b999053d Merge pull request #497 from khabibr/master
Update apihelper.py
2018-04-24 23:52:45 +08:00
99ff104a3f Update apihelper.py
Correct files downloading when proxy used.
2018-04-24 16:48:39 +06:00
662c2c8797 Merge pull request #495 from BennyThink/master
fix issue #403: UnicodeEncodeError when sending a non-ASCII file in Python 2
2018-04-18 20:05:19 +09:00
72a0199a2f Merge pull request #496 from Jay-T/master
Update README.md
2018-04-18 20:04:27 +09:00
989cae597b Update README.md 2018-04-18 13:10:22 +05:00
5dd88f8223 fix issue #403: UnicodeEncodeError when sending a non-ASCII file in Python 2.7 2018-04-18 15:00:05 +08:00
28111bdf4e Merge pull request #494 from LeoNeeDpk1/patch-1
Update README.md SOCKS5 proxy info
2018-04-18 11:51:44 +09:00
10ec897fb5 Update README.md
Updated and 100% working info in SOCKS5 proxy block.
2018-04-18 10:05:26 +12:00
ffe3a0c3d7 Update types.py
-- Fix encoding bug (emoji shifted offset)
2018-04-15 19:19:29 +03:00
f5f48db6ba Merge pull request #1 from eternnoir/master
Update
2018-04-15 19:17:46 +03:00
183230e927 Update setup.py 2018-04-12 17:24:04 +03:00
7957bc45a8 Fixing and upgrading next step and reply handlers. + minor fixes
Rename telebot package to pytelegrambotapi becouse lib named telebot exists and it raising many errors

Add methods:
|     register_for_reply_by_message_id,
|     register_next_step_handler_by_chat_id,
|     clear_reply_handlers,
|     clear_reply_handlers_by_message_id
2018-04-12 13:45:32 +03:00
373d4d37ff Fix test case. 2018-04-10 14:48:39 +08:00
0d0e37dae5 Merge pull request #487 from sviat9440/master
Bugfixes and minor improvements
2018-04-10 15:25:36 +09:00
36d088dfbf Bugfixes and minor improvements 2018-04-04 10:47:37 +03:00
9ae20b4815 Merge pull request #482 from sviat9440/master
Minor improvements
2018-03-25 21:39:27 +08:00
e761e1e1d9 Update apihelper.py 2018-03-25 14:54:28 +03:00
cb0256b37d Update __init__.py 2018-03-25 13:22:35 +03:00
ff3cbaf45b Update apihelper.py 2018-03-25 13:21:55 +03:00
d231b1fbaa Bump version. 2018-03-23 19:59:42 +08:00
7f47f11444 Fix #481 2018-03-23 19:58:43 +08:00
0422e62f65 Update types.py
Fix
2018-03-21 10:45:34 +03:00
82e252ec46 Update types.py
Fix
2018-03-21 10:44:37 +03:00
c11a9f810c Update types.py
Added 'json' property to class 'Message', to quickly save a message to the database
2018-03-21 07:35:42 +03:00
d17d28a144 create field forward_from_message_id in Message
https://core.telegram.org/bots/api#message
2018-03-20 23:36:29 +03:00
dadcd5a577 Update "Bots using this API" entry. 2018-03-19 15:13:48 +08:00
afc9abc269 Add ci test for python 3.6 2018-03-10 14:48:30 +08:00
e01f17e3a0 Bump version. 2018-03-10 14:46:47 +08:00
48e6757686 Fix import logger problem. 2018-03-10 14:41:34 +08:00
8495229ce1 Update version 3.6.0 2018-03-02 19:28:34 +08:00
3b60f7ca67 Merge pull request #462 from heyyyoyy/Bot_Api_3.6
Bot Api 3.6
2018-02-22 22:11:46 +08:00
a1930e05c2 Merge pull request #463 from dmytrostriletskyi/master
Make the Heroku example actual
2018-02-22 20:52:53 +08:00
bc067662dc Make the Heroku example actual 2018-02-16 17:52:37 +02:00
518c49f23a fixing formatting of caption in the method send_document 2018-02-16 18:29:29 +03:00
903b1dfd50 added parse_mode in edit_message_caption 2018-02-16 14:19:35 +00:00
2e199a5684 Bot Api 3.6 2018-02-14 20:27:55 +00:00
55302cb972 Merge pull request #445 from heyyyoyy/update_send_media_group
Added support for local files in the sendMediaGroup method
2018-02-01 19:24:50 +08:00
f47653d2e4 Update README.md 2018-01-24 19:13:29 +08:00
94b4a25980 Update version. 2018-01-24 19:11:03 +08:00
afac177d7d Fix missing media_group_id in message. 2018-01-24 19:05:38 +08:00
2637e29dbe Updated sendMediaGroup method 2018-01-15 16:08:50 +03:00
6d180e30f0 Merge pull request #443 from reacheight/master
fixed example of usage sendVideoNote method
2018-01-15 10:25:06 +08:00
e2ed4cf065 Merge pull request #387 from fumycat/patch-1
Fix optional parameter
2018-01-15 10:23:26 +08:00
8b2dea1d56 fixed example of usage sendVideoNote method 2018-01-10 00:26:44 +03:00
41e31de034 Merge pull request #429 from JekaFST/master
Fix for SendLocation with live period
2017-12-06 15:50:49 +08:00
ae074fd5c9 Merge pull request #2 from JekaFST/JekaFST-live_location_fixes
Edit and stop live location fixes
2017-12-05 01:32:06 +03:00
60596a95b8 Edit and stop live location fixes
.token was missed in apihelper's methods calls
2017-12-05 01:31:47 +03:00
44531bcedf Merge pull request #1 from JekaFST/JekaFST-FixLiveLocation
Fix for SendLocation with live period
2017-12-05 00:23:32 +03:00
8aa8fa5986 Fix for SendLocation with live period
Fix for payload['live_perion'] typo -> payload['live_period']
2017-12-05 00:21:05 +03:00
f0e64b3653 Merge pull request #428 from 0xVK/patch-1
Added SmartySBot to README
2017-12-04 16:21:41 +08:00
8444ea588a Added SmartySBot to README
Added SmartySBot to README
2017-12-03 20:34:37 +02:00
b2f376a906 Remove debug message. Add content_type 2017-11-30 23:34:07 +08:00
d0b4bb7c69 Bump version. 2017-11-29 14:09:57 +08:00
c300195b49 Add provider_data . 2017-11-29 13:59:47 +08:00
2493b200a4 Add provider_data to sendInvoice. 2017-11-29 13:53:39 +08:00
8528ca9e4e Add some message content type. Fix #426 2017-11-29 13:48:34 +08:00
e1a3ccadb7 Add sendMediaGroup method. 2017-11-29 13:45:25 +08:00
a43f037bc9 Version update. 2017-11-15 00:42:50 +08:00
7ac246b801 Fix inline_query_handler not work. 2017-11-15 00:42:27 +08:00
47624a556e Update version. 2017-11-13 10:26:27 +08:00
8bdbc24014 Fix ShippingOption to_json. #414 2017-11-13 10:25:39 +08:00
e45ced958a Update version to support 3.4. 2017-11-13 10:19:13 +08:00
46c803bf55 Fix shipping_options bug. #414
Update version.
2017-11-13 10:14:10 +08:00
3986f33d3a Fix caption_entities without default value. 2017-11-06 08:42:57 +08:00
c327be5a03 Merge pull request #410 from 0xVK/fix-exec-handlers
#405 - Message has callback function but it exec command handlers too.
2017-11-04 21:28:21 +08:00
d8587419e1 Fixed bug when message has next step handler and exec command handlers. 2017-11-04 15:09:29 +02:00
35d7293ebd Merge pull request #402 from MasterGroosha/master
Bot API 3.4: new methods for live locations, new objects.
2017-10-31 09:51:23 +08:00
8e71a612a6 Added missing methods definitions to __init__.py and Async Telebot 2017-10-22 20:07:51 +03:00
5f8d99664e Bot API 3.4: new methods for live locations, new objects. 2017-10-22 19:50:51 +03:00
aaa968c27f Merge pull request #396 from MasterGroosha/patch-1
Added missing author_signature field to Message object
2017-09-25 16:42:38 +08:00
600c014515 Added missing author_signature field to Message object
`author_signature` field was checked, but never added to `Message` object.
2017-09-22 01:08:54 +03:00
0a80fafd76 Update README.md 2017-09-09 21:32:13 +08:00
4a11bb60b4 Update new_chat_member to new_chat_members 2017-09-08 15:47:16 +08:00
7d37374667 Fix test case. 2017-08-28 12:13:26 +08:00
6ad56eb30f Merge pull request #392 from MasterGroosha/master
Bot API 3.3
2017-08-28 11:50:25 +08:00
211f1c607d Bot API 3.3:
- Added the new field pinned_message to the Chat object.
- Added the new fields author_signature and forward_signature to the Message object.
- Added the new field is_bot to the User object.
2017-08-23 10:30:32 +03:00
be786021dc Merge pull request #389 from mostafaqanbaryan/patch-1
Update __init__.py
2017-08-22 04:06:08 +07:00
15d287919d Update __init__.py
Add clear_step_handler() for resetting bot
2017-08-21 14:40:47 +04:30
af991ea76e Fix optional parameter 2017-08-20 01:36:08 +07:00
064b84ad3d Merge pull request #377 from GabrielRF/master
Update README.md
2017-08-13 00:40:50 +08:00
39b4f0a068 Merge branch 'master' into master 2017-08-10 15:03:39 -03:00
246e7e31d7 Update README.md 2017-08-08 21:50:54 +08:00
48bfb7b84f Update version. 2017-08-06 15:47:59 +08:00
e92dc3717e Update README.md 2017-07-24 14:28:27 -03:00
26 changed files with 1781 additions and 1181 deletions

View File

@ -1,10 +1,10 @@
language: python
python:
- "2.6"
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "3.6"
- "3.7"
- "3.8"
- "pypy"
- "pypy3"
install: "pip install -r requirements.txt"

View File

@ -26,6 +26,7 @@
* [Using web hooks](#using-web-hooks)
* [Logging](#logging)
* [Proxy](#proxy)
* [New in library](#new-in-library)
* [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)
@ -124,7 +125,7 @@ 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_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`.
`text`, `audio`, `document`, `photo`, `sticker`, `video`, `video_note`, `voice`, `location`, `contact`, `new_chat_members`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`.
You can use some types in one function. Example:
@ -250,7 +251,7 @@ updates = tb.get_updates()
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
# sendMessage
tb.send_message(chatid, text)
tb.send_message(chat_id, text)
# forwardMessage
tb.forward_message(to_chat_id, from_chat_id, message_id)
@ -291,8 +292,8 @@ 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")
tb.send_video_note(chat_id, videonote)
tb.send_video_note(chat_id, "FILEID")
# sendLocation
tb.send_location(chat_id, lat, lon)
@ -500,19 +501,20 @@ You can use proxy for request. `apihelper.proxy` object will use by call `reques
```python
from telebot import apihelper
apihelper.proxy = {'http', 'http://10.10.1.10:3128'}
apihelper.proxy = {'http':'http://10.10.1.10:3128'}
```
If you want to use socket5 proxy you need install dependency `pip install requests[socks]`.
If you want to use socket5 proxy you need install dependency `pip install requests[socks]` and make sure, that you have the latest version of `gunicorn`, `PySocks`, `pyTelegramBotAPI`, `requests` and `urllib3`.
```python
proxies = {
'http': 'socks5://user:pass@host:port',
'https': 'socks5://user:pass@host:port'
}
apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}
```
## New in library
06.06.2019 - Добавленна поддержка опросов (Poll). Добавлены функции send_poll, stop_poll
## F.A.Q.
### Bot 2.0
@ -527,16 +529,16 @@ Telegram Bot API support new type Chat for message.chat.
- Check the ```type``` attribute in ```Chat``` object:
-
```python
if message.chat.type == private:
if message.chat.type == "private":
# private chat message
if message.chat.type == group:
if message.chat.type == "group":
# group chat message
if message.chat.type == supergroup:
if message.chat.type == "supergroup":
# supergroup chat message
if message.chat.type == channel:
if message.chat.type == "channel":
# channel message
```
@ -564,7 +566,7 @@ Get help. Discuss. Chat.
* [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*
* [NeoBot](https://github.com/neoranger/NeoBot) by *neoranger*
* [NeoBot](https://github.com/neoranger/NeoBot) by [@NeoRanger](https://github.com/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.
@ -579,6 +581,24 @@ Get help. Discuss. Chat.
* [EmaProject](https://github.com/halkliff/emaproject) by [*halkliff*](https://github.com/halkliff) - Ema - Eastern Media Assistant was made thinking on the ease-to-use feature. Coding here is simple, as much as is fast and powerful.
* [filmratingbot](http://t.me/filmratingbot)([source](https://github.com/jcolladosp/film-rating-bot)) by [*jcolladosp*](https://github.com/jcolladosp) - Telegram bot using the Python API that gets films rating from IMDb and metacritic
* [you2mp3bot](http://t.me/you2mp3bot)([link](https://storebot.me/bot/you2mp3bot)) - This bot can convert a Youtube video to Mp3. All you need is send the URL video.
* [areajugonesbot](http://t.me/areajugonesbot)([link](http://t.me/areajugonesbot)) - The areajugonesbot sends news published on the videogames blog Areajugones to Telegram.
* [Send2Kindlebot](http://t.me/Send2KindleBot) ([source](https://github.com/GabrielRF/Send2KindleBot)) by *GabrielRF* - Send to Kindle service.
* [RastreioBot](http://t.me/RastreioBot) ([source](https://github.com/GabrielRF/RastreioBot)) by *GabrielRF* - Bot used to track packages on the Brazilian Mail Service.
* [filex_bot](http://t.me/filex_bot)([link](https://github.com/victor141516/FileXbot-telegram))
* [Spbu4UBot](http://t.me/Spbu4UBot)([link](https://github.com/EeOneDown/spbu4u)) by *EeOneDown* - Bot with timetables for SPbU students.
* [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students.
* [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free.
* [LearnIt](https://t.me/LearnItbot)([link](https://github.com/tiagonapoli/LearnIt)) - A Telegram Bot created to help people to memorize other languages vocabulary.
* [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audiosamles and try to name the performer of the song.
* [Bot-Telegram-Shodan ](https://github.com/rubenleon/Bot-Telegram-Shodan) by [rubenleon](https://github.com/rubenleon)
* [MandangoBot](https://t.me/MandangoBot) by @Alvaricias - Bot for managing Marvel Strike Force alliances (only in spanish, atm).
* [ManjaroBot](https://t.me/ManjaroBot) by [@NeoRanger](https://github.com/neoranger) - Bot for Manjaro Linux Spanish group with a lot of info for Manjaro Newbies.
* [VigoBusTelegramBot](https://t.me/vigobusbot) ([GitHub](https://github.com/Pythoneiro/VigoBus-TelegramBot)) - Bot that provides buses coming to a certain stop and their remaining time for the city of Vigo (Galicia - Spain)
* [kaishnik-bot](https://t.me/kaishnik_bot) ([source](https://github.com/airatk/kaishnik-bot)) by *airatk* - bot which shows all the necessary information to KNTRU-KAI students.
* [Creation Date](https://t.me/creationdatebot) by @karipov - interpolates account creation dates based on telegram given IDs
* [m0xbot](https://t.me/m0xbot) by [kor0p](https://github.com/kor0p) - tic-tac-toe.
* [kboardbot](https://t.me/kboardbot) by [kor0p](https://github.com/kor0p) - inline switches keyboard layout (English, Hebrew, Ukrainian, Russian).
* [Robbie](https://t.me/romdeliverybot) ([source](https://github.com/FacuM/romdeliverybot_support)) by @FacuM - Support Telegram bot for developers and maintainers.
* [AsadovBot](https://t.me/asadov_bot) ([source](https://github.com/desexcile/BotApi)) by @DesExcile - Сatalog of poems by Eduard Asadov.
* [thesaurus_com_bot](https://t.me/thesaurus_com_bot) ([source](https://github.com/LeoSvalov/words-i-learn-bot)) by @LeoSvalov - words and synonyms from [dictionary.com](https://www.dictionary.com) and [thesaurus.com](https://www.thesaurus.com) in the telegram.
Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh.

View File

@ -1,807 +0,0 @@
#
.. raw:: html
<p align="center">
pyTelegramBotAPI
.. raw:: html
<p align="center">
A simple, but extensible Python implementation for the `Telegram Bot
API <https://core.telegram.org/bots/api>`__.
|Download Month| |Build Status| |Download Month|
- `Getting started. <#getting-started>`__
- `Writing your first bot <#writing-your-first-bot>`__
- `Prerequisites <#prerequisites>`__
- `A simple echo bot <#a-simple-echo-bot>`__
- `General API Documentation <#general-api-documentation>`__
- `Types <#types>`__
- `Methods <#methods>`__
- `General use of the API <#general-use-of-the-api>`__
- `Message handlers <#message-handlers>`__
- `Callback Query handlers <#callback-query-handler>`__
- `TeleBot <#telebot>`__
- `Reply markup <#reply-markup>`__
- `Inline Mode <#inline-mode>`__
- `Advanced use of the API <#advanced-use-of-the-api>`__
- `Asynchronous delivery of
messages <#asynchronous-delivery-of-messages>`__
- `Sending large text messages <#sending-large-text-messages>`__
- `Controlling the amount of Threads used by
TeleBot <#controlling-the-amount-of-threads-used-by-telebot>`__
- `The listener mechanism <#the-listener-mechanism>`__
- `Using web hooks <#using-web-hooks>`__
- `Logging <#logging>`__
- `F.A.Q. <#faq>`__
- `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>`__
- `The Telegram Chat Group <#the-telegram-chat-group>`__
- `More examples <#more-examples>`__
- `Bots using this API <#bots-using-this-api>`__
Getting started.
----------------
This API is tested with Python 2.6, Python 2.7, Python 3.4, Pypy and
Pypy 3. There are two ways to install the library:
- Installation using pip (a Python package manager)\*:
::
$ pip install pyTelegramBotAPI
- Installation from source (requires git):
::
$ git clone https://github.com/eternnoir/pyTelegramBotAPI.git
$ cd pyTelegramBotAPI
$ python setup.py install
It is generally recommended to use the first option.
\*\*While the API is production-ready, it is still under development and
it has regular updates, do not forget to update it regularly by calling
``pip install pytelegrambotapi --upgrade``\ \*
Writing your first bot
----------------------
Prerequisites
~~~~~~~~~~~~~
It is presumed that you [have obtained an API token with
@BotFather](https://core.telegram.org/bots#botfather). We will call this
token ``TOKEN``. Furthermore, you have basic knowledge of the Python
programming language and more importantly `the Telegram Bot
API <https://core.telegram.org/bots/api>`__.
A simple echo bot
~~~~~~~~~~~~~~~~~
The TeleBot class (defined in \_\_init\_\_.py) encapsulates all API
calls in a single class. It provides functions such as ``send_xyz``
(``send_message``, ``send_document`` etc.) and several ways to listen
for incoming messages.
Create a file called ``echo_bot.py``. Then, open the file and create an
instance of the TeleBot class.
.. code:: python
import telebot
bot = telebot.TeleBot("TOKEN")
*Note: Make sure to actually replace TOKEN with your own API token.*
After that declaration, we need to register some so-called message
handlers. Message handlers define filters which a message must pass. If
a message passes the filter, the decorated function is called and the
incoming message is passed as an argument.
Let's define a message handler which handles incoming ``/start`` and
``/help`` commands.
.. code:: python
@bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
bot.reply_to(message, "Howdy, how are you doing?")
A function which is decorated by a message handler **can have an
arbitrary name, however, it must have only one parameter (the
message)**.
Let's add another handler:
.. code:: python
@bot.message_handler(func=lambda m: True)
def echo_all(message):
bot.reply_to(message, message.text)
This one echoes all incoming text messages back to the sender. It uses a
lambda function to test a message. If the lambda returns True, the
message is handled by the decorated function. Since we want all messages
to be handled by this function, we simply always return True.
*Note: all handlers are tested in the order in which they were declared*
We now have a basic bot which replies a static message to "/start" and
"/help" commands and which echoes the rest of the sent messages. To
start the bot, add the following to our source file:
.. code:: python
bot.polling()
Alright, that's it! Our source file now looks like this:
.. code:: python
import telebot
bot = telebot.TeleBot("TOKEN")
@bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
bot.reply_to(message, "Howdy, how are you doing?")
@bot.message_handler(func=lambda message: True)
def echo_all(message):
bot.reply_to(message, message.text)
bot.polling()
To start the bot, simply open up a terminal and enter
``python echo_bot.py`` to run the bot! Test it by sending commands
('/start' and '/help') and arbitrary text messages.
General API Documentation
-------------------------
Types
~~~~~
All types are defined in types.py. They are all completely in line with
the `Telegram API's definition of the
types <https://core.telegram.org/bots/api#available-types>`__, except
for the Message's ``from`` field, which is renamed to ``from_user``
(because ``from`` is a Python reserved token). Thus, attributes such as
``message_id`` can be accessed directly with ``message.message_id``.
Note that ``message.chat`` can be either an instance of ``User`` or
``GroupChat`` (see `How can I distinguish a User and a GroupChat in
message.chat? <#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat>`__).
The Message object also has a ``content_types``\ attribute, which
defines the type of the Message. ``content_types`` can be one of the
following strings: 'text', 'audio', 'document', 'photo', 'sticker',
'video', 'voice', 'location', 'contact', 'new\_chat\_participant',
'left\_chat\_participant', 'new\_chat\_title', 'new\_chat\_photo',
'delete\_chat\_photo', 'group\_chat\_created'.
Methods
~~~~~~~
All `API
methods <https://core.telegram.org/bots/api#available-methods>`__ are
located in the TeleBot class. They are renamed to follow common Python
naming conventions. E.g. ``getMe`` is renamed to ``get_me`` and
``sendMessage`` to ``send_message``.
General use of the API
~~~~~~~~~~~~~~~~~~~~~~
Outlined below are some general use cases of the API.
Message handlers
^^^^^^^^^^^^^^^^
A message handler is a function that is decorated with the
``message_handler`` decorator of a TeleBot instance. Message handlers
consist of one or multiple filters. Each filter much return True for a
certain message in order for a message handler to become eligible to
handle that message. A message handler is declared in the following way
(provided ``bot`` is an instance of TeleBot):
.. code:: python
@bot.message_handler(filters)
def function_name(message):
bot.reply_to(message, "This is a message handler")
``function_name`` is not bound to any restrictions. Any function name is
permitted with message handlers. The function must accept at most one
argument, which will be the message that the function must handle.
``filters`` is a list of keyword arguments. A filter is declared in the
following manner: ``name=argument``. One handler may have multiple
filters. TeleBot supports the following filters:
+--------+------+------+
| name | argu | Cond |
| | ment | itio |
| | (s) | n |
+========+======+======+
| conten | list | ``Tr |
| t\_typ | of | ue`` |
| es | stri | if |
| | ngs | mess |
| | (def | age. |
| | ault | cont |
| | ``[' | ent\ |
| | text | _typ |
| | ']`` | e |
| | ) | is |
| | | in |
| | | the |
| | | list |
| | | of |
| | | stri |
| | | ngs. |
+--------+------+------+
| regexp | a | ``Tr |
| | regu | ue`` |
| | lar | if |
| | expr | ``re |
| | essi | .sea |
| | on | rch( |
| | as a | rege |
| | stri | xp_a |
| | ng | rg)` |
| | | ` |
| | | retu |
| | | rns |
| | | ``Tr |
| | | ue`` |
| | | and |
| | | ``me |
| | | ssag |
| | | e.co |
| | | nten |
| | | t_ty |
| | | pe = |
| | | = 't |
| | | ext' |
| | | `` |
| | | (See |
| | | `Pyt |
| | | hon |
| | | Regu |
| | | lar |
| | | Expr |
| | | essi |
| | | ons |
| | | <htt |
| | | ps:/ |
| | | /doc |
| | | s.py |
| | | thon |
| | | .org |
| | | /2/l |
| | | ibra |
| | | ry/r |
| | | e.ht |
| | | ml>` |
| | | __ |
+--------+------+------+
| comman | list | ``Tr |
| ds | of | ue`` |
| | stri | if |
| | ngs | ``me |
| | | ssag |
| | | e.co |
| | | nten |
| | | t_ty |
| | | pe = |
| | | = 't |
| | | ext' |
| | | `` |
| | | and |
| | | ``me |
| | | ssag |
| | | e.te |
| | | xt`` |
| | | star |
| | | ts |
| | | with |
| | | a |
| | | comm |
| | | and |
| | | that |
| | | is |
| | | in |
| | | the |
| | | list |
| | | of |
| | | stri |
| | | ngs. |
+--------+------+------+
| func | a | ``Tr |
| | func | ue`` |
| | tion | if |
| | (lam | the |
| | bda | lamb |
| | or | da |
| | func | or |
| | tion | func |
| | refe | tion |
| | renc | refe |
| | e) | renc |
| | | e |
| | | retu |
| | | rns |
| | | ``Tr |
| | | ue`` |
+--------+------+------+
Here are some examples of using the filters and message handlers:
.. code:: python
import telebot
bot = telebot.TeleBot("TOKEN")
# Handles all text messages that contains the commands '/start' or '/help'.
@bot.message_handler(commands=['start', 'help'])
def handle_start_help(message):
pass
# Handles all sent documents and audio files
@bot.message_handler(content_types=['document', 'audio'])
def handle_docs_audio(message):
pass
# Handles all text messages that match the regular expression
@bot.message_handler(regexp="SOME_REGEXP")
def handle_message(message):
pass
#Handles all messages for which the lambda returns True
@bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document'])
def handle_text_doc(message):
pass
#Which could also be defined as:
def test_message(message):
return message.document.mime_type == 'text/plan'
@bot.message_handler(func=test_message, content_types=['document'])
def handle_text_doc(message)
pass
# Handlers can be stacked to create a function which will be called if either message_handler is eligible
# This handler will be called if the message starts with '/hello' OR is some emoji
@bot.message_handler(commands=['hello'])
@bot.message_handler(func=lambda msg: msg.text.encode("utf-8") == SOME_FANCY_EMOJI)
def send_something(message):
pass
**Important: all handlers are tested in the order in which they were
declared**
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.
.. code:: python
@bot.callback_query_handler(func=lambda call: True)
def test_callback(call):
logger.info(call)
TeleBot
^^^^^^^
.. code:: python
import telebot
TOKEN = '<token_string>'
tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object
# Upon calling this function, TeleBot starts polling the Telegram servers for new messages.
# - none_stop: True/False (default False) - Don't stop polling when receiving an error from the Telegram servers
# - interval: True/False (default False) - The interval between polling requests
# Note: Editing this parameter harms the bot's response time
# - timeout: integer (default 20) - Timeout in seconds for long polling.
tb.polling(none_stop=False, interval=0, timeout=20)
# getMe
user = tb.get_me()
# setWebhook
tb.set_webhook(url="http://example.com", certificate=open('mycert.pem'))
# unset webhook
tb.remove_webhook()
# getUpdates
updates = tb.get_updates()
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
# sendMessage
tb.send_message(chatid, text)
# forwardMessage
tb.forward_message(to_chat_id, from_chat_id, message_id)
# All send_xyz functions which can take a file as an argument, can also take a file_id instead of a file.
# sendPhoto
photo = open('/tmp/photo.png', 'rb')
tb.send_photo(chat_id, photo)
tb.send_photo(chat_id, "FILEID")
# sendAudio
audio = open('/tmp/audio.mp3', 'rb')
tb.send_audio(chat_id, audio)
tb.send_audio(chat_id, "FILEID")
## sendAudio with duration, performer and title.
tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram')
# sendVoice
voice = open('/tmp/voice.ogg', 'rb')
tb.send_voice(chat_id, voice)
tb.send_voice(chat_id, "FILEID")
# sendDocument
doc = open('/tmp/file.txt', 'rb')
tb.send_document(chat_id, doc)
tb.send_document(chat_id, "FILEID")
# sendSticker
sti = open('/tmp/sti.webp', 'rb')
tb.send_sticker(chat_id, sti)
tb.send_sticker(chat_id, "FILEID")
# sendVideo
video = open('/tmp/video.mp4', 'rb')
tb.send_video(chat_id, video)
tb.send_video(chat_id, "FILEID")
# sendLocation
tb.send_location(chat_id, lat, lon)
# sendChatAction
# action_string can be one of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video',
# 'record_audio', 'upload_audio', 'upload_document' or 'find_location'.
tb.send_chat_action(chat_id, action_string)
# getFile
# Downloading a file is straightforward
# Returns a File object
import requests
file_info = tb.get_file(file_id)
file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(API_TOKEN, file_info.file_path))
Reply markup
^^^^^^^^^^^^
All ``send_xyz`` functions of TeleBot take an optional ``reply_markup``
argument. This argument must be an instance of ``ReplyKeyboardMarkup``,
``ReplyKeyboardRemove`` or ``ForceReply``, which are defined in types.py.
.. code:: python
from telebot import types
# Using the ReplyKeyboardMarkup class
# It's constructor can take the following optional arguments:
# - resize_keyboard: True/False (default False)
# - one_time_keyboard: True/False (default False)
# - selective: True/False (default False)
# - row_width: integer (default 3)
# row_width is used in combination with the add() function.
# It defines how many buttons are fit on each row before continuing on the next row.
markup = types.ReplyKeyboardMarkup(row_width=2)
itembtn1 = types.KeyboardButton('a')
itembtn2 = types.KeyboardButton('v')
itembtn3 = types.KeyboardButton('d')
markup.add(itembtn1, itembtn2, itembtn3)
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
# or add strings one row at a time:
markup = types.ReplyKeyboardMarkup()
itembtna = types.KeyboardButton('a')
itembtnv = types.KeyboardButton('v')
itembtnc = types.KeyboardButton('c')
itembtnd = types.KeyboardButton('d')
itembtne = types.KeyboardButton('e')
markup.row(itembtna, itembtnv)
markup.row(itembtnc, itembtnd, itembtne)
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
The last example yields this result:
.. figure:: https://pp.vk.me/c624430/v624430512/473e5/_mxxW7FPe4U.jpg
:alt: ReplyKeyboardMarkup
ReplyKeyboardMarkup
.. code:: python
# ReplyKeyboardRemove: hides a previously sent ReplyKeyboardMarkup
# Takes an optional selective argument (True/False, default False)
markup = types.ReplyKeyboardRemove(selective=False)
tb.send_message(chat_id, message, reply_markup=markup)
.. code:: python
# ForceReply: forces a user to reply to a message
# Takes an optional selective argument (True/False, default False)
markup = types.ForceReply(selective=False)
tb.send_message(chat_id, "Send me another word:", reply_markup=markup)
ForceReply:
.. figure:: https://pp.vk.me/c624430/v624430512/473ec/602byyWUHcs.jpg
:alt: ForceReply
ForceReply
Inline Mode
~~~~~~~~~~~
More information about `Inline
mode <https://core.telegram.org/bots/inline>`__.
inline\_handler
^^^^^^^^^^^^^^^
Now, you can use inline\_handler to get inline\_query in telebot.
.. code:: python
@bot.inline_handler(lambda query: query.query == 'text')
def query_text(inline_query):
# Query message is text
chosen\_inline\_handler
^^^^^^^^^^^^^^^^^^^^^^^
Use chosen\_inline\_handler to get chosen\_inline\_result in telebot.
Don't forgot add the /setinlinefeedback command for @Botfather.
More information :
`collecting-feedback <https://core.telegram.org/bots/inline#collecting-feedback>`__
.. code:: python
@bot.chosen_inline_handler(func=lambda chosen_inline_result: True)
def test_chosen(chosen_inline_result):
# Process all chosen_inline_result.
answer\_inline\_query
^^^^^^^^^^^^^^^^^^^^^
.. code:: python
@bot.inline_handler(lambda query: query.query == 'text')
def query_text(inline_query):
try:
r = types.InlineQueryResultArticle('1', 'Result', 'Result message.')
r2 = types.InlineQueryResultArticle('2', 'Result2', 'Result message2.')
bot.answer_inline_query(inline_query.id, [r, r2])
except Exception as e:
print(e)
Advanced use of the API
-----------------------
Asynchronous delivery of messages
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There exists an implementation of TeleBot which executes all
``send_xyz`` and the ``get_me`` functions asynchronously. This can speed
up you bot **significantly**, but it has unwanted side effects if used
without caution. To enable this behaviour, create an instance of
AsyncTeleBot instead of TeleBot.
.. code:: python
tb = telebot.AsyncTeleBot("TOKEN")
Now, every function that calls the Telegram API is executed in a
separate Thread. The functions are modified to return an AsyncTask
instance (defined in util.py). Using AsyncTeleBot allows you to do the
following:
.. code:: python
import telebot
tb = telebot.AsyncTeleBot("TOKEN")
task = tb.get_me() # Execute an API call
# Do some other operations...
a = 0
for a in range(100):
a += 10
result = task.wait() # Get the result of the execution
*Note: if you execute send\_xyz functions after eachother without
calling wait(), the order in which messages are delivered might be
wrong.*
Sending large text messages
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sometimes you must send messages that exceed 5000 characters. The
Telegram API can not handle that many characters in one request, so we
need to split the message in multiples. Here is how to do that using the
API:
.. code:: python
from telebot import util
large_text = open("large_text.txt", "rb").read()
# Split the text each 3000 characters.
# split_string returns a list with the splitted text.
splitted_text = util.split_string(large_text, 3000)
for text in splitted_text:
tb.send_message(chat_id, text)
Controlling the amount of Threads used by TeleBot
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The TeleBot constructor takes the following optional arguments:
- create\_threads: True/False (default True). A flag to indicate
whether TeleBot should execute message handlers on it's polling
Thread.
- num\_threads: integer (default 4). Controls the amount of
WorkerThreads created for the internal thread pool that TeleBot uses
to execute message handlers. Is not used when create\_threads is
False.
The listener mechanism
~~~~~~~~~~~~~~~~~~~~~~
As an alternative to the message handlers, one can also register a
function as a listener to TeleBot. Example:
.. code:: python
def handle_messages(messages):
for message in messsages:
# Do something with the message
bot.reply_to(message, 'Hi')
bot.set_update_listener(handle_messages)
bot.polling()
Using webhooks
~~~~~~~~~~~~~~
When using webhooks telegram sends one Update per call, for processing
it you should call process\_new\_messages([update.message]) when you
recieve it.
There are some examples using webhooks in the
*examples/webhook\_examples* directory.
Logging
~~~~~~~
You can use the Telebot module logger to log debug info about Telebot.
Use ``telebot.logger`` to get the logger of the TeleBot module. It is
possible to add custom logging Handlers to the logger. Refer to the
`Python logging module
page <https://docs.python.org/2/library/logging.html>`__ for more info.
.. code:: python
import logging
logger = telebot.logger
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
F.A.Q.
------
Bot 2.0
~~~~~~~
April 9,2016 Telegram release new bot 2.0 API, which has a drastic
revision especially for the change of method's interface.If you want to
update to the latest version, please make sure you've switched bot's
code to bot 2.0 method interface.
`More information about pyTelegramBotAPI support
bot2.0 <https://github.com/eternnoir/pyTelegramBotAPI/issues/130>`__
How can I distinguish a User and a GroupChat in message.chat?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Telegram Bot API support new type Chat for message.chat.
- Check the ``type`` attribute in ``Chat`` object:
- \`\`\`python if message.chat.type == “private”: # private chat
message
if message.chat.type == “group”: # group chat message
if message.chat.type == “supergroup”: # supergroup chat message
if message.chat.type == “channel”: # channel message
\`\`\`
The Telegram Chat Group
-----------------------
Get help. Discuss. Chat.
- Join the pyTelegramBotAPI Telegram Chat Group
- Messge to @eternnoir by telegram for Invitation.
- We now have a Telegram Channel as well! Keep yourself up to date with
API changes, and `join it <https://telegram.me/pytelegrambotapi>`__.
More examples
-------------
- `Echo
Bot <https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/echo_bot.py>`__
- `Deep
Linking <https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/deep_linking.py>`__
- `next\_step\_handler
Example <https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py>`__
Bots using this API
-------------------
- `SiteAlert bot <https://telegram.me/SiteAlert_bot>`__
(`source <https://github.com/ilteoood/SiteAlert-Python>`__) by
*ilteoood* - Monitors websites and sends a notification on changes
- `TelegramLoggingBot <https://github.com/aRandomStranger/TelegramLoggingBot>`__
by *aRandomStranger*
- `Telegram
LMGTFY\_bot <https://github.com/GabrielRF/telegram-lmgtfy_bot>`__ by
*GabrielRF*
- `Telegram
UrlProBot <https://github.com/GabrielRF/telegram-urlprobot>`__ by
*GabrielRF*
- `Telegram Proxy
Bot <https://bitbucket.org/master_groosha/telegram-proxy-bot>`__ by
*Groosha* - A simple BITM (bot-in-the-middle) for Telegram acting as
some kind of "proxy".
- `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*
- `NeoBot <https://github.com/neoranger/NeoBot>`__ by *neoranger*
- `TagAlertBot <https://github.com/pitasi/TagAlertBot>`__ by *pitasi*
Want to have your bot listed here? Send a Telegram message to @eternnoir
or @pevdh.
.. |Download Month| image:: https://img.shields.io/pypi/v/pyTelegramBotAPI.svg
:target: https://pypi.python.org/pypi/pyTelegramBotAPI
.. |Build Status| image:: https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master
:target: https://travis-ci.org/eternnoir/pyTelegramBotAPI
.. |Download Month| image:: https://img.shields.io/pypi/dm/pyTelegramBotAPI.svg
:target: https://pypi.python.org/pypi/pyTelegramBotAPI

View File

@ -1,3 +1,5 @@
#!/usr/bin/python
# This example shows how to implement deep linking (https://core.telegram.org/bots#deep-linking)
# with the pyTelegramBotAPI.
# Note: This is not a working, production-ready sample.
@ -31,34 +33,38 @@
# steps are not shown here. Only steps 5 to 7 are illustrated, some in pseudo-code, with this example.
import telebot
import time
bot = telebot.TeleBot('TOKEN')
def extract_unique_code(text):
# Extracts the unique_code from the sent /start command.
return text.split()[1] if len(text.split()) > 1 else None
def in_storage(unique_code):
# (pseudo-code) Should check if a unique code exists in storage
return True
def get_username_from_storage(unique_code):
# (pseudo-code) Does a query to the storage, retrieving the associated username
# Should be replaced by a real database-lookup.
return "ABC" if in_storage(unique_code) else None
def save_chat_id(chat_id, username):
# (pseudo-code) Save the chat_id->username to storage
# Should be replaced by a real database query.
pass
@bot.message_handler(commands=['start'])
def send_welcome(message):
unique_code = extract_unique_code(message.text)
if unique_code: # if the '/start' command contains a unique_code
if unique_code: # if the '/start' command contains a unique_code
username = get_username_from_storage(unique_code)
if username: # if the username exists in our database
if username: # if the username exists in our database
save_chat_id(message.chat.id, username)
reply = "Hello {0}, how are you?".format(username)
else:
@ -67,4 +73,5 @@ def send_welcome(message):
reply = "Please visit me via a provided URL from the website."
bot.reply_to(message, reply)
bot.polling()

View File

@ -2,9 +2,10 @@
This is a detailed example using almost every command of the API
"""
import time
import telebot
from telebot import types
import time
TOKEN = '<token_string>'
@ -12,14 +13,14 @@ knownUsers = [] # todo: save these in a file,
userStep = {} # so they won't reset every time the bot restarts
commands = { # command description used in the "help" command
'start': 'Get used to the bot',
'help': 'Gives you information about the available commands',
'sendLongText': 'A test using the \'send_chat_action\' command',
'getImage': 'A test using multi-stage messages, custom keyboard, and media sending'
'start' : 'Get used to the bot',
'help' : 'Gives you information about the available commands',
'sendLongText': 'A test using the \'send_chat_action\' command',
'getImage' : 'A test using multi-stage messages, custom keyboard, and media sending'
}
imageSelect = types.ReplyKeyboardMarkup(one_time_keyboard=True) # create the image selection keyboard
imageSelect.add('cock', 'pussy')
imageSelect.add('Mickey', 'Minnie')
hideBoard = types.ReplyKeyboardRemove() # if sent as reply_markup, will hide the keyboard
@ -33,7 +34,7 @@ def get_user_step(uid):
else:
knownUsers.append(uid)
userStep[uid] = 0
print "New user detected, who hasn't used \"/start\" yet"
print("New user detected, who hasn't used \"/start\" yet")
return 0
@ -45,7 +46,7 @@ def listener(messages):
for m in messages:
if m.content_type == 'text':
# print the sent message to the console
print str(m.chat.first_name) + " [" + str(m.chat.id) + "]: " + m.text
print(str(m.chat.first_name) + " [" + str(m.chat.id) + "]: " + m.text)
bot = telebot.TeleBot(TOKEN)
@ -104,15 +105,15 @@ def msg_image_select(m):
# for some reason the 'upload_photo' status isn't quite working (doesn't show at all)
bot.send_chat_action(cid, 'typing')
if text == "cock": # send the appropriate image based on the reply to the "/getImage" command
if text == 'Mickey': # send the appropriate image based on the reply to the "/getImage" command
bot.send_photo(cid, open('rooster.jpg', 'rb'),
reply_markup=hideBoard) # send file and hide keyboard, after image is sent
userStep[cid] = 0 # reset the users step back to 0
elif text == "pussy":
elif text == 'Minnie':
bot.send_photo(cid, open('kitten.jpg', 'rb'), reply_markup=hideBoard)
userStep[cid] = 0
else:
bot.send_message(cid, "Don't type bullsh*t, if I give you a predefined keyboard!")
bot.send_message(cid, "Please, use the predefined keyboard!")
bot.send_message(cid, "Please try again")
@ -128,4 +129,5 @@ def command_default(m):
# this is the standard reply to a normal message
bot.send_message(m.chat.id, "I don't understand \"" + m.text + "\"\nMaybe try the help page at /help")
bot.polling()

View File

@ -1,3 +1,5 @@
#!/usr/bin/python
# This is a simple echo bot using the decorator mechanism.
# It echoes any incoming text messages.
@ -7,6 +9,7 @@ API_TOKEN = '<api_token>'
bot = telebot.TeleBot(API_TOKEN)
# Handle '/start' and '/help'
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
@ -21,4 +24,5 @@ I am here to echo your kind words back to you. Just say anything nice and I'll s
def echo_message(message):
bot.reply_to(message, message.text)
bot.polling()

View File

@ -1,8 +1,9 @@
# This example show how to write an inline mode telegramt bot use pyTelegramBotAPI.
import telebot
import time
import sys
# This example show how to write an inline mode telegram bot use pyTelegramBotAPI.
import logging
import sys
import time
import telebot
from telebot import types
API_TOKEN = '<TOKEN>'
@ -50,7 +51,7 @@ def query_video(inline_query):
print(e)
@bot.inline_handler(lambda query: len(query.query) is 0)
@bot.inline_handler(lambda query: len(query.query) == 0)
def default_query(inline_query):
try:
r = types.InlineQueryResultArticle('1', 'default', types.InputTextMessageContent('default'))
@ -69,5 +70,5 @@ if __name__ == '__main__':
try:
main_loop()
except KeyboardInterrupt:
print >> sys.stderr, '\nExiting by user request.\n'
print('\nExiting by user request.\n')
sys.exit(0)

View File

@ -0,0 +1,27 @@
# This example show how to use inline keyboards and process button presses
import telebot
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
TELEGRAM_TOKEN = '<TOKEN>'
bot = telebot.TeleBot(TELEGRAM_TOKEN)
def gen_markup():
markup = InlineKeyboardMarkup()
markup.row_width = 2
markup.add(InlineKeyboardButton("Yes", callback_data="cb_yes"),
InlineKeyboardButton("No", callback_data="cb_no"))
return markup
@bot.callback_query_handler(func=lambda call: True)
def callback_query(call):
if call.data == "cb_yes":
bot.answer_callback_query(call.id, "Answer is Yes")
elif call.data == "cb_no":
bot.answer_callback_query(call.id, "Answer is No")
@bot.message_handler(func=lambda message: True)
def message_handler(message):
bot.send_message(message.chat.id, "Yes/no?", reply_markup=gen_markup())
bot.polling(none_stop=True)

View File

@ -1,6 +1,5 @@
import telebot
from telebot.types import LabeledPrice
from telebot.types import ShippingOption
from telebot.types import LabeledPrice, ShippingOption
token = '1234567890:AAAABBBBCCCCDDDDeeeeFFFFgggGHHHH'
provider_token = '1234567890:TEST:AAAABBBBCCCCDDDD' # @BotFather -> Bot Settings -> Payments

View File

@ -0,0 +1,69 @@
# Using Azure Functions for serverless bots.
# (Thanks to twitter.com/masyan for the idea)
# By default the Azure Functions url is https://.../api/HttpTrigger for HttpTrigger type.
# In this example we will use clear webhook url without /api/ -> https://.../HttpTrigger.
# Also we set "authLevel": "anonymous".
# For HttpTrigger type set "route" and "authLevel" in functions.json
# {
# "bindings": [
# ...
# "authLevel": "anonymous"
# "route": "HttpTrigger"
# ]
# }
# To avoid using /api/ in url set "routePrefix":"" in host.json
# {
# ...
# "extensions": {
# "http": {
# "routePrefix": ""
# }
# }
# }
import logging
import azure.functions as func
import telebot
from telebot import apihelper, types
logger = telebot.logger
telebot.logger.setLevel(logging.DEBUG)
# Set bot token
TOKEN = ''
# Uncomment this for using proxy for request
# PROXY = ''
# apihelper.proxy = {'https': PROXY}
# Set WEBHOOK as your Azure Functions url (https://...azurewebsites.net/HttpTrigger)
WEBHOOK = ''
bot = telebot.TeleBot(TOKEN)
@bot.message_handler(commands=['start'])
def start(message):
bot.reply_to(message, 'Hello, ' + message.from_user.first_name)
@bot.message_handler(func=lambda message: True, content_types=['text'])
def echo_message(message):
bot.reply_to(message, message.text)
# To avoid "error 429 too many request" set webhook only once. Or use time.sleep(1).
def main(req: func.HttpRequest) -> func.HttpResponse:
bot.set_webhook(url=WEBHOOK)
request_body_dict = req.get_json()
update = telebot.types.Update.de_json(request_body_dict)
bot.process_new_messages([update.message])
return func.HttpResponse(body='', status_code=200)
# Sometimes "requests" version is important.
# azure-functions==1.0.4
# PySocks==1.7.1
# pyTelegramBotAPI==3.6.6
# requests==2.10.0

View File

@ -2,7 +2,6 @@
"""
This Example will show you how to use register_next_step handler.
"""
import time
import telebot
from telebot import types
@ -75,4 +74,13 @@ def process_sex_step(message):
bot.reply_to(message, 'oooops')
# Enable saving next step handlers to file "./.handlers-saves/step.save".
# Delay=2 means that after any change in next step handlers (e.g. calling register_next_step_handler())
# saving will hapen after delay 2 seconds.
bot.enable_save_next_step_handlers(delay=2)
# Load next_step_handlers from save file (default "./.handlers-saves/step.save")
# WARNING It will work only if enable_save_next_step_handlers was called!
bot.load_next_step_handlers()
bot.polling()

View File

@ -3,9 +3,10 @@
# and goes by the name 'TeleBot (@pyTeleBot)'. Join our group to talk to him!
# WARNING: Tested with Python 2.7
import telebot
import os
import telebot
text_messages = {
'welcome':
u'Please welcome {name}!\n\n'
@ -33,8 +34,10 @@ if "TELEBOT_BOT_TOKEN" not in os.environ or "GROUP_CHAT_ID" not in os.environ:
bot = telebot.AsyncTeleBot(os.environ["TELEBOT_BOT_TOKEN"])
GROUP_CHAT_ID = int(os.environ["GROUP_CHAT_ID"])
def is_api_group(chat_id):
return chat_id== GROUP_CHAT_ID
return chat_id == GROUP_CHAT_ID
@bot.message_handler(func=lambda m: True, content_types=['new_chat_participant'])
def on_user_joins(message):
@ -50,6 +53,7 @@ def on_user_joins(message):
bot.reply_to(message, text_messages['welcome'].format(name=name))
@bot.message_handler(commands=['info', 'help'])
def on_info(message):
if not is_api_group(message.chat.id):
@ -58,21 +62,23 @@ def on_info(message):
bot.reply_to(message, text_messages['info'])
@bot.message_handler(commands=["ping"])
def on_ping(message):
bot.reply_to(message, "Still alive and kicking!")
@bot.message_handler(commands=['start'])
def on_start(message):
if not is_api_group(message.chat.id):
bot.reply_to(message, text_messages['wrong_chat'])
return
def listener(messages):
for m in messages:
print str(m)
print(str(m))
bot.set_update_listener(listener)
bot.polling()

View File

@ -11,7 +11,6 @@ from aiohttp import web
import telebot
API_TOKEN = '<api_token>'
WEBHOOK_HOST = '<ip/host where the bot is running>'
@ -32,7 +31,6 @@ WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
WEBHOOK_URL_BASE = "https://{}:{}".format(WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/{}/".format(API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.INFO)
@ -51,6 +49,7 @@ async def handle(request):
else:
return web.Response(status=403)
app.router.add_post('/{token}/', handle)
@ -72,7 +71,7 @@ def echo_message(message):
bot.remove_webhook()
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
# Build ssl context

View File

@ -4,10 +4,11 @@
# This is a simple echo bot using decorators and webhook with CherryPy
# It echoes any incoming text messages and does not use the polling method.
import cherrypy
import telebot
import logging
import cherrypy
import telebot
API_TOKEN = '<api_token>'
@ -29,7 +30,6 @@ WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.INFO)
@ -70,7 +70,7 @@ def echo_message(message):
bot.remove_webhook()
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
# Disable CherryPy requests log
@ -80,9 +80,9 @@ for handler in tuple(access_log.handlers):
# Start cherrypy server
cherrypy.config.update({
'server.socket_host': WEBHOOK_LISTEN,
'server.socket_port': WEBHOOK_PORT,
'server.ssl_module': 'builtin',
'server.socket_host' : WEBHOOK_LISTEN,
'server.socket_port' : WEBHOOK_PORT,
'server.ssl_module' : 'builtin',
'server.ssl_certificate': WEBHOOK_SSL_CERT,
'server.ssl_private_key': WEBHOOK_SSL_PRIV
})

View File

@ -4,11 +4,17 @@
# This is a simple echo bot using decorators and webhook with BaseHTTPServer
# It echoes any incoming text messages and does not use the polling method.
import BaseHTTPServer
import ssl
import telebot
import logging
try:
# Python 2
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
except ImportError:
# Python 3
from http.server import BaseHTTPRequestHandler, HTTPServer
import logging
import ssl
import telebot
API_TOKEN = '<api_token>'
@ -30,7 +36,6 @@ WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.INFO)
@ -38,7 +43,7 @@ bot = telebot.TeleBot(API_TOKEN)
# WebhookHandler, process webhook calls
class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler):
class WebhookHandler(BaseHTTPRequestHandler):
server_version = "WebhookHandler/1.0"
def do_HEAD(self):
@ -81,15 +86,17 @@ def echo_message(message):
# Remove webhook, it fails sometimes the set if there is a previous webhook
bot.remove_webhook()
#bot.remove_webhook()
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
# Beacuse telegram bot api server will check webhook server is alive.
# Here we need set webhook after server started manually.
#bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
# certificate=open(WEBHOOK_SSL_CERT, 'r'))
# Start server
httpd = BaseHTTPServer.HTTPServer((WEBHOOK_LISTEN, WEBHOOK_PORT),
WebhookHandler)
httpd = HTTPServer((WEBHOOK_LISTEN, WEBHOOK_PORT),
WebhookHandler)
httpd.socket = ssl.wrap_socket(httpd.socket,
certfile=WEBHOOK_SSL_CERT,

View File

@ -4,10 +4,12 @@
# This is a simple echo bot using decorators and webhook with flask
# It echoes any incoming text messages and does not use the polling method.
import flask
import telebot
import logging
import time
import flask
import telebot
API_TOKEN = '<api_token>'
@ -29,7 +31,6 @@ WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.INFO)
@ -73,8 +74,10 @@ def echo_message(message):
# Remove webhook, it fails sometimes the set if there is a previous webhook
bot.remove_webhook()
time.sleep(0.1)
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH,
bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
# Start flask server

View File

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

View File

@ -4,13 +4,15 @@
# 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
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import telebot
API_TOKEN = '<api_token>'
WEBHOOK_CERT = "./cert.pem"
WEBHOOK_PKEY = "./pkey.pem"
@ -29,15 +31,18 @@ WEBHOOK_URL_BASE = "https://{0}:{1}/{2}".format(WEBHOOK_HOST, str(WEBHOOK_PORT),
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 \
@ -52,21 +57,26 @@ class webhook_serv(tornado.web.RequestHandler):
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):
@ -74,6 +84,7 @@ def send_welcome(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'))
@ -86,9 +97,9 @@ application = tornado.web.Application([
])
http_server = tornado.httpserver.HTTPServer(application, ssl_options={
"certfile": WEBHOOK_CERT,
"keyfile": WEBHOOK_PKEY,
})
"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==3.0.2
requests==2.7.0
requests==2.20.0
six==1.9.0
wheel==0.24.0

View File

@ -2,14 +2,15 @@
from setuptools import setup
from io import open
def readme():
with open('README.rst', encoding='utf-8') as f:
return f.read()
def read(filename):
with open(filename, encoding='utf-8') as file:
return file.read()
setup(name='pyTelegramBotAPI',
version='3.1.1',
version='3.6.6',
description='Python Telegram bot api. ',
long_description=readme(),
long_description=read('README.md'),
long_description_content_type="text/markdown",
author='eternnoir',
author_email='eternnoir@gmail.com',
url='https://github.com/eternnoir/pyTelegramBotAPI',
@ -23,7 +24,6 @@ setup(name='pyTelegramBotAPI',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Environment :: Console',

File diff suppressed because it is too large Load Diff

View File

@ -20,18 +20,18 @@ 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}"
API_URL = None
FILE_URL = None
CONNECT_TIMEOUT = 3.5
READ_TIMEOUT = 9999
def _get_req_session():
return util.per_thread('req_session', lambda: requests.session())
def _get_req_session(reset=False):
return util.per_thread('req_session', lambda: requests.session(), reset)
def _make_request(token, method_name, method='get', params=None, files=None, base_url=API_URL):
def _make_request(token, method_name, method='get', params=None, files=None):
"""
Makes a request to the Telegram API.
:param token: The bot's API token. (Created with @BotFather)
@ -41,7 +41,11 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas
:param files: Optional files.
:return: The result parsed to a JSON dictionary.
"""
request_url = base_url.format(token, method_name)
if API_URL is None:
request_url = "https://api.telegram.org/bot{0}/{1}".format(token, method_name)
else:
request_url = API_URL.format(token, method_name)
logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files))
read_timeout = READ_TIMEOUT
connect_timeout = CONNECT_TIMEOUT
@ -98,9 +102,20 @@ def get_file(token, file_id):
return _make_request(token, method_url, params={'file_id': file_id})
def get_file_url(token, file_id):
if FILE_URL is None:
return "https://api.telegram.org/file/bot{0}/{1}".format(token, get_file(token, file_id)['file_path'])
else:
return FILE_URL.format(token, get_file(token, file_id)['file_path'])
def download_file(token, file_path):
url = FILE_URL.format(token, file_path)
result = _get_req_session().get(url)
if FILE_URL is None:
url = "https://api.telegram.org/file/bot{0}/{1}".format(token, file_path)
else:
url = FILE_URL.format(token, file_path)
result = _get_req_session().get(url, proxies=proxy)
if result.status_code != 200:
msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \
.format(result.status_code, result.reason, result.text)
@ -109,7 +124,7 @@ def download_file(token, file_path):
def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None):
parse_mode=None, disable_notification=None, timeout=None):
"""
Use this method to send text messages. On success, the sent Message is returned.
:param token:
@ -118,6 +133,8 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m
:param disable_web_page_preview:
:param reply_to_message_id:
:param reply_markup:
:param parse_mode:
:param disable_notification:
:return:
"""
method_url = r'sendMessage'
@ -132,6 +149,8 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
return _make_request(token, method_url, params=payload, method='post')
@ -209,6 +228,18 @@ def get_chat_members_count(token, chat_id):
return _make_request(token, method_url, params=payload)
def set_chat_sticker_set(token, chat_id, sticker_set_name):
method_url = r'setChatStickerSet'
payload = {'chat_id': chat_id, 'sticker_set_name': sticker_set_name}
return _make_request(token, method_url, params=payload)
def delete_chat_sticker_set(token, chat_id):
method_url = r'deleteChatStickerSet'
payload = {'chat_id': chat_id}
return _make_request(token, method_url, params=payload)
def get_chat_member(token, chat_id, user_id):
method_url = r'getChatMember'
payload = {'chat_id': chat_id, 'user_id': user_id}
@ -224,7 +255,7 @@ def forward_message(token, chat_id, from_chat_id, message_id, disable_notificati
def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
parse_mode=None, disable_notification=None):
method_url = r'sendPhoto'
payload = {'chat_id': chat_id}
files = None
@ -238,15 +269,31 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_location(token, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None,
def send_media_group(token, chat_id, media, disable_notification=None, reply_to_message_id=None):
method_url = r'sendMediaGroup'
media_json, files = _convert_input_media_array(media)
payload = {'chat_id': chat_id, 'media': media_json}
if disable_notification:
payload['disable_notification'] = disable_notification
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
return _make_request(token, method_url, params=payload, method='post' if files else 'get',
files=files if files else None)
def send_location(token, chat_id, latitude, longitude, live_period=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
method_url = r'sendLocation'
payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude}
if live_period:
payload['live_period'] = live_period
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
@ -256,6 +303,36 @@ def send_location(token, chat_id, latitude, longitude, reply_to_message_id=None,
return _make_request(token, method_url, params=payload)
def edit_message_live_location(token, latitude, longitude, chat_id=None, message_id=None,
inline_message_id=None, reply_markup=None):
method_url = r'editMessageLiveLocation'
payload = {'latitude': latitude, 'longitude': longitude}
if chat_id:
payload['chat_id'] = chat_id
if message_id:
payload['message_id'] = message_id
if inline_message_id:
payload['inline_message_id'] = inline_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload)
def stop_message_live_location(token, chat_id=None, message_id=None,
inline_message_id=None, reply_markup=None):
method_url = r'stopMessageLiveLocation'
payload = {}
if chat_id:
payload['chat_id'] = chat_id
if message_id:
payload['message_id'] = message_id
if inline_message_id:
payload['inline_message_id'] = inline_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload)
def send_venue(token, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None,
reply_to_message_id=None, reply_markup=None):
method_url = r'sendVenue'
@ -293,7 +370,7 @@ def send_chat_action(token, chat_id, action):
def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None):
method_url = r'sendVideo'
payload = {'chat_id': chat_id}
files = None
@ -309,6 +386,36 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if supports_streaming:
payload['supports_streaming'] = supports_streaming
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None, timeout=None):
method_url = r'sendAnimation'
payload = {'chat_id': chat_id}
files = None
if not util.is_string(data):
files = {'animation': data}
else:
payload['animation'] = data
if duration:
payload['duration'] = duration
if caption:
payload['caption'] = caption
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
@ -317,7 +424,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
parse_mode=None, disable_notification=None, timeout=None):
method_url = r'sendVoice'
payload = {'chat_id': chat_id}
files = None
@ -333,6 +440,8 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
@ -367,7 +476,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m
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):
reply_markup=None, parse_mode=None, disable_notification=None, timeout=None):
method_url = r'sendAudio'
payload = {'chat_id': chat_id}
files = None
@ -387,6 +496,8 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
@ -394,8 +505,8 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, disable_notification=None,
timeout=None, caption=None):
def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None,
disable_notification=None, timeout=None, caption=None):
method_url = get_method_by_type(data_type)
payload = {'chat_id': chat_id}
files = None
@ -407,6 +518,8 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode and data_type == 'document':
payload['parse_mode'] = parse_mode
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
@ -545,10 +658,11 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message
payload['disable_web_page_preview'] = disable_web_page_preview
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload)
return _make_request(token, method_url, params=payload, method='post')
def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None,
parse_mode=None, reply_markup=None):
method_url = r'editMessageCaption'
payload = {'caption': caption}
if chat_id:
@ -557,9 +671,26 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m
payload['message_id'] = message_id
if inline_message_id:
payload['inline_message_id'] = inline_message_id
if parse_mode:
payload['parse_mode'] = parse_mode
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload)
return _make_request(token, method_url, params=payload, method='post')
def edit_message_media(token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
method_url = r'editMessageMedia'
media_json, file = _convert_input_media(media)
payload = {'media': media_json}
if chat_id:
payload['chat_id'] = chat_id
if message_id:
payload['message_id'] = message_id
if inline_message_id:
payload['inline_message_id'] = inline_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload, files=file, method='post' if file else 'get')
def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
@ -573,13 +704,13 @@ def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_messa
payload['inline_message_id'] = inline_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload)
return _make_request(token, method_url, params=payload, method='post')
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)
return _make_request(token, method_url, params=payload, method='post')
# Game
@ -654,7 +785,7 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m
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):
disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None):
"""
Use this method to send invoices. On success, the sent Message is returned.
:param token: Bot's token (you don't need to fill this)
@ -678,7 +809,8 @@ def send_invoice(token, chat_id, title, description, invoice_payload, provider_t
: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:
:param provider_data:
:return:
"""
method_url = r'sendInvoice'
payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload,
@ -708,6 +840,8 @@ def send_invoice(token, chat_id, title, description, invoice_payload, provider_t
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if provider_data:
payload['provider_data'] = provider_data
return _make_request(token, method_url, params=payload)
@ -719,12 +853,12 @@ def answer_shipping_query(token, shipping_query_id, ok, shipping_options=None, e
: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:
: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)
payload['shipping_options'] = _convert_list_json_serializable(shipping_options)
if error_message:
payload['error_message'] = error_message
return _make_request(token, method_url, params=payload)
@ -737,7 +871,7 @@ def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_message=No
: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:
:return:
"""
method_url = 'answerPreCheckoutQuery'
payload = {'pre_checkout_query_id': pre_checkout_query_id, 'ok': ok}
@ -769,7 +903,7 @@ def answer_callback_query(token, callback_query_id, text=None, show_alert=None,
payload['show_alert'] = show_alert
if url:
payload['url'] = url
if cache_time:
if cache_time is not None:
payload['cache_time'] = cache_time
return _make_request(token, method_url, params=payload, method='post')
@ -778,7 +912,7 @@ def answer_inline_query(token, inline_query_id, results, cache_time=None, is_per
switch_pm_text=None, switch_pm_parameter=None):
method_url = 'answerInlineQuery'
payload = {'inline_query_id': inline_query_id, 'results': _convert_list_json_serializable(results)}
if cache_time:
if cache_time is not None:
payload['cache_time'] = cache_time
if is_personal:
payload['is_personal'] = is_personal
@ -843,6 +977,26 @@ def delete_sticker_from_set(token, sticker):
return _make_request(token, method_url, params=payload, method='post')
def send_poll(token, chat_id, question, options, disable_notifications=False, reply_to_message_id=None, reply_markup=None):
method_url = r'sendPoll'
payload = {'chat_id': str(chat_id), 'question': question, 'options': _convert_list_json_serializable(options)}
if disable_notifications:
payload['disable_notification'] = disable_notifications
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 stop_poll(token, chat_id, message_id, reply_markup=None):
method_url = r'stopPoll'
payload = {'chat_id': str(chat_id), 'message_id': message_id}
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload)
def _convert_list_json_serializable(results):
ret = ''
for r in results:
@ -859,10 +1013,29 @@ def _convert_markup(markup):
return markup
def _convert_input_media(media):
if isinstance(media, types.InputMedia):
return media._convert_input_media()
return None, None
def _convert_input_media_array(array):
media = []
files = {}
for input_media in array:
if isinstance(input_media, types.InputMedia):
media_dict = input_media.to_dic()
if media_dict['media'].startswith('attach://'):
key = media_dict['media'].replace('attach://', '')
files[key] = input_media.media
media.append(media_dict)
return json.dumps(media), files
def _no_encode(func):
def wrapper(key, val):
if key == 'filename':
return '{0}={1}'.format(key, val)
return u'{0}={1}'.format(key, val)
else:
return func(key, val)

View File

@ -10,7 +10,7 @@ import six
from telebot import util
class JsonSerializable:
class JsonSerializable(object):
"""
Subclasses of this class are guaranteed to be able to be converted to JSON format.
All subclasses of this class must override to_json.
@ -26,7 +26,7 @@ class JsonSerializable:
raise NotImplementedError
class Dictionaryable:
class Dictionaryable(object):
"""
Subclasses of this class are guaranteed to be able to be converted to dictionary.
All subclasses of this class must override to_dic.
@ -42,7 +42,7 @@ class Dictionaryable:
raise NotImplementedError
class JsonDeserializable:
class JsonDeserializable(object):
"""
Subclasses of this class are guaranteed to be able to be created from a json-style dict or json formatted string.
All subclasses of this class must override de_json.
@ -127,7 +127,6 @@ class Update(JsonDeserializable):
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
@ -177,14 +176,16 @@ class User(JsonDeserializable):
def de_json(cls, json_string):
obj = cls.check_json(json_string)
id = obj['id']
is_bot = obj['is_bot']
first_name = obj['first_name']
last_name = obj.get('last_name')
username = obj.get('username')
language_code = obj.get('language_code')
return cls(id, first_name, last_name, username, language_code)
return cls(id, is_bot, first_name, last_name, username, language_code)
def __init__(self, id, first_name, last_name=None, username=None, language_code=None):
def __init__(self, id, is_bot, first_name, last_name=None, username=None, language_code=None):
self.id = id
self.is_bot = is_bot
self.first_name = first_name
self.username = username
self.last_name = last_name
@ -220,11 +221,17 @@ class Chat(JsonDeserializable):
photo = ChatPhoto.de_json(obj['photo'])
description = obj.get('description')
invite_link = obj.get('invite_link')
pinned_message = None
if 'pinned_message' in obj:
pinned_message = Message.de_json(obj['pinned_message'])
sticker_set_name = obj.get('sticker_set_name')
can_set_sticker_set = obj.get('can_set_sticker_set')
return cls(id, type, title, username, first_name, last_name, all_members_are_administrators,
photo, description, invite_link)
photo, description, invite_link, pinned_message, sticker_set_name, can_set_sticker_set)
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):
all_members_are_administrators=None, photo=None, description=None, invite_link=None,
pinned_message=None, sticker_set_name=None, can_set_sticker_set=None):
self.type = type
self.last_name = last_name
self.first_name = first_name
@ -235,6 +242,9 @@ class Chat(JsonDeserializable):
self.photo = photo
self.description = description
self.invite_link = invite_link
self.pinned_message = pinned_message
self.sticker_set_name = sticker_set_name
self.can_set_sticker_set = can_set_sticker_set
class Message(JsonDeserializable):
@ -255,20 +265,31 @@ class Message(JsonDeserializable):
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_signature' in obj:
opts['forward_signature'] = obj.get('forward_signature')
if 'forward_date' in obj:
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:
opts['edit_date'] = obj.get('edit_date')
if 'media_group_id' in obj:
opts['media_group_id'] = obj.get('media_group_id')
if 'author_signature' in obj:
opts['author_signature'] = obj.get('author_signature')
if 'text' in obj:
opts['text'] = obj['text']
content_type = 'text'
if 'entities' in obj:
opts['entities'] = Message.parse_entities(obj['entities'])
if 'caption_entities' in obj:
opts['caption_entities'] = Message.parse_entities(obj['caption_entities'])
if 'audio' in obj:
opts['audio'] = Audio.de_json(obj['audio'])
content_type = 'audio'
if 'animation' in obj:
opts['animation'] = Animation.de_json(obj['animation'])
content_type = 'animation'
if 'document' in obj:
opts['document'] = Document.de_json(obj['document'])
content_type = 'document'
@ -316,29 +337,47 @@ class Message(JsonDeserializable):
content_type = 'left_chat_member'
if 'new_chat_title' in obj:
opts['new_chat_title'] = obj['new_chat_title']
content_type = 'new_chat_title'
if 'new_chat_photo' in obj:
opts['new_chat_photo'] = Message.parse_photo(obj['new_chat_photo'])
content_type = 'new_chat_photo'
if 'delete_chat_photo' in obj:
opts['delete_chat_photo'] = obj['delete_chat_photo']
content_type = 'delete_chat_photo'
if 'group_chat_created' in obj:
opts['group_chat_created'] = obj['group_chat_created']
content_type = 'group_chat_created'
if 'supergroup_chat_created' in obj:
opts['supergroup_chat_created'] = obj['supergroup_chat_created']
content_type = 'supergroup_chat_created'
if 'channel_chat_created' in obj:
opts['channel_chat_created'] = obj['channel_chat_created']
content_type = 'channel_chat_created'
if 'migrate_to_chat_id' in obj:
opts['migrate_to_chat_id'] = obj['migrate_to_chat_id']
content_type = 'migrate_to_chat_id'
if 'migrate_from_chat_id' in obj:
opts['migrate_from_chat_id'] = obj['migrate_from_chat_id']
content_type = 'migrate_from_chat_id'
if 'pinned_message' in obj:
opts['pinned_message'] = Message.de_json(obj['pinned_message'])
content_type = '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)
if 'connected_website' in obj:
opts['connected_website'] = obj['connected_website']
content_type = 'connected_website'
if 'poll' in obj:
opts['poll'] = Poll.de_json(obj['poll'])
content_type = 'poll'
if 'passport_data' in obj:
opts['passport_data'] = obj['passport_data']
content_type = 'passport_data'
return cls(message_id, from_user, date, chat, content_type, opts, json_string)
@classmethod
def parse_chat(cls, chat):
@ -361,19 +400,23 @@ class Message(JsonDeserializable):
ret.append(MessageEntity.de_json(me))
return ret
def __init__(self, message_id, from_user, date, chat, content_type, options):
def __init__(self, message_id, from_user, date, chat, content_type, options, json_string):
self.content_type = content_type
self.message_id = message_id
self.from_user = from_user
self.date = date
self.chat = chat
self.forward_from_chat = None
self.forward_from_message_id = None
self.forward_from = None
self.forward_date = None
self.reply_to_message = None
self.edit_date = None
self.media_group_id = None
self.author_signature = None
self.text = None
self.entities = None
self.caption_entities = None
self.audio = None
self.document = None
self.photo = None
@ -385,6 +428,7 @@ class Message(JsonDeserializable):
self.contact = None
self.location = None
self.venue = None
self.animation = None
self.new_chat_member = None
self.new_chat_members = None
self.left_chat_member = None
@ -399,8 +443,75 @@ class Message(JsonDeserializable):
self.pinned_message = None
self.invoice = None
self.successful_payment = None
self.connected_website = None
for key in options:
setattr(self, key, options[key])
self.json = json_string
def __html_text(self, text, entities):
"""
Author: @sviat9440
Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username"
Example:
message.html_text
>> "<b>Test</b> parse <i>formatting</i>, <a href=\"https://example.com\">url</a>, <a href=\"tg://user?id=123456\">text_mention</a> and mention @username"
Cusom subs:
You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity.
Example:
message.custom_subs = {"bold": "<strong class=\"example\">{text}</strong>", "italic": "<i class=\"example\">{text}</i>", "mention": "<a href={url}>{text}</a>"}
message.html_text
>> "<strong class=\"example\">Test</strong> parse <i class=\"example\">formatting</i>, <a href=\"https://example.com\">url</a> and <a href=\"tg://user?id=123456\">text_mention</a> and mention <a href=\"https://t.me/username\">@username</a>"
"""
if not entities:
return text
_subs = {
"bold" : "<b>{text}</b>",
"italic" : "<i>{text}</i>",
"pre" : "<pre>{text}</pre>",
"code" : "<code>{text}</code>",
"url" : "<a href=\"{url}\">{text}</a>",
"text_link": "<a href=\"{url}\">{text}</a>"
}
if hasattr(self, "custom_subs"):
for type in self.custom_subs:
_subs[type] = self.custom_subs[type]
utf16_text = text.encode("utf-16-le")
html_text = ""
def func(text, type=None, url=None, user=None):
text = text.decode("utf-16-le")
if type == "text_mention":
type = "url"
url = "tg://user?id={0}".format(user.id)
elif type == "mention":
url = "https://t.me/{0}".format(text[1:])
text = text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
if not type or not _subs.get(type):
return text
subs = _subs.get(type)
return subs.format(text=text, url=url)
offset = 0
for entity in entities:
if entity.offset > offset:
html_text += func(utf16_text[offset * 2 : entity.offset * 2])
offset = entity.offset
html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user)
offset += entity.length
if offset * 2 < len(utf16_text):
html_text += func(utf16_text[offset * 2:])
return html_text
@property
def html_text(self):
return self.__html_text(self.text, self.entities)
@property
def html_caption(self):
return self.__html_text(self.caption, self.caption_entities)
class MessageEntity(JsonDeserializable):
@ -500,29 +611,6 @@ class Document(JsonDeserializable):
self.file_size = file_size
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')
file_size = obj.get('file_size')
return cls(file_id, width, height, thumb, emoji, file_size)
def __init__(self, file_id, width, height, thumb, emoji=None, file_size=None):
self.file_id = file_id
self.width = width
self.height = height
self.thumb = thumb
self.emoji = emoji
self.file_size = file_size
class Video(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
@ -808,9 +896,30 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable):
return json_dict
class LoginUrl(JsonSerializable):
def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None):
self.url = url
self.forward_text = forward_text
self.bot_username = bot_username
self.request_write_access = request_write_access
def to_json(self):
return json.dumps(self.to_dic())
def to_dic(self):
json_dic = {'url': self.url}
if self.forward_text:
json_dic['forward_text'] = self.forward_text
if self.bot_username:
json_dic['bot_username'] = self.bot_username
if self.request_write_access:
json_dic['request_write_access'] = self.request_write_access
return json_dic
class InlineKeyboardButton(JsonSerializable):
def __init__(self, text, url=None, callback_data=None, switch_inline_query=None,
switch_inline_query_current_chat=None, callback_game=None, pay=None):
switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None):
self.text = text
self.url = url
self.callback_data = callback_data
@ -818,6 +927,7 @@ class InlineKeyboardButton(JsonSerializable):
self.switch_inline_query_current_chat = switch_inline_query_current_chat
self.callback_game = callback_game
self.pay = pay
self.login_url = login_url
def to_json(self):
return json.dumps(self.to_dic())
@ -836,6 +946,8 @@ class InlineKeyboardButton(JsonSerializable):
json_dic['callback_game'] = self.callback_game
if self.pay is not None:
json_dic['pay'] = self.pay
if self.login_url is not None:
json_dic['login_url'] = self.login_url.to_dic()
return json_dic
@ -972,12 +1084,15 @@ class InputTextMessageContent(Dictionaryable):
class InputLocationMessageContent(Dictionaryable):
def __init__(self, latitude, longitude):
def __init__(self, latitude, longitude, live_period=None):
self.latitude = latitude
self.longitude = longitude
self.live_period = live_period
def to_dic(self):
json_dic = {'latitude': self.latitude, 'longitude': self.longitude}
if self.live_period:
json_dic['live_period'] = self.live_period
return json_dic
@ -991,7 +1106,7 @@ class InputVenueMessageContent(Dictionaryable):
def to_dic(self):
json_dic = {'latitude': self.latitude, 'longitude': self.longitude, 'title': self.title,
'address': self.address}
'address' : self.address}
if self.foursquare_id:
json_dic['foursquare_id'] = self.foursquare_id
return json_dic
@ -1090,7 +1205,7 @@ class InlineQueryResultArticle(JsonSerializable):
class InlineQueryResultPhoto(JsonSerializable):
def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None, title=None,
description=None, caption=None, reply_markup=None, input_message_content=None):
description=None, caption=None, parse_mode=None, reply_markup=None, input_message_content=None):
"""
Represents a link to a photo.
:param id: Unique identifier for this result, 1-64 bytes
@ -1101,6 +1216,8 @@ class InlineQueryResultPhoto(JsonSerializable):
:param title: Title for the result.
:param description: Short description of the result.
:param caption: Caption of the photo to be sent, 0-200 characters.
:param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or
inline URLs in the media caption.
:param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message
:param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo
:return:
@ -1114,6 +1231,7 @@ class InlineQueryResultPhoto(JsonSerializable):
self.title = title
self.description = description
self.caption = caption
self.parse_mode = parse_mode
self.reply_markup = reply_markup
self.input_message_content = input_message_content
@ -1129,6 +1247,8 @@ class InlineQueryResultPhoto(JsonSerializable):
json_dict['description'] = self.description
if self.caption:
json_dict['caption'] = self.caption
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.reply_markup:
json_dict['reply_markup'] = self.reply_markup.to_dic()
if self.input_message_content:
@ -1185,7 +1305,7 @@ class InlineQueryResultGif(JsonSerializable):
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, mpeg4_duration=None):
parse_mode=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
@ -1195,6 +1315,8 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
:param mpeg4_height: Video height
:param title: Title for the result
:param caption: Caption of the MPEG-4 file to be sent, 0-200 characters
:param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text
or inline URLs in the media caption.
:param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message
:param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo
:return:
@ -1207,6 +1329,7 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
self.thumb_url = thumb_url
self.title = title
self.caption = caption
self.parse_mode = parse_mode
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.mpeg4_duration = mpeg4_duration
@ -1221,6 +1344,8 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
json_dict['title'] = self.title
if self.caption:
json_dict['caption'] = self.caption
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.reply_markup:
json_dict['reply_markup'] = self.reply_markup.to_dic()
if self.input_message_content:
@ -1232,8 +1357,8 @@ class InlineQueryResultMpeg4Gif(JsonSerializable):
class InlineQueryResultVideo(JsonSerializable):
def __init__(self, id, video_url, mime_type, thumb_url, title,
caption=None, video_width=None, video_height=None, video_duration=None, description=None,
reply_markup=None, input_message_content=None):
caption=None, parse_mode=None, video_width=None, video_height=None, video_duration=None,
description=None, reply_markup=None, input_message_content=None):
"""
Represents link to a page containing an embedded video player or a video file.
:param id: Unique identifier for this result, 1-64 bytes
@ -1241,6 +1366,8 @@ class InlineQueryResultVideo(JsonSerializable):
:param mime_type: Mime type of the content of video url, “text/html” or “video/mp4”
:param thumb_url: URL of the thumbnail (jpeg only) for the video
:param title: Title for the result
:param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or
inline URLs in the media caption.
:param video_width: Video width
:param video_height: Video height
:param video_duration: Video duration in seconds
@ -1257,6 +1384,7 @@ class InlineQueryResultVideo(JsonSerializable):
self.thumb_url = thumb_url
self.title = title
self.caption = caption
self.parse_mode = parse_mode
self.description = description
self.input_message_content = input_message_content
self.reply_markup = reply_markup
@ -1274,6 +1402,8 @@ class InlineQueryResultVideo(JsonSerializable):
json_dict['description'] = self.description
if self.caption:
json_dict['caption'] = self.caption
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.reply_markup:
json_dict['reply_markup'] = self.reply_markup.to_dic()
if self.input_message_content:
@ -1282,13 +1412,14 @@ class InlineQueryResultVideo(JsonSerializable):
class InlineQueryResultAudio(JsonSerializable):
def __init__(self, id, audio_url, title, caption=None, performer=None, audio_duration=None, reply_markup=None,
input_message_content=None):
def __init__(self, id, audio_url, title, caption=None, parse_mode=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.parse_mode = parse_mode
self.performer = performer
self.audio_duration = audio_duration
self.reply_markup = reply_markup
@ -1298,6 +1429,8 @@ class InlineQueryResultAudio(JsonSerializable):
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.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.performer:
json_dict['performer'] = self.performer
if self.audio_duration:
@ -1310,13 +1443,14 @@ class InlineQueryResultAudio(JsonSerializable):
class InlineQueryResultVoice(JsonSerializable):
def __init__(self, id, voice_url, title, caption=None, performer=None, voice_duration=None, reply_markup=None,
input_message_content=None):
def __init__(self, id, voice_url, title, caption=None, parse_mode=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.parse_mode = parse_mode
self.performer = performer
self.voice_duration = voice_duration
self.reply_markup = reply_markup
@ -1326,6 +1460,8 @@ class InlineQueryResultVoice(JsonSerializable):
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.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.performer:
json_dict['performer'] = self.performer
if self.voice_duration:
@ -1338,14 +1474,15 @@ class InlineQueryResultVoice(JsonSerializable):
class InlineQueryResultDocument(JsonSerializable):
def __init__(self, id, title, document_url, mime_type, caption=None, description=None, reply_markup=None,
input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None):
def __init__(self, id, title, document_url, mime_type, caption=None, parse_mode=None, description=None,
reply_markup=None, input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None):
self.type = 'document'
self.id = id
self.title = title
self.document_url = document_url
self.mime_type = mime_type
self.caption = caption
self.parse_mode = parse_mode
self.description = description
self.reply_markup = reply_markup
self.input_message_content = input_message_content
@ -1358,6 +1495,8 @@ class InlineQueryResultDocument(JsonSerializable):
'mime_type': self.mime_type}
if self.caption:
json_dict['caption'] = self.caption
if self.parse_mode:
json_dict['parse_mode'] = self.parse_mode
if self.description:
json_dict['description'] = self.description
if self.thumb_url:
@ -1374,13 +1513,14 @@ class InlineQueryResultDocument(JsonSerializable):
class InlineQueryResultLocation(JsonSerializable):
def __init__(self, id, title, latitude, longitude, reply_markup=None,
def __init__(self, id, title, latitude, longitude, live_period=None, reply_markup=None,
input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None):
self.type = 'location'
self.id = id
self.title = title
self.latitude = latitude
self.longitude = longitude
self.live_period = live_period
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.thumb_url = thumb_url
@ -1390,6 +1530,8 @@ class InlineQueryResultLocation(JsonSerializable):
def to_json(self):
json_dict = {'type': self.type, 'id': self.id, 'latitude': self.latitude, 'longitude': self.longitude,
'title': self.title}
if self.live_period:
json_dict['live_period'] = self.live_period
if self.thumb_url:
json_dict['thumb_url'] = self.thumb_url
if self.thumb_width:
@ -1477,6 +1619,7 @@ class BaseInlineQueryResultCached(JsonSerializable):
self.caption = None
self.reply_markup = None
self.input_message_content = None
self.parse_mode = None
self.payload_dic = {}
def to_json(self):
@ -1493,12 +1636,14 @@ class BaseInlineQueryResultCached(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.parse_mode:
json_dict['parse_mode'] = self.parse_mode
return json.dumps(json_dict)
class InlineQueryResultCachedPhoto(BaseInlineQueryResultCached):
def __init__(self, id, photo_file_id, title=None, description=None, caption=None, reply_markup=None,
input_message_content=None):
def __init__(self, id, photo_file_id, title=None, description=None, caption=None, parse_mode=None,
reply_markup=None, input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'photo'
self.id = id
@ -1508,11 +1653,12 @@ class InlineQueryResultCachedPhoto(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['photo_file_id'] = photo_file_id
class InlineQueryResultCachedGif(BaseInlineQueryResultCached):
def __init__(self, id, gif_file_id, title=None, description=None, caption=None, reply_markup=None,
def __init__(self, id, gif_file_id, title=None, description=None, caption=None, parse_mode=None, reply_markup=None,
input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'gif'
@ -1523,12 +1669,13 @@ class InlineQueryResultCachedGif(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['gif_file_id'] = gif_file_id
class InlineQueryResultCachedMpeg4Gif(BaseInlineQueryResultCached):
def __init__(self, id, mpeg4_file_id, title=None, description=None, caption=None, reply_markup=None,
input_message_content=None):
def __init__(self, id, mpeg4_file_id, title=None, description=None, caption=None, parse_mode=None,
reply_markup=None, input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'mpeg4_gif'
self.id = id
@ -1538,6 +1685,7 @@ class InlineQueryResultCachedMpeg4Gif(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['mpeg4_file_id'] = mpeg4_file_id
@ -1553,7 +1701,7 @@ class InlineQueryResultCachedSticker(BaseInlineQueryResultCached):
class InlineQueryResultCachedDocument(BaseInlineQueryResultCached):
def __init__(self, id, document_file_id, title, description=None, caption=None, reply_markup=None,
def __init__(self, id, document_file_id, title, description=None, caption=None, parse_mode=None, reply_markup=None,
input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'document'
@ -1564,11 +1712,12 @@ class InlineQueryResultCachedDocument(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['document_file_id'] = document_file_id
class InlineQueryResultCachedVideo(BaseInlineQueryResultCached):
def __init__(self, id, video_file_id, title, description=None, caption=None, reply_markup=None,
def __init__(self, id, video_file_id, title, description=None, caption=None, parse_mode=None, reply_markup=None,
input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'video'
@ -1579,11 +1728,13 @@ class InlineQueryResultCachedVideo(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['video_file_id'] = video_file_id
class InlineQueryResultCachedVoice(BaseInlineQueryResultCached):
def __init__(self, id, voice_file_id, title, caption=None, reply_markup=None, input_message_content=None):
def __init__(self, id, voice_file_id, title, caption=None, parse_mode=None, reply_markup=None,
input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'voice'
self.id = id
@ -1592,11 +1743,12 @@ class InlineQueryResultCachedVoice(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['voice_file_id'] = voice_file_id
class InlineQueryResultCachedAudio(BaseInlineQueryResultCached):
def __init__(self, id, audio_file_id, caption=None, reply_markup=None, input_message_content=None):
def __init__(self, id, audio_file_id, caption=None, parse_mode=None, reply_markup=None, input_message_content=None):
BaseInlineQueryResultCached.__init__(self)
self.type = 'audio'
self.id = id
@ -1604,6 +1756,7 @@ class InlineQueryResultCachedAudio(BaseInlineQueryResultCached):
self.caption = caption
self.reply_markup = reply_markup
self.input_message_content = input_message_content
self.parse_mode = parse_mode
self.payload_dic['audio_file_id'] = audio_file_id
@ -1780,16 +1933,17 @@ class ShippingOption(JsonSerializable):
def add_price(self, *args):
"""
Add LabeledPrice to ShippingOption
:param args: LabeledPrices
:param args: LabeledPrices
"""
for price in args:
self.prices.append(price)
return self
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}
json_dict = json.dumps({'id': self.id, 'title': self.title, 'prices': price_list})
return json_dict
@ -1890,6 +2044,7 @@ class Sticker(JsonDeserializable):
file_id = obj['file_id']
width = obj['width']
height = obj['height']
is_animated = obj['is_animated']
thumb = None
if 'thumb' in obj:
thumb = PhotoSize.de_json(obj['thumb'])
@ -1899,9 +2054,9 @@ class Sticker(JsonDeserializable):
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)
return cls(file_id, width, height, thumb, emoji, set_name, mask_position, file_size, is_animated)
def __init__(self, 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, is_animated):
self.file_id = file_id
self.width = width
self.height = height
@ -1910,7 +2065,7 @@ class Sticker(JsonDeserializable):
self.set_name = set_name
self.mask_position = mask_position
self.file_size = file_size
self.is_animated = is_animated
class MaskPosition(JsonDeserializable, JsonSerializable):
@classmethod
@ -1934,3 +2089,169 @@ class MaskPosition(JsonDeserializable, JsonSerializable):
def to_dic(self):
return {'point': self.point, 'x_shift': self.x_shift, 'y_shift': self.y_shift, 'scale': self.scale}
# InputMedia
class InputMedia(JsonSerializable):
def __init__(self, type, media, caption=None, parse_mode=None):
self.type = type
self.media = media
self.caption = caption
self.parse_mode = parse_mode
if util.is_string(self.media):
self._media_name = ''
self._media_dic = self.media
else:
self._media_name = util.generate_random_token()
self._media_dic = 'attach://{0}'.format(self._media_name)
def to_json(self):
return json.dumps(self.to_dic())
def to_dic(self):
ret = {'type': self.type, 'media': self._media_dic}
if self.caption:
ret['caption'] = self.caption
if self.parse_mode:
ret['parse_mode'] = self.parse_mode
return ret
def _convert_input_media(self):
if util.is_string(self.media):
return self.to_json(), None
return self.to_json(), {self._media_name: self.media}
class InputMediaPhoto(InputMedia):
def __init__(self, media, caption=None, parse_mode=None):
super(InputMediaPhoto, self).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode)
def to_dic(self):
ret = super(InputMediaPhoto, self).to_dic()
return ret
class InputMediaVideo(InputMedia):
def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None,
supports_streaming=None):
super(InputMediaVideo, self).__init__(type="video", media=media, caption=caption, parse_mode=parse_mode)
self.thumb = thumb
self.width = width
self.height = height
self.duration = duration
self.supports_streaming = supports_streaming
def to_dic(self):
ret = super(InputMediaVideo, self).to_dic()
if self.thumb:
ret['thumb'] = self.thumb
if self.width:
ret['width'] = self.width
if self.height:
ret['height'] = self.height
if self.duration:
ret['duration'] = self.duration
if self.supports_streaming:
ret['supports_streaming'] = self.supports_streaming
return ret
class InputMediaAnimation(InputMedia):
def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None):
super(InputMediaAnimation, self).__init__(type="animation", media=media, caption=caption, parse_mode=parse_mode)
self.thumb = thumb
self.width = width
self.height = height
self.duration = duration
def to_dic(self):
ret = super(InputMediaAnimation, self).to_dic()
if self.thumb:
ret['thumb'] = self.thumb
if self.width:
ret['width'] = self.width
if self.height:
ret['height'] = self.height
if self.duration:
ret['duration'] = self.duration
return ret
class InputMediaAudio(InputMedia):
def __init__(self, media, thumb=None, caption=None, parse_mode=None, duration=None, performer=None, title=None):
super(InputMediaAudio, self).__init__(type="audio", media=media, caption=caption, parse_mode=parse_mode)
self.thumb = thumb
self.duration = duration
self.performer = performer
self.title = title
def to_dic(self):
ret = super(InputMediaAudio, self).to_dic()
if self.thumb:
ret['thumb'] = self.thumb
if self.duration:
ret['duration'] = self.duration
if self.performer:
ret['performer'] = self.performer
if self.title:
ret['title'] = self.title
return ret
class InputMediaDocument(InputMedia):
def __init__(self, media, thumb=None, caption=None, parse_mode=None):
super(InputMediaDocument, self).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode)
self.thumb = thumb
def to_dic(self):
ret = super(InputMediaDocument, self).to_dic()
if self.thumb:
ret['thumb'] = self.thumb
return ret
class PollOption(JsonSerializable, JsonDeserializable):
@classmethod
def de_json(cls, json_type):
obj = cls.check_json(json_type)
text = obj['text']
voter_count = int(obj['voter_count'])
option = cls(text)
option.voter_count = voter_count
return option
def __init__(self, text):
self.text = text
self.voter_count = 0
def to_json(self):
return json.dumps(self.text)
class Poll(JsonDeserializable):
@classmethod
def de_json(cls, json_type):
obj = cls.check_json(json_type)
poll_id = obj['id']
question = obj['question']
poll = cls(question)
options = []
for opt in obj['options']:
options.append(PollOption.de_json(opt))
poll.options = options
is_closed = obj['is_closed']
poll.id = poll_id
poll.is_closed = is_closed
return poll
def __init__(self, question):
self.options = []
self.question = question
def add(self, option):
if type(option) is PollOption:
self.options.append(option)
else:
self.options.append(PollOption(option))

View File

@ -1,9 +1,11 @@
# -*- coding: utf-8 -*-
import random
import re
import string
import sys
import threading
import traceback
import re
import sys
import six
from six import string_types
@ -13,9 +15,9 @@ try:
import Queue
except ImportError:
import queue as Queue
import logging
from telebot import logger
logger = logging.getLogger('TeleBot')
thread_local = threading.local()
@ -142,7 +144,7 @@ class AsyncTask:
return self.result
def async():
def async_dec():
def decorator(fn):
def wrapper(*args, **kwargs):
return AsyncTask(fn, *args, **kwargs)
@ -242,15 +244,18 @@ def extract_arguments(text):
:param text: String to extract the arguments from a command
:return: the arguments if `text` is a command (according to is_command), else None.
"""
regexp = re.compile("\/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE)
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:
def per_thread(key, construct_value, reset=False):
if reset or not hasattr(thread_local, key):
value = construct_value()
setattr(thread_local, key, value)
return value
return getattr(thread_local, key)
def generate_random_token():
return ''.join(random.sample(string.ascii_letters, 16))

View File

@ -201,7 +201,8 @@ class TestTeleBot:
def test_send_audio_dis_noti(self):
file_data = open('./test_data/record.mp3', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='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'
@ -360,6 +361,20 @@ class TestTeleBot:
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_edit_message_media(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
file_data_2 = open('../examples/detailed_example/rooster.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
msg = tb.send_photo(CHAT_ID, file_data)
new_msg = tb.edit_message_media(chat_id=CHAT_ID, message_id=msg.message_id,
media=types.InputMediaPhoto(file_data_2, caption='Test editMessageMedia 0'))
assert type(new_msg) != bool
new_msg = tb.edit_message_media(chat_id=CHAT_ID, message_id=msg.message_id,
media=types.InputMediaPhoto(msg.photo[0].file_id, caption='Test editMessageMedia'))
assert type(new_msg) != bool
assert new_msg.caption == 'Test editMessageMedia'
def test_get_chat(self):
tb = telebot.TeleBot(TOKEN)
ch = tb.get_chat(GROUP_ID)
@ -387,10 +402,11 @@ class TestTeleBot:
new_msg = tb.edit_message_reply_markup(chat_id=CHAT_ID, message_id=ret_msg.message_id, reply_markup=markup)
assert new_msg.message_id
def create_text_message(self, text):
@staticmethod
def create_text_message(text):
params = {'text': text}
chat = types.User(11, 'test')
return types.Message(1, None, None, chat, 'text', params)
chat = types.User(11, False, 'test')
return types.Message(1, None, None, chat, 'text', params, "")
def test_is_string_unicode(self):
s1 = u'string'
@ -409,3 +425,67 @@ class TestTeleBot:
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_video_note(CHAT_ID, file_data)
assert ret_msg.message_id
def test_send_media_group(self):
tb = telebot.TeleBot(TOKEN)
img1 = 'https://i.imgur.com/CjXjcnU.png'
img2 = 'https://i.imgur.com/CjXjcnU.png'
medias = [types.InputMediaPhoto(img1, "View"), types.InputMediaPhoto(img2, "Dog")]
result = tb.send_media_group(CHAT_ID, medias)
assert len(result) == 2
assert result[0].media_group_id is not None
assert result[0].media_group_id == result[1].media_group_id
def test_send_media_group_local_files(self):
photo = open('../examples/detailed_example/kitten.jpg', 'rb')
video = open('./test_data/test_video.mp4', 'rb')
tb = telebot.TeleBot(TOKEN)
medias = [types.InputMediaPhoto(photo, "View"),
types.InputMediaVideo(video)]
result = tb.send_media_group(CHAT_ID, medias)
assert len(result) == 2
assert result[0].media_group_id is not None
assert result[1].media_group_id is not None
def test_send_photo_formating_caption(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_photo(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
assert ret_msg.caption_entities[0].type == 'italic'
def test_send_video_formatting_caption(self):
file_data = open('./test_data/test_video.mp4', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_video(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
assert ret_msg.caption_entities[0].type == 'italic'
def test_send_audio_formatting_caption(self):
file_data = open('./test_data/record.mp3', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_audio(CHAT_ID, file_data, caption='<b>bold</b>', parse_mode='HTML')
assert ret_msg.caption_entities[0].type == 'bold'
def test_send_voice_formatting_caprion(self):
file_data = open('./test_data/record.ogg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_voice(CHAT_ID, file_data, caption='<b>bold</b>', parse_mode='HTML')
assert ret_msg.caption_entities[0].type == 'bold'
assert ret_msg.voice.mime_type == 'audio/ogg'
def test_send_media_group_formatting_caption(self):
tb = telebot.TeleBot(TOKEN)
img1 = 'https://i.imgur.com/CjXjcnU.png'
img2 = 'https://i.imgur.com/CjXjcnU.png'
medias = [types.InputMediaPhoto(img1, "*View*", parse_mode='Markdown'),
types.InputMediaPhoto(img2, "_Dog_", parse_mode='Markdown')]
result = tb.send_media_group(CHAT_ID, medias)
assert len(result) == 2
assert result[0].media_group_id is not None
assert result[0].caption_entities[0].type == 'bold'
assert result[1].caption_entities[0].type == 'italic'
def test_send_document_formating_caption(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_document(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
assert ret_msg.caption_entities[0].type == 'italic'

View File

@ -6,19 +6,19 @@ from telebot import types
def test_json_user():
jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","username":"rdss_bot"}'
jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","username":"rdss_bot","is_bot":true}'
u = types.User.de_json(jsonstring)
assert u.id == 101176298
def test_json_message():
jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir"},"chat":{"id":1734,"first_name":"F","type":"private","last_name":"Wa","username":"oir"},"date":1435296025,"text":"HIHI"}'
jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir","is_bot":true},"chat":{"id":1734,"first_name":"F","type":"private","last_name":"Wa","username":"oir"},"date":1435296025,"text":"HIHI"}'
msg = types.Message.de_json(jsonstring)
assert msg.text == 'HIHI'
def test_json_message_group():
json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG"},"chat":{"id":-866,"type":"private","title":"\u4ea4"},"date":1435303157,"text":"HIHI"}'
json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG","is_bot":true},"chat":{"id":-866,"type":"private","title":"\u4ea4"},"date":1435303157,"text":"HIHI"}'
msg = types.Message.de_json(json_string)
assert msg.text == 'HIHI'
assert len(msg.chat.title) != 0
@ -39,7 +39,7 @@ def test_json_Document():
def test_json_Message_Audio():
json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd"},"chat":{"id":10834,"first_name":"dd","type":"private","type":"private","last_name":"dd","username":"dd"},"date":1439978364,"audio":{"duration":1,"mime_type":"audio\/mpeg","title":"pyTelegram","performer":"eternnoir","file_id":"BQADBQADDH1JaB8-1KyWUss2-Ag","file_size":20096}}'
json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd","is_bot":true },"chat":{"id":10834,"first_name":"dd","type":"private","type":"private","last_name":"dd","username":"dd"},"date":1439978364,"audio":{"duration":1,"mime_type":"audio\/mpeg","title":"pyTelegram","performer":"eternnoir","file_id":"BQADBQADDH1JaB8-1KyWUss2-Ag","file_size":20096}}'
msg = types.Message.de_json(json_string)
assert msg.audio.duration == 1
assert msg.content_type == 'audio'
@ -48,7 +48,7 @@ def test_json_Message_Audio():
def test_json_Message_Sticker():
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"is_animated":true,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
msg = types.Message.de_json(json_string)
assert msg.sticker.height == 368
assert msg.sticker.thumb.height == 60
@ -56,29 +56,29 @@ def test_json_Message_Sticker():
def test_json_Message_Sticker_without_thumb():
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"is_animated":true,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
msg = types.Message.de_json(json_string)
assert msg.sticker.height == 368
assert msg.sticker.thumb == None
assert msg.sticker.thumb is None
assert msg.content_type == 'sticker'
def test_json_Message_Document():
json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}}'
json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}}'
msg = types.Message.de_json(json_string)
assert msg.document.file_name == 'Text File'
assert msg.content_type == 'document'
def test_json_Message_Photo():
json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_size":53013,"width":759,"height":570}]}'
json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_size":53013,"width":759,"height":570}]}'
msg = types.Message.de_json(json_string)
assert len(msg.photo) == 3
assert msg.content_type == 'photo'
def test_json_Message_Video():
json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":109734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_size":260699}}'
json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd","is_bot":true },"chat":{"id":109734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_size":260699}}'
msg = types.Message.de_json(json_string)
assert msg.video
assert msg.video.duration == 3
@ -87,7 +87,7 @@ def test_json_Message_Video():
def test_json_Message_Location():
json_string = r'{"message_id":102,"from":{"id":108734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":1089734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1535482469,"location":{"longitude":127.479471,"latitude":26.090577}}'
json_string = r'{"message_id":102,"from":{"id":108734,"first_name":"dd","last_name":"dd","username":"dd","is_bot":true },"chat":{"id":1089734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1535482469,"location":{"longitude":127.479471,"latitude":26.090577}}'
msg = types.Message.de_json(json_string)
assert msg.location.latitude == 26.090577
assert msg.content_type == 'location'
@ -113,13 +113,15 @@ def test_json_voice():
assert voice.duration == 0
assert voice.file_size == 10481
def test_json_update():
json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"id":9734,"first_name":"Fk","last_name":"Wg","username":"nir"},"chat":{"id":1111,"first_name":"Fk","type":"private","last_name":"Wg","username":"oir"},"date":1441447009,"text":"HIHI"}}'
json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"is_bot":true,"id":9734,"first_name":"Fk","last_name":"Wg","username":"nir"},"chat":{"id":1111,"first_name":"Fk","type":"private","last_name":"Wg","username":"oir"},"date":1441447009,"text":"HIHI"}}'
update = types.Update.de_json(json_string)
assert update.update_id == 938203
assert update.message.message_id == 241
assert update.message.from_user.id == 9734
def test_json_chat():
json_string = r'{"id": -111111,"title": "Test Title","type": "group"}'
chat = types.Chat.de_json(json_string)
@ -127,6 +129,7 @@ def test_json_chat():
assert chat.type == 'group'
assert chat.title == 'Test Title'
def test_InlineQueryResultCachedPhoto():
iq = types.InlineQueryResultCachedPhoto('aaa', 'Fileid')
json_str = iq.to_json()
@ -143,6 +146,7 @@ def test_InlineQueryResultCachedPhoto_with_title():
assert 'Title' in json_str
assert 'caption' not in json_str
def test_InlineQueryResultCachedPhoto_with_markup():
markup = types.InlineKeyboardMarkup()
markup.add(types.InlineKeyboardButton("Google", url="http://www.google.com"))
@ -154,4 +158,3 @@ def test_InlineQueryResultCachedPhoto_with_markup():
assert 'Title' in json_str
assert 'caption' not in json_str
assert 'reply_markup' in json_str