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

Compare commits

..

224 Commits
3.6.6 ... 3.6.8

Author SHA1 Message Date
e9f925e14c Merge pull request #906 from daveusa31/master
Added ability to set default parse_mode in main TeleBot class.
2020-07-04 22:29:32 +03:00
0304e6507f Append default parse_mode 2020-07-04 21:07:42 +03:00
0f387db8d2 Fix tabs 2020-07-04 20:45:48 +03:00
30664f396a Merge pull request #904 from timbyxty/master
Added NST bot to the bot list
2020-07-04 00:17:43 +03:00
cdffeba829 added NST 2020-07-03 22:05:47 +05:00
8e82d1c462 Merge pull request #902 from 0xnu/patch-1
Update README.md
2020-07-02 16:08:22 +03:00
b5a4276282 Update README.md
Link to Sports bot added.
2020-07-02 13:27:51 +01:00
d43292e42b Merge pull request #892 from Otxoto/Otxoto-patch-2
Added thumbnail support for send_audio and send_video
2020-06-24 19:10:15 +03:00
99de5490a0 Added thumb parameter to send_video 2020-06-23 20:17:21 +02:00
53ccef5e5e added thumb parameter to send_video 2020-06-23 20:14:52 +02:00
29b432e65a Added thumb to send_audio 2020-06-23 20:12:46 +02:00
4f4c0891d9 Added thumb support to send_audio 2020-06-23 20:10:12 +02:00
03b1531bd7 Merge pull request #890 from Otxoto/master
Update User type
Added:
can_join_groups
can_read_all_group_messages
supports_inline_queries
2020-06-22 23:14:45 +03:00
ab496f995e Merge pull request #1 from Otxoto/Otxoto-patch-1
Complete User Class
2020-06-22 13:18:42 +02:00
44872ce87d Complete User Class
Added following fields:
can_join_groups
can_read_all_group_messages
supports_inline_queries
2020-06-22 13:18:13 +02:00
c24d1e2d0b Update README.md 2020-06-07 02:50:30 +08:00
38694a9173 Merge pull request #865 from Badiboy/master
Update test_telebot.py
2020-05-29 12:43:24 +03:00
1494946d02 Update test_telebot.py
Build fix.
2020-05-29 12:37:23 +03:00
5facf7de92 Merge pull request #808 from pikss86/master
Add possibility to create and use custom session, for examle - torpy
2020-05-29 12:17:58 +03:00
f7008d4d99 Merge pull request #856 from Badiboy/master
send_chat_permissions fix
2020-05-20 12:03:05 +03:00
32dc03ec44 send_chat_permissions fix 2020-05-20 11:57:41 +03:00
dbff7cbb3e Merge pull request #852 from olegshek/keyboard_button_poll_type
Keyboard button poll type
2020-05-18 11:23:02 +03:00
27e2cbc7ea Remove unnecessary test 2020-05-18 12:22:26 +05:00
592dcbfedf Add PollAnswer type;
Add poll_answer_handler
2020-05-18 12:05:30 +05:00
03b02561a5 Add request_poll attribute to KeyboardButton;
Add KeyboardButtonPollType object
2020-05-18 11:48:54 +05:00
783fe56566 Merge pull request #848 from Badiboy/master
Correct processing of bool parameters. Some timeouts added.
2020-05-16 17:45:30 +03:00
2368421332 Correct processing of bool parameters. Some timeouts added. 2020-05-16 17:34:56 +03:00
046276b491 Merge pull request #841 from drforse/master
fix test 1
2020-05-12 21:02:01 +03:00
3de8140c0b fix test 1 2020-05-12 18:29:36 +01:00
e5ad9ab383 Merge pull request #840 from drforse/master
fix test
2020-05-12 20:21:40 +03:00
d04e708438 fix test 2020-05-12 18:09:04 +01:00
200c6ccd07 Merge pull request #839 from drforse/master
add PollAnswer, poll_answer_handler
2020-05-12 19:56:22 +03:00
75a018e18b add PollAnswer, poll_answer_handler; make User Serializable and Dictionaryble; some pep fixes 2020-05-12 01:09:34 +01:00
aacc494a55 Merge pull request #834 from tohabyuraev/master
Bot API 4.5 partial support (in regards to chat).
2020-05-11 23:11:11 +03:00
ee00d0458d Fix some bugs 2020-05-11 22:26:03 +03:00
a60253bf60 UPG: Add ChatPermissions, set_chat_permissions 2020-05-11 16:38:09 +03:00
a80927baf9 UPG: add setChatAdministratorCustomTitle 2020-05-09 23:23:08 +03:00
8be9bcc8ed UPG: Add custom_title, slow_mode_delay 2020-05-09 20:28:29 +03:00
1824637617 UPG: Refactoring InlineKeyboardMarkup 2020-05-09 20:06:33 +03:00
df640966c2 Merge pull request #831 from Badiboy/master
Fix to_dic->to_dict refactoring
2020-05-09 00:54:22 +03:00
2849e67029 Fix to_dic->to_dict refactoring 2020-05-09 00:51:18 +03:00
d02de07142 Merge pull request #830 from tohabyuraev/newcon
Add BotCommand, setMyCommands
2020-05-08 22:06:24 +03:00
a56fb8cc54 UPG: Add BotCommand, setMyCommands 2020-05-08 21:06:39 +03:00
c5e5af96d1 Merge pull request #827 from CSRedRat/patch-1
Add handler decorator example
2020-05-07 18:01:39 +03:00
5d388f7ec4 Add handler decorator example 2020-05-07 19:56:17 +05:00
6c45511605 Update version 3.7.1 2020-05-02 19:41:46 +08:00
d8a08638a7 Merge pull request #821 from Badiboy/master
send_poll revised to standart signature
2020-05-02 13:35:34 +03:00
e2d70da694 Fix poll options serialization 2020-05-02 13:27:39 +03:00
6e1cf24946 send_poll revised to standart signature 2020-05-02 13:09:52 +03:00
be0fe94ee8 Merge pull request #819 from Badiboy/master
Fix Deprecation warning due to invalid escape sequences
2020-05-01 11:51:45 +03:00
ef81868ebc Fix Deprecation warning due to invalid escape sequences 2020-05-01 11:25:51 +03:00
57fb8d2fad Bump version. 3.7.0 2020-04-28 19:18:44 +08:00
c2590ab5ed Merge pull request #815 from bedilbek/remove-type-hinting
Remove type hinting for python 3.5 compatibility
2020-04-27 22:48:04 +03:00
24deb8a51d Change class from new-style class to object class 2020-04-28 00:34:52 +05:00
d7ebaa5bb3 Fix importing dependencies 2020-04-28 00:24:47 +05:00
601b570b85 Fix python2.7 compatibility for class inheritance 2020-04-28 00:22:05 +05:00
bdaabc4752 Merge pull request #814 from Badiboy/master
Travis update: remove Python 2
2020-04-27 22:01:37 +03:00
72d088940c Readme content fix 2020-04-27 22:00:20 +03:00
f1a960c56b Travis update: remove Python 2 2020-04-27 21:56:37 +03:00
bcc3a1afb4 Remove Type Hinting 2020-04-27 23:43:39 +05:00
d0edf44774 Merge pull request #813 from drforse/patch-1
Fix Dice test
2020-04-27 18:36:17 +03:00
9c87ed3679 fix test 2020-04-27 16:20:30 +01:00
67cfa04737 Merge pull request #812 from Badiboy/master
Disable REDIS tests to save Travis
2020-04-27 17:47:30 +03:00
be5d7bb73d Disable REDIS tests to save Travis
To enable REDIS set
test_handler_backeds.REDIS_TEST = True
before running tests.
2020-04-27 17:46:19 +03:00
f3a65ef9b3 Merge pull request #811 from drforse/master
update dice as the api has updated
2020-04-27 17:14:39 +03:00
99c63e9eba add emoji field for dice 2020-04-27 06:30:05 +01:00
e89a552e06 Merge pull request #810 from Badiboy/master
Polls are updated to the latest API state
2020-04-26 23:23:15 +03:00
bb4f6a7190 Polls are updated to the latest API state.
Polls are updated to the latest API state.

Minor code refactoring.
2020-04-25 22:22:08 +03:00
197dd2a582 add requests session for use torpy 2020-04-24 23:30:10 +05:00
dc3df70f9f Merge pull request #807 from Badiboy/master
Minor code refactoring (naming)
2020-04-24 18:21:54 +03:00
aac9ce45a3 Merge remote-tracking branch 'upstream/master' 2020-04-24 18:19:55 +03:00
24e984adf8 Minor code refactoring (naming) 2020-04-24 18:19:30 +03:00
1ed3bc2a53 Merge pull request #803 from noideaw/patch-2
added can_invite_users parameter to restrict_chat_member function param description
2020-04-24 18:16:25 +03:00
ce11b6f523 Merge pull request #806 from noideaw/patch-1
added can_invite_users parameter to restrict_chat_member function
2020-04-24 18:15:05 +03:00
8c7c7b31b2 Update __init__.py
added can_invite_users parameter to restrict_chat_member function
2020-04-24 19:38:23 +04:30
39e3be6673 Merge pull request #804 from noideaw/patch-3
Add can_invite_users to restrict_chat_member #1
2020-04-24 01:34:58 +03:00
b1b2726ef6 Update apihelper.py
added can_invite_users parameter to restrict_chat_member function
2020-04-24 00:21:05 +04:30
da924dbaeb Update __init__.py
added can_invite_users parameter to restrict_chat_member function
2020-04-23 23:59:04 +04:30
7966def331 Merge pull request #801 from bedilbek/fix-middleware-invocations
Fix not needed invocations on typed middleware handlers
2020-04-20 11:10:04 +03:00
aab560b4ee Fix all the time invocations on typed_middleware handlers even if update did not have that update_type message 2020-04-20 11:30:03 +05:00
646bbb8330 Merge pull request #794 from drforse/master
Add Dice and send_dice
2020-04-16 15:56:46 +03:00
339a5c01c1 Merge pull request #789 from bedilbek/step-handler-backend
HandlerBackend Mechanism Implementation
Codebase Refactoring
2020-04-16 15:53:11 +03:00
615402e4f8 return a line as it was 2020-04-15 06:16:07 +01:00
51b1fb7695 added Dice and send_dice 2020-04-15 06:10:05 +01:00
0881e34381 Refactor tests 2020-04-15 01:11:43 +05:00
3aec66bc0d Remove class static variable 2020-04-15 01:11:43 +05:00
e7e7c58133 Add Memory, File, Redis Backend tests 2020-04-15 01:11:43 +05:00
003c5db37f Add filename checking 2020-04-15 01:11:43 +05:00
286188f380 Add Step/Reply Handler Backend Mechanism
Implement Memory, File, Redis Backends
2020-04-15 01:11:43 +05:00
dd726b0759 Merge pull request #792 from Badiboy/master
Refactoring and API conformance
2020-04-14 22:22:48 +03:00
1bd9f5187c Merge pull request #787 from bedilbek/middleware-handler-readme
Middleware Handler README Update
2020-04-12 02:20:05 +03:00
68330c9a07 Update contents with middleware handler 2020-04-12 01:44:15 +05:00
b912e4dbaf Update with middleware handler 2020-04-12 01:41:34 +05:00
dab80d421b Refactoring update 2 2020-04-11 17:38:47 +03:00
247fe6e947 Refactoring bugfix 2020-04-11 17:06:14 +03:00
995814d846 Refactoring and API conformance
Refactoring.

new_chat_member is out of support.

Bugfix in html_text.

Started Bot API conformance checking.
2020-04-11 16:54:25 +03:00
36a228da92 Merge pull request #786 from Badiboy/master
Possibility to use alternative serializer
2020-04-11 16:52:53 +03:00
ec86182f62 Possibility to use alternative serializer
With apihelper.CUSTOM_SERIALIZER you can replace pickle with other "dumper" like dill.
2020-04-11 13:42:34 +03:00
2c385bf077 Merge pull request #785 from bedilbek/middleware-support
Add Middleware Handler
2020-04-11 11:20:58 +03:00
56cbc2ff93 Add examples to better understand middleware handler 2020-04-11 13:03:52 +05:00
932ac9477b Add ENABLE_MIDDLEWARE=False in apihelpers to keep backward compatibility 2020-04-11 13:02:50 +05:00
1e242f2263 Add Middleware support 2020-04-08 23:13:19 +05:00
862f17c716 Merge pull request #776 from irevenko/master
Add new bot in README
2020-03-28 01:15:50 +08:00
ed7cf30034 Add new bot in README 2020-03-27 16:38:22 +02:00
100f6d77f6 Merge pull request #767 from Tkachov/master
Add @bot.poll_handler to be notified of new poll states
2020-03-09 13:58:23 +03:00
d2f9c51a5a Handle Poll update 2020-03-09 17:25:54 +07:00
12547efa08 Fix order for consistency in process_new_updates 2020-03-09 17:25:37 +07:00
9410a3d310 Merge pull request #762 from Lin-Buo-Ren/patch-4
Use proper language for * query mentions in README
2020-03-01 21:14:32 +03:00
a4e5a09ab2 Use proper language for * query mentions in README
Signed-off-by: 林博仁(Buo-ren, Lin) <Buo.Ren.Lin@gmail.com>
2020-02-27 16:49:28 +08:00
ebfba49a8f Merge pull request #761 from Lin-Buo-Ren/patch-3
Fix missing padding space in code sample comments in README
2020-02-27 11:45:59 +03:00
6e6420a331 Merge pull request #760 from HolidayMan/patch-1
Some clarification of custom listeners working
2020-02-27 11:45:04 +03:00
7ca629dc10 Fix missing padding space in code sample comments in README
Signed-off-by: 林博仁(Buo-ren, Lin) <Buo.Ren.Lin@gmail.com>
2020-02-27 16:41:13 +08:00
3ecb7cef3b Some clarification of custom listeners working 2020-02-27 00:59:21 +02:00
e789407774 Merge pull request #743 from Quantum-0/master
Add is_anonymous flag to Poll type
2020-02-14 16:49:54 +03:00
8f32dec5dd Merge pull request #744 from FaeeLoL/master
Add missed colon in readme
2020-02-14 16:48:44 +03:00
c57cfa3949 Add missed colon 2020-02-08 17:44:28 +03:00
dfac26706e Add is_anonymous flag to Poll type 2020-02-07 12:53:31 +03:00
385fa98bc6 Merge pull request #738 from andrey18106/patch-1
Added missed bracket
2020-01-30 01:10:51 +03:00
d68e89fc9a Added missed bracket 2020-01-28 20:56:03 +02:00
6023bae728 Merge pull request #735 from Lin-Buo-Ren/patch-2
Fix typo in README
2020-01-24 23:01:07 +03:00
b323a868f0 Fix typo in README
Signed-off-by: 林博仁(Buo-ren, Lin) <Buo.Ren.Lin@gmail.com>
2020-01-17 17:21:30 +08:00
583021d114 Update version to 3.6.7, 2020-01-14 16:33:05 +08:00
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
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
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
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
d17d28a144 create field forward_from_message_id in Message
https://core.telegram.org/bots/api#message
2018-03-20 23:36:29 +03:00
30 changed files with 2651 additions and 791 deletions

5
.gitignore vendored
View File

@ -58,4 +58,7 @@ docs/_build/
# PyBuilder
target/
testMain.py
testMain.py
#VS Code
.vscode/

View File

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

106
README.md
View File

@ -2,7 +2,8 @@
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.
[![Download Month](https://img.shields.io/pypi/v/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI)
[![PyPi Package Version](https://img.shields.io/pypi/v/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI)
[![Build Status](https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master)](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
* [Getting started.](#getting-started)
@ -15,6 +16,7 @@
* [General use of the API](#general-use-of-the-api)
* [Message handlers](#message-handlers)
* [Callback Query handlers](#callback-query-handler)
* [Middleware handlers](#middleware-handler)
* [TeleBot](#telebot)
* [Reply markup](#reply-markup)
* [Inline Mode](#inline-mode)
@ -26,6 +28,8 @@
* [Using web hooks](#using-web-hooks)
* [Logging](#logging)
* [Proxy](#proxy)
* [API conformance](#api-conformance)
* [Change log](#change-log)
* [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)
@ -154,7 +158,7 @@ TeleBot supports the following filters:
|name|argument(s)|Condition|
|:---:|---| ---|
|content_types|list of strings (default `['text']`)|`True` if message.content_type is in the list of strings.|
|regexp|a regular expression as a string|`True` if `re.search(regexp_arg)` returns `True` and `message.content_type == 'text'` (See [Python Regular Expressions](https://docs.python.org/2/library/re.html)|
|regexp|a regular expression as a string|`True` if `re.search(regexp_arg)` returns `True` and `message.content_type == 'text'` (See [Python Regular Expressions](https://docs.python.org/2/library/re.html))|
|commands|list of strings|`True` if `message.content_type == 'text'` and `message.text` starts with a command that is in the list of strings.|
|func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True`
@ -179,17 +183,17 @@ def handle_docs_audio(message):
def handle_message(message):
pass
#Handles all messages for which the lambda returns True
# 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:
# Which could also be defined as:
def test_message(message):
return message.document.mime_type == 'text/plain'
@bot.message_handler(func=test_message, content_types=['document'])
def handle_text_doc(message)
def handle_text_doc(message):
pass
# Handlers can be stacked to create a function which will be called if either message_handler is eligible
@ -203,25 +207,43 @@ def send_something(message):
#### Edited Message handlers
Same as Message handlers
@bot.edited_message_handler(filters)
#### channel_post_handler
Same as Message handlers
@bot.channel_post_handler(filters)
#### edited_channel_post_handler
Same as Message handlers
@bot.edited_channel_post_handler(filters)
#### 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.
In bot2.0 update. You can get `callback_query` in update object. In telebot use `callback_query_handler` to process callback queries.
```python
@bot.callback_query_handler(func=lambda call: True)
def test_callback(call):
logger.info(call)
```
#### Middleware Handler
A middleware handler is a function that allows you to modify requests or the bot context as they pass through the
Telegram to the bot. You can imagine middleware as a chain of logic connection handled before any other handlers are
executed.
```python
@bot.middleware_handler(update_types=['message'])
def modify_message(bot_instance, message):
# modifying the message before it reaches any other handler
message.another_text = message.text + ':changed'
@bot.message_handler(commands=['start'])
def start(message):
# the message is already modified when it reaches message handler
assert message.another_text == message.text + ':changed'
```
There are other examples using middleware handler in the [examples/middleware](examples/middleware) directory.
#### TeleBot
```python
@ -250,7 +272,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)
@ -371,7 +393,7 @@ 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.
Now, you can use inline_handler to get inline queries in telebot.
```python
@ -465,7 +487,10 @@ The TeleBot constructor takes the following optional arguments:
TeleBot should execute message handlers on it's polling Thread.
### The listener mechanism
As an alternative to the message handlers, one can also register a function as a listener to TeleBot. Example:
As an alternative to the message handlers, one can also register a function as a listener to TeleBot.
NOTICE: handlers won't disappear! Your message will be processed both by handlers and listeners. Also, it's impossible to predict which will work at first because of threading. If you use threaded=False, custom listeners will work earlier, after them handlers will be called.
Example:
```python
def handle_messages(messages):
for message in messages:
@ -510,6 +535,33 @@ apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}
```
## API conformance
_Checking is in progress..._
✅ [Bot API 3.5](https://core.telegram.org/bots/api-changelog#november-17-2017) _- To be checked..._
* ✔ [Bot API 3.4](https://core.telegram.org/bots/api-changelog#october-11-2017)
* ✔ [Bot API 3.3](https://core.telegram.org/bots/api-changelog#august-23-2017)
* ✔ [Bot API 3.2](https://core.telegram.org/bots/api-changelog#july-21-2017)
* ✔ [Bot API 3.1](https://core.telegram.org/bots/api-changelog#june-30-2017)
* ✔ [Bot API 3.0](https://core.telegram.org/bots/api-changelog#may-18-2017)
* ✔ [Bot API 2.3.1](https://core.telegram.org/bots/api-changelog#december-4-2016)
* ✔ [Bot API 2.3](https://core.telegram.org/bots/api-changelog#november-21-2016)
* ✔ [Bot API 2.2](https://core.telegram.org/bots/api-changelog#october-3-2016)
* ✔ [Bot API 2.1](https://core.telegram.org/bots/api-changelog#may-22-2016)
* ✔ [Bot API 2.0](https://core.telegram.org/bots/api-changelog#april-9-2016)
## Change log
27.04.2020 - Poll and Dice are up to date.
Python2 conformance is not checked any more due to EOL.
11.04.2020 - Refactoring. new_chat_member is out of support. Bugfix in html_text. Started Bot API conformance checking.
06.06.2019 - Added polls support (Poll). Added functions send_poll, stop_poll
## F.A.Q.
### Bot 2.0
@ -524,16 +576,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
```
@ -561,7 +613,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.
@ -576,16 +628,28 @@ 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.
* [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.
* [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audio samples 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.
* [InfoBot](https://t.me/info2019_bot) ([source](https://github.com/irevenko/info-bot)) by @irevenko - An all-round bot that displays some statistics (weather, time, crypto etc...)
* [FoodBot](https://t.me/ChensonUz_bot) ([source](https://github.com/Fliego/old_restaurant_telegram_chatbot)) by @Fliego - a simple bot for food ordering
* [Sporty](https://t.me/SportydBot) ([source](https://github.com/0xnu/sporty)) by @0xnu - Telegram bot for displaying the latest news, sports schedules and injury updates.
* [Neural style transfer](https://t.me/ebanyivolshebnikBot) ([source](https://github.com/timbyxty/StyleTransfer-tgbot)) by @timbyxty - bot for transferring style from one picture to another based on neural network.
Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh.

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 telegram bot use pyTelegramBotAPI.
import telebot
import time
import sys
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

@ -9,8 +9,8 @@ bot = telebot.TeleBot(TELEGRAM_TOKEN)
def gen_markup():
markup = InlineKeyboardMarkup()
markup.row_width = 2
markup.add(InlineKeyboardButton("Yes", callback_data=f"cb_yes"),
InlineKeyboardButton("No", callback_data=f"cb_no"))
markup.add(InlineKeyboardButton("Yes", callback_data="cb_yes"),
InlineKeyboardButton("No", callback_data="cb_no"))
return markup
@bot.callback_query_handler(func=lambda call: True)

View File

@ -0,0 +1,53 @@
#!/usr/bin/python
# This example shows how to implement i18n (internationalization) l10n (localization) to create
# multi-language bots with middleware handler.
#
# Note: For the sake of simplicity of this example no extra library is used. However, it is recommended to use
# better i18n systems (gettext and etc) for handling multilingual translations.
# This is not a working, production-ready sample and it is highly recommended not to use it in production.
#
# In this example let's imagine we want to introduce localization or internationalization into our project and
# we need some global function to activate the language once and to use that language in all other message
# handler functions for not repeatedly activating it.
# The middleware (i18n and l10n) is explained:
import telebot
from telebot import apihelper
apihelper.ENABLE_MIDDLEWARE = True
TRANSLATIONS = {
'hello': {
'en': 'hello',
'ru': 'привет',
'uz': 'salom'
}
}
_lang = 'en'
def activate(lang):
global _lang
_lang = lang
def _(string):
return TRANSLATIONS[string][_lang]
bot = telebot.TeleBot('TOKEN')
@bot.middleware_handler(update_types=['message'])
def activate_language(bot_instance, message):
activate(message.from_user.language_code)
@bot.message_handler(commands=['start'])
def start(message):
bot.send_message(message.chat.id, _('hello'))
bot.polling()

View File

@ -0,0 +1,61 @@
#!/usr/bin/python
# This example shows how to implement session creation and retrieval based on user id with middleware handler.
#
# Note: For the sake of simplicity of this example no extra library is used. However, it is recommended to use
# in-memory or on-disk storage implementations (redis, mysql, postgres and etc) for storing and retrieving structures.
# This is not a working, production-ready sample and it is highly recommended not to use it in production.
#
# In this example let's imagine we want to create a session for each user who communicates with the bot to store
# different kind of temporary data while session is active. As an example we want to track the state of the user
# with the help of this session. So, we need a way to store this session data somewhere globally to enable other
# message handler functions to be able to use it.
# The middleware session is explained:
import telebot
from telebot import apihelper
apihelper.ENABLE_MIDDLEWARE = True
INFO_STATE = 'ON_INFO_MENU'
MAIN_STATE = 'ON_MAIN_MENU'
SESSIONS = {
-10000: {
'state': INFO_STATE
},
-11111: {
'state': MAIN_STATE
}
}
def get_or_create_session(user_id):
try:
return SESSIONS[user_id]
except KeyError:
SESSIONS[user_id] = {'state': MAIN_STATE}
return SESSIONS[user_id]
bot = telebot.TeleBot('TOKEN')
@bot.middleware_handler(update_types=['message'])
def set_session(bot_instance, message):
bot_instance.session = get_or_create_session(message.from_user.id)
@bot.message_handler(commands=['start'])
def start(message):
bot.session['state'] = MAIN_STATE
bot.send_message(message.chat.id, bot.session['state'])
@bot.message_handler(commands=['info'])
def start(message):
bot.session['state'] = INFO_STATE
bot.send_message(message.chat.id, bot.session['state'])
bot.polling()

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
@ -84,5 +83,4 @@ bot.enable_save_next_step_handlers(delay=2)
# 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,11 +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>'
@ -30,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)
@ -77,7 +77,7 @@ 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,8 +1,9 @@
import os
import telebot
from flask import Flask, request
import telebot
TOKEN = '<api_token>'
bot = telebot.TeleBot(TOKEN)
server = Flask(__name__)

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

@ -7,7 +7,7 @@ def read(filename):
return file.read()
setup(name='pyTelegramBotAPI',
version='3.6.6',
version='3.7.1',
description='Python Telegram bot api. ',
long_description=read('README.md'),
long_description_content_type="text/markdown",
@ -20,12 +20,10 @@ setup(name='pyTelegramBotAPI',
install_requires=['requests', 'six'],
extras_require={
'json': 'ujson',
'redis': 'redis>=3.4.1'
},
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',
'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',

File diff suppressed because it is too large Load Diff

View File

@ -18,20 +18,26 @@ from telebot import types
from telebot import util
logger = telebot.logger
proxy = None
API_URL = "https://api.telegram.org/bot{0}/{1}"
FILE_URL = "https://api.telegram.org/file/bot{0}/{1}"
proxy = None
session = None
API_URL = None
FILE_URL = None
CONNECT_TIMEOUT = 3.5
READ_TIMEOUT = 9999
CUSTOM_SERIALIZER = None
ENABLE_MIDDLEWARE = False
def _get_req_session(reset=False):
return util.per_thread('req_session', lambda: requests.session(), reset)
return util.per_thread('req_session', lambda: session if session else 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,17 +47,24 @@ 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
if files and format_header_param:
fields.format_header_param = _no_encode(format_header_param)
if params:
if 'timeout' in params: read_timeout = params['timeout'] + 10
if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10
result = _get_req_session().request(method, request_url, params=params, files=files,
timeout=(connect_timeout, read_timeout), proxies=proxy)
if 'timeout' in params:
read_timeout = params.pop('timeout') + 10
if 'connect-timeout' in params:
connect_timeout = params.pop('connect-timeout') + 10
result = _get_req_session().request(
method, request_url, params=params, files=files,
timeout=(connect_timeout, read_timeout), proxies=proxy)
logger.debug("The server returned: '{0}'".format(result.text.encode('utf8')))
return _check_result(method_name, result)['result']
@ -99,12 +112,18 @@ def get_file(token, file_id):
def get_file_url(token, file_id):
method_url = r'getFile'
return FILE_URL.format(token, get_file(token, file_id).file_path)
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)
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}]' \
@ -113,8 +132,10 @@ def download_file(token, file_path):
return result.content
def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None):
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, timeout=None):
"""
Use this method to send text messages. On success, the sent Message is returned.
:param token:
@ -123,11 +144,14 @@ 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:
:param timeout:
:return:
"""
method_url = r'sendMessage'
payload = {'chat_id': str(chat_id), 'text': text}
if disable_web_page_preview:
if disable_web_page_preview is not None:
payload['disable_web_page_preview'] = disable_web_page_preview
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
@ -135,8 +159,10 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
return _make_request(token, method_url, params=payload, method='post')
@ -232,16 +258,41 @@ def get_chat_member(token, chat_id, user_id):
return _make_request(token, method_url, params=payload)
def forward_message(token, chat_id, from_chat_id, message_id, disable_notification=None):
def forward_message(
token, chat_id, from_chat_id, message_id,
disable_notification=None, timeout=None):
method_url = r'forwardMessage'
payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id}
if disable_notification:
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
return _make_request(token, method_url, params=payload)
def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None):
def send_dice(
token, chat_id,
emoji=None, disable_notification=None, reply_to_message_id=None,
reply_markup=None, timeout=None):
method_url = r'sendDice'
payload = {'chat_id': chat_id}
if emoji:
payload['emoji'] = emoji
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
return _make_request(token, method_url, params=payload)
def send_photo(
token, chat_id, photo,
caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None, timeout=None):
method_url = r'sendPhoto'
payload = {'chat_id': chat_id}
files = None
@ -257,25 +308,36 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
if disable_notification is not None:
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_media_group(token, chat_id, media, disable_notification=None, reply_to_message_id=None):
def send_media_group(
token, chat_id, media,
disable_notification=None, reply_to_message_id=None,
timeout=None):
method_url = r'sendMediaGroup'
media_json, files = _convert_input_media_array(media)
payload = {'chat_id': chat_id, 'media': media_json}
if disable_notification:
if disable_notification is not None:
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)
if timeout:
payload['connect-timeout'] = timeout
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):
def send_location(
token, chat_id, latitude, longitude,
live_period=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
method_url = r'sendLocation'
payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude}
if live_period:
@ -284,13 +346,15 @@ def send_location(token, chat_id, latitude, longitude, live_period=None, reply_t
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if disable_notification:
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
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):
inline_message_id=None, reply_markup=None, timeout=None):
method_url = r'editMessageLiveLocation'
payload = {'latitude': latitude, 'longitude': longitude}
if chat_id:
@ -301,11 +365,14 @@ def edit_message_live_location(token, latitude, longitude, chat_id=None, message
payload['inline_message_id'] = inline_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
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):
def stop_message_live_location(
token, chat_id=None, message_id=None,
inline_message_id=None, reply_markup=None, timeout=None):
method_url = r'stopMessageLiveLocation'
payload = {}
if chat_id:
@ -316,47 +383,59 @@ def stop_message_live_location(token, chat_id=None, message_id=None,
payload['inline_message_id'] = inline_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
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):
def send_venue(
token, chat_id, latitude, longitude, title, address,
foursquare_id=None, disable_notification=None,
reply_to_message_id=None, reply_markup=None, timeout=None):
method_url = r'sendVenue'
payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address}
if foursquare_id:
payload['foursquare_id'] = foursquare_id
if disable_notification:
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
return _make_request(token, method_url, params=payload)
def send_contact(token, chat_id, phone_number, first_name, last_name=None, disable_notification=None,
reply_to_message_id=None, reply_markup=None):
def send_contact(
token, chat_id, phone_number, first_name,
last_name=None, disable_notification=None,
reply_to_message_id=None, reply_markup=None, timeout=None):
method_url = r'sendContact'
payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name}
if last_name:
payload['last_name'] = last_name
if disable_notification:
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
return _make_request(token, method_url, params=payload)
def send_chat_action(token, chat_id, action):
def send_chat_action(token, chat_id, action, timeout=None):
method_url = r'sendChatAction'
payload = {'chat_id': chat_id, 'action': action}
if timeout:
payload['connect-timeout'] = timeout
return _make_request(token, method_url, params=payload)
def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None):
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None):
method_url = r'sendVideo'
payload = {'chat_id': chat_id}
files = None
@ -374,9 +453,40 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if supports_streaming:
if supports_streaming is not None:
payload['supports_streaming'] = supports_streaming
if disable_notification:
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
if thumb:
if not util.is_string(thumb):
files['thumb'] = thumb
else:
payload['thumb'] = thumb
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 is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
@ -402,7 +512,7 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
@ -428,7 +538,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if disable_notification:
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
@ -436,7 +546,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, parse_mode=None, disable_notification=None, timeout=None):
reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None):
method_url = r'sendAudio'
payload = {'chat_id': chat_id}
files = None
@ -458,10 +568,15 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_notification:
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
if thumb:
if not util.is_string(thumb):
files['thumb'] = thumb
else:
payload['thumb'] = thumb
return _make_request(token, method_url, params=payload, files=files, method='post')
@ -480,7 +595,7 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m
payload['reply_markup'] = _convert_markup(reply_markup)
if parse_mode and data_type == 'document':
payload['parse_mode'] = parse_mode
if disable_notification:
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
@ -510,49 +625,77 @@ def unban_chat_member(token, chat_id, user_id):
return _make_request(token, method_url, params=payload, method='post')
def restrict_chat_member(token, chat_id, user_id, until_date=None, can_send_messages=None,
can_send_media_messages=None, can_send_other_messages=None,
can_add_web_page_previews=None):
def restrict_chat_member(
token, chat_id, user_id, until_date=None,
can_send_messages=None, can_send_media_messages=None,
can_send_polls=None, can_send_other_messages=None,
can_add_web_page_previews=None, can_change_info=None,
can_invite_users=None, can_pin_messages=None):
method_url = 'restrictChatMember'
payload = {'chat_id': chat_id, 'user_id': user_id}
if until_date:
if until_date is not None:
payload['until_date'] = until_date
if can_send_messages:
if can_send_messages is not None:
payload['can_send_messages'] = can_send_messages
if can_send_media_messages:
if can_send_media_messages is not None:
payload['can_send_media_messages'] = can_send_media_messages
if can_send_other_messages:
if can_send_polls is not None:
payload['can_send_polls'] = can_send_polls
if can_send_other_messages is not None:
payload['can_send_other_messages'] = can_send_other_messages
if can_add_web_page_previews:
if can_add_web_page_previews is not None:
payload['can_add_web_page_previews'] = can_add_web_page_previews
if can_change_info is not None:
payload['can_change_info'] = can_change_info
if can_invite_users is not None:
payload['can_invite_users'] = can_invite_users
if can_pin_messages is not None:
payload['can_pin_messages'] = can_pin_messages
return _make_request(token, method_url, params=payload, method='post')
def promote_chat_member(token, chat_id, user_id, can_change_info=None, can_post_messages=None,
can_edit_messages=None, can_delete_messages=None, can_invite_users=None,
can_restrict_members=None, can_pin_messages=None, can_promote_members=None):
def promote_chat_member(
token, chat_id, user_id, can_change_info=None, can_post_messages=None,
can_edit_messages=None, can_delete_messages=None, can_invite_users=None,
can_restrict_members=None, can_pin_messages=None, can_promote_members=None):
method_url = 'promoteChatMember'
payload = {'chat_id': chat_id, 'user_id': user_id}
if can_change_info:
if can_change_info is not None:
payload['can_change_info'] = can_change_info
if can_post_messages:
if can_post_messages is not None:
payload['can_post_messages'] = can_post_messages
if can_edit_messages:
if can_edit_messages is not None:
payload['can_edit_messages'] = can_edit_messages
if can_delete_messages:
if can_delete_messages is not None:
payload['can_delete_messages'] = can_delete_messages
if can_invite_users:
if can_invite_users is not None:
payload['can_invite_users'] = can_invite_users
if can_restrict_members:
if can_restrict_members is not None:
payload['can_restrict_members'] = can_restrict_members
if can_pin_messages:
if can_pin_messages is not None:
payload['can_pin_messages'] = can_pin_messages
if can_promote_members:
if can_promote_members is not None:
payload['can_promote_members'] = can_promote_members
return _make_request(token, method_url, params=payload, method='post')
def set_chat_administrator_custom_title(token, chat_id, user_id, custom_title):
method_url = 'setChatAdministratorCustomTitle'
payload = {
'chat_id': chat_id, 'user_id': user_id, 'custom_title': custom_title
}
return _make_request(token, method_url, params=payload, method='post')
def set_chat_permissions(token, chat_id, permissions):
method_url = 'setChatPermissions'
payload = {
'chat_id': chat_id,
'permissions': permissions.to_json()
}
return _make_request(token, method_url, params=payload, method='post')
def export_chat_invite_link(token, chat_id):
method_url = 'exportChatInviteLink'
payload = {'chat_id': chat_id}
@ -582,15 +725,23 @@ def set_chat_title(token, chat_id, title):
return _make_request(token, method_url, params=payload, method='post')
def set_my_commands(token, commands):
method_url = r'setMyCommands'
payload = {'commands': _convert_list_json_serializable(commands)}
return _make_request(token, method_url, params=payload, method='post')
def set_chat_description(token, chat_id, description):
method_url = 'setChatDescription'
payload = {'chat_id': chat_id, 'description': description}
return _make_request(token, method_url, params=payload, method='post')
def pin_chat_message(token, chat_id, message_id, disable_notification=False):
def pin_chat_message(token, chat_id, message_id, disable_notification=None):
method_url = 'pinChatMessage'
payload = {'chat_id': chat_id, 'message_id': message_id, 'disable_notification': disable_notification}
payload = {'chat_id': chat_id, 'message_id': message_id}
if disable_notification is not None:
payload['disable_notification'] = disable_notification
return _make_request(token, method_url, params=payload, method='post')
@ -614,11 +765,11 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message
payload['inline_message_id'] = inline_message_id
if parse_mode:
payload['parse_mode'] = parse_mode
if disable_web_page_preview:
if disable_web_page_preview is not None:
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,
@ -635,7 +786,7 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m
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):
@ -664,26 +815,30 @@ 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
def send_game(token, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, reply_markup=None):
def send_game(
token, chat_id, game_short_name,
disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None):
method_url = r'sendGame'
payload = {'chat_id': chat_id, 'game_short_name': game_short_name}
if disable_notification:
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
return _make_request(token, method_url, params=payload)
@ -704,7 +859,7 @@ def set_game_score(token, user_id, score, force=None, disable_edit_message=None,
"""
method_url = r'setGameScore'
payload = {'user_id': user_id, 'score': score}
if force:
if force is not None:
payload['force'] = force
if chat_id:
payload['chat_id'] = chat_id
@ -712,7 +867,7 @@ def set_game_score(token, user_id, score, force=None, disable_edit_message=None,
payload['message_id'] = message_id
if inline_message_id:
payload['inline_message_id'] = inline_message_id
if disable_edit_message:
if disable_edit_message is not None:
payload['disable_edit_message'] = disable_edit_message
return _make_request(token, method_url, params=payload)
@ -742,10 +897,12 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m
# Payments (https://core.telegram.org/bots/api#payments)
def send_invoice(token, chat_id, title, description, invoice_payload, provider_token, currency, prices,
start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None,
need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None,
disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None):
def send_invoice(
token, chat_id, title, description, invoice_payload, provider_token, currency, prices,
start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None,
need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None,
disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None,
timeout=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)
@ -769,7 +926,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,
@ -783,17 +941,17 @@ def send_invoice(token, chat_id, title, description, invoice_payload, provider_t
payload['photo_width'] = photo_width
if photo_height:
payload['photo_height'] = photo_height
if need_name:
if need_name is not None:
payload['need_name'] = need_name
if need_phone_number:
if need_phone_number is not None:
payload['need_phone_number'] = need_phone_number
if need_email:
if need_email is not None:
payload['need_email'] = need_email
if need_shipping_address:
if need_shipping_address is not None:
payload['need_shipping_address'] = need_shipping_address
if is_flexible:
if is_flexible is not None:
payload['is_flexible'] = is_flexible
if disable_notification:
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
@ -801,6 +959,8 @@ def send_invoice(token, chat_id, title, description, invoice_payload, provider_t
payload['reply_markup'] = _convert_markup(reply_markup)
if provider_data:
payload['provider_data'] = provider_data
if timeout:
payload['connect-timeout'] = timeout
return _make_request(token, method_url, params=payload)
@ -812,7 +972,7 @@ 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}
@ -830,7 +990,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}
@ -858,7 +1018,7 @@ def answer_callback_query(token, callback_query_id, text=None, show_alert=None,
payload = {'callback_query_id': callback_query_id}
if text:
payload['text'] = text
if show_alert:
if show_alert is not None:
payload['show_alert'] = show_alert
if url:
payload['url'] = url
@ -873,7 +1033,7 @@ def answer_inline_query(token, inline_query_id, results, cache_time=None, is_per
payload = {'inline_query_id': inline_query_id, 'results': _convert_list_json_serializable(results)}
if cache_time is not None:
payload['cache_time'] = cache_time
if is_personal:
if is_personal is not None:
payload['is_personal'] = is_personal
if next_offset is not None:
payload['next_offset'] = next_offset
@ -896,7 +1056,9 @@ def upload_sticker_file(token, user_id, png_sticker):
return _make_request(token, method_url, params=payload, files=files, method='post')
def create_new_sticker_set(token, user_id, name, title, png_sticker, emojis, contains_masks=None, mask_position=None):
def create_new_sticker_set(
token, user_id, name, title, png_sticker, emojis,
contains_masks=None, mask_position=None):
method_url = 'createNewStickerSet'
payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis}
files = None
@ -904,7 +1066,7 @@ def create_new_sticker_set(token, user_id, name, title, png_sticker, emojis, con
files = {'png_sticker': png_sticker}
else:
payload['png_sticker'] = png_sticker
if contains_masks:
if contains_masks is not None:
payload['contains_masks'] = contains_masks
if mask_position:
payload['mask_position'] = mask_position.to_json()
@ -936,6 +1098,56 @@ 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,
is_anonymous = None, type = None, allows_multiple_answers = None, correct_option_id = None,
explanation = None, explanation_parse_mode=None, open_period = None, close_date = None, is_closed = None,
disable_notifications=False, reply_to_message_id=None, reply_markup=None, timeout=None):
method_url = r'sendPoll'
payload = {
'chat_id': str(chat_id),
'question': question,
'options': json.dumps(options)}
if is_anonymous is not None:
payload['is_anonymous'] = is_anonymous
if type is not None:
payload['type'] = type
if allows_multiple_answers is not None:
payload['allows_multiple_answers'] = allows_multiple_answers
if correct_option_id is not None:
payload['correct_option_id'] = correct_option_id
if explanation is not None:
payload['explanation'] = explanation
if explanation_parse_mode is not None:
payload['explanation_parse_mode'] = explanation_parse_mode
if open_period is not None:
payload['open_period'] = open_period
if close_date is not None:
payload['close_date'] = close_date
if is_closed is not None:
payload['is_closed'] = is_closed
if disable_notifications:
payload['disable_notification'] = disable_notifications
if reply_to_message_id is not None:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup is not None:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
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:
@ -963,7 +1175,7 @@ def _convert_input_media_array(array):
files = {}
for input_media in array:
if isinstance(input_media, types.InputMedia):
media_dict = input_media.to_dic()
media_dict = input_media.to_dict()
if media_dict['media'].startswith('attach://'):
key = media_dict['media'].replace('attach://', '')
files[key] = input_media.media

145
telebot/handler_backends.py Normal file
View File

@ -0,0 +1,145 @@
import os
import pickle
import threading
from telebot import apihelper
class HandlerBackend(object):
"""
Class for saving (next step|reply) handlers
"""
def __init__(self, handlers=None):
if handlers is None:
handlers = {}
self.handlers = handlers
def register_handler(self, handler_group_id, handler):
raise NotImplementedError()
def clear_handlers(self, handler_group_id):
raise NotImplementedError()
def get_handlers(self, handler_group_id):
raise NotImplementedError()
class MemoryHandlerBackend(HandlerBackend):
def register_handler(self, handler_group_id, handler):
if handler_group_id in self.handlers:
self.handlers[handler_group_id].append(handler)
else:
self.handlers[handler_group_id] = [handler]
def clear_handlers(self, handler_group_id):
self.handlers.pop(handler_group_id, [])
def get_handlers(self, handler_group_id):
return self.handlers.pop(handler_group_id, [])
class FileHandlerBackend(HandlerBackend):
def __init__(self, handlers=None, filename='./.handler-saves/handlers.save', delay=120):
super(FileHandlerBackend, self).__init__(handlers)
self.filename = filename
self.delay = delay
self.timer = threading.Timer(delay, self.save_handlers)
def register_handler(self, handler_group_id, handler):
if handler_group_id in self.handlers:
self.handlers[handler_group_id].append(handler)
else:
self.handlers[handler_group_id] = [handler]
self.start_save_timer()
def clear_handlers(self, handler_group_id):
self.handlers.pop(handler_group_id, [])
self.start_save_timer()
def get_handlers(self, handler_group_id):
handlers = self.handlers.pop(handler_group_id, [])
self.start_save_timer()
return handlers
def start_save_timer(self):
if not self.timer.is_alive():
if self.delay <= 0:
self.save_handlers()
else:
self.timer = threading.Timer(self.delay, self.save_handlers)
self.timer.start()
def save_handlers(self):
self.dump_handlers(self.handlers, self.filename)
def load_handlers(self, filename=None, del_file_after_loading=True):
if not filename:
filename = self.filename
tmp = self.return_load_handlers(filename, del_file_after_loading=del_file_after_loading)
if tmp is not None:
self.handlers.update(tmp)
@staticmethod
def dump_handlers(handlers, filename, file_mode="wb"):
dirs = filename.rsplit('/', maxsplit=1)[0]
os.makedirs(dirs, exist_ok=True)
with open(filename + ".tmp", file_mode) as file:
if (apihelper.CUSTOM_SERIALIZER is None):
pickle.dump(handlers, file)
else:
apihelper.CUSTOM_SERIALIZER.dump(handlers, file)
if os.path.isfile(filename):
os.remove(filename)
os.rename(filename + ".tmp", filename)
@staticmethod
def return_load_handlers(filename, del_file_after_loading=True):
if os.path.isfile(filename) and os.path.getsize(filename) > 0:
with open(filename, "rb") as file:
if (apihelper.CUSTOM_SERIALIZER is None):
handlers = pickle.load(file)
else:
handlers = apihelper.CUSTOM_SERIALIZER.load(file)
if del_file_after_loading:
os.remove(filename)
return handlers
class RedisHandlerBackend(HandlerBackend):
def __init__(self, handlers=None, host='localhost', port=6379, db=0, prefix='telebot'):
super(RedisHandlerBackend, self).__init__(handlers)
from redis import Redis
self.prefix = prefix
self.redis = Redis(host, port, db)
def _key(self, handle_group_id):
return ':'.join((self.prefix, str(handle_group_id)))
def register_handler(self, handler_group_id, handler):
handlers = []
value = self.redis.get(self._key(handler_group_id))
if value:
handlers = pickle.loads(value)
handlers.append(handler)
self.redis.set(self._key(handler_group_id), pickle.dumps(handlers))
def clear_handlers(self, handler_group_id):
self.redis.delete(self._key(handler_group_id))
def get_handlers(self, handler_group_id):
handlers = []
value = self.redis.get(self._key(handler_group_id))
if value:
handlers = pickle.loads(value)
self.clear_handlers(handler_group_id)
return handlers

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,13 @@
# -*- coding: utf-8 -*-
import random
import re
import string
import sys
import threading
import traceback
import re
import sys
import warnings
import functools
import six
from six import string_types
@ -22,67 +25,67 @@ thread_local = threading.local()
class WorkerThread(threading.Thread):
count = 0
count = 0
def __init__(self, exception_callback=None, queue=None, name=None):
if not name:
name = "WorkerThread{0}".format(self.__class__.count + 1)
self.__class__.count += 1
if not queue:
queue = Queue.Queue()
def __init__(self, exception_callback=None, queue=None, name=None):
if not name:
name = "WorkerThread{0}".format(self.__class__.count + 1)
self.__class__.count += 1
if not queue:
queue = Queue.Queue()
threading.Thread.__init__(self, name=name)
self.queue = queue
self.daemon = True
threading.Thread.__init__(self, name=name)
self.queue = queue
self.daemon = True
self.received_task_event = threading.Event()
self.done_event = threading.Event()
self.exception_event = threading.Event()
self.continue_event = threading.Event()
self.received_task_event = threading.Event()
self.done_event = threading.Event()
self.exception_event = threading.Event()
self.continue_event = threading.Event()
self.exception_callback = exception_callback
self.exc_info = None
self._running = True
self.start()
self.exception_callback = exception_callback
self.exc_info = None
self._running = True
self.start()
def run(self):
while self._running:
try:
task, args, kwargs = self.queue.get(block=True, timeout=.5)
self.continue_event.clear()
self.received_task_event.clear()
self.done_event.clear()
self.exception_event.clear()
logger.debug("Received task")
self.received_task_event.set()
def run(self):
while self._running:
try:
task, args, kwargs = self.queue.get(block=True, timeout=.5)
self.continue_event.clear()
self.received_task_event.clear()
self.done_event.clear()
self.exception_event.clear()
logger.debug("Received task")
self.received_task_event.set()
task(*args, **kwargs)
logger.debug("Task complete")
self.done_event.set()
except Queue.Empty:
pass
except Exception as e:
logger.error(type(e).__name__ + " occurred, args=" + str(e.args) + "\n" + traceback.format_exc())
self.exc_info = sys.exc_info()
self.exception_event.set()
task(*args, **kwargs)
logger.debug("Task complete")
self.done_event.set()
except Queue.Empty:
pass
except Exception as e:
logger.error(type(e).__name__ + " occurred, args=" + str(e.args) + "\n" + traceback.format_exc())
self.exc_info = sys.exc_info()
self.exception_event.set()
if self.exception_callback:
self.exception_callback(self, self.exc_info)
self.continue_event.wait()
if self.exception_callback:
self.exception_callback(self, self.exc_info)
self.continue_event.wait()
def put(self, task, *args, **kwargs):
self.queue.put((task, args, kwargs))
def put(self, task, *args, **kwargs):
self.queue.put((task, args, kwargs))
def raise_exceptions(self):
if self.exception_event.is_set():
six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
def raise_exceptions(self):
if self.exception_event.is_set():
six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
def clear_exceptions(self):
self.exception_event.clear()
self.continue_event.set()
def clear_exceptions(self):
self.exception_event.clear()
self.continue_event.set()
def stop(self):
self._running = False
def stop(self):
self._running = False
class ThreadPool:
@ -243,7 +246,7 @@ 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(r"/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE)
result = regexp.match(text)
return result.group(2) if is_command(text) else None
@ -257,4 +260,20 @@ def per_thread(key, construct_value, reset=False):
def generate_random_token():
return ''.join(random.sample(string.ascii_letters, 16))
return ''.join(random.sample(string.ascii_letters, 16))
def deprecated(func):
"""This is a decorator which can be used to mark functions
as deprecated. It will result in a warning being emitted
when the function is used."""
# https://stackoverflow.com/a/30253848/441814
@functools.wraps(func)
def new_func(*args, **kwargs):
warnings.simplefilter('always', DeprecationWarning) # turn off filter
warnings.warn("Call to deprecated function {}.".format(func.__name__),
category=DeprecationWarning,
stacklevel=2)
warnings.simplefilter('default', DeprecationWarning) # reset filter
return func(*args, **kwargs)
return new_func

View File

@ -0,0 +1,270 @@
import sys
sys.path.append('../')
REDIS_TESTS = False
import os
import time
import pytest
import telebot
from telebot import types
from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend
if REDIS_TESTS:
from telebot.handler_backends import RedisHandlerBackend
@pytest.fixture()
def telegram_bot():
return telebot.TeleBot('', threaded=False)
@pytest.fixture
def private_chat():
return types.Chat(id=11, type='private')
@pytest.fixture
def user():
return types.User(id=10, is_bot=False, first_name='Some User')
@pytest.fixture()
def message(user, private_chat):
params = {'text': '/start'}
return types.Message(
message_id=1, from_user=user, date=None, chat=private_chat, content_type='text', options=params, json_string=""
)
@pytest.fixture()
def reply_to_message(user, private_chat, message):
params = {'text': '/start'}
reply_message = types.Message(
message_id=2, from_user=user, date=None, chat=private_chat, content_type='text', options=params, json_string=""
)
reply_message.reply_to_message = message
return reply_message
@pytest.fixture()
def update_type(message):
edited_message = None
channel_post = None
edited_channel_post = None
inline_query = None
chosen_inline_result = None
callback_query = None
shipping_query = None
pre_checkout_query = None
poll = None
poll_answer = None
return types.Update(1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query,
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer)
@pytest.fixture()
def reply_to_message_update_type(reply_to_message):
edited_message = None
channel_post = None
edited_channel_post = None
inline_query = None
chosen_inline_result = None
callback_query = None
shipping_query = None
pre_checkout_query = None
poll = None
poll_answer = None
return types.Update(1001234038284, reply_to_message, edited_message, channel_post, edited_channel_post,
inline_query,
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer)
def next_handler(message):
message.text = 'entered next_handler'
def test_memory_handler_backend_default_backend(telegram_bot):
assert telegram_bot.reply_backend.__class__ == MemoryHandlerBackend
assert telegram_bot.next_step_backend.__class__ == MemoryHandlerBackend
def test_memory_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type):
@telegram_bot.message_handler(commands=['start'])
def start(message):
message.text = 'entered start'
telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler)
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered start'
assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered next_handler'
assert private_chat.id not in telegram_bot.next_step_backend.handlers
def test_memory_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type):
@telegram_bot.message_handler(commands=['start'])
def start(message):
message.text = 'entered start'
telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler)
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered start'
assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1
telegram_bot.clear_step_handler_by_chat_id(private_chat.id)
assert private_chat.id not in telegram_bot.next_step_backend.handlers
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered start'
def test_memory_handler_backend_register_reply_handler(telegram_bot, private_chat, update_type,
reply_to_message_update_type):
@telegram_bot.message_handler(commands=['start'])
def start(message):
message.text = 'entered start'
telegram_bot.register_for_reply_by_message_id(message.message_id, next_handler)
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered start'
assert len(telegram_bot.reply_backend.handlers[update_type.message.message_id]) == 1
telegram_bot.process_new_updates([reply_to_message_update_type])
assert reply_to_message_update_type.message.text == 'entered next_handler'
assert private_chat.id not in telegram_bot.reply_backend.handlers
def test_memory_handler_backend_clear_reply_handler(telegram_bot, private_chat, update_type,
reply_to_message_update_type):
@telegram_bot.message_handler(commands=['start'])
def start(message):
message.text = 'entered start'
telegram_bot.register_for_reply_by_message_id(message.message_id, next_handler)
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered start'
assert len(telegram_bot.reply_backend.handlers[update_type.message.message_id]) == 1
telegram_bot.clear_reply_handlers_by_message_id(update_type.message.message_id)
assert update_type.message.message_id not in telegram_bot.reply_backend.handlers
telegram_bot.process_new_updates([reply_to_message_update_type])
assert reply_to_message_update_type.message.text == 'entered start'
def test_file_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type):
telegram_bot.next_step_backend=FileHandlerBackend(filename='./.handler-saves/step1.save', delay=0.1)
@telegram_bot.message_handler(commands=['start'])
def start(message):
message.text = 'entered start'
telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler)
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered start'
time.sleep(0.2)
assert os.path.exists(telegram_bot.next_step_backend.filename)
assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1
telegram_bot.next_step_backend.handlers = {}
telegram_bot.next_step_backend.load_handlers()
assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered next_handler'
assert private_chat.id not in telegram_bot.next_step_backend.handlers
time.sleep(0.2)
if os.path.exists(telegram_bot.next_step_backend.filename):
os.remove(telegram_bot.next_step_backend.filename)
def test_file_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type):
telegram_bot.next_step_backend=FileHandlerBackend(filename='./.handler-saves/step2.save', delay=0.1)
@telegram_bot.message_handler(commands=['start'])
def start(message):
message.text = 'entered start'
telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler)
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered start'
assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1
time.sleep(0.2)
assert os.path.exists(telegram_bot.next_step_backend.filename)
telegram_bot.clear_step_handler_by_chat_id(private_chat.id)
time.sleep(0.2)
telegram_bot.next_step_backend.load_handlers()
assert private_chat.id not in telegram_bot.next_step_backend.handlers
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered start'
time.sleep(0.2)
if os.path.exists(telegram_bot.next_step_backend.filename):
os.remove(telegram_bot.next_step_backend.filename)
def test_redis_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type):
if not REDIS_TESTS:
pytest.skip('please install redis and configure redis server, then enable REDIS_TESTS')
telegram_bot.next_step_backend = RedisHandlerBackend(prefix='pyTelegramBotApi:step_backend1')
@telegram_bot.message_handler(commands=['start'])
def start(message):
message.text = 'entered start'
telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler)
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered start'
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered next_handler'
def test_redis_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type):
if not REDIS_TESTS:
pytest.skip('please install redis and configure redis server, then enable REDIS_TESTS')
telegram_bot.next_step_backend = RedisHandlerBackend(prefix='pyTelegramBotApi:step_backend2')
@telegram_bot.message_handler(commands=['start'])
def start(message):
message.text = 'entered start'
telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler)
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered start'
telegram_bot.clear_step_handler_by_chat_id(private_chat.id)
telegram_bot.process_new_updates([update_type])
assert update_type.message.text == 'entered start'

View File

@ -48,7 +48,7 @@ class TestTeleBot:
bot = telebot.TeleBot('')
msg = self.create_text_message(r'https://web.telegram.org/')
@bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
@bot.message_handler(regexp=r'((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
def command_url(message):
msg.text = 'got'
@ -84,7 +84,7 @@ class TestTeleBot:
bot = telebot.TeleBot('')
msg = self.create_text_message(r'web.telegram.org/')
@bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
@bot.message_handler(regexp=r'((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
def command_url(message):
msg.text = 'got'
@ -241,6 +241,12 @@ class TestTeleBot:
ret_msg = tb.send_message(CHAT_ID, text)
assert ret_msg.message_id
def test_send_dice(self):
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_dice(CHAT_ID, emoji='🎯')
assert ret_msg.message_id
assert ret_msg.content_type == 'dice'
def test_send_message_dis_noti(self):
text = 'CI Test Message'
tb = telebot.TeleBot(TOKEN)
@ -402,11 +408,30 @@ 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, False, 'test')
return types.Message(1, None, None, chat, 'text', params, "")
@staticmethod
def create_message_update(text):
params = {'text': text}
chat = types.User(11, False, 'test')
message = types.Message(1, None, None, chat, 'text', params, "")
edited_message = None
channel_post = None
edited_channel_post = None
inline_query = None
chosen_inline_result = None
callback_query = None
shipping_query = None
pre_checkout_query = None
poll = None
poll_answer = None
return types.Update(-1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query,
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer)
def test_is_string_unicode(self):
s1 = u'string'
assert util.is_string(s1)
@ -488,3 +513,49 @@ class TestTeleBot:
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'
def test_typed_middleware_handler(self):
from telebot import apihelper
apihelper.ENABLE_MIDDLEWARE = True
tb = telebot.TeleBot('')
update = self.create_message_update('/help')
@tb.middleware_handler(update_types=['message'])
def middleware(tb_instance, message):
message.text = 'got'
@tb.message_handler(func=lambda m: m.text == 'got')
def command_handler(message):
message.text = message.text + message.text
tb.process_new_updates([update])
time.sleep(1)
assert update.message.text == 'got' * 2
def test_default_middleware_handler(self):
from telebot import apihelper
apihelper.ENABLE_MIDDLEWARE = True
tb = telebot.TeleBot('')
update = self.create_message_update('/help')
@tb.middleware_handler()
def middleware(tb_instance, update):
update.message.text = 'got'
@tb.message_handler(func=lambda m: m.text == 'got')
def command_handler(message):
message.text = message.text + message.text
tb.process_new_updates([update])
time.sleep(1)
assert update.message.text == 'got' * 2
def test_chat_permissions(self):
return # CHAT_ID is private chat, no permissions can be set
tb = telebot.TeleBot(TOKEN)
permissions = types.ChatPermissions(can_send_messages=True, can_send_polls=False)
msg = tb.set_chat_permissions(CHAT_ID, permissions)

View File

@ -17,6 +17,15 @@ def test_json_message():
assert msg.text == 'HIHI'
def test_json_message_with_dice():
jsonstring = r'{"message_id":5560,"from":{"id":879343317,"is_bot":false,"first_name":"George","last_name":"Forse","username":"dr_fxrse","language_code":"ru"},"chat":{"id":879343317,"first_name":"George","last_name":"Forse","username":"dr_fxrse","type":"private"},"date":1586926330,"dice":{"value": 4, "emoji": "\ud83c\udfaf"}}'
msg = types.Message.de_json(jsonstring)
assert msg.content_type == 'dice'
assert isinstance(msg.dice, types.Dice)
assert msg.dice.value == 4
assert msg.dice.emoji == '🎯'
def test_json_message_group():
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)
@ -48,7 +57,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","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
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,10 +65,10 @@ 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","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
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'
@ -113,6 +122,7 @@ 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":{"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)
@ -120,6 +130,7 @@ def test_json_update():
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 +138,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 +155,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"))
@ -155,3 +168,31 @@ def test_InlineQueryResultCachedPhoto_with_markup():
assert 'caption' not in json_str
assert 'reply_markup' in json_str
def test_json_poll_1():
jsonstring = r'{"message_id": 395020,"from": {"id": 111,"is_bot": false,"first_name": "FN","last_name": "LN","username": "Badiboy","language_code": "ru"},"chat": {"id": 111,"first_name": "FN","last_name": "LN","username": "Badiboy","type": "private"},"date": 1587841239,"poll": {"id": "5272018969396510722","question": "Test poll 1","options": [{"text": "Answer 1","voter_count": 0},{"text": "Answer 2","voter_count": 0}],"total_voter_count": 0,"is_closed": false,"is_anonymous": true,"type": "regular","allows_multiple_answers": true}}'
msg = types.Message.de_json(jsonstring)
assert msg.poll is not None
assert isinstance(msg.poll, types.Poll)
assert msg.poll.id == '5272018969396510722'
assert msg.poll.question is not None
assert msg.poll.options is not None
assert len(msg.poll.options) == 2
assert msg.poll.allows_multiple_answers is True
def test_json_poll_answer():
jsonstring = r'{"poll_id": "5895675970559410186", "user": {"id": 329343347, "is_bot": false, "first_name": "Test", "username": "test_user", "last_name": "User", "language_code": "en"}, "option_ids": [1]}'
__import__('pprint').pprint(__import__('json').loads(jsonstring))
poll_answer = types.PollAnswer.de_json(jsonstring)
assert poll_answer.poll_id == '5895675970559410186'
assert isinstance(poll_answer.user, types.User)
assert poll_answer.options_ids == [1]
def test_KeyboardButtonPollType():
markup = types.ReplyKeyboardMarkup()
markup.add(types.KeyboardButton('send me a poll', request_poll=types.KeyboardButtonPollType(type='quiz')))
json_str = markup.to_json()
assert 'request_poll' in json_str
assert 'quiz' in json_str