Compare commits

...

251 Commits

Author SHA1 Message Date
Hinrich Mahler 21ded420e2 Bump version to v13.4 2021-03-14 17:00:26 +01:00
Bibo-Joshi 7d893fd04b Doc Fixes (#2404)
* Improve some badges for PTB-Raw

* doc fix for add_error_handler

* Some rendering

* Bump sphinx dependency

* Change signature annotation setting

* fix: chat_id can be string, message_id only int

* feat: add RTD link to documentation

* improving sender chat docstring (#2412)

* fix: improving sender chat docstring

also adding a note to a weird edge case

* fix: words being hard

* Add note on donations

* typo

* typo in User.get_profile_pictures docstrings

* Fix: meth, not attr for meth, not attr

* filters + inlinequery doc fix

* Bump versions, update RTD config file

* Try fix build

* Revert "fix: chat_id can be string, message_id only int"

This reverts commit ba04e5aa

* Add Starry & Harshil to credits

Co-authored-by: poolitzer <25934244+Poolitzer@users.noreply.github.com>
Co-authored-by: Harshil <ilovebhagwan@gmail.com>
2021-03-14 16:46:37 +01:00
Bibo-Joshi 7015f8dedc Type Hinting Fixes (#2425)
Co-authored-by: poolitzer <25934244+Poolitzer@users.noreply.github.com>
2021-03-14 16:42:03 +01:00
Poolitzer ac02bce109 API 5.1 (#2424)
* Feat: New invite links

* Fix: doc strings

Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>

* new dice, new admin privilege, revoke_messages, update and fix some docs

* add missing param to shortcut

* Add ChatMemberUpdated

* Add voicechat related objects

Signed-off-by: starry69 <starry369126@outlook.com>

* add versionadd tags

Signed-off-by: starry69 <starry369126@outlook.com>

* Fix filter tests

* Update tg.Update

* ChatMemberHandler

* Add versioning directives

* add can_manage_voice_chats attr and fix docs

Signed-off-by: starry69 <starry369126@outlook.com>

* fix chat shortcut

Signed-off-by: starry69 <starry369126@outlook.com>

* address review

* MADTC

* Chat.message_auto_delete_time

* Some doc fixes

* address review

Signed-off-by: starry69 <starry369126@outlook.com>

* welp

Signed-off-by: starry69 <starry369126@outlook.com>

* Add voicechat related filters

Signed-off-by: starry69 <starry369126@outlook.com>

* Fix: Addressing review

change place of version adding, added obj:True as doc string, changing how member limit is initiated

* feat: adding chat shortcuts for invite links

* fix: changing equality of chatinviteobjects

* Non-test comments

* Some test fixes

* A bit more tests

* Bump API version in both readmes

* Increase coverage

* Add Bot API Version in telegram.constants (#2429)

* add bot api version in constants

Signed-off-by: starry69 <starry369126@outlook.com>

* addressing review

Signed-off-by: starry69 <starry369126@outlook.com>

* add versioning directive

Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>

* pre-commit & coverage

Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>
Co-authored-by: Harshil <ilovebhagwan@gmail.com>
Co-authored-by: starry69 <starry369126@outlook.com>
2021-03-14 16:41:35 +01:00
Bibo-Joshi 3a9a0ab96d Update pre-commit Settings (#2415)
Co-authored-by: Harshil <ilovebhagwan@gmail.com>
2021-03-13 16:21:03 +01:00
Bibo-Joshi aba17cb997 Improve Updater.set_webhook (#2419)
* Get started

* tests

* Some doc fixes

* Some doc fixes
2021-03-13 15:35:26 +01:00
Bibo-Joshi 038a3b4452 Fix Logging for Vendored urllib3 (#2427) 2021-03-13 15:14:10 +01:00
Bibo-Joshi b03ebc5a65 Stabilize Tests (#2409)
* Fix Photo Tests

* Try harder

* Try stabalizing pin&unpin test

* Drop file size tests

* Fix that one failing test …
2021-03-10 16:51:56 +01:00
Hinrich Mahler e9c01c7772 Bump version to v13.3 2021-02-19 19:55:49 +01:00
Bibo-Joshi 552298595c Doc Fixes (#2359)
* Improve some badges for PTB-Raw

* doc fix for add_error_handler

* Some rendering

* Doc fixes for filters.py

* render '@' as code

Co-authored-by: Harshil <ilovebhagwan@gmail.com>
2021-02-19 19:27:17 +01:00
Bibo-Joshi 2a4a0d0ccd Refactor Defaults Integration (#2363)
* Change default handling, update signatures, get existing tests to pass.

* Try running tests on ubuntu 18.04

* Roll back

* Rework check_shortcut_call tests

* Further improve check_shortcut_call tests

* Start on defaults-checks for shortcuts, get it working for test_message

* Add check_shortcut_defaults to all other shortcut tests

* Some fine tuning

* Add defaults checking for bot methods

* Missing tests for TestCallbackQuery

* Test edit_message_media with defaults & some comments

* Fix cryptography requirement

* drop debug prints

* Remove debug prints

* Another try

* Try to fix coverage & logs

* Rearrange test order

* increase coverage

* Try to fix coverage reports

* address review

* Adapt tests like in #2386

* fix CI

* fix CI
2021-02-19 19:07:48 +01:00
Harshil eb993db473 Add Missing telegram.SecureValue to init and Docs (#2398) 2021-02-19 17:46:52 +01:00
Bibo-Joshi c77ef7eef3 Deprecate MessageQueue (#2393) 2021-02-19 17:29:50 +01:00
Bibo-Joshi a34f0b9bee Make cryptography Dependency Optional & Refactor Some Tests (#2386)
* Make cryptography optional

* Try fixing CI

* Try some more

* Update pytest, mypy & pyupgrade, refactor test_meta, hope that things start to work

* Fix filterwarnings

* Mama mia! Here we go again!

* Add stupid debug prints

* A new hope
2021-02-13 22:07:37 +01:00
Bibo-Joshi eee8921598 Update cryptography Dependency (#2370) 2021-02-08 19:28:54 +01:00
Hinrich Mahler 1902c0ac36 Bump version to v13.2 2021-02-02 20:57:14 +01:00
Bibo-Joshi 544a3fbf48 ConversationHandler: Docs & edited_channel_post behavior (#2339)
* Update docs & ignore edited channel posts

* typo

* Apply suggestions from code review

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>

* Update telegram/ext/conversationhandler.py

* fix pre-commit

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>
2021-02-01 19:26:03 +01:00
Bibo-Joshi 36d49ea9cd Doc Fixes (#2253)
* Render-fixes for BP

* docs: fix simple typo, submition -> submission (#2260)

There is a small typo in tests/test_bot.py.

Should read `submission` rather than `submition`.

* Type on rawapibot.py docstring

* typo

* Typo: Filters.document(s)

* Typo fix

* Doc fix for messageentity (#2311)

* Add New Shortcuts to Chat (#2291)

* Add shortcuts

* Add a note

* Add run_async Parameter to ConversationHandler (#2292)

* Add run_async parameter

* Update docstring

* Update test to explicitly specify parameter

* Fix test job queue

* Add version added tag to docs

* Update docstring

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>

* Doc nitpicking

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>
Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>

* Fix rendering in messageentity

Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>
Co-authored-by: zeshuaro <joshuaystang@gmail.com>
Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>

* fix: type hints for TelegramError

changed :class:`telegram.TelegramError` to :class:`telegram.error.TelegramError`

* fix: the error can be more then just a Telegram error

* Doc fix for inlinekeyboardbutton.py

added missing colon which broke rendering

* fix: remove context argument and doc remark

look at us already being in post 12

* use rtd badge

* filters doc fixes

* fix some rendering

* Doc & Rendering fixes for helpers.py

Co-authored-by: Tim Gates <tim.gates@iress.com>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
Co-authored-by: zeshuaro <joshuaystang@gmail.com>
Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>
Co-authored-by: Harshil <ilovebhagwan@gmail.com>
2021-02-01 17:59:39 +01:00
Bibo-Joshi 25506f131d python-telegram-bot-raw (#2324)
* POC

* Remove decorator dependency

* Rework setup.py & build, add separate readme

* Move utils -> ext.utils

* Move pytz dep to ext

* Try fixing timing stuff

* Add 'Typed' classifier

* Update README_RAW.rst

Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>

* Some wording

* Deprecation warnings for moved tg.utils

* Tests for Promise

* Test time-helpers without pytz

* Try fixing time-helper tests

* Merge master

Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
2021-01-30 14:15:39 +01:00
Bibo-Joshi 70aba136e4 Reduce Usage of typing.Any (#2321)
* Use object instead of Any where possible

* Revert a lof of noise from the PR
2021-01-30 11:38:54 +01:00
Stɑrry Shivɑm 40995b19fe Extend Deeplinking Example (#2335)
* extend deeplinking example

Signed-off-by: starry69 <starry369126@outlook.com>

* use username property instead of get_me calls

Signed-off-by: starry69 <starry369126@outlook.com>
2021-01-28 17:12:13 +01:00
zeshuaro 32da6d6fce Add Missing Shortcuts to Message (#2330)
* Add shortcuts for Message

* Fix docs in Chat

* Update docs in Message

* Fix tests in request.py

* Rollback changes made to fix mypy errors
2021-01-23 13:40:19 +01:00
Bibo-Joshi f31787a8ef Add pyupgrade to pre-commit Hooks (#2301)
* Add pyupgrade to pre-commit

* update test_chat
2021-01-17 23:24:20 +01:00
Stɑrry Shivɑm b43a599e53 Rich Comparison for Bot (#2320)
* Make telegram.Bot comparable

Signed-off-by: starry69 <starry369126@outlook.com>

* Address review

Signed-off-by: starry69 <starry369126@outlook.com>

* Enhance tests & add docstring about comparison

Signed-off-by: starry69 <starry369126@outlook.com>

* Minor doc fix

* Extend tests

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2021-01-17 09:23:36 +01:00
Bibo-Joshi 7a3fd83570 Add PR Template (#2299)
* Add PR template

* Apply suggestions from code review

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>

* Some more

* reformulate

* Apply suggestions from code review

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>
2021-01-16 13:57:25 +01:00
Bibo-Joshi 9ada2a7cca Drop Nightly Tests & Update Badges (#2323) 2021-01-16 11:27:06 +01:00
Eana Hufwe be54cf4ece Fix Escaping in Nested Entities in Message Properties (#2312)
* fix: overly escape and offset error markdown v2 symbols when nested

Signed-off-by: Eana Hufwe <ilove@1a23.com>

* fix: nested entity escape for HTML parsers and tests

Signed-off-by: Eana Hufwe <ilove@1a23.com>
2021-01-12 19:33:26 +01:00
zeshuaro 0c9915243d Add run_async Parameter to ConversationHandler (#2292)
* Add run_async parameter

* Update docstring

* Update test to explicitly specify parameter

* Fix test job queue

* Add version added tag to docs

* Update docstring

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>

* Doc nitpicking

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>
Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2021-01-09 20:05:58 +01:00
Bibo-Joshi 9930725e2a Add New Shortcuts to Chat (#2291)
* Add shortcuts

* Add a note
2021-01-09 17:48:56 +01:00
Bibo-Joshi ffd675daec Adjust Calling of Dispatcher.update_persistence (#2285)
* Adjust calling of update_persistence

(cherry picked from commit 89c522d883)

* Fix tests and stuff
2021-01-07 21:31:00 +01:00
Bibo-Joshi 6a831f926b Add New Constant MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH (#2282) 2021-01-07 21:27:51 +01:00
Bibo-Joshi 6903d58142 Update Copyright (#2289)
* Update coypright headers

* typos
2021-01-04 17:24:14 +01:00
Bibo-Joshi 07b6ee69d2 Update Copyright (#2287)
Signed-off-by: starry69 <starry369126@outlook.com>
2021-01-03 10:30:02 +01:00
starry69 c8a3c31dcc Update copyright of docs config
Signed-off-by: starry69 <starry369126@outlook.com>
2021-01-03 14:42:51 +05:30
starry69 91e0271e4c Update copyright headers
Signed-off-by: starry69 <starry369126@outlook.com>
2021-01-03 10:40:24 +05:30
Hinrich Mahler 9ddb361f76 Revert "Adjust calling of update_persistence"
This reverts commit 89c522d883.
2021-01-01 21:44:23 +01:00
Hinrich Mahler 89c522d883 Adjust calling of update_persistence 2021-01-01 21:40:42 +01:00
Bibo-Joshi 2effff8254 Change Order of Class DocStrings (#2256) 2020-12-30 15:59:50 +01:00
Bibo-Joshi 2788191657 Add macOS to Test Matrix (#2266)
* Try testing on macos

* Skip JobQueue tests on macos

* Try stabilizing tests

* Try harder
2020-12-30 15:39:38 +01:00
Bibo-Joshi aec6d3bada Explicit Signatures for Shortcuts (#2240)
* First POC

* Actually get it to work

* locals-less POC

* pre-commit

* Work on Message shortcuts, update some annotations in Bot methods

* Tippity Tappity, coding stuff

* CallbackQuery

* InlineQuery & Some other stuff

* Media Classes and PassportFile

* Fix tests

* PreCheckout- & ShippingQuery

* User

* Fix tests

* Chat

* Update rawapibot

* Update annotations for answer_inline_query
2020-12-30 13:41:07 +01:00
Bibo-Joshi 80b34811ab Handle Bytes as File Input (#2233)
* Handle bytes file input

* fix tests

* Docs, Tests & Rearrangements

* Use versioning directives

* fixing type hinting of send_photo

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>
2020-12-18 11:20:03 +01:00
Poolitzer 2d7a974b8f Start Using Versioning Directives in Docs (#2252)
* adding versioning in sphinx, closing #2250

* adding version requirement to contributors doc
2020-12-17 19:05:12 +01:00
Bibo-Joshi ef703d19e9 Doc Fixes (#2225)
* Fix dscr for soccer ball values

* Update docs of BasePersistence regarding abc

* elaborate run_daily docs

* Make docstring of send_document.filename more precise

* increase bot API and add Discussion link

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>
2020-12-17 09:29:17 +01:00
Bibo-Joshi 77a8c64f6c Improve Annotations & Docs of Handlers (#2243)
* Improve typing & docs of handlers

* Apply suggestions from code review

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>

Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>
2020-12-16 17:34:57 +01:00
Bibo-Joshi 786762bb73 Allow Passing Custom Filename For All Media (#2249)
* Add filename arg to send_media methods and InputMedia*

* Tests
2020-12-16 14:28:53 +01:00
Bibo-Joshi e0dbb99b08 Add quote kwarg to Message.reply_copy (#2232)
* add quote kwrag to Message.reply_copy

* Add docs & update tests
2020-12-03 20:51:47 +01:00
Hinrich Mahler 73b0e29a30 Bump version to v13.1 2020-11-29 17:01:03 +01:00
Bibo-Joshi d27d1ea4d5 Correct Some Type Hints (#2204)
* Correct some reply_markup hints

* Fix type hints of effective_message_type

* fixup
2020-11-29 16:32:38 +01:00
Bibo-Joshi ca04daf782 Doc Fixes & Extensions (#2201)
* Add note on dispatcherhandlerstop to conversationhandler

* Fine tune @run_async deprecation warning

* Refine docs of JobQueue.jobs/get_jobs_by_name
2020-11-29 16:25:47 +01:00
Bibo-Joshi ae9ce60b55 API 5.0 (#2181)
Co-authored-by: poolitzer <25934244+Poolitzer@users.noreply.github.com>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
2020-11-29 16:20:46 +01:00
Bibo-Joshi 1cd3a0a156 Handle Non-Binary File Input (#2202)
* Don't fail if stream is not bytes

* move logger

* Refactor InputFile.is_image

* Use f-strings

* some clean up
2020-11-24 20:31:34 +01:00
Bibo-Joshi 58b9882021 Use F-Strings Where Possible (#2222) 2020-11-23 22:09:29 +01:00
Bibo-Joshi df6d5f0840 Fix bugs in replace/insert_bot (#2218)
* Fix bugs in replace/insert_bot

* Some tweaks
2020-11-22 11:08:46 +01:00
Stɑrry Shivɑm 425716f966 Add Defaults.run_async (#2210)
* Add Defaults.run_async support

Signed-off-by: starry69 <starry369126@outlook.com>

* Address some requested changes.

Signed-off-by: starry69 <starry369126@outlook.com>

* Add tests for defaults.run_async

Signed-off-by: starry69 <starry369126@outlook.com>

* Fix tests logic & add default value support for dp.add_error_handler

Signed-off-by: starry69 <starry369126@outlook.com>

* Fix tests, with requested changes

Signed-off-by: starry69 <starry369126@outlook.com>

* Add tests for error_handler

Signed-off-by: starry69 <starry369126@outlook.com>

* try to fix pre-commit

Signed-off-by: starry69 <starry369126@outlook.com>

* Enhance tests & address suggested changes

Signed-off-by: starry69 <starry369126@outlook.com>

* Improve docs

Signed-off-by: starry69 <starry369126@outlook.com>
2020-11-17 21:31:01 +01:00
Bibo-Joshi 8d9bb26cca Improve Handling of Custom Objects in BasePersistence.insert/replace_bot (#2151)
* Handle unpickable objects

* Improve coverage

* Add user warning

* make comparison to REPLACED_BOT safe

* make pre-commit happy

* Shorten warning
2020-11-14 03:08:18 +01:00
Bibo-Joshi d1438a9b23 Add XOR Filters and make Filters.name a Property (#2179)
* XOR Filters and make Filters.name a property

* add XORFilter to __all__

* Change example
2020-11-07 08:44:45 +01:00
Evgeny Denisov 27b03edc59 Expand Type Hints to Tuples (#2167)
Co-authored-by: Pranjalya <pranjalyawarrior@gmail.com>
2020-11-07 08:26:32 +01:00
Evgeny Denisov ac449deb5d Add Filters.document.file_extension (#2169)
Co-authored-by: Matheus Lemos <matheuslemosf@protonmail.com>
Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>
2020-11-06 18:41:54 +01:00
Bibo-Joshi 3b9187ed5a Rename kwargs to _kwargs where possible (#2182) 2020-11-05 18:12:01 +01:00
Bibo-Joshi 9831458e22 Improve and Expand CallbackQuery Shortcuts (#2172)
* CallbackQuery.delete_message()

* Improve internals of CQ shortcuts
2020-11-05 17:11:35 +01:00
Marco Fincato a0cd6e8fef Add Filters.caption_regex (#2163)
* Check caption in Filters.regex

Added regex matching for message caption in Filters.regex.

* Moved caption check to Filters.caption_regex

* Added caption_regex tests

The same as for regex, with only the content of the message changed, that is now inside caption.

* Fixed pre-commit tests

Lines too long and an additional blank line

* Moved line break to comply

* Reformatted code with black

* Added docstrings
2020-11-04 20:54:24 +01:00
Bibo-Joshi 8e7c0d6976 Comply with PEP561 (#2168)
* Comply with PEP561

* Try harder

* third time's (hopefully) a charme
2020-11-01 19:33:01 +01:00
Bibo-Joshi 92b9370c23 Improve Code Quality (#2131)
* Make pre-commit more strict

* Get pylint to read setup.cfg

* Make pylint & mypy happy aka ignore all the things

* use LogRecord.getMessage() in tests

* Make noam happy

* Update both pylint & mypy while we're at it

* Bring reqs-dev and makefile up to speed

* try making pre-commit happy

* fix jobqueue tests on the fly
2020-10-31 16:33:34 +01:00
GauthamramRavichandran 237e73bfb4 Add Filters.chat_type (#2128)
* add supergroup filter

* add chat_type filter

* re-implemented ChatType

* Add deprecations, improve tests

* Fix some docs

* Fix black

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2020-10-29 19:42:08 +01:00
Bibo-Joshi ff3fd34f08 Update Credits (#2161) 2020-10-27 17:43:23 +01:00
Bibo-Joshi 83791d34e7 Fix Regex in Configuration of Black Formatter (#2159) 2020-10-23 14:46:56 +02:00
Bibo-Joshi 02cd7b642f Fix Configuration of Black Formatter (#2158)
* Fix Black in pre-commit

* Fix black some more
2020-10-23 13:40:02 +02:00
NikitaPirate 165a24e13d Add Convenience Properties for Service Chats and Anonymous Admins (#2147)
Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>
2020-10-18 16:15:56 +02:00
Michael K 88440079e3 Update Wheel Settings (#2142)
Wheels are only universal if they support both Python 2 and 3.
2020-10-15 21:50:25 +02:00
Bibo-Joshi 9be4c7563b Improve Type Hinting for Class Variables (#2136) 2020-10-15 18:50:47 +02:00
Timur Kushukov b554f1a85d Update timerbot.py to v13.0 (#2149)
* Update timerbot example (#2144)

* update timerbot example (suggestions from review)

Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>
2020-10-15 18:48:12 +02:00
NikitaPirate 3b4559dd95 Overhaul Constants (#2137)
* Move all constants to constants.py and documentation refactor.

* Move all constants to constants.py and documentation refactor.

* Overhaul constants

* Overhaul constants

* Minor docstring change

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2020-10-13 17:58:36 +02:00
Bibo-Joshi 9ae48fecfe Add Python 3.9 to Test Matrix (#2132)
* Add Py 3.9 to tests

* update setup-python action
2020-10-11 11:47:19 +02:00
Bibo-Joshi 6af6648509 Switch Codecov to GitHub Action (#2127) 2020-10-10 10:50:19 +02:00
Bibo-Joshi 264b2c9c72 Switch Code Formatting to Black (#2122)
* Swtich code formatting to Black

* Update docs

* Fix tests

* TRy fixing pre-commit
2020-10-09 17:22:07 +02:00
Nano 8efb05290a Specify Required pytz Version (#2121) 2020-10-09 09:15:34 +02:00
Harshil 83a8874bb5 Correct Some Type Hints (#2118)
* Add string type hint

* fixed type hint in send_message()

* change type hint of send_chat_action to str

* make flaky happy

* fixed another type hint in edit_message_text
2020-10-09 08:22:44 +02:00
Hinrich Mahler bf68942c91 Bump to v13.0 2020-10-07 21:23:55 +02:00
Hinrich Mahler 5fd7606084 Type Hinting (#1920) 2020-10-07 20:30:41 +02:00
Bibo-Joshi 103b115486 Defaults.tzinfo (#2042) 2020-10-07 20:30:41 +02:00
Hinrich Mahler b07e42ef33 Make context-based callbacks the default setting (#2050) 2020-10-07 20:30:41 +02:00
Bibo-Joshi 3842846b2d Refactor Handling of Message VS Update Filters (#2032)
* Refactor handling of message vs update filters

* address review
2020-10-07 20:30:41 +02:00
Bibo-Joshi 7daddfb54d Refactor handling of default_quote (#1965)
* Refactor handling of `default_quote`

* Make it a breaking change

* Pickle a bots defaults

* Temporarily enable tests for the v13 branch

* Temporarily enable tests for the v13 branch

* Refactor handling of kwargs in Bot methods (#1924)

* Unify kwargs handling in Bot methods

* Remove Request.get, make api_kwargs an explicit argument, move note to head of Bot class

* Fix test_official

* Update get_file methods

* Refactor JobQueue (#1981)

* First go on refactoring JobQueue

* Temporarily enable tests for the v13 branch

* Work on tests

* Temporarily enable tests for the v13 branch

* Increase coverage

* Remove JobQueue.tick()

* Address review

* Temporarily enable tests for the v13 branch

* Address review

* Dispatch errors

* Fix handling of job_kwargs

* Remove possibility to pass a Bot to JobQueue

* Refactor persistence of Bot instances (#1994)

* Refactor persistence of bots

* User BP.set_bot in Dispatcher

* Temporarily enable tests for the v13 branch

* Add documentation

* Add warning to Updater for passing both defaults and bot

* Address review

* Fix test
2020-10-07 20:30:41 +02:00
Bibo-Joshi 2d4d48b89d Extend rich comparison of objects (#1724)
* Make most objects comparable

* ID attrs for PollAnswer

* fix test_game

* fix test_userprofilephotos

* update for API 4.7

* Warn on meaningless comparisons

* Update for API 4.8

* Address review

* Get started on docs, update Message._id_attrs

* Change PollOption & InputLocation

* Some more changes

* Even more changes
2020-10-07 20:30:41 +02:00
Bibo-Joshi 2381724b7c Refactor persistence of Bot instances (#1994)
* Refactor persistence of bots

* Use BP.set_bot in Dispatcher

* Add documentation
2020-10-07 20:30:41 +02:00
Bibo-Joshi 19a4f9e53a Refactor JobQueue (#1981)
* First go on refactoring JobQueue

* Temporarily enable tests for the v13 branch

* Work on tests

* Temporarily enable tests for the v13 branch

* Increase coverage

* Remove JobQueue.tick()

* Address review

* Temporarily enable tests for the v13 branch

* Address review

* Dispatch errors

* Fix handling of job_kwargs

* Remove possibility to pass a Bot to JobQueue
2020-10-07 20:30:41 +02:00
Bibo-Joshi 3930072659 Refactor handling of kwargs in Bot methods (#1924)
* Unify kwargs handling in Bot methods

* Remove Request.get, make api_kwargs an explicit argument, move note to head of Bot class

* Fix test_official

* Update get_file methods
2020-10-07 20:30:41 +02:00
Bibo-Joshi 5555582b72 Doc Fixes/Additions (#2094)
* Add notes on thumbs being ignored for small video files

* Fix some cross refs

* Add not to DictPersistence about it not actually writing to file

* Fix reply_to_message docs of Message
2020-10-07 17:12:05 +02:00
Delgan e67b995e64 Make Errors picklable (#2106)
* Fix TypeError while unpickling TelegramError (and children)

* Add more extensive unit tests for errors pickling

* Move error pickling tests back to "test_error.py"

* Add test making sure that new errors are covered by tests

* Make meta test independent of sorting

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2020-10-04 18:15:36 +02:00
Bibo-Joshi 0d419ed6b4 Refactor Dispatcher.run_async (#2051) 2020-10-04 17:20:33 +02:00
Bibo-Joshi 97adcdf538 Automatic Pagination for answer_inline_query (#2072)
* Auto Pagination

* Fix test_official

* Get things to actually work

* Fine tune

* Tweak tests

* Address review

* Add warning to answer_inline_query
2020-09-27 14:11:49 +02:00
Poolitzer 2989108e95 Fix setting thumbs with send_media_group (#2093)
* fix: uploading thumb in media group

* add test

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2020-09-19 16:50:34 +02:00
Bibo-Joshi 897a20d758 Link Ask-Right Wiki Page in Question Template (#2090)
* Link ask-right wiki page in question template

* typo
2020-09-16 20:53:48 +02:00
Bibo-Joshi ed3a9b64e2 Add Note on PyPy to Readme (#2089) 2020-09-16 20:53:19 +02:00
Bibo-Joshi 49c0c9e4d1 Make MessageHandler filter for Filters.update first (#2085)
* Tweak MessageHandlers filters

* Improve test
2020-09-14 17:55:01 +02:00
Bibo-Joshi bb34c79909 Fix Webhook not working on Windows with Python 3.8+ (#2067)
* Fix start_webhook NotImplementedError on windows with python3.8

* Fine-tune and add tests.

* Minor fixes

* typos

* Make Codacy happy

Co-authored-by: n5y <41209360+n5y@users.noreply.github.com>
2020-08-25 22:21:24 +02:00
Bibo-Joshi a0720b9ac6 Documentation Improvements (#2008)
* Minor doc updates, following official API docs

* Fix spelling in Defaults docstrings

* Clarify Changelog of v12.7 about aware dates

* Fix typo in CHANGES.rst (#2024)

* Fix PicklePersistence.flush() with only bot_data (#2017)

* Update pylint in pre-commit to fix CI (#2018)

* Add Filters.via_bot (#2009)

* feat: via_bot filter

also fixing a small mistake in the empty parameter of the user filter and improve docs slightly

* fix: forgot to set via_bot to None

* fix: redoing subclassing to copy paste solution

* Cosmetic changes

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>

* Update CHANGES.rst

Fixed Typo

Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>
Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>

* Update downloads badge, add info on IRC Channel to Getting Help section

* Remove RegexHandler from ConversationHandlers Docs (#1973)

Replaced RegexHandler with MessageHandler, since the former is deprecated

* Fix Filters.via_bot docstrings

* Add notes on Markdown v1 being legacy mode

* Fixed typo in the Regex doc.. (#2036)

* Typo: Spelling

* Minor cleanup from #2043

* Document CommandHandler ignoring channel posts

* Doc fixes for a few telegram.ext classes

* Doc fixes for most `telegram` classes.

* pep-8

forgot the hard wrap is at 99 chars, not 100!
fixed a few spelling mistakes too.

* Address review and made rendering of booleans consistent

True, False, None are now rendered with ``bool`` wherever they weren't in telegram and telegram.ext classes.

* Few doc fixes for inline* classes

As usual, docs were cross-checked with official tg api docs.

* Doc fixes for telegram/files classes

As usual, docs were cross-checked with official tg api docs.

* Doc fixes for telegram.Game

Mostly just added hyperlinks. And fixed message length doc.

As usual, docs were cross-checked with official tg api docs.

* Very minor doc fix for passportfile.py and passportelementerrors.py

Didn't bother changing too much since this seems to be a custom implementation.

* Doc fixes for telegram.payments

As usual, cross-checked with official bot api docs.

* Address review 2

Few tiny other fixes too.

* Changed from ``True/False/None`` to :obj:`True/False/None` project-wide.

Few tiny other doc fixes too.

Co-authored-by: Robert Geislinger <mitachundkrach@gmail.com>
Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>
Co-authored-by: GauthamramRavichandran <30320759+GauthamramRavichandran@users.noreply.github.com>
Co-authored-by: Mahesh19 <maheshvagicherla99438@gmail.com>
Co-authored-by: hoppingturtles <ilovebhagwan@gmail.com>
2020-08-24 19:35:57 +02:00
Bibo-Joshi faa93fbf75 Refine Codecov Settings (#2061) 2020-08-21 23:37:07 +02:00
Bibo-Joshi da452df07d Allow DispatcherHandlerStop in ConversationHandler (#2059)
* First go

* Fix bug with nested convs
2020-08-21 23:20:28 +02:00
Bibo-Joshi 3304cc5c90 Fix Lock-Bot configuration (#2053)
* Run lock bot twice a day and don't give a reason for locking

* Fix indentation

* Fix invalid time
2020-08-18 20:52:43 +02:00
Bibo-Joshi 9105d83d37 Refine Lock-Bot (#2052)
* Run lock bot twice a day and don't give a reason for locking

* Fix indentation
2020-08-18 20:43:27 +02:00
Bibo-Joshi b6b42b2043 Switch from Lock-Bot to GH Actions (#2049) 2020-08-16 10:43:30 +02:00
Bibo-Joshi f857e1c23b Add Lock-Bot (#2048) 2020-08-14 12:57:18 +02:00
Bibo-Joshi fc5844c13d Add missing shortcuts (#2043)
* Add all the methods

* Add tests and fix some typos

* A few more minor changes

* Address review
2020-08-13 13:39:43 +02:00
Leonardo Rezende dea24bcb7c Refine pollbot.py example (#2047)
* pollbot.py example was sending the poll to the effective_user and not effective_chat... well, it's a poll to be answered by multiple uses.

* Use User.mention_html() shortcut

Co-authored-by: Leonardo <leonardo.rezende@trt19.jus.br>
Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2020-08-13 13:38:23 +02:00
Bibo-Joshi 2789fd2bff Refine Filters in examples (#2027) 2020-07-18 12:17:42 +02:00
Bibo-Joshi d6f8077a50 Rename echobot examples (#2025) 2020-07-16 19:17:57 +02:00
Poolitzer 0189442525 Add Filters.via_bot (#2009)
* feat: via_bot filter

also fixing a small mistake in the empty parameter of the user filter and improve docs slightly

* fix: forgot to set via_bot to None

* fix: redoing subclassing to copy paste solution

* Cosmetic changes

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2020-07-14 21:51:36 +02:00
Bibo-Joshi fd0325fbe5 Update pylint in pre-commit to fix CI (#2018) 2020-07-10 12:57:27 +02:00
Bibo-Joshi ff4bb15fef Fix PicklePersistence.flush() with only bot_data (#2017) 2020-07-10 11:10:53 +02:00
ikkemaniac 9288e4f2e4 Fix docstring of Message.reply_media_group (#2005) 2020-06-25 07:54:23 +02:00
ikkemaniac e60318166e Add test for clean argument of Updater.start_polling/webhook (#2002)
* added test for 'clean' argument passed to 'start_polling()'

* remove TODO

* prettify

* remove bool from func name

* improve name-ing of fake update func

* cleanup class and nameing

* replace while for for

* swap valueerror for runtimeerror

* remove all other code to reduce testing

* add comments

* don't raise error, complete cycle and assert

* remove inf loop protection

* Revert "remove all other code to reduce testing"

This reverts commit 4566a1debd.

* remove error parametrization

* remove comment

* remove pass from class

* rename update_id to offset as the original get_updates() takes argument offset (which is the update_id)

* rename test func to match original func

* fix comment

* shorten for loop

* mock get_updates() behavior when 'offset' is passed. Assert with get_updates()

* remove other functions to reduce testing

* replicate original get_updates()

* move fakeupdate class and list creation outside get_updates and store in var

* loop from 0 to make update_id consistant w array key, just easier to debug

* update comments

* Revert "remove other functions to reduce testing"

This reverts commit 1fb498a6cc.

* fix typo

* Revert "fix typo"

This reverts commit ade9fec609.

* Revert "Revert "remove other functions to reduce testing""

This reverts commit 734de1371c.

* Revert "update comments"

This reverts commit f3a032e75e.

* Revert "loop from 0 to make update_id consistant w array key, just easier to debug"

This reverts commit 0c6881d8a1.

* Revert "move fakeupdate class and list creation outside get_updates and store in var"

This reverts commit 71de999300.

* Revert "replicate original get_updates()"

This reverts commit 5d0710ac3a.

* Revert "remove other functions to reduce testing"

This reverts commit 1fb498a6cc.

* Revert "mock get_updates() behavior when 'offset' is passed. Assert with get_updates()"

This reverts commit 8c727ba1e8.

* loop from 0 to make update_id consistant w array key, for consitency

Co-authored-by: ikkemaniac <ikkemaniac@localhost>
2020-06-24 00:25:58 +02:00
Hinrich Mahler 15268acb27 Bump to v12.8 2020-06-22 20:20:00 +02:00
Poolitzer 927502e588 API 4.9 (#1980)
* Add Basketball Dice

Added Basketball Dice Variation

* Update dice.py

* Update dice.py

* Update telegram/dice.py

Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>

* Update bot.py

* Update filters.py

* Update test_filters.py

* Fixed whitespaces

* Update dice.py

* Fix line length

* adding dice values to docstring

* adding via_bot to message and thumb_mime_type to iqresults

* feat: updating docs

* feat: improving message attribute test

* Fix flake8

Co-authored-by: Jannik <32801117+code1mountain@users.noreply.github.com>
Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>
2020-06-22 20:09:52 +02:00
Yan 0af5cc2db8 Don't override builtin method help() in examples (#1997)
* Do not override builtin method help()

* Rename inlinebot and inlinekeyboard /help function to not conflict with builtin
2020-06-16 17:07:05 +02:00
Bibo-Joshi 6005861f46 Stabilize CI (#2000) 2020-06-15 18:45:38 +02:00
Nils K 8406889179 Remove Python 2 Support (#1715)
* Remove usages of python-future lib

* Remove python2 datetime.timezone replacement

* Remove python2 workaround in InputFile.__init__

* Remove import of str necessary for python2

* Remove urllib2 import necessary for python2

* Remove a mention of python 2 in doc

* Remove python 2 from travis config file

* Remove python 2 from appveyor config

* Remove python2 from debian build rules

* Remove unnecessarry aliasing of time.perf_counter

* Remove python 2 from github workflow

* Remove mention of python 2 in descriptions/readme

* Remove version check for queue import

* Remove version checks in tests

* Adjust docs to correctly mention supported version

* Fix indentation

* Remove unused 'sys' imports

* Fix indentation

* Remove references to mq.curtime in tests

* Replace super calls by argumentsless version

* Remove future dependency

* Fix error in de_json declaration

* Use python3 metaclass syntax

* Use implicit inheriting from object

* Remove accidentally committed .vscode folder

* Use nameless f-string and raw string

* Fix regex string literal syntax

* Remove old style classes

* Run pyupgrade

* Fix leftover from automatic merge

* Fix lint errors

* Update telegram/files/sticker.py

Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>
2020-06-15 18:20:51 +02:00
n5y a4e78f6183 Add standalone example on error handlers (#1983)
* Remove error handlers from examples

Most examples use the same error handler, that error handler logs
update.to_dict but doesn't log error traceback. Hiding error traceback
is quite bad, removing the error handler entirely causes PTB to use
default error logging which does include error traceback.

* adding error handling example

* Change error handler example

Including:
- Change the telegram message to include usual python error message.
- HTML-escape the strings used to build the telegram message.
- Capitalize comments and add more empty lines to hopefully unify the
  style with other examples, at least a bit.
- Reorder imports.

* Add an error-rising command to the error handler example

* Slightly change example error handler docstring and comments

* Make telegram message sent by the error handler example more readable

* Rename error_handler.py to errorhandlerbot.py and add a start command

* Change error handler example to work without developer chat id

* Revert "Change error handler example to work without developer chat id"

This reverts commit c4efea6f

* Make bot token a module level constant in the error handler example

Otherwise the example will require two edits 40 lines apart to run.

* Show chat id in start command of the error handler example

The example requires you to set developer chat id, this change will
make things easier for users that don't know how to see their chat id.

* Add errorhandlerbot.py to the examples folder readme

Co-authored-by: poolitzer <25934244+poolitzer@users.noreply.github.com>
Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>
2020-06-12 18:50:12 +02:00
Bibo-Joshi 8c6cb44a85 Update examples Readme (#1995) 2020-06-12 13:58:20 +02:00
n5y ac7cc7fe5e Ignore private attributes in TelegramObject.to_dict() (#1989) 2020-06-10 22:21:25 +02:00
Bibo-Joshi a42b68933c Add User.send_poll (#1968) 2020-05-27 21:59:49 +02:00
ѕнιναм c2d91c752f Typo-Fix (#1962) 2020-05-22 15:32:03 +02:00
Bibo-Joshi 5057825586 Doc fixes (#1940)
* Update notes on editing messages

* Update thumb and InputMedia* doc strings

* Fix attribute docstring for Updater.user_sig_handler

* Improve rendering for CCs attributes

* fix doc str for InputMedia*.media attribute

* Minor fix
2020-05-15 15:59:41 +02:00
Bibo-Joshi 613175b2c4 Allow updating ids/usernames of Filters.{user, chat} (#1757)
* Make Filters.user attrs mutable

* Add test_filters_user_empty_args

* Add test_filters_user_empty_args

* fix locks

* Make codecov happy

* Make user_ids and usernames sets

* Correct doc string

* Address review

* Review Vol. II

* Apply suggestions from code review

Co-authored-by: Noam Meltzer <tsnoam@gmail.com>

* Review Vol III.

* propery setter is now only a wrapper to a private method + more cleanups

pylint complained on some extra stuff, so cleaned them as well

* Review Vol. IV

* Review Vol. V

* Apply changes to Filters.chat

Co-authored-by: Noam Meltzer <tsnoam@gmail.com>
2020-05-10 12:15:11 +02:00
Bibo-Joshi 1330696259 Improve readability of nested conversation example (#1943) 2020-05-04 16:59:51 +02:00
n5y 5898e1fe7a Remove NullHandlers (#1913) 2020-05-03 10:28:03 +02:00
Bibo-Joshi b6dec118c1 Update contribution guide and stale bot (#1937)
* * Remove developers Mail from contrib guide
* stricter settings for stale bot

* Remove link, too ...

* Link to TG Group instead
2020-05-02 22:24:12 +02:00
Hinrich Mahler 186fd1b418 Bump version to v12.7 2020-05-02 12:14:38 +02:00
Hinrich Mahler 284786fdb8 Fix doc string of run_monthly 2020-05-02 12:14:01 +02:00
Bibo-Joshi c7c56ad24e Api 4.8 (#1917)
* API 4.8

* Elaborate docs

* Address review

* Fix Message.to_json/dict() test

* More coverage

* Update telegram/bot.py

Co-authored-by: Noam Meltzer <tsnoam@gmail.com>

Co-authored-by: Noam Meltzer <tsnoam@gmail.com>
2020-05-02 11:56:52 +02:00
D David Livingston ae17ce977e Add JobQueue.run_monthly() (#1705)
* added monthly job

* removed fold argument

* addressed pr comments

* addressed pr comments

* made changes from pr review

* updated comments

* clean up code

* Update .pre-commit-config.yaml

* Minor cleanup

* Update according to #1685, minor robustness changes

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2020-05-02 08:59:50 +02:00
Bibo-Joshi 7e231183c4 Add tzinfo kwarg to from_timestamp() (#1621)
* Add tz kwarg to from_timestamp()

* Correct handling of tzinfo=None

* Small Improvements

* None-tz yields naive dto

* Remove legacey compatibility of UTC stuff

* Update telegram/utils/helpers.py

Co-authored-by: Noam Meltzer <tsnoam@gmail.com>
2020-05-01 22:55:13 +02:00
Bibo-Joshi 8427346a0d Add supegroup for each test bot (#1919) 2020-05-01 21:29:18 +03:00
Bibo-Joshi 632b989d90 Use @abstractmethod instead of raising NotImplementedError (#1905) 2020-05-01 21:27:34 +03:00
Bibo-Joshi 76567ba635 Stabilize CI (#1931) 2020-05-01 13:27:46 +02:00
Bibo-Joshi 2bd3f2a65a Render Notes correctly (#1914)
* Renders Notes in JobQueues docs correctly

* Notes: -> Note:
2020-04-25 12:34:13 +02:00
Bibo-Joshi 26a5006bf1 Update question template (#1910)
* Update formulation in question template

* grammar
2020-04-20 18:11:48 +02:00
Andrej730 110e2df443 Job.next_t (#1685)
* next_t property is added to Job class

Added new property to Job class - next_t, it will show the datetime when the job will be executed next time.
The property is updated during JobQueue._put method, right after job is added to queue.
Related to #1676

* Fixed newline and trailing whitespace

* Fixed PR issues, added test

1. Added setter for next_t - now JobQueue doesn't access protected Job._next_t.
2. Fixed Job class docstring.
3. Added test for next_t property.
4. Set next_t to None for run_once jobs that already ran.

* Fixed Flake8 issues

* Added next_t setter for datetime, added test

1. next_t setter now can accept datetime type.
2. added test for setting datetime to next_t and added some asserts that check tests results.
3. Also noticed Job.days setter raises ValueError when it's more appropriate to raise TypeError.

* Fixed test_warnings, added Number type to next_t setter

1. Changed type of error raised by interval setter from ValueError to TypeError..
2. Fixed test_warning after changing type of errors in Job.days and Job.interval.
3. Added Number type to next_t setter - now it can accept int too.

* Python 2 compatibility for test_job_next_t_property

Added _UTC and _UtcOffsetTimezone for python 2 compatibility

* Fixed PR issues

1. Replaced "datetime.replace tzinfo" with "datetime.astimezone"
2. Moved testing next_t setter to separate test.
3. Changed test_job_next_t_setter so it now uses non UTC timezone.

* Defining tzinfo from run_once, run_repeating

1. Added option to define Job.tzinfo from run_once (by when.tzinfo) and run_repeating (first.tzinfo)
2. Added test to check that tzinfo is always passed correctly.

* address review

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2020-04-18 15:08:16 +02:00
Bibo-Joshi 57546795c5 Notes on Filters.text accepting command messages (#1902) 2020-04-18 12:16:14 +02:00
Hinrich Mahler 314f87ec44 Bump version to v12.6.1 2020-04-11 09:53:29 +02:00
Bibo-Joshi 4bbcd51ef5 Fix serialization of reply_markups (#1889) 2020-04-11 09:44:40 +02:00
Hinrich Mahler 38a33581b1 Bump version to v12.6 2020-04-10 23:52:08 +02:00
Hinrich Mahler fe821c08e6 Doc Fixes 2020-04-10 23:43:58 +02:00
Harshil 0a9f4bfbdd Doc fixes (#1884)
* Bot.py doc fixes

All docs obtained from official Bot API docs

* made flake8 happy

* address review

Also improved consistency of `returns:` in docs

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2020-04-10 20:05:01 +02:00
Bibo-Joshi c4364c7166 GitHub Actions: Use checkout@v2 (#1887) 2020-04-10 19:57:52 +02:00
Bibo-Joshi d63e710784 API 4.7 (#1858)
* Pure API changes

* Address review

* set Bot.commands on successfull call of set_my_commands

* Get started on tests

* More tests!

* More Coverage!

* Reset changes in utils.request

* Filters.dice, Filters.dice.text

* more coverage

* Address review

* Address review

* Test stop_poll with reply_markup

* Test stop_poll also without reply_markup

* Rephrase note on 'dice'

* Fix grammar in note on Filters.dice

* update api version readme

* address review
2020-04-10 19:22:45 +02:00
Bibo-Joshi f379f54d5a Tweak persistence handling (#1827)
* Unify persistence updates in dispatcher

* Ensure user/chat_data is not None when updating it

* Update persistence after job runs

* Increase coverage
2020-04-10 13:23:13 +02:00
Bibo-Joshi bdf0cb91f3 Pass last valid context to TIMEOUT handlers (#1826) 2020-04-10 13:18:43 +02:00
Bibo-Joshi 3101ea8432 Favor concrete types over "Iterable" (#1882)
* Use concrete types instead of 'iterable'

* Fix overlooked docstring

* address review
2020-04-08 22:49:01 +02:00
Harshil beb8ba3db0 Doc Fixes (#1874)
* doc fixes

* Update AUTHORS.rst

* More doc fixes

All docs were obtained from official Bot API docs.

* Shortened line length

Did this so it passes codacy check

* Revert id docstring changes

* typo

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2020-04-07 17:25:17 +02:00
Bibo-Joshi f0b1aeb6fd Customize issue template chooser (#1880)
* Customize issue template chooser

* Improve wording
2020-04-07 15:45:17 +02:00
Bibo-Joshi d65558888e Add note on UTC to run_{repeating, once} (#1854) 2020-03-31 00:05:08 +02:00
Bibo-Joshi 61a66a32c8 Add tests for empty string as switch_inline_query(_current_chat) (#1635) 2020-03-31 00:03:45 +02:00
Hinrich Mahler 392d4e1a9c Bump version to v12.5.1 2020-03-30 18:25:53 +02:00
Andrej730 9cb34af65a Fix UTC as default tzinfo for Jobs (#1696)
1. Made sure that default tzinfo in JobQueue is UTC #1693.
2. Added test that checks that all methods by default set job.tzinfo as UTC.
2020-03-30 18:10:27 +02:00
Bibo-Joshi e9cb6675ca PrefixHandlers command and prefix editable (#1636)
* Rename internal list of PrefixHandler

* Make PFH.prefix and .command setable attributes

* Improve coverage
2020-03-30 17:49:50 +02:00
Bibo-Joshi 982f6707e1 Make ConversationHandler attributes immutable (#1756)
* Make ConversationHandler attributes immutable

* Add forgotten name property to test_immutable
2020-03-30 17:37:37 +02:00
Rys Artem d55d981e22 Reorder tests to make them more stable (#1835) 2020-03-30 17:06:24 +02:00
Iulian Onofrei f20953f7a9 Fix docs wording (#1855) 2020-03-30 00:32:06 +03:00
Bibo-Joshi e18220be10 Add docs for PollHandler and PollAnswerHandler (#1853) 2020-03-29 11:24:44 +02:00
Hinrich Mahler 90729c21d7 Bump version to v12.5 2020-03-29 10:01:11 +02:00
Poolitzer 55e3ecf9f8 API 4.6 (#1723)
* First take on 4.6 support

* improved docs

* Minor doc formattings

* added poll and poll_answer to filters

* added tests, fixed mentioned issues

* added poll_answer + poll filter tests

* Update docs according to official API docs

* introducing pollhandler and pollanswerhandler

* First take on 4.6 support

* improved docs

* Minor doc formattings

* added poll and poll_answer to filters

* added tests, fixed mentioned issues

* added poll_answer + poll filter tests

* Update docs according to official API docs

* introducing pollhandler and pollanswerhandler

* correct_option_id validated with None

when trying to send a poll with correct option id 0 it was failing. Now None check is done so that even when 0 is passed it is assigned.

* improving example

* improving code

* adding poll filter example to the pollbot.py

* Update Readme

* simplify pollbot.py and add some comments

* add tests for Poll(Answer)Handler

* We just want Filters.poll, not Filters.update.poll

* Make test_official fail again

* Handle ME.language in M._parse_*

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
Co-authored-by: Sharun Kumar <715417+sharunkumar@users.noreply.github.com>
2020-03-29 09:52:30 +02:00
Bibo-Joshi 8d2c7af1f3 API 4.5 (#1508)
* Allow for nested MessageEntities in Message._parse_markdown/html, adjust tests

* remove testing relict

* Use MessageEntitys new equality check (#1465)

* Remove unused variable

* Update to custom_title feature and slow_mode_delay option

Changes:

 - custom_title for ChatMember
 - new method setChatAdministratorCustomTitle for Bot
 - new slow_mode_delay for Chat

Update due to new API future `custom_title` from API 4.5 (https://core.telegram.org/bots/api#december-31-2019)

* Minor typo fix

* Comply with Flake8

* Add new MessageEntities and MarkdownV2

* Added file_unique_id attrs from API 4.5 and updated tests for it

* Fixed test and checked using flake8

* Fixed ChatPhoto documentation

* Fix Flake8

* Add setChatAdminCstmTitle to Bot

* Rename MDV2 methods

* Change files id attrs to unique id

* correct id_attrs for chat_photo

* Revert "temporarily skip tests failing b/c missing api 4.5 (#1738)"

This reverts commit 7cde6ca268.

* Fix text_markdown_v2 for monospace and text_links

* closing remarks from pieter

* Minor fix in escape_markdown, improve tests for it

* Fix offset bug in Message._parse_*

* Add test_chatphoto.py

* remove debug print from test_message.py

* try making codecov happy

* Update readme

* all hail codecov

* Improve Link handling for MarkdownV1 and adjust tests. Closes #1654

* Dont use beginning of pre-entity as language in _parse_markdown

* Remove debug print

* Dummy commit to try fix codecov

Co-authored-by: Hoi Dmytro <dmytro.hoi@gmail.com>
Co-authored-by: Dmytro Hoi <code@dmytrohoi.com>
Co-authored-by: poolitzer <25934244+poolitzer@users.noreply.github.com>
2020-03-28 16:37:26 +01:00
Pietu R e86ae25a62 Update CallbackQuery docstrings (#1818)
* mark chat_instance as required

* change ordering and add bot
2020-03-28 15:52:37 +01:00
Bibo-Joshi 2d3357bfeb Ignore Message.default_quote in test_official (#1848) 2020-03-28 14:32:16 +01:00
Bibo-Joshi b6f4783fd3 Revert accitendtal change in vendored urllib3 (#1775) 2020-03-28 12:15:51 +01:00
Bibo-Joshi f94ea9acbb Answer CQs and use edit_message_text in examples (#1721) 2020-03-28 12:07:23 +01:00
Aleksey 157652cfdf Fixe typo in edit_message_media (#1779) 2020-03-28 12:01:06 +01:00
Bibo-Joshi 104d0127aa Doc Fixes (#1778)
* Update docs according to official docs

* Add note to Updater according to #1772

* Add note on ChatPermissions

* Fix rendering for arg type of conversation_timeout
2020-03-28 11:49:47 +01:00
Bibo-Joshi e0b22e60b4 Update label in question template (#1840) 2020-03-20 23:19:32 +02:00
Bibo-Joshi 16613d7ce0 Don't comment when labeling issue as stale (#1829) 2020-03-12 10:14:00 +02:00
Bibo-Joshi eac7f02211 Add Py3.8 to docs (#1824) 2020-03-11 23:36:34 +02:00
Ak4zh 28ded6718e Add link property to Bot (#1770)
* added link property to bot

link property was available in User and Chat objects but not in Bot which was inconsistent.

* added 'link' property to Bot object

Bot will always have username so it does not require hasattr check

* add tests

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2020-03-09 23:17:05 +02:00
Noam Meltzer 13a641b3d7 Python 3.8 support (#1614)
* github workflow: Add python-3.8

* workaround for tornado issue on win with py>=3.8

* add workaround to webhookhandler

* Try making codecov and codacy happy

* Fix stupid mistake

Co-authored-by: Hinrich Mahler <hinrich.mahler@freenet.de>
2020-03-09 23:13:16 +02:00
Bibo-Joshi 27cccc7734 Add stale bot (#1820)
* add stale.yml for stale bot

* move stale.yml to the right directory
2020-03-08 23:40:22 +02:00
Noam Meltzer f7ec7a7c4c Use temporary directories in test_persistence (#1808) 2020-03-04 23:58:19 +02:00
Bibo-Joshi 8d6970ab02 Remove references to travis and appveyor (#1791) 2020-02-23 23:04:56 +02:00
Bibo-Joshi 1dc67dcbda Remove builtin names where possible (#1792) 2020-02-23 23:03:58 +02:00
Poolitzer 14f712b3c4 Update pre-commit config file (#1787) 2020-02-21 07:43:48 +02:00
Poolitzer 0fb0fbb93f Remove legacy CI files (#1783) 2020-02-20 17:33:03 +02:00
Pieter Schutz 72ecc696cb Bump to v12.4.2 2020-02-10 11:39:26 +01:00
Bibo-Joshi a447760411 Make sure PP can read files that dont have bot_data (#1760)
* Make sure PP can read files that dont have bot_data

* Improve workaround
2020-02-08 19:24:35 +02:00
Bibo-Joshi 6da529c46b Pass correct parse_mode to InlineResults if bot.defaults is None (#1763)
* Pass correct parse_mode to InlineResults if bot.defaults is None

* Add tests for inlinequeryresults with (default)parse_mode

* enhance tests
2020-02-08 18:52:22 +02:00
Noam Meltzer bd1b2fb6c1 Bump version to v12.4.1 2020-02-08 15:06:05 +02:00
Bibo-Joshi f9a8cd924c Make Filters.command only accept MessageEntity commands (#1744)
* Make Filters.command only accept MessageEntitie commands

* Add option to filters.command to allow cmds anywhere in the message

* Make codecov happy, also retroactive for #1631
2020-02-08 14:54:21 +02:00
Bibo-Joshi 7b3b278c7c Bump version to v12.4 2020-02-08 10:01:41 +02:00
Bibo-Joshi cf3635d408 make test_webhook_invalid_posts flaky (#1758) 2020-02-07 18:32:38 +02:00
Bibo-Joshi 84cfc6f7fa Rename Test suite … (#1750)
* Rename Test suite

* Actually change the badge in readme

* fix download without path arguments (#1591)

* fix download without path arguments

* fix download without path arguments

* solved downloading a file without file_path or custom_path

* if no file_path, download as file_id

* Add test case

* Elaborate doc string

Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>

* Add default values (#1490)

* added parse_mode parameter in Updater and in Bot class to set a default parse mode for bot

* DefaultValue

* Add default parse_mode everywhere

* Renome to default_parse_mode

* Test def parse_mode for send_*, edit_message_*

* Remove duplicate code in Input* classes

* Improve handling of def_p_m for Bot methods

* Remove unneeded deletion of kwargs

* Make @log preserve signature, add bots with defaults to tests

* Fix Codacy

* Fix Travis Error

* Add default_disable_notification

* Add default_disable_web_page_preview

* Add default_disable_timeout

* add default_disable_web_page_preview for InputTextMessageContent

* add default_quote

* Try fixing test_pin_and_unpin

* Just run 3.5 tests for paranoia

* add tests for Defaults

* Revert "Just run 3.5 tests for paranoia"

This reverts commit 1baa91a3a1.

* Tidy up parameters, move Defaults to ext

* Stage new files, because im an idiot

* remove debug prints

* change equality checks for DEFAULT_NONE

* Some last changes

* fix S&R error so that tests actually run

Co-authored-by: Ak4zh <agwl.akash@gmail.com>
Co-authored-by: Eldinnie <Eldinnie@users.noreply.github.com>

* Skip test relying on ordered dicts for 3.5 (#1752)

* Rename Test suite

* Actually change the badge in readme

Co-authored-by: Gabriel Simonetto <42247511+GabrielSimonetto@users.noreply.github.com>
Co-authored-by: Ak4zh <agwl.akash@gmail.com>
Co-authored-by: Eldinnie <Eldinnie@users.noreply.github.com>
2020-02-06 11:38:21 +01:00
Bibo-Joshi bf06fa2c18 Skip test relying on ordered dicts for 3.5 (#1752) 2020-02-06 11:28:27 +01:00
Bibo-Joshi 960c7a0c70 Add default values (#1490)
* added parse_mode parameter in Updater and in Bot class to set a default parse mode for bot

* DefaultValue

* Add default parse_mode everywhere

* Renome to default_parse_mode

* Test def parse_mode for send_*, edit_message_*

* Remove duplicate code in Input* classes

* Improve handling of def_p_m for Bot methods

* Remove unneeded deletion of kwargs

* Make @log preserve signature, add bots with defaults to tests

* Fix Codacy

* Fix Travis Error

* Add default_disable_notification

* Add default_disable_web_page_preview

* Add default_disable_timeout

* add default_disable_web_page_preview for InputTextMessageContent

* add default_quote

* Try fixing test_pin_and_unpin

* Just run 3.5 tests for paranoia

* add tests for Defaults

* Revert "Just run 3.5 tests for paranoia"

This reverts commit 1baa91a3a1.

* Tidy up parameters, move Defaults to ext

* Stage new files, because im an idiot

* remove debug prints

* change equality checks for DEFAULT_NONE

* Some last changes

* fix S&R error so that tests actually run

Co-authored-by: Ak4zh <agwl.akash@gmail.com>
Co-authored-by: Eldinnie <Eldinnie@users.noreply.github.com>
2020-02-06 11:22:56 +01:00
Gabriel Simonetto bacabbe767 fix download without path arguments (#1591)
* fix download without path arguments

* fix download without path arguments

* solved downloading a file without file_path or custom_path

* if no file_path, download as file_id

* Add test case

* Elaborate doc string

Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de>
2020-02-06 11:21:21 +01:00
Eldinnie d8dcdeea75 fix test_pin_and_unpin_message (#1749)
Make it flaky and timeout
2020-02-02 23:39:08 +01:00
isinstance 818475bd93 Fix Server response could not be decoded using UTF-8 (#1623)
Co-authored-by: Noam Meltzer <tsnoam@gmail.com>
2020-02-03 00:12:27 +02:00
Bibo-Joshi f97ac90af7 skip test_json on TestDictPersistence on py3.5 (#1748) 2020-02-03 00:10:48 +02:00
billchenchina 90eeb40ae8 Add base_file_url support for telegram.ext.Updater (#1679)
Co-authored-by: Noam Meltzer <tsnoam@gmail.com>
2020-02-02 23:47:02 +02:00
Bibo-Joshi 6d9d11b8bd Handler persistence for nested ConversationHandlers (#1711)
* Handler persistence for nested ConversationHandlers

* Add tests for persistence w/ nested CHs
2020-02-02 22:31:56 +01:00
Bibo-Joshi f6b663f175 bot_data as global memory (V12 version of #1322) (#1325)
* Update AUTHORS.rst

* Update AUTHORS.rst

* Add bot_data to CallbackContext as global memory

* Minor fixes in docstrings

* Incorp. req. changes, Flake8 Fixes

* Persist before stop

* Fix CI errors

* Implement #1342 for bot_data

* Add check pickle_persistence_only_bot similar to #1462

* Fix test_persistence

* Try dispatching error before logging it

* Fix test

Co-authored-by: Eldinnie <Eldinnie@users.noreply.github.com>
2020-02-02 22:20:31 +01:00
Noam Meltzer 43bfebb150 Update copyright date to 2020 (#1746) 2020-02-02 23:08:54 +02:00
Jelle Besseling 743e2fce07 Add --with-upstream-urllib3 option to remove vendored version (#1725)
Co-authored-by: Noam Meltzer <tsnoam@gmail.com>
2020-02-02 22:56:25 +02:00
Shreyas Thirumalai 4c2a3d07ce Remove duplicate entries from docs. (#1739)
Fixes #1726
2020-02-02 20:35:52 +01:00
Poolitzer 423794f473 fixing message.link (#1741)
* fixing message.link

* improving if clause
2020-02-02 20:27:53 +01:00
Bibo-Joshi a397e6c3c6 Doc fixes (#1642)
* Add missing DispatcherHandlerStop to docs

* Forgot to stage new file ...

* update docstring for bot.edit_message_*

* make job callback docs context based

* Flake8

* Fix doc strings of message.reply_

* Update thumb size docs

* Add missing DispatcherHandlerStop to docs

* Forgot to stage new file ...

* update docstring for bot.edit_message_*

* make job callback docs context based

* Flake8

* Fix doc strings of message.reply_

* Update thumb size docs

* Update docs on InlineQuery.query length

* Minor doc updates

* change module to class in to_float_timestamp doc string
2020-02-02 20:20:54 +01:00
Bibo-Joshi 7cde6ca268 temporarily skip tests failing b/c missing api 4.5 (#1738) 2020-01-30 20:57:16 +02:00
Bibo-Joshi 408062dd43 Add note on how to run test_official to contrib guide (#1740) 2020-01-29 22:43:57 +02:00
Poolitzer d96d233dc5 dropping 2.7, 3.3, 3.4 support from setup.py and README.rst (#1734)
Fixes #1720
2020-01-26 23:47:52 +02:00
Bibo-Joshi 3eb2cef600 Make Filters.text accept leading slash (#1680)
Fixes #1678
2020-01-26 23:19:38 +02:00
Poolitzer 0b87f4b274 contibuting guide: warning about adding requirements (#1718) 2020-01-26 23:15:42 +02:00
Bibo-Joshi 0df526d390 jobqueue: Log datetimes correctly (minor change) (#1714) 2020-01-26 23:08:33 +02:00
Bibo-Joshi cb9af36937 Fix None check in JobQueue._put() (#1707)
fixes #1701
2020-01-26 23:07:17 +02:00
Poolitzer fbb7e0e645 No more unitests for py2.7 (#1731)
Still not removing any py2.7 specific code, but no reason to waste precious CPU and unitest time on py2.7.
2020-01-26 23:01:29 +02:00
Eana Hufwe d9d65cc2ac Add poll messages support to filters (#1673) 2020-01-26 22:57:48 +02:00
Bibo-Joshi 62f514f068 CallbackContext: Expose dispatcher as a property (#1684)
Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>
2020-01-26 22:55:00 +02:00
David Auer 08bbeca8ec Fix typo in example text (#1703) 2020-01-26 22:35:34 +02:00
Mayur Newase 38b9f4b9bc Dispatcher.__init__: Remove double assignmed to self.job_queue (#1698)
Co-authored-by: mayur741 <mayur@wipadika.com>
2020-01-26 22:34:25 +02:00
Viktor Oreshkin 3d59b2f581 Docstring fix: thumb limits were changed with Bot API 4.2 (#1669) 2020-01-26 22:30:26 +02:00
rizlas e3c8466e41 Rename enocde_conversations_to_json() -> enocde_conversations_to_json() (#1661)
Fixes #1660
2020-01-26 22:24:00 +02:00
compSciKai 1d92f52c6a Minor documentation fix (#1647) 2020-01-26 22:19:59 +02:00
Bibo-Joshi 33280a7fe0 Add missing name for Filters.update classes (#1632) 2020-01-26 22:18:29 +02:00
Poolitzer 4b5ba15d31 both google python style links have been moved (#1624)
I choose https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html, based on https://web.archive.org/web/20160322212330/http://www.sphinx-doc.org/en/stable/ext/example_google.html
and http://google.github.io/styleguide/pyguide.html based on https://web.archive.org/web/20160304111857/https://google.github.io/styleguide/pyguide.html
2020-01-26 22:16:19 +02:00
Poolitzer d2466f1e6e CI: Fix running on master after push + official can fail (#1716)
allowing official to fail is only temporary until we'll add the latest bot api support
2020-01-26 22:07:24 +02:00
Bibo-Joshi 883c6b5901 Add dispatcher argument to Updater (#1484) 2020-01-26 21:59:47 +02:00
Noam Meltzer 1e7f4fae6f Bump version to v12.3.0 2020-01-11 21:21:46 +02:00
Noam Meltzer dae5ab47a0 CHANGES.rst: Update towards v12.3.0 release 2020-01-11 21:08:03 +02:00
Bibo-Joshi a9d9b1d750 Add #1653 to changelog 2020-01-11 20:57:58 +02:00
Hinrich Mahler 90496f70a5 Prepare for v12.3.0 2020-01-11 20:57:58 +02:00
tobiaswicker 940b42e048 ConversationHandler: Fix wrong signature call for timeout handlers
Fixes #1652

Co-authored-by: Eldinnie <Eldinnie@users.noreply.github.com>
2020-01-11 20:51:31 +02:00
Poolitzer a582515766 README.rst: replace travis and appveyor with github (#1645) 2020-01-11 20:47:21 +02:00
Eldinnie 3d42df3366 Fix documentation about return values in message (#1656)
* Fix documentation about return values in message

* flake8 satisfaction
2019-12-16 14:22:11 +01:00
Poolitzer 2c67a9833b add private /c links to message.links object (#1619)
* add private /c links to message.links object

* fixing ids for basic groups

* fixing ids for non basic chats and the test

* Improve tests for Message.link

* Simplify id_to_link
2019-11-29 13:50:44 +01:00
Bibo-Joshi 5e8a961669 Refactor msg_in (#1631)
* Revert "Add msg_in filter (new) (#1570)"

This reverts commit 34bdbc632a.

* Refactor msg_in, add Filters.caption on the fly

* Update docstrings

* Fix copy-paste typo
2019-11-29 13:09:44 +01:00
Bibo-Joshi a5ba64becb Remove checks for None on assignement for opt args (#1600) 2019-11-23 18:05:03 +02:00
Noam Meltzer 2a3169a22f Fix deprecation warning with Python 3.8 triggered by vendored urllib3 (#1618)
Fixes #1586
2019-11-16 14:37:15 +02:00
Poolitzer 894d8281ab github workflow: add a cron job (#1615) 2019-11-16 00:19:47 +02:00
Noam Meltzer 2fdf48023b github workflow: give pre-commit its own job (#1612) 2019-11-15 23:35:44 +02:00
Paolo Lammens 4e717a172b Fix UTC/local inconsistencies for naive datetimes (#1506) 2019-11-15 22:51:22 +02:00
Noam Meltzer 10c9ec2313 workflow: run test-official in its own job (#1607) 2019-11-09 22:38:23 +02:00
Noam Meltzer 096a7c3593 Allow PRs to test (#1606) 2019-11-09 22:13:02 +02:00
Noam Meltzer e9d9f01bd4 Fix windows actions (#1605)
* Stop testing with ujson

* Fix timing issue with test_delete_message

* ignore pip deprecation warning. hopefully it will fix windows+py2.7

* telegram replies to deleteing old messages are not consistent
2019-11-09 20:33:51 +02:00
Jasmin Bom 8b4b22cc89 Implement Github Actions CI (#1556)
* Add test workflow

* Attempt github grouping

* Improve bot info fetching

- Add support for b64+json encoded github secret with all the vars
- Add bot_name and bot_username since it's needed for a proper get_me test

* Improve test workflow a lot

- Add coverage
- Install ujson
- test_official only run on in single job
- Pass bot info to pytest

* Improve github grouping by having shorter titles

* Run pytest with coverage

* Improve coverage report

* Proper exitcode behaviour for pytest

* Proper test official handling

* Proper error handling

* Skip jobqueue tests on windows

* run coverage tests even if nocoverage ones fail

* Skip messagequeue tests on windows

* Clean up to satisfy flake8

* Run meta tests
2019-10-27 14:28:33 +01:00
Bibo-Joshi b294c92bad question template: Add info about users' group (#1577) 2019-10-27 14:33:30 +02:00
Poolitzer 264de2b7c1 Github actions - notify maintainers about changed examples (#1555) 2019-10-27 01:42:47 +03:00
Julian Ste ac64027580 Fixed comments in examples (#1566) 2019-10-27 01:15:09 +03:00
Bibo-Joshi 34bdbc632a Add msg_in filter (new) (#1570)
Closes #1144
2019-10-27 01:12:54 +03:00
Bibo-Joshi bbcff96804 Doc fixes (#1572)
Fixes #1576
2019-10-27 01:04:48 +03:00
Bibo-Joshi 93449443b2 Add *args, **kwargs to Message.forward() (#1574) 2019-10-27 01:00:27 +03:00
Poolitzer 8cdb20a85a updating example to V12 (#1579) 2019-10-27 00:51:29 +03:00
Jannes Höke 6fddb49af5 📝 Update section "Getting help" 2019-10-22 00:04:31 +02:00
Jannes Höke b0aef0c718 🔀 Update issue templates (#1569)
* 📝 Update issue templates

* 👌 Update question template

* 🔥 Delete old issue template
2019-10-21 23:51:58 +02:00
Iulian Onofrei 88eccc6608 Add MAX_PHOTOSIZE_UPLOAD size limit constant (#1560)
* Add MAX_PHOTOSIZE_UPLOAD size limit constant

* Add the other source of the constants' values
2019-10-17 22:24:44 +02:00
Lorenzo Rossi 3d8771bbdf 🔀 Add mutex protection on ConversationHandler (#1533)
* Add mutex protection on ConversationHandler

* Remove timeout job before child update

* Make locks private

* Add conversation timeout conflict test
2019-10-17 00:03:53 +02:00
Iulian Onofrei 7152b5aaf9 Fix CONTRIBUTING.rst not to install requirements as root (#1558) 2019-10-16 21:50:38 +03:00
346 changed files with 39815 additions and 12405 deletions
+35 -23
View File
@@ -25,7 +25,7 @@ Setting things up
.. code-block:: bash
$ sudo pip install -r requirements.txt -r requirements-dev.txt
$ pip install -r requirements.txt -r requirements-dev.txt
5. Install pre-commit hooks:
@@ -41,13 +41,17 @@ If you already know what you'd like to work on, you can skip this section.
If you have an idea for something to do, first check if it's already been filed on the `issue tracker`_. If so, add a comment to the issue saying you'd like to work on it, and we'll help you get started! Otherwise, please file a new issue and assign yourself to it.
Another great way to start contributing is by writing tests. Tests are really important because they help prevent developers from accidentally breaking existing code, allowing them to build cool things faster. If you're interested in helping out, let the development team know by posting to the `developers' mailing list`_, and we'll help you get started.
Another great way to start contributing is by writing tests. Tests are really important because they help prevent developers from accidentally breaking existing code, allowing them to build cool things faster. If you're interested in helping out, let the development team know by posting to the `Telegram group`_ (use `@admins` to mention the maintainers), and we'll help you get started.
That being said, we want to mention that we are very hesitant about adding new requirements to our projects. If you intend to do this, please state this in an issue and get a verification from one of the maintainers.
Instructions for making a code change
#####################################
The central development branch is ``master``, which should be clean and ready for release at any time. In general, all changes should be done as feature branches based off of ``master``.
If you want to do solely documentation changes, base them and PR to the branch ``doc-fixes``. This branch also has its own `RTD build`_.
Here's how to make a one-off code change.
1. **Choose a descriptive branch name.** It should be lowercase, hyphen-separated, and a noun describing the change (so, ``fuzzy-rules``, but not ``implement-fuzzy-rules``). Also, it shouldn't start with ``hotfix`` or ``release``.
@@ -66,7 +70,9 @@ Here's how to make a one-off code change.
- You can refer to relevant issues in the commit message by writing, e.g., "#105".
- Your code should adhere to the `PEP 8 Style Guide`_, with the exception that we have a maximum line length of 99.
- Provide static typing with signature annotations. The documentation of `MyPy`_ will be a good start, the cheat sheet is `here`_. We also have some custom type aliases in ``telegram.utils.helpers.typing``.
- Document your code. This project uses `sphinx`_ to generate static HTML docs. To build them, first make sure you have the required dependencies:
.. code-block:: bash
@@ -87,12 +93,16 @@ Here's how to make a one-off code change.
Once the process terminates, you can view the built documentation by opening ``docs/build/html/index.html`` with a browser.
- For consistency, please conform to `Google Python Style Guide`_ and `Google Python Style Docstrings`_. In addition, code should be formatted consistently with other code around it.
- Add ``.. versionadded:: version``, ``.. versionchanged:: version`` or ``.. deprecated:: version`` to the associated documentation of your changes, depending on what kind of change you made. This only applies if the change you made is visible to an end user. The directives should be added to class/method descriptions if their general behaviour changed and to the description of all arguments & attributes that changed.
- For consistency, please conform to `Google Python Style Guide`_ and `Google Python Style Docstrings`_.
- The following exceptions to the above (Google's) style guides applies:
- Documenting types of global variables and complex types of class members can be done using the Sphinx docstring convention.
- In addition, PTB uses the `Black`_ coder formatting. Plugins for Black exist for some `popular editors`_. You can use those instead of manually formatting everything.
- Please ensure that the code you write is well-tested.
- Dont break backward compatibility.
@@ -103,26 +113,28 @@ Here's how to make a one-off code change.
.. code-block::
$ make test
$ pytest -v
If you don't have ``make``, do:
To run ``test_official`` (particularly useful if you made API changes), run
.. code-block::
$ pytest -v
$ export TEST_OFFICIAL=true
- To actually make the commit (this will trigger tests for yapf, lint and pep8 automatically):
prior to running the tests.
- If you want run style & type checks before committing run
.. code-block::
$ pre-commit run -a
- To actually make the commit (this will trigger tests style & type checks automatically):
.. code-block:: bash
$ git add your-file-changed.py
- yapf may change code formatting, make sure to re-add them to your commit.
.. code-block:: bash
$ git commit -a -m "your-commit-message-here"
- Finally, push it to your GitHub fork, run:
.. code-block:: bash
@@ -177,15 +189,10 @@ Here's how to make a one-off code change.
Style commandments
------------------
Specific commandments
#####################
- Avoid using "double quotes" where you can reasonably use 'single quotes'.
Assert comparison order
#######################
- assert statements should compare in **actual** == **expected** order.
Assert statements should compare in **actual** == **expected** order.
For example (assuming ``test_call`` is the thing being tested):
.. code-block:: python
@@ -235,9 +242,14 @@ break the API classes. For example:
.. _`Code of Conduct`: https://www.python.org/psf/codeofconduct/
.. _`issue tracker`: https://github.com/python-telegram-bot/python-telegram-bot/issues
.. _`developers' mailing list`: mailto:devs@python-telegram-bot.org
.. _`Telegram group`: https://telegram.me/pythontelegrambotgroup
.. _`PEP 8 Style Guide`: https://www.python.org/dev/peps/pep-0008/
.. _`sphinx`: http://sphinx-doc.org
.. _`Google Python Style Guide`: https://google-styleguide.googlecode.com/svn/trunk/pyguide.html
.. _`Google Python Style Docstrings`: http://sphinx-doc.org/latest/ext/example_google.html
.. _`Google Python Style Guide`: http://google.github.io/styleguide/pyguide.html
.. _`Google Python Style Docstrings`: https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html
.. _AUTHORS.rst: ../AUTHORS.rst
.. _`MyPy`: https://mypy.readthedocs.io/en/stable/index.html
.. _`here`: https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html
.. _`Black`: https://black.readthedocs.io/en/stable/index.html
.. _`popular editors`: https://black.readthedocs.io/en/stable/editor_integration.html
.. _`RTD build`: https://python-telegram-bot.readthedocs.io/en/doc-fixes
@@ -1,9 +1,16 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: 'bug :bug:'
assignees: ''
---
<!--
Thanks for reporting issues of python-telegram-bot!
Use this template to notify us if you found a bug, or if you want to request a new feature.
If you're looking for help with programming your bot using our library, feel free to ask your
questions in out telegram group at: https://t.me/pythontelegrambotgroup
Use this template to notify us if you found a bug.
To make it easier for us to help you please enter detailed information below.
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Telegram Group
url: https://telegram.me/pythontelegrambotgroup
about: Questions asked on the group usually get answered faster.
- name: IRC Channel
url: https://webchat.freenode.net/?channels=##python-telegram-bot
about: In case you are unable to join our group due to Telegram restrictions, you can use our IRC channel
+24
View File
@@ -0,0 +1,24 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[FEATURE]"
labels: enhancement
assignees: ''
---
#### Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is.
Ex. *I want to do X, but there is no way to do it.*
#### Describe the solution you'd like
A clear and concise description of what you want to happen.
Ex. *I think it would be nice if you would add feature Y so it will make it easier.*
#### Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
Ex. *I considered Z, but that didn't work because...*
#### Additional context
Add any other context or screenshots about the feature request here.
Ex. *Here's a photo of my cat!*
+29
View File
@@ -0,0 +1,29 @@
---
name: Question
about: Get help with errors or general questions
title: "[QUESTION]"
labels: question
assignees: ''
---
<!--
Hey there, you have a question? We are happy to answer. Please make sure no similar question was opened already.
To make it easier for us to help you, please read this article https://git.io/JURJO and try to follow the template below as closely as possible.
Please mind that there is also a users' Telegram group at https://t.me/pythontelegrambotgroup for questions about the library. Questions asked there might be answered quicker than here. In case you are unable to join our group due to Telegram restrictions, you can use our IRC channel at https://webchat.freenode.net/?channels=##python-telegram-bot to participate in the group.
-->
### Issue I am facing
Please describe the issue here in as much detail as possible
### Traceback to the issue
```
put it here
```
### Related part of your code
```python
put it here
```
+30
View File
@@ -0,0 +1,30 @@
<!--
Hey! You're PRing? Cool! Please have a look at the below checklist. It's here to help both you and the maintainers to remember some aspects. Make sure to check out our contribution guide (https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst).
-->
### Checklist for PRs
- [ ] Added `.. versionadded:: version`, `.. versionchanged:: version` or `.. deprecated:: version` to the docstrings for user facing changes (for methods/class descriptions, arguments and attributes)
- [ ] Created new or adapted existing unit tests
- [ ] Added myself alphabetically to `AUTHORS.rst` (optional)
### If the PR contains API changes (otherwise, you can delete this passage)
* New classes:
- [ ] Added `self._id_attrs` and corresponding documentation
- [ ] `__init__` accepts `**_kwargs`
* Added new shortcuts:
- [ ] In `Chat` & `User` for all methods that accept `chat/user_id`
- [ ] In `Message` for all methods that accept `chat_id` and `message_id`
- [ ] For new `Message` shortcuts: Added `quote` argument if methods accepts `reply_to_message_id`
- [ ] In `CallbackQuery` for all methods that accept either `chat_id` and `message_id` or `inline_message_id`
* If relevant:
- [ ] Added new constants at `telegram.constants` and shortcuts to them as class variables
- [ ] Added new handlers for new update types
- [ ] Added new filters for new message (sub)types
- [ ] Added or updated documentation for the changed class(es) and/or method(s)
- [ ] Updated the Bot API version number in all places: `README.rst` and `README_RAW.rst` (including the badge), as well as `telegram.constants.BOT_API_VERSION`
+14
View File
@@ -0,0 +1,14 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 3
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 2
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
onlyLabels: question
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking as stale. Set to `false` to disable
markComment: false
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
This issue has been automatically closed due to inactivity. Feel free to comment in order to reopen
or ask again in our Telegram support group at https://t.me/pythontelegrambotgroup.
+14
View File
@@ -0,0 +1,14 @@
name: Warning maintainers
on:
pull_request:
paths: examples/**
jobs:
job:
runs-on: ubuntu-latest
name: about example change
steps:
- name: running the check
uses: Poolitzer/notifier-action@master
with:
notify-message: Hey there. Relax, I am just a little warning for the maintainers to release directly after merging your PR, otherwise we have broken examples and people might get confused :)
repo-token: ${{ secrets.GITHUB_TOKEN }}
+18
View File
@@ -0,0 +1,18 @@
name: 'Lock Closed Threads'
on:
schedule:
- cron: '8 4 * * *'
- cron: '42 17 * * *'
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2.0.1
with:
github-token: ${{ github.token }}
issue-lock-inactive-days: '1'
issue-lock-reason: ''
pr-lock-inactive-days: '1'
pr-lock-reason: ''
@@ -0,0 +1,17 @@
name: Warning maintainers
on:
pull_request:
paths:
- requirements.txt
- requirements-dev.txt
- .pre-commit-config.yaml
jobs:
job:
runs-on: ubuntu-latest
name: about pre-commit and dependency change
steps:
- name: running the check
uses: Poolitzer/notifier-action@master
with:
notify-message: Hey! Looks like you edited the (dev) requirements or the pre-commit hooks. I'm just a friendly reminder to keep the pre-commit hook versions in sync with the dev requirements and the additional dependencies for the hooks in sync with the requirements :)
repo-token: ${{ secrets.GITHUB_TOKEN }}
+16
View File
@@ -0,0 +1,16 @@
name: Warning maintainers
on:
pull_request:
paths:
- README.rst
- README_RAW.rst
jobs:
job:
runs-on: ubuntu-latest
name: about readme change
steps:
- name: running the check
uses: Poolitzer/notifier-action@master
with:
notify-message: Hey! Looks like you edited README.rst or README_RAW.rst. I'm just a friendly reminder to apply relevant changes to both of those files :)
repo-token: ${{ secrets.GITHUB_TOKEN }}
+120
View File
@@ -0,0 +1,120 @@
name: GitHub Actions
on:
pull_request:
branches:
- master
push:
branches:
- master
jobs:
pytest:
name: pytest
runs-on: ${{matrix.os}}
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: False
steps:
- uses: actions/checkout@v2
- name: Initialize vendored libs
run:
git submodule update --init --recursive
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -W ignore -m pip install --upgrade pip
python -W ignore -m pip install -U codecov pytest-cov
python -W ignore -m pip install -r requirements.txt
python -W ignore -m pip install -r requirements-dev.txt
- name: Test with pytest
# We run 3 different suites here
# 1. Test just utils.helpers.py without pytz being installed
# 2. Test just test_no_passport.py without passport dependencies being installed
# 3. Test everything else
# The first & second one are achieved by mocking the corresponding import
# See test_helpers.py & test_no_passport.py for details
run: |
pytest -v --cov -k test_no_passport.py
no_passport_exit=$?
export TEST_NO_PASSPORT='false'
pytest -v --cov --cov-append -k test_helpers.py
no_pytz_exit=$?
export TEST_NO_PYTZ='false'
pytest -v --cov --cov-append
full_exit=$?
special_exit=$(( no_pytz_exit > no_passport_exit ? no_pytz_exit : no_passport_exit ))
global_exit=$(( special_exit > full_exit ? special_exit : full_exit ))
exit ${global_exit}
env:
JOB_INDEX: ${{ strategy.job-index }}
BOTS: W3sidG9rZW4iOiAiNjk2MTg4NzMyOkFBR1Z3RUtmSEhsTmpzY3hFRE5LQXdraEdzdFpfa28xbUMwIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WldGaU1UUmxNbVF5TnpNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMi43IiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxMzkwOTgzOTk3IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzI3X2JvdCJ9LCB7InRva2VuIjogIjY3MTQ2ODg4NjpBQUdQR2ZjaVJJQlVORmU4MjR1SVZkcTdKZTNfWW5BVE5HdyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpHWXdPVGxrTXpNeE4yWTIiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNCIsICJzdXBlcl9ncm91cF9pZCI6ICItMTAwMTQ0NjAyMjUyMiIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zNF9ib3QifSwgeyJ0b2tlbiI6ICI2MjkzMjY1Mzg6QUFGUnJaSnJCN29CM211ekdzR0pYVXZHRTVDUXpNNUNVNG8iLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpNbU01WVdKaFl6a3hNMlUxIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgQ1B5dGhvbiAzLjUiLCAic3VwZXJfZ3JvdXBfaWQiOiAiLTEwMDE0OTY5MTc3NTAiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX2NweXRob25fMzVfYm90In0sIHsidG9rZW4iOiAiNjQwMjA4OTQzOkFBRmhCalFwOXFtM1JUeFN6VXBZekJRakNsZS1Kano1aGNrIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WXpoa1pUZzFOamMxWXpWbCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMy42IiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxMzMzODcxNDYxIiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzM2X2JvdCJ9LCB7InRva2VuIjogIjY5NTEwNDA4ODpBQUhmenlsSU9qU0lJUy1lT25JMjB5MkUyMEhvZEhzZnotMCIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOk9HUTFNRGd3WmpJd1pqRmwiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNyIsICJzdXBlcl9ncm91cF9pZCI6ICItMTAwMTQ3ODI5MzcxNCIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zN19ib3QifSwgeyJ0b2tlbiI6ICI2OTE0MjM1NTQ6QUFGOFdrakNaYm5IcVBfaTZHaFRZaXJGRWxackdhWU9oWDAiLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpZamM1TlRoaU1tUXlNV1ZoIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgUHlQeSAyLjciLCAic3VwZXJfZ3JvdXBfaWQiOiAiLTEwMDEzNjM5MzI1NzMiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX3B5cHlfMjdfYm90In0sIHsidG9rZW4iOiAiNjg0MzM5OTg0OkFBRk1nRUVqcDAxcjVyQjAwN3lDZFZOc2c4QWxOc2FVLWNjIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TVRBek1UWTNNR1V5TmpnMCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIFB5UHkgMy41IiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxNDA3ODM2NjA1IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19weXB5XzM1X2JvdCJ9LCB7InRva2VuIjogIjY5MDA5MTM0NzpBQUZMbVI1cEFCNVljcGVfbU9oN3pNNEpGQk9oMHozVDBUbyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpEaGxOekU1TURrd1lXSmkiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIEFwcFZleW9yIHVzaW5nIENQeXRob24gMy40IiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxMjc5NjAwMDI2IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX2FwcHZleW9yX2NweXRob25fMzRfYm90In0sIHsidG9rZW4iOiAiNjk0MzA4MDUyOkFBRUIyX3NvbkNrNTVMWTlCRzlBTy1IOGp4aVBTNTVvb0JBIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WW1aaVlXWm1NakpoWkdNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gQXBwVmV5b3IgdXNpbmcgQ1B5dGhvbiAyLjciLCAic3VwZXJfZ3JvdXBfaWQiOiAiLTEwMDEyOTMwNzkxNjUiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfYXBwdmV5b3JfY3B5dGhvbl8yN19ib3QifSwgeyJ0b2tlbiI6ICIxMDU1Mzk3NDcxOkFBRzE4bkJfUzJXQXd1SjNnN29oS0JWZ1hYY2VNbklPeVNjIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TmpBd056QXpZalZpTkdOayIsICJuYW1lIjogIlBUQiB0ZXN0cyBbMF0iLCAic3VwZXJfZ3JvdXBfaWQiOiAiLTEwMDExODU1MDk2MzYiLCAidXNlcm5hbWUiOiAicHRiXzBfYm90In0sIHsidG9rZW4iOiAiMTA0NzMyNjc3MTpBQUY4bk90ODFGcFg4bGJidno4VWV3UVF2UmZUYkZmQnZ1SSIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOllUVTFOVEk0WkdSallqbGkiLCAibmFtZSI6ICJQVEIgdGVzdHMgWzFdIiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxNDg0Nzk3NjEyIiwgInVzZXJuYW1lIjogInB0Yl8xX2JvdCJ9LCB7InRva2VuIjogIjk3MTk5Mjc0NTpBQUdPa09hVzBOSGpnSXY1LTlqUWJPajR2R3FkaFNGLVV1cyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOk5XWmtNV1ZoWWpsallqVTUiLCAibmFtZSI6ICJQVEIgdGVzdHMgWzJdIiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxNDAyMjU1MDcwIiwgInVzZXJuYW1lIjogInB0Yl8yX2JvdCJ9XQ==
TEST_NO_PYTZ : "true"
TEST_NO_PASSPORT: "true"
TEST_BUILD: "true"
shell: bash --noprofile --norc {0}
- name: Submit coverage
uses: codecov/codecov-action@v1.0.13
with:
env_vars: OS,PYTHON
name: ${{ matrix.os }}-${{ matrix.python-version }}
fail_ci_if_error: true
test_official:
name: test-official
runs-on: ${{matrix.os}}
strategy:
matrix:
python-version: [3.7]
os: [ubuntu-latest]
fail-fast: False
steps:
- uses: actions/checkout@v2
- name: Initialize vendored libs
run:
git submodule update --init --recursive
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -W ignore -m pip install --upgrade pip
python -W ignore -m pip install -r requirements.txt
python -W ignore -m pip install -r requirements-dev.txt
- name: Compare to official api
run: |
pytest -v tests/test_official.py
exit $?
env:
TEST_OFFICIAL: "true"
shell: bash --noprofile --norc {0}
test_pre_commit:
name: test-pre-commit
runs-on: ${{matrix.os}}
strategy:
matrix:
python-version: [3.7]
os: [ubuntu-latest]
fail-fast: False
steps:
- uses: actions/checkout@v2
- name: Initialize vendored libs
run:
git submodule update --init --recursive
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -W ignore -m pip install --upgrade pip
python -W ignore -m pip install -r requirements.txt
python -W ignore -m pip install -r requirements-dev.txt
- name: Run pre-commit tests
run: pre-commit run --all-files
+4
View File
@@ -46,6 +46,7 @@ htmlcov/
.coverage.*
.cache
.pytest_cache
.mypy_cache
nosetests.xml
coverage.xml
*,cover
@@ -83,3 +84,6 @@ telegram.jpg
# Exclude .exrc file for Vim
.exrc
# virtual env
venv*
+47 -11
View File
@@ -1,20 +1,56 @@
# Make sure that
# * the revs specified here match requirements-dev.txt
# * the additional_dependencies here match requirements.txt
repos:
- repo: git://github.com/python-telegram-bot/mirrors-yapf
sha: 5769e088ef6e0a0d1eb63bd6d0c1fe9f3606d6c8
- repo: https://github.com/psf/black
rev: 20.8b1
hooks:
- id: yapf
files: ^(telegram|tests)/.*\.py$
- id: black
args:
- --diff
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: 0b70e285e369bcb24b57b74929490ea7be9c4b19
- --check
- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.4
hooks:
- id: flake8
- repo: git://github.com/pre-commit/mirrors-pylint
sha: 9d8dcbc2b86c796275680f239c1e90dcd50bd398
- repo: https://github.com/PyCQA/pylint
rev: pylint-2.7.2
hooks:
- id: pylint
files: ^telegram/.*\.py$
files: ^(telegram|examples)/.*\.py$
args:
- --errors-only
- --disable=import-error
- --rcfile=setup.cfg
additional_dependencies:
- certifi
- tornado>=5.1
- APScheduler==3.6.3
- . # this basically does `pip install -e .`
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.812
hooks:
- id: mypy
name: mypy-ptb
files: ^telegram/.*\.py$
additional_dependencies:
- certifi
- tornado>=5.1
- APScheduler==3.6.3
- . # this basically does `pip install -e .`
- id: mypy
name: mypy-examples
files: ^examples/.*\.py$
args:
- --no-strict-optional
- --follow-imports=silent
additional_dependencies:
- certifi
- tornado>=5.1
- APScheduler==3.6.3
- . # this basically does `pip install -e .`
- repo: https://github.com/asottile/pyupgrade
rev: v2.10.0
hooks:
- id: pyupgrade
files: ^(telegram|examples|tests)/.*\.py$
args:
- --py36-plus
+17 -5
View File
@@ -1,10 +1,22 @@
# syntax: https://docs.readthedocs.io/en/latest/yaml-config.html
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py
# Optionally build your docs in additional formats such as PDF
formats:
- pdf
- pdf
# Optionally set the version of Python and requirements required to build your docs
python:
setup_py_install: true
version: 3
requirements_file: docs/requirements-docs.txt
install:
- method: pip
path: .
- requirements: docs/requirements-docs.txt
-55
View File
@@ -1,55 +0,0 @@
language: python
matrix:
include:
- python: 2.7
- python: 3.5
- python: 3.6
- python: 3.7
dist: xenial
sudo: true
- python: 3.7
dist: xenial
env: TEST_OFFICIAL=true
- python: pypy2.7-5.10.0
dist: xenial
- python: pypy3.5-5.10.1
dist: xenial
- python: 3.8-dev
dist: xenial
allow_failures:
- python: pypy2.7-5.10.0
- python: pypy3.5-5.10.1
dist: trusty
sudo: false
branches:
only:
- master
- /^[vV]\d+$/
cache:
directories:
- $HOME/.cache/pip
- $HOME/.pre-commit
before_cache:
- rm -f $HOME/.cache/pip/log/debug.log
- rm -f $HOME/.pre-commit/pre-commit.log
install:
# fix TypeError from old version of this
- pip install -U codecov pytest-cov
- echo $TRAVIS_PYTHON_VERSION
- if [[ $TRAVIS_PYTHON_VERSION == '3.7'* ]]; then pip install -U git+https://github.com/yaml/pyyaml.git; else true; fi
- pip install -U -r requirements.txt
- pip install -U -r requirements-dev.txt
- if [[ $TRAVIS_PYTHON_VERSION != 'pypy'* ]]; then pip install ujson; else true; fi
script:
- if [[ $TEST_OFFICIAL != 'true' ]]; then pytest -v -m nocoverage; else true; fi
- if [[ $TEST_OFFICIAL != 'true' ]]; then pytest -v -m "not nocoverage" --cov; else true; fi
- if [[ $TEST_OFFICIAL == 'true' ]]; then pytest -v tests/test_official.py; else true; fi
after_success:
- coverage combine
- codecov -F Travis
+27 -2
View File
@@ -2,9 +2,20 @@ Credits
=======
``python-telegram-bot`` was originally created by
`Leandro Toledo <https://github.com/leandrotoledo>`_ and is now maintained by
`Leandro Toledo <https://github.com/leandrotoledo>`_.
The current development team includes
- `Hinrich Mahler <https://github.com/Bibo-Joshi>`_ (maintainer)
- `Poolitzer <https://github.com/Poolitzer>`_ (community liaison)
- `Shivam <https://github.com/Starry69>`_
- `Harshil <https://github.com/harshil21>`_
Emeritus maintainers include
`Jannes Höke <https://github.com/jh0ker>`_ (`@jh0ker <https://t.me/jh0ker>`_ on Telegram),
`Noam Meltzer <https://github.com/tsnoam>`_, `Pieter Schutz <https://github.com/eldinnie>`_, `Jasmin Bom <https://github.com/jsmnbom>`_ and `Hinrich Mahler <https://github.com/Bibo-Joshi>`_.
`Noam Meltzer <https://github.com/tsnoam>`_, `Pieter Schutz <https://github.com/eldinnie>`_ and `Jasmin Bom <https://github.com/jsmnbom>`_.
Vendored packages
-----------------
We're vendoring urllib3 as part of ``python-telegram-bot`` which is distributed under the MIT
license. For more info, full credits & license terms, the sources can be found here:
@@ -18,6 +29,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `Alateas <https://github.com/alateas>`_
- `Ales Dokshanin <https://github.com/alesdokshanin>`_
- `Ambro17 <https://github.com/Ambro17>`_
- `Andrej Zhilenkov <https://github.com/Andrej730>`_
- `Anton Tagunov <https://github.com/anton-tagunov>`_
- `Avanatiker <https://github.com/Avanatiker>`_
- `Balduro <https://github.com/Balduro>`_
@@ -26,6 +38,8 @@ The following wonderful people contributed directly or indirectly to this projec
- `d-qoi <https://github.com/d-qoi>`_
- `daimajia <https://github.com/daimajia>`_
- `Daniel Reed <https://github.com/nmlorg>`_
- `D David Livingston <https://github.com/daviddl9>`_
- `Eana Hufwe <https://github.com/blueset>`_
- `Ehsan Online <https://github.com/ehsanonline>`_
- `Eli Gao <https://github.com/eligao>`_
- `Emilio Molinari <https://github.com/xates>`_
@@ -33,8 +47,12 @@ The following wonderful people contributed directly or indirectly to this projec
- `Eugene Lisitsky <https://github.com/lisitsky>`_
- `Eugenio Panadero <https://github.com/azogue>`_
- `Evan Haberecht <https://github.com/habereet>`_
- `Evgeny Denisov <https://github.com/eIGato>`_
- `evgfilim1 <https://github.com/evgfilim1>`_
- `franciscod <https://github.com/franciscod>`_
- `gamgi <https://github.com/gamgi>`_
- `Gauthamram Ravichandran <https://github.com/GauthamramRavichandran>`_
- `Harshil <https://github.com/harshil21>`_
- `Hugo Damer <https://github.com/HakimusGIT>`_
- `ihoru <https://github.com/ihoru>`_
- `Jasmin Bom <https://github.com/jsmnbom>`_
@@ -51,11 +69,14 @@ The following wonderful people contributed directly or indirectly to this projec
- `Kjwon15 <https://github.com/kjwon15>`_
- `Li-aung Yip <https://github.com/LiaungYip>`_
- `Loo Zheng Yuan <https://github.com/loozhengyuan>`_
- `LRezende <https://github.com/lrezende>`_
- `macrojames <https://github.com/macrojames>`_
- `Matheus Lemos <https://github.com/mlemosf>`_
- `Michael Elovskikh <https://github.com/wronglink>`_
- `Mischa Krüger <https://github.com/Makman2>`_
- `naveenvhegde <https://github.com/naveenvhegde>`_
- `neurrone <https://github.com/neurrone>`_
- `NikitaPirate <https://github.com/NikitaPirate>`_
- `njittam <https://github.com/njittam>`_
- `Noam Meltzer <https://github.com/tsnoam>`_
- `Oleg Shlyazhko <https://github.com/ollmer>`_
@@ -66,7 +87,10 @@ The following wonderful people contributed directly or indirectly to this projec
- `Paul Larsen <https://github.com/PaulSonOfLars>`_
- `Pieter Schutz <https://github.com/eldinnie>`_
- `Poolitzer <https://github.com/Poolitzer>`_
- `Pranjalya Tiwari <https://github.com/Pranjalya>`_
- `Rahiel Kasim <https://github.com/rahiel>`_
- `Riko Naka <https://github.com/rikonaka>`_
- `Rizlas <https://github.com/rizlas>`_
- `Sahil Sharma <https://github.com/sahilsharma811>`_
- `Sascha <https://github.com/saschalalala>`_
- `Shelomentsev D <https://github.com/shelomentsevd>`_
@@ -74,6 +98,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `sooyhwang <https://github.com/sooyhwang>`_
- `syntx <https://github.com/syntx>`_
- `thodnev <https://github.com/thodnev>`_
- `Timur Kushukov <https://github.com/timqsh>`_
- `Trainer Jono <https://github.com/Tr-Jono>`_
- `Valentijn <https://github.com/Faalentijn>`_
- `voider1 <https://github.com/voider1>`_
+590
View File
@@ -2,8 +2,598 @@
Changelog
=========
Version 13.4
============
*Released 2021-03-14*
**Major Changes:**
- Full support of Bot API 5.1 (`#2424`_)
**Minor changes, CI improvements, doc fixes and type hinting:**
- Improve ``Updater.set_webhook`` (`#2419`_)
- Doc Fixes (`#2404`_)
- Type Hinting Fixes (`#2425`_)
- Update ``pre-commit`` Settings (`#2415`_)
- Fix Logging for Vendored ``urllib3`` (`#2427`_)
- Stabilize Tests (`#2409`_)
.. _`#2424`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2424
.. _`#2419`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2419
.. _`#2404`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2404
.. _`#2425`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2425
.. _`#2415`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2415
.. _`#2427`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2427
.. _`#2409`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2409
Version 13.3
============
*Released 2021-02-19*
**Major Changes:**
- Make ``cryptography`` Dependency Optional & Refactor Some Tests (`#2386`_, `#2370`_)
- Deprecate ``MessageQueue`` (`#2393`_)
**Bug Fixes:**
- Refactor ``Defaults`` Integration (`#2363`_)
- Add Missing ``telegram.SecureValue`` to init and Docs (`#2398`_)
**Minor changes:**
- Doc Fixes (`#2359`_)
.. _`#2386`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2386
.. _`#2370`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2370
.. _`#2393`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2393
.. _`#2363`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2363
.. _`#2398`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2398
.. _`#2359`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2359
Version 13.2
============
*Released 2021-02-02*
**Major Changes:**
- Introduce ``python-telegram-bot-raw`` (`#2324`_)
- Explicit Signatures for Shortcuts (`#2240`_)
**New Features:**
- Add Missing Shortcuts to ``Message`` (`#2330`_)
- Rich Comparison for ``Bot`` (`#2320`_)
- Add ``run_async`` Parameter to ``ConversationHandler`` (`#2292`_)
- Add New Shortcuts to ``Chat`` (`#2291`_)
- Add New Constant ``MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH`` (`#2282`_)
- Allow Passing Custom Filename For All Media (`#2249`_)
- Handle Bytes as File Input (`#2233`_)
**Bug Fixes:**
- Fix Escaping in Nested Entities in ``Message`` Properties (`#2312`_)
- Adjust Calling of ``Dispatcher.update_persistence`` (`#2285`_)
- Add ``quote`` kwarg to ``Message.reply_copy`` (`#2232`_)
- ``ConversationHandler``: Docs & ``edited_channel_post`` behavior (`#2339`_)
**Minor changes, CI improvements, doc fixes and type hinting:**
- Doc Fixes (`#2253`_, `#2225`_)
- Reduce Usage of ``typing.Any`` (`#2321`_)
- Extend Deeplinking Example (`#2335`_)
- Add pyupgrade to pre-commit Hooks (`#2301`_)
- Add PR Template (`#2299`_)
- Drop Nightly Tests & Update Badges (`#2323`_)
- Update Copyright (`#2289`_, `#2287`_)
- Change Order of Class DocStrings (`#2256`_)
- Add macOS to Test Matrix (`#2266`_)
- Start Using Versioning Directives in Docs (`#2252`_)
- Improve Annotations & Docs of Handlers (`#2243`_)
.. _`#2324`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2324
.. _`#2240`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2240
.. _`#2330`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2330
.. _`#2320`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2320
.. _`#2292`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2292
.. _`#2291`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2291
.. _`#2282`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2282
.. _`#2249`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2249
.. _`#2233`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2233
.. _`#2312`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2312
.. _`#2285`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2285
.. _`#2232`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2232
.. _`#2339`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2339
.. _`#2253`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2253
.. _`#2225`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2225
.. _`#2321`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2321
.. _`#2335`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2335
.. _`#2301`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2301
.. _`#2299`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2299
.. _`#2323`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2323
.. _`#2289`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2289
.. _`#2287`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2287
.. _`#2256`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2256
.. _`#2266`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2266
.. _`#2252`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2252
.. _`#2243`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2243
Version 13.1
============
*Released 2020-11-29*
**Major Changes:**
- Full support of Bot API 5.0 (`#2181`_, `#2186`_, `#2190`_, `#2189`_, `#2183`_, `#2184`_, `#2188`_, `#2185`_, `#2192`_, `#2196`_, `#2193`_, `#2223`_, `#2199`_, `#2187`_, `#2147`_, `#2205`_)
**New Features:**
- Add ``Defaults.run_async`` (`#2210`_)
- Improve and Expand ``CallbackQuery`` Shortcuts (`#2172`_)
- Add XOR Filters and make ``Filters.name`` a Property (`#2179`_)
- Add ``Filters.document.file_extension`` (`#2169`_)
- Add ``Filters.caption_regex`` (`#2163`_)
- Add ``Filters.chat_type`` (`#2128`_)
- Handle Non-Binary File Input (`#2202`_)
**Bug Fixes:**
- Improve Handling of Custom Objects in ``BasePersistence.insert``/``replace_bot`` (`#2151`_)
- Fix bugs in ``replace/insert_bot`` (`#2218`_)
**Minor changes, CI improvements, doc fixes and type hinting:**
- Improve Type hinting (`#2204`_, `#2118`_, `#2167`_, `#2136`_)
- Doc Fixes & Extensions (`#2201`_, `#2161`_)
- Use F-Strings Where Possible (`#2222`_)
- Rename kwargs to _kwargs where possible (`#2182`_)
- Comply with PEP561 (`#2168`_)
- Improve Code Quality (`#2131`_)
- Switch Code Formatting to Black (`#2122`_, `#2159`_, `#2158`_)
- Update Wheel Settings (`#2142`_)
- Update ``timerbot.py`` to ``v13.0`` (`#2149`_)
- Overhaul Constants (`#2137`_)
- Add Python 3.9 to Test Matrix (`#2132`_)
- Switch Codecov to ``GitHub`` Action (`#2127`_)
- Specify Required pytz Version (`#2121`_)
.. _`#2181`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2181
.. _`#2186`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2186
.. _`#2190`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2190
.. _`#2189`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2189
.. _`#2183`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2183
.. _`#2184`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2184
.. _`#2188`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2188
.. _`#2185`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2185
.. _`#2192`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2192
.. _`#2196`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2196
.. _`#2193`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2193
.. _`#2223`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2223
.. _`#2199`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2199
.. _`#2187`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2187
.. _`#2147`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2147
.. _`#2205`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2205
.. _`#2210`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2210
.. _`#2172`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2172
.. _`#2179`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2179
.. _`#2169`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2169
.. _`#2163`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2163
.. _`#2128`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2128
.. _`#2202`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2202
.. _`#2151`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2151
.. _`#2218`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2218
.. _`#2204`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2204
.. _`#2118`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2118
.. _`#2167`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2167
.. _`#2136`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2136
.. _`#2201`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2201
.. _`#2161`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2161
.. _`#2222`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2222
.. _`#2182`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2182
.. _`#2168`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2168
.. _`#2131`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2131
.. _`#2122`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2122
.. _`#2159`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2159
.. _`#2158`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2158
.. _`#2142`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2142
.. _`#2149`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2149
.. _`#2137`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2137
.. _`#2132`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2132
.. _`#2127`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2127
.. _`#2121`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2121
Version 13.0
============
*Released 2020-10-07*
**For a detailed guide on how to migrate from v12 to v13, see this** `wiki page <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Transition-guide-to-Version-13.0>`_.
**Major Changes:**
- Deprecate old-style callbacks, i.e. set ``use_context=True`` by default (`#2050`_)
- Refactor Handling of Message VS Update Filters (`#2032`_)
- Deprecate ``Message.default_quote`` (`#1965`_)
- Refactor persistence of Bot instances (`#1994`_)
- Refactor ``JobQueue`` (`#1981`_)
- Refactor handling of kwargs in Bot methods (`#1924`_)
- Refactor ``Dispatcher.run_async``, deprecating the ``@run_async`` decorator (`#2051`_)
**New Features:**
- Type Hinting (`#1920`_)
- Automatic Pagination for ``answer_inline_query`` (`#2072`_)
- ``Defaults.tzinfo`` (`#2042`_)
- Extend rich comparison of objects (`#1724`_)
- Add ``Filters.via_bot`` (`#2009`_)
- Add missing shortcuts (`#2043`_)
- Allow ``DispatcherHandlerStop`` in ``ConversationHandler`` (`#2059`_)
- Make Errors picklable (`#2106`_)
**Minor changes, CI improvements, doc fixes or bug fixes:**
- Fix Webhook not working on Windows with Python 3.8+ (`#2067`_)
- Fix setting thumbs with ``send_media_group`` (`#2093`_)
- Make ``MessageHandler`` filter for ``Filters.update`` first (`#2085`_)
- Fix ``PicklePersistence.flush()`` with only ``bot_data`` (`#2017`_)
- Add test for clean argument of ``Updater.start_polling/webhook`` (`#2002`_)
- Doc fixes, refinements and additions (`#2005`_, `#2008`_, `#2089`_, `#2094`_, `#2090`_)
- CI fixes (`#2018`_, `#2061`_)
- Refine ``pollbot.py`` example (`#2047`_)
- Refine Filters in examples (`#2027`_)
- Rename ``echobot`` examples (`#2025`_)
- Use Lock-Bot to lock old threads (`#2048`_, `#2052`_, `#2049`_, `#2053`_)
.. _`#2050`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2050
.. _`#2032`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2032
.. _`#1965`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1965
.. _`#1994`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1994
.. _`#1981`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1981
.. _`#1924`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1924
.. _`#2051`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2051
.. _`#1920`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1920
.. _`#2072`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2072
.. _`#2042`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2042
.. _`#1724`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1724
.. _`#2009`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2009
.. _`#2043`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2043
.. _`#2059`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2059
.. _`#2106`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2106
.. _`#2067`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2067
.. _`#2093`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2093
.. _`#2085`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2085
.. _`#2017`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2017
.. _`#2002`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2002
.. _`#2005`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2005
.. _`#2008`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2008
.. _`#2089`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2089
.. _`#2094`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2094
.. _`#2090`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2090
.. _`#2018`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2018
.. _`#2061`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2061
.. _`#2047`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2047
.. _`#2027`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2027
.. _`#2025`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2025
.. _`#2048`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2048
.. _`#2052`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2052
.. _`#2049`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2049
.. _`#2053`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2053
Version 12.8
============
*Released 2020-06-22*
**Major Changes:**
- Remove Python 2 support (`#1715`_)
- Bot API 4.9 support (`#1980`_)
- IDs/Usernames of ``Filters.user`` and ``Filters.chat`` can now be updated (`#1757`_)
**Minor changes, CI improvements, doc fixes or bug fixes:**
- Update contribution guide and stale bot (`#1937`_)
- Remove ``NullHandlers`` (`#1913`_)
- Improve and expand examples (`#1943`_, `#1995`_, `#1983`_, `#1997`_)
- Doc fixes (`#1940`_, `#1962`_)
- Add ``User.send_poll()`` shortcut (`#1968`_)
- Ignore private attributes en ``TelegramObject.to_dict()`` (`#1989`_)
- Stabilize CI (`#2000`_)
.. _`#1937`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1937
.. _`#1913`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1913
.. _`#1943`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1943
.. _`#1757`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1757
.. _`#1940`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1940
.. _`#1962`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1962
.. _`#1968`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1968
.. _`#1989`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1989
.. _`#1995`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1995
.. _`#1983`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1983
.. _`#1715`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1715
.. _`#2000`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2000
.. _`#1997`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1997
.. _`#1980`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1980
Version 12.7
============
*Released 2020-05-02*
**Major Changes:**
- Bot API 4.8 support. **Note:** The ``Dice`` object now has a second positional argument ``emoji``. This is relevant, if you instantiate ``Dice`` objects manually. (`#1917`_)
- Added ``tzinfo`` argument to ``helpers.from_timestamp``. It now returns an timezone aware object. This is relevant for ``Message.{date,forward_date,edit_date}``, ``Poll.close_date`` and ``ChatMember.until_date`` (`#1621`_)
**New Features:**
- New method ``run_monthly`` for the ``JobQueue`` (`#1705`_)
- ``Job.next_t`` now gives the datetime of the jobs next execution (`#1685`_)
**Minor changes, CI improvements, doc fixes or bug fixes:**
- Stabalize CI (`#1919`_, `#1931`_)
- Use ABCs ``@abstractmethod`` instead of raising ``NotImplementedError`` for ``Handler``, ``BasePersistence`` and ``BaseFilter`` (`#1905`_)
- Doc fixes (`#1914`_, `#1902`_, `#1910`_)
.. _`#1902`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1902
.. _`#1685`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1685
.. _`#1910`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1910
.. _`#1914`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1914
.. _`#1931`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1931
.. _`#1905`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1905
.. _`#1919`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1919
.. _`#1621`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1621
.. _`#1705`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1705
.. _`#1917`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1917
Version 12.6.1
==============
*Released 2020-04-11*
**Bug fixes:**
- Fix serialization of ``reply_markup`` in media messages (`#1889`_)
.. _`#1889`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1889
Version 12.6
============
*Released 2020-04-10*
**Major Changes:**
- Bot API 4.7 support. **Note:** In ``Bot.create_new_sticker_set`` and ``Bot.add_sticker_to_set``, the order of the parameters had be changed, as the ``png_sticker`` parameter is now optional. (`#1858`_)
**Minor changes, CI improvements or bug fixes:**
- Add tests for ``swtich_inline_query(_current_chat)`` with empty string (`#1635`_)
- Doc fixes (`#1854`_, `#1874`_, `#1884`_)
- Update issue templates (`#1880`_)
- Favor concrete types over "Iterable" (`#1882`_)
- Pass last valid ``CallbackContext`` to ``TIMEOUT`` handlers of ``ConversationHandler`` (`#1826`_)
- Tweak handling of persistence and update persistence after job calls (`#1827`_)
- Use checkout@v2 for GitHub actions (`#1887`_)
.. _`#1858`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1858
.. _`#1635`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1635
.. _`#1854`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1854
.. _`#1874`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1874
.. _`#1884`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1884
.. _`#1880`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1880
.. _`#1882`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1882
.. _`#1826`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1826
.. _`#1827`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1827
.. _`#1887`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1887
Version 12.5.1
==============
*Released 2020-03-30*
**Minor changes, doc fixes or bug fixes:**
- Add missing docs for `PollHandler` and `PollAnswerHandler` (`#1853`_)
- Fix wording in `Filters` docs (`#1855`_)
- Reorder tests to make them more stable (`#1835`_)
- Make `ConversationHandler` attributes immutable (`#1756`_)
- Make `PrefixHandler` attributes `command` and `prefix` editable (`#1636`_)
- Fix UTC as default `tzinfo` for `Job` (`#1696`_)
.. _`#1853`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1853
.. _`#1855`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1855
.. _`#1835`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1835
.. _`#1756`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1756
.. _`#1636`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1636
.. _`#1696`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1696
Version 12.5
============
*Released 2020-03-29*
**New Features:**
- `Bot.link` gives the `t.me` link of the bot (`#1770`_)
**Major Changes:**
- Bot API 4.5 and 4.6 support. (`#1508`_, `#1723`_)
**Minor changes, CI improvements or bug fixes:**
- Remove legacy CI files (`#1783`_, `#1791`_)
- Update pre-commit config file (`#1787`_)
- Remove builtin names (`#1792`_)
- CI improvements (`#1808`_, `#1848`_)
- Support Python 3.8 (`#1614`_, `#1824`_)
- Use stale bot for auto closing stale issues (`#1820`_, `#1829`_, `#1840`_)
- Doc fixes (`#1778`_, `#1818`_)
- Fix typo in `edit_message_media` (`#1779`_)
- In examples, answer CallbackQueries and use `edit_message_text` shortcut (`#1721`_)
- Revert accidental change in vendored urllib3 (`#1775`_)
.. _`#1783`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1783
.. _`#1787`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1787
.. _`#1792`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1792
.. _`#1791`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1791
.. _`#1808`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1808
.. _`#1614`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1614
.. _`#1770`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1770
.. _`#1824`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1824
.. _`#1820`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1820
.. _`#1829`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1829
.. _`#1840`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1840
.. _`#1778`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1778
.. _`#1779`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1779
.. _`#1721`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1721
.. _`#1775`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1775
.. _`#1848`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1848
.. _`#1818`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1818
.. _`#1508`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1508
.. _`#1723`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1723
Version 12.4.2
==============
*Released 2020-02-10*
**Bug Fixes**
- Pass correct parse_mode to InlineResults if bot.defaults is None (`#1763`_)
- Make sure PP can read files that dont have bot_data (`#1760`_)
.. _`#1763`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1763
.. _`#1760`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1760
Version 12.4.1
==============
*Released 2020-02-08*
This is a quick release for `#1744`_ which was accidently left out of v12.4.0 though mentioned in the
release notes.
Version 12.4.0
==============
*Released 2020-02-08*
**New features:**
- Set default values for arguments appearing repeatedly. We also have a `wiki page for the new defaults`_. (`#1490`_)
- Store data in ``CallbackContext.bot_data`` to access it in every callback. Also persists. (`#1325`_)
- ``Filters.poll`` allows only messages containing a poll (`#1673`_)
**Major changes:**
- ``Filters.text`` now accepts messages that start with a slash, because ``CommandHandler`` checks for ``MessageEntity.BOT_COMMAND`` since v12. This might lead to your MessageHandlers receiving more updates than before (`#1680`_).
- ``Filters.command`` new checks for ``MessageEntity.BOT_COMMAND`` instead of just a leading slash. Also by ``Filters.command(False)`` you can now filters for messages containing a command `anywhere` in the text (`#1744`_).
**Minor changes, CI improvements or bug fixes:**
- Add ``disptacher`` argument to ``Updater`` to allow passing a customized ``Dispatcher`` (`#1484`_)
- Add missing names for ``Filters`` (`#1632`_)
- Documentation fixes (`#1624`_, `#1647`_, `#1669`_, `#1703`_, `#1718`_, `#1734`_, `#1740`_, `#1642`_, `#1739`_, `#1746`_)
- CI improvements (`#1716`_, `#1731`_, `#1738`_, `#1748`_, `#1749`_, `#1750`_, `#1752`_)
- Fix spelling issue for ``encode_conversations_to_json`` (`#1661`_)
- Remove double assignement of ``Dispatcher.job_queue`` (`#1698`_)
- Expose dispatcher as property for ``CallbackContext`` (`#1684`_)
- Fix ``None`` check in ``JobQueue._put()`` (`#1707`_)
- Log datetimes correctly in ``JobQueue`` (`#1714`_)
- Fix false ``Message.link`` creation for private groups (`#1741`_)
- Add option ``--with-upstream-urllib3`` to `setup.py` to allow using non-vendored version (`#1725`_)
- Fix persistence for nested ``ConversationHandlers`` (`#1679`_)
- Improve handling of non-decodable server responses (`#1623`_)
- Fix download for files without ``file_path`` (`#1591`_)
- test_webhook_invalid_posts is now considered flaky and retried on failure (`#1758`_)
.. _`wiki page for the new defaults`: https://github.com/python-telegram-bot/python-telegram-bot/wiki/Adding-defaults-to-your-bot
.. _`#1744`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1744
.. _`#1752`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1752
.. _`#1750`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1750
.. _`#1591`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1591
.. _`#1490`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1490
.. _`#1749`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1749
.. _`#1623`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1623
.. _`#1748`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1748
.. _`#1679`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1679
.. _`#1711`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1711
.. _`#1325`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1325
.. _`#1746`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1746
.. _`#1725`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1725
.. _`#1739`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1739
.. _`#1741`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1741
.. _`#1642`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1642
.. _`#1738`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1738
.. _`#1740`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1740
.. _`#1734`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1734
.. _`#1680`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1680
.. _`#1718`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1718
.. _`#1714`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1714
.. _`#1707`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1707
.. _`#1731`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1731
.. _`#1673`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1673
.. _`#1684`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1684
.. _`#1703`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1703
.. _`#1698`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1698
.. _`#1669`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1669
.. _`#1661`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1661
.. _`#1647`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1647
.. _`#1632`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1632
.. _`#1624`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1624
.. _`#1716`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1716
.. _`#1484`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1484
.. _`#1758`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1484
Version 12.3.0
==============
*Released 2020-01-11*
**New features:**
- `Filters.caption` allows only messages with caption (`#1631`_).
- Filter for exact messages/captions with new capability of `Filters.text` and `Filters.caption`. Especially useful in combination with ReplyKeyboardMarkup. (`#1631`_).
**Major changes:**
- Fix inconsistent handling of naive datetimes (`#1506`_).
**Minor changes, CI improvements or bug fixes:**
- Documentation fixes (`#1558`_, `#1569`_, `#1579`_, `#1572`_, `#1566`_, `#1577`_, `#1656`_).
- Add mutex protection on `ConversationHandler` (`#1533`_).
- Add `MAX_PHOTOSIZE_UPLOAD` constant (`#1560`_).
- Add args and kwargs to `Message.forward()` (`#1574`_).
- Transfer to GitHub Actions CI (`#1555`_, `#1556`_, `#1605`_, `#1606`_, `#1607`_, `#1612`_, `#1615`_, `#1645`_).
- Fix deprecation warning with Py3.8 by vendored urllib3 (`#1618`_).
- Simplify assignements for optional arguments (`#1600`_)
- Allow private groups for `Message.link` (`#1619`_).
- Fix wrong signature call for `ConversationHandler.TIMEOUT` handlers (`#1653`_).
.. _`#1631`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1631
.. _`#1506`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1506
.. _`#1558`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1558
.. _`#1569`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1569
.. _`#1579`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1579
.. _`#1572`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1572
.. _`#1566`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1566
.. _`#1577`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1577
.. _`#1533`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1533
.. _`#1560`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1560
.. _`#1574`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1574
.. _`#1555`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1555
.. _`#1556`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1556
.. _`#1605`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1605
.. _`#1606`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1606
.. _`#1607`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1607
.. _`#1612`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1612
.. _`#1615`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1615
.. _`#1618`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1618
.. _`#1600`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1600
.. _`#1619`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1619
.. _`#1653`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1653
.. _`#1656`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1656
.. _`#1645`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1645
Version 12.2.0
==============
*Released 2019-10-14*
**New features:**
+1 -1
View File
@@ -1 +1 @@
include LICENSE LICENSE.lesser Makefile requirements.txt
include LICENSE LICENSE.lesser Makefile requirements.txt README_RAW.rst telegram/py.typed
-52
View File
@@ -1,52 +0,0 @@
.DEFAULT_GOAL := help
.PHONY: clean pep257 pep8 yapf lint test install
PYLINT := pylint
PYTEST := pytest
PEP257 := pep257
PEP8 := flake8
YAPF := yapf
PIP := pip
clean:
rm -fr build
rm -fr dist
find . -name '*.pyc' -exec rm -f {} \;
find . -name '*.pyo' -exec rm -f {} \;
find . -name '*~' -exec rm -f {} \;
find . -regex "./telegram.\(mp3\|mp4\|ogg\|png\|webp\)" -exec rm {} \;
pep257:
$(PEP257) telegram
pep8:
$(PEP8) telegram
yapf:
$(YAPF) -r telegram
lint:
$(PYLINT) -E telegram --disable=no-name-in-module,import-error
test:
$(PYTEST) -v
install:
$(PIP) install -r requirements.txt -r requirements-dev.txt
help:
@echo "Available targets:"
@echo "- clean Clean up the source directory"
@echo "- pep257 Check docstring style with pep257"
@echo "- pep8 Check style with flake8"
@echo "- lint Check style with pylint"
@echo "- yapf Check style with yapf"
@echo "- test Run tests using pytest"
@echo
@echo "Available variables:"
@echo "- PYLINT default: $(PYLINT)"
@echo "- PYTEST default: $(PYTEST)"
@echo "- PEP257 default: $(PEP257)"
@echo "- PEP8 default: $(PEP8)"
@echo "- YAPF default: $(YAPF)"
@echo "- PIP default: $(PIP)"
+52 -22
View File
@@ -1,3 +1,6 @@
..
Make user to apply any changes to this file to README_RAW.rst as well!
.. image:: https://github.com/python-telegram-bot/logos/blob/master/logo-text/png/ptb-logo-text_768.png?raw=true
:align: center
:target: https://python-telegram-bot.org
@@ -17,31 +20,30 @@ We have a vibrant community of developers helping each other in our `Telegram gr
:target: https://pypi.org/project/python-telegram-bot/
:alt: Supported Python versions
.. image:: https://www.cpu.re/static/python-telegram-bot/downloads.svg
:target: https://www.cpu.re/static/python-telegram-bot/downloads-by-python-version.txt
.. image:: https://img.shields.io/badge/Bot%20API-5.1-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API versions
.. image:: https://img.shields.io/pypi/dm/python-telegram-bot
:target: https://pypistats.org/packages/python-telegram-bot
:alt: PyPi Package Monthly Download
.. image:: https://img.shields.io/badge/docs-latest-af1a97.svg
:target: https://python-telegram-bot.readthedocs.io/
.. image:: https://readthedocs.org/projects/python-telegram-bot/badge/?version=stable
:target: https://python-telegram-bot.readthedocs.io/en/stable/?badge=stable
:alt: Documentation Status
.. image:: https://img.shields.io/pypi/l/python-telegram-bot.svg
:target: https://www.gnu.org/licenses/lgpl-3.0.html
:alt: LGPLv3 License
.. image:: https://travis-ci.org/python-telegram-bot/python-telegram-bot.svg?branch=master
:target: https://travis-ci.org/python-telegram-bot/python-telegram-bot
:alt: Travis CI Status
.. image:: https://img.shields.io/appveyor/ci/python-telegram-bot/python-telegram-bot/master.svg?logo=appveyor
:target: https://ci.appveyor.com/project/python-telegram-bot/python-telegram-bot
:alt: AppVeyor CI Status
.. image:: https://github.com/python-telegram-bot/python-telegram-bot/workflows/GitHub%20Actions/badge.svg
:target: https://github.com/python-telegram-bot/python-telegram-bot/
:alt: Github Actions workflow
.. image:: https://codecov.io/gh/python-telegram-bot/python-telegram-bot/branch/master/graph/badge.svg
:target: https://codecov.io/gh/python-telegram-bot/python-telegram-bot
:alt: Code coverage
.. image:: http://isitmaintained.com/badge/resolution/python-telegram-bot/python-telegram-bot.svg
:target: http://isitmaintained.com/project/python-telegram-bot/python-telegram-bot
:alt: Median time to resolve an issue
@@ -50,7 +52,10 @@ We have a vibrant community of developers helping each other in our `Telegram gr
:target: https://www.codacy.com/app/python-telegram-bot/python-telegram-bot?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=python-telegram-bot/python-telegram-bot&amp;utm_campaign=Badge_Grade
:alt: Code quality
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg?logo=telegram
:target: https://telegram.me/pythontelegrambotgroup
:alt: Telegram Group
@@ -88,17 +93,25 @@ Introduction
This library provides a pure Python interface for the
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
It's compatible with Python versions 2.7, 3.3+ and `PyPy <http://pypy.org/>`_.
It's compatible with Python versions 3.6+. PTB might also work on `PyPy <http://pypy.org/>`_, though there have been a lot of issues before. Hence, PyPy is not officially supported.
In addition to the pure API implementation, this library features a number of high-level classes to
make the development of bots easy and straightforward. These classes are contained in the
``telegram.ext`` submodule.
A pure API implementation *without* ``telegram.ext`` is available as the standalone package ``python-telegram-bot-raw``. `See here for details. <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/README_RAW.rst>`_
----
Note
----
Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conjunction will result in undesired side-effects, so only install *one* of both.
====================
Telegram API support
====================
All types and methods of the Telegram Bot API **4.1** are supported.
All types and methods of the Telegram Bot API **5.1** are supported.
==========
Installing
@@ -124,6 +137,16 @@ In case you have a previously cloned local repository already, you should initia
$ git submodule update --init --recursive
---------------------
Optional Dependencies
---------------------
PTB can be installed with optional dependencies:
* ``pip install python-telegram-bot[passport]`` installs the `cryptography <https://cryptography.io>`_ library. Use this, if you want to use Telegram Passport related functionality.
* ``pip install python-telegram-bot[ujson]`` installs the `ujson <https://pypi.org/project/ujson/>`_ library. It will then be used for JSON de- & encoding, which can bring speed up compared to the standard `json <https://docs.python.org/3/library/json.html>`_ library.
* ``pip install python-telegram-bot[socks]`` installs the `PySocks <https://pypi.org/project/PySocks/>`_ library. Use this, if you want to work behind a Socks5 server.
===============
Getting started
===============
@@ -142,9 +165,9 @@ Other references:
Learning by example
-------------------
We believe that the best way to learn and understand this simple package is by example. So here
are some examples for you to review. Even if it's not your approach for learning, please take a
look at ``echobot2``, it is de facto the base for most of the bots out there. Best of all,
We believe that the best way to learn this package is by example. Here
are some examples for you to review. Even if it is not your approach for learning, please take a
look at ``echobot.py``, it is the de facto base for most of the bots out there. Best of all,
the code for these examples are released to the public domain, so you can start by grabbing the
code and building on top of it.
@@ -192,11 +215,13 @@ You can get help in several ways:
1. We have a vibrant community of developers helping each other in our `Telegram group <https://telegram.me/pythontelegrambotgroup>`_. Join us!
2. Our `Wiki pages <https://github.com/python-telegram-bot/python-telegram-bot/wiki/>`_ offer a growing amount of resources.
2. In case you are unable to join our group due to Telegram restrictions, you can use our `IRC channel <https://webchat.freenode.net/?channels=##python-telegram-bot>`_.
3. You can ask for help on Stack Overflow using the `python-telegram-bot tag <https://stackoverflow.com/questions/tagged/python-telegram-bot>`_.
3. Report bugs, request new features or ask questions by `creating an issue <https://github.com/python-telegram-bot/python-telegram-bot/issues/new/choose>`_ or `a discussion <https://github.com/python-telegram-bot/python-telegram-bot/discussions/new>`_.
4. As last resort, the developers are ready to help you with `serious issues <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
4. Our `Wiki pages <https://github.com/python-telegram-bot/python-telegram-bot/wiki/>`_ offer a growing amount of resources.
5. You can even ask for help on Stack Overflow using the `python-telegram-bot tag <https://stackoverflow.com/questions/tagged/python-telegram-bot>`_.
============
@@ -205,6 +230,11 @@ Contributing
Contributions of all sizes are welcome. Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst>`_ to get started. You can also help by `reporting bugs <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
========
Donating
========
Occasionally we are asked if we accept donations to support the development. While we appreciate the thought, maintaining PTB is our hobby and we have almost no running costs for it. We therefore have nothing set up to accept donations. If you still want to donate, we kindly ask you to donate to another open source project/initiative of your choice instead.
=======
License
=======
+224
View File
@@ -0,0 +1,224 @@
..
Make user to apply any changes to this file to README.rst as well!
.. image:: https://github.com/python-telegram-bot/logos/blob/master/logo-text/png/ptb-raw-logo-text_768.png?raw=true
:align: center
:target: https://python-telegram-bot.org
:alt: python-telegram-bot-raw Logo
We have made you a wrapper you can't refuse
We have a vibrant community of developers helping each other in our `Telegram group <https://telegram.me/pythontelegrambotgroup>`_. Join us!
*Stay tuned for library updates and new releases on our* `Telegram Channel <https://telegram.me/pythontelegrambotchannel>`_.
.. image:: https://img.shields.io/pypi/v/python-telegram-bot-raw.svg
:target: https://pypi.org/project/python-telegram-bot-raw/
:alt: PyPi Package Version
.. image:: https://img.shields.io/pypi/pyversions/python-telegram-bot-raw.svg
:target: https://pypi.org/project/python-telegram-bot-raw/
:alt: Supported Python versions
.. image:: https://img.shields.io/badge/Bot%20API-5.1-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API versions
.. image:: https://img.shields.io/pypi/dm/python-telegram-bot-raw
:target: https://pypistats.org/packages/python-telegram-bot-raw
:alt: PyPi Package Monthly Download
.. image:: https://readthedocs.org/projects/python-telegram-bot/badge/?version=stable
:target: https://python-telegram-bot.readthedocs.io/
:alt: Documentation Status
.. image:: https://img.shields.io/pypi/l/python-telegram-bot-raw.svg
:target: https://www.gnu.org/licenses/lgpl-3.0.html
:alt: LGPLv3 License
.. image:: https://github.com/python-telegram-bot/python-telegram-bot/workflows/GitHub%20Actions/badge.svg
:target: https://github.com/python-telegram-bot/python-telegram-bot/
:alt: Github Actions workflow
.. image:: https://codecov.io/gh/python-telegram-bot/python-telegram-bot/branch/master/graph/badge.svg
:target: https://codecov.io/gh/python-telegram-bot/python-telegram-bot
:alt: Code coverage
.. image:: http://isitmaintained.com/badge/resolution/python-telegram-bot/python-telegram-bot.svg
:target: http://isitmaintained.com/project/python-telegram-bot/python-telegram-bot
:alt: Median time to resolve an issue
.. image:: https://api.codacy.com/project/badge/Grade/99d901eaa09b44b4819aec05c330c968
:target: https://www.codacy.com/app/python-telegram-bot/python-telegram-bot?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=python-telegram-bot/python-telegram-bot&amp;utm_campaign=Badge_Grade
:alt: Code quality
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg?logo=telegram
:target: https://telegram.me/pythontelegrambotgroup
:alt: Telegram Group
.. image:: https://img.shields.io/badge/IRC-Channel-blue.svg
:target: https://webchat.freenode.net/?channels=##python-telegram-bot
:alt: IRC Bridge
=================
Table of contents
=================
- `Introduction`_
- `Telegram API support`_
- `Installing`_
- `Getting started`_
#. `Logging`_
#. `Documentation`_
- `Getting help`_
- `Contributing`_
- `License`_
============
Introduction
============
This library provides a pure Python, lightweight interface for the
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
It's compatible with Python versions 3.6+. PTB-Raw might also work on `PyPy <http://pypy.org/>`_, though there have been a lot of issues before. Hence, PyPy is not officially supported.
``python-telegram-bot-raw`` is part of the `python-telegram-bot <https://python-telegram-bot.org>`_ ecosystem and provides the pure API functionality extracted from PTB. It therefore does *not* have independent release schedules, changelogs or documentation. Please consult the PTB resources.
----
Note
----
Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conjunction will result in undesired side-effects, so only install *one* of both.
====================
Telegram API support
====================
All types and methods of the Telegram Bot API **5.1** are supported.
==========
Installing
==========
You can install or upgrade python-telegram-bot-raw with:
.. code:: shell
$ pip install python-telegram-bot-raw --upgrade
Or you can install from source with:
.. code:: shell
$ git clone https://github.com/python-telegram-bot/python-telegram-bot --recursive
$ cd python-telegram-bot
$ python setup-raw.py install
In case you have a previously cloned local repository already, you should initialize the added urllib3 submodule before installing with:
.. code:: shell
$ git submodule update --init --recursive
----
Note
----
Installing the `.tar.gz` archive available on PyPi directly via `pip` will *not* work as expected, as `pip` does not recognize that it should use `setup-raw.py` instead of `setup.py`.
---------------------
Optional Dependencies
---------------------
PTB can be installed with optional dependencies:
* ``pip install python-telegram-bot-raw[passport]`` installs the `cryptography <https://cryptography.io>`_ library. Use this, if you want to use Telegram Passport related functionality.
* ``pip install python-telegram-bot-raw[ujson]`` installs the `ujson <https://pypi.org/project/ujson/>`_ library. It will then be used for JSON de- & encoding, which can bring speed up compared to the standard `json <https://docs.python.org/3/library/json.html>`_ library.
===============
Getting started
===============
Our Wiki contains an `Introduction to the API <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Introduction-to-the-API>`_. Other references are:
- the `Telegram API documentation <https://core.telegram.org/bots/api>`_
- the `python-telegram-bot documentation <https://python-telegram-bot.readthedocs.io/>`_
-------
Logging
-------
This library uses the ``logging`` module. To set up logging to standard output, put:
.. code:: python
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
at the beginning of your script.
You can also use logs in your application by calling ``logging.getLogger()`` and setting the log level you want:
.. code:: python
logger = logging.getLogger()
logger.setLevel(logging.INFO)
If you want DEBUG logs instead:
.. code:: python
logger.setLevel(logging.DEBUG)
=============
Documentation
=============
``python-telegram-bot``'s documentation lives at `readthedocs.io <https://python-telegram-bot.readthedocs.io/>`_, which
includes the relevant documentation for ``python-telegram-bot-raw``.
============
Getting help
============
You can get help in several ways:
1. We have a vibrant community of developers helping each other in our `Telegram group <https://telegram.me/pythontelegrambotgroup>`_. Join us!
2. In case you are unable to join our group due to Telegram restrictions, you can use our `IRC channel <https://webchat.freenode.net/?channels=##python-telegram-bot>`_.
3. Report bugs, request new features or ask questions by `creating an issue <https://github.com/python-telegram-bot/python-telegram-bot/issues/new/choose>`_ or `a discussion <https://github.com/python-telegram-bot/python-telegram-bot/discussions/new>`_.
4. Our `Wiki pages <https://github.com/python-telegram-bot/python-telegram-bot/wiki/>`_ offer a growing amount of resources.
5. You can even ask for help on Stack Overflow using the `python-telegram-bot tag <https://stackoverflow.com/questions/tagged/python-telegram-bot>`_.
============
Contributing
============
Contributions of all sizes are welcome. Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst>`_ to get started. You can also help by `reporting bugs <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
========
Donating
========
Occasionally we are asked if we accept donations to support the development. While we appreciate the thought, maintaining PTB is our hobby and we have almost no running costs for it. We therefore have nothing set up to accept donations. If you still want to donate, we kindly ask you to donate to another open source project/initiative of your choice instead.
=======
License
=======
You may copy, distribute and modify the software provided that modifications are described and licensed for free under `LGPL-3 <https://www.gnu.org/licenses/lgpl-3.0.html>`_. Derivatives works (including modifications or anything statically linked to the library) can only be redistributed under LGPL-3, but applications that use the library don't have to be.
-43
View File
@@ -1,43 +0,0 @@
environment:
matrix:
# For Python versions available on Appveyor, see
# https://www.appveyor.com/docs/windows-images-software/#python
# The list here is complete (excluding Python 2.6, which
# isn't covered by this document) at the time of writing.
- PYTHON: "C:\\Python27"
- PYTHON: "C:\\Python35"
- PYTHON: "C:\\Python36"
- PYTHON: "C:\\Python37"
# - PYTHON: "C:\\Python38"
branches:
only:
- master
- /^[vV]\d+$/
skip_branch_with_pr: true
max_jobs: 1
install:
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- "git submodule update --init --recursive"
# Check that we have the expected version and architecture for Python
- "python --version"
# We need wheel installed to build wheels
# fix TypeError from an old version of this
- "pip install attrs==17.4.0"
- "pip install -U codecov pytest-cov"
- "pip install -r requirements.txt"
- "pip install -r requirements-dev.txt"
build: off
test_script:
- "pytest --version"
- "pytest -m \"not nocoverage\" --cov --cov-report xml:coverage.xml"
after_test:
- "codecov -f coverage.xml -F Appveyor"
+9
View File
@@ -1 +1,10 @@
comment: false
coverage:
status:
project:
default:
# We allow small coverage decreases in the project because we don't retry
# on hitting flood limits, which adds noise to the coverage
target: auto
threshold: 0.1%
+1 -1
View File
@@ -15,7 +15,7 @@ Depends: ${python3:Depends}, ${misc:Depends}
Description: We have made you a wrapper you can't refuse!
The Python Telegram bot (Python 3)
This library provides a pure Python interface for the Telegram Bot API.
It's compatible with Python versions 2.7, 3.3+ and PyPy.
It's compatible with Python versions 3.5+ and PyPy.
.
In addition to the pure API implementation, this library features
a number of high-level
+1 -1
View File
@@ -6,7 +6,7 @@
export PYBUILD_NAME=telegram
%:
DEB_BUILD_OPTIONS=nocheck dh $@ --with python2,python3 --buildsystem=pybuild
DEB_BUILD_OPTIONS=nocheck dh $@ --with python3 --buildsystem=pybuild
# If you need to rebuild the Sphinx documentation
+2 -2
View File
@@ -1,3 +1,3 @@
sphinx>=1.7.9
sphinx_rtd_theme
sphinx==3.5.2
sphinx_rtd_theme==0.5.1
sphinx-pypi-upload
+7 -4
View File
@@ -24,7 +24,7 @@ sys.path.insert(0, os.path.abspath('../..'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = '1.7.9'
needs_sphinx = '3.5.2'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@@ -33,6 +33,9 @@ extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.napoleon'
]
# Don't show type hints in the signature - that just makes it hardly readable
# and we document the types anyway
autodoc_typehints = 'none'
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -50,7 +53,7 @@ master_doc = 'index'
# General information about the project.
project = u'Python Telegram Bot'
copyright = u'2015-2018, Leandro Toledo'
copyright = u'2015-2021, Leandro Toledo'
author = u'Leandro Toledo'
# The version info for the project you're documenting, acts as replacement for
@@ -58,9 +61,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '12.1' # telegram.__version__[:3]
version = '13.4' # telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = '12.2.0' # telegram.__version__
release = '13.4' # telegram.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
+6
View File
@@ -0,0 +1,6 @@
telegram.BotCommand
===================
.. autoclass:: telegram.BotCommand
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
telegram.ChatInviteLink
=======================
.. autoclass:: telegram.ChatInviteLink
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
telegram.ChatLocation
=====================
.. autoclass:: telegram.ChatLocation
:members:
:show-inheritance:
@@ -0,0 +1,6 @@
telegram.ChatMemberUpdated
==========================
.. autoclass:: telegram.ChatMemberUpdated
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
telegram.Dice
=============
.. autoclass:: telegram.Dice
:members:
:show-inheritance:
@@ -0,0 +1,6 @@
telegram.ext.ChatMemberHandler
==============================
.. autoclass:: telegram.ext.ChatMemberHandler
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
telegram.ext.Defaults
=====================
.. autoclass:: telegram.ext.Defaults
:members:
:show-inheritance:
@@ -0,0 +1,6 @@
telegram.ext.DispatcherHandlerStop
==================================
.. autoclass:: telegram.ext.DispatcherHandlerStop
:members:
:show-inheritance:
@@ -0,0 +1,6 @@
telegram.ext.PollAnswerHandler
==============================
.. autoclass:: telegram.ext.PollAnswerHandler
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
telegram.ext.PollHandler
========================
.. autoclass:: telegram.ext.PollHandler
:members:
:show-inheritance:
+16 -4
View File
@@ -5,12 +5,13 @@ telegram.ext package
telegram.ext.updater
telegram.ext.dispatcher
telegram.ext.filters
telegram.ext.dispatcherhandlerstop
telegram.ext.callbackcontext
telegram.ext.defaults
telegram.ext.job
telegram.ext.jobqueue
telegram.ext.messagequeue
telegram.ext.delayqueue
telegram.ext.callbackcontext
Handlers
--------
@@ -20,10 +21,14 @@ Handlers
telegram.ext.handler
telegram.ext.callbackqueryhandler
telegram.ext.choseninlineresulthandler
telegram.ext.conversationhandler
telegram.ext.chatmemberhandler
telegram.ext.commandhandler
telegram.ext.conversationhandler
telegram.ext.inlinequeryhandler
telegram.ext.messagehandler
telegram.ext.filters
telegram.ext.pollanswerhandler
telegram.ext.pollhandler
telegram.ext.precheckoutqueryhandler
telegram.ext.prefixhandler
telegram.ext.regexhandler
@@ -39,4 +44,11 @@ Persistence
telegram.ext.basepersistence
telegram.ext.picklepersistence
telegram.ext.dictpersistence
telegram.ext.dictpersistence
utils
-----
.. toctree::
telegram.ext.utils.promise
@@ -0,0 +1,6 @@
telegram.ext.utils.promise.Promise
==================================
.. autoclass:: telegram.ext.utils.promise.Promise
:members:
:show-inheritance:
@@ -0,0 +1,6 @@
telegram.KeyboardButtonPollType
===============================
.. autoclass:: telegram.KeyboardButtonPollType
:members:
:show-inheritance:
@@ -0,0 +1,6 @@
telegram.MessageAutoDeleteTimerChanged
======================================
.. autoclass:: telegram.MessageAutoDeleteTimerChanged
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
telegram.MessageId
==================
.. autoclass:: telegram.MessageId
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
telegram.PollAnswer
===================
.. autoclass:: telegram.PollAnswer
:members:
:show-inheritance:
@@ -0,0 +1,6 @@
telegram.ProximityAlertTriggered
================================
.. autoclass:: telegram.ProximityAlertTriggered
:members:
:show-inheritance:
+14
View File
@@ -9,14 +9,19 @@ telegram package
telegram.animation
telegram.audio
telegram.bot
telegram.botcommand
telegram.callbackquery
telegram.chat
telegram.chataction
telegram.chatinvitelink
telegram.chatlocation
telegram.chatmember
telegram.chatmemberupdated
telegram.chatpermissions
telegram.chatphoto
telegram.constants
telegram.contact
telegram.dice
telegram.document
telegram.error
telegram.file
@@ -31,14 +36,19 @@ telegram package
telegram.inputmediaphoto
telegram.inputmediavideo
telegram.keyboardbutton
telegram.keyboardbuttonpolltype
telegram.location
telegram.loginurl
telegram.message
telegram.messageautodeletetimerchanged
telegram.messageid
telegram.messageentity
telegram.parsemode
telegram.photosize
telegram.poll
telegram.pollanswer
telegram.polloption
telegram.proximityalerttriggered
telegram.replykeyboardremove
telegram.replykeyboardmarkup
telegram.replymarkup
@@ -50,6 +60,9 @@ telegram package
telegram.video
telegram.videonote
telegram.voice
telegram.voicechatstarted
telegram.voicechatended
telegram.voicechatparticipantsinvited
telegram.webhookinfo
Stickers
@@ -133,6 +146,7 @@ Passport
telegram.credentials
telegram.datacredentials
telegram.securedata
telegram.securevalue
telegram.filecredentials
telegram.iddocumentdata
telegram.personaldetails
+6
View File
@@ -0,0 +1,6 @@
telegram.SecureValue
====================
.. autoclass:: telegram.SecureValue
:members:
:show-inheritance:
+6 -3
View File
@@ -1,6 +1,9 @@
telegram.utils.promise.Promise
==============================
.. autoclass:: telegram.utils.promise.Promise
:members:
:show-inheritance:
.. py:class:: telegram.utils.promise.Promise
Shortcut for :class:`telegram.ext.utils.promise.Promise`.
.. deprecated:: 13.2
Use :class:`telegram.ext.utils.promise.Promise` instead.
+1
View File
@@ -6,3 +6,4 @@ telegram.utils package
telegram.utils.helpers
telegram.utils.promise
telegram.utils.request
telegram.utils.types
+6
View File
@@ -0,0 +1,6 @@
telegram.utils.types Module
===========================
.. automodule:: telegram.utils.types
:members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.VoiceChatEnded
=======================
.. autoclass:: telegram.VoiceChatEnded
:members:
:show-inheritance:
@@ -0,0 +1,7 @@
telegram.VoiceChatParticipantsInvited
=====================================
.. autoclass:: telegram.VoiceChatParticipantsInvited
:members:
:show-inheritance:
@@ -0,0 +1,7 @@
telegram.VoiceChatStarted
=========================
.. autoclass:: telegram.VoiceChatStarted
:members:
:show-inheritance:
+17 -5
View File
@@ -1,10 +1,10 @@
# Examples
In this folder there are small examples to show what a bot written with `python-telegram-bot` looks like. Some bots focus on one specific aspect of the Telegram Bot API while others focus on one of the mechanics of this library. Except for the [`echobot.py`](#pure-api) example, they all use the high-level framework this library provides with the [`telegram.ext`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.html) submodule.
In this folder are small examples to show what a bot written with `python-telegram-bot` looks like. Some bots focus on one specific aspect of the Telegram Bot API while others focus on one of the mechanics of this library. Except for the [`rawapibot.py`](#pure-api) example, they all use the high-level framework this library provides with the [`telegram.ext`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.html) submodule.
All examples are licensed under the [CC0 License](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/LICENSE.txt) and are therefore fully dedicated to the public domain. You can use them as the base for your own bots without worrying about copyrights.
### [`echobot2.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot2.py)
### [`echobot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot.py)
This is probably the base for most of the bots made with `python-telegram-bot`. It simply replies to each text message with a message that contains the same text.
### [`timerbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py)
@@ -19,20 +19,32 @@ A more complex example of a bot that uses the `ConversationHandler`. It is also
### [`nestedconversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/nestedconversationbot.py)
A even more complex example of a bot that uses the nested `ConversationHandler`s. While it's certainly not that complex that you couldn't built it without nested `ConversationHanldler`s, it gives a good impression on how to work with them. Of course, there is a [fancy state diagram](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/nestedconversationbot.png) for this example, too!
### [`persistentconversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/persistentconversationbot.py)
A basic example of a bot store conversation state and user_data over multiple restarts.
### [`inlinekeyboard.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinekeyboard.py)
This example sheds some light on inline keyboards, callback queries and message editing.
### [`inlinekeyboard2.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinekeyboard2.py)
A more complex example about inline keyboards, callback queries and message editing. This example showcases how an interactive menu could be build using inline keyboards.
### [`deeplinking.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/deeplinking.py)
A basic example on how to use deeplinking with inline keyboards.
### [`inlinebot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinebot.py)
A basic example of an [inline bot](https://core.telegram.org/bots/inline). Don't forget to enable inline mode with [@BotFather](https://telegram.me/BotFather).
### [`pollbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/pollbot.py)
This example sheds some light on polls, poll answers and the corresponding handlers.
### [`passportbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/passportbot.py)
A basic example of a bot that can accept passports. Use in combination with [`passportbot.html`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/passportbot.html). Don't forget to enable and configure payments with [@BotFather](https://telegram.me/BotFather). Check out this [guide](https://git.io/fAvYd) on Telegram passports in PTB.
### [`paymentbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/paymentbot.py)
A basic example of a bot that can accept payments. Don't forget to enable and configure payments with [@BotFather](https://telegram.me/BotFather).
### [`persistentconversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/persistentconversationbot.py)
A basic example of a bot store conversation state and user_data over multiple restarts.
### [`errorhandlerbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/errorhandlerbot.py)
A basic example on how to set up a custom error handler.
## Pure API
The [`echobot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot.py) example uses only the pure, "bare-metal" API wrapper.
The [`rawapibot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/rawapibot.py) example uses only the pure, "bare-metal" API wrapper.
+57 -55
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
"""
@@ -16,82 +16,97 @@ bot.
import logging
from telegram import (ReplyKeyboardMarkup, ReplyKeyboardRemove)
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters,
ConversationHandler)
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
from telegram.ext import (
Updater,
CommandHandler,
MessageHandler,
Filters,
ConversationHandler,
CallbackContext,
)
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
GENDER, PHOTO, LOCATION, BIO = range(4)
def start(update, context):
def start(update: Update, _: CallbackContext) -> int:
reply_keyboard = [['Boy', 'Girl', 'Other']]
update.message.reply_text(
'Hi! My name is Professor Bot. I will hold a conversation with you. '
'Send /cancel to stop talking to me.\n\n'
'Are you a boy or a girl?',
reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True),
)
return GENDER
def gender(update, context):
def gender(update: Update, _: CallbackContext) -> int:
user = update.message.from_user
logger.info("Gender of %s: %s", user.first_name, update.message.text)
update.message.reply_text('I see! Please send me a photo of yourself, '
'so I know what you look like, or send /skip if you don\'t want to.',
reply_markup=ReplyKeyboardRemove())
update.message.reply_text(
'I see! Please send me a photo of yourself, '
'so I know what you look like, or send /skip if you don\'t want to.',
reply_markup=ReplyKeyboardRemove(),
)
return PHOTO
def photo(update, context):
def photo(update: Update, _: CallbackContext) -> int:
user = update.message.from_user
photo_file = update.message.photo[-1].get_file()
photo_file.download('user_photo.jpg')
logger.info("Photo of %s: %s", user.first_name, 'user_photo.jpg')
update.message.reply_text('Gorgeous! Now, send me your location please, '
'or send /skip if you don\'t want to.')
update.message.reply_text(
'Gorgeous! Now, send me your location please, or send /skip if you don\'t want to.'
)
return LOCATION
def skip_photo(update, context):
def skip_photo(update: Update, _: CallbackContext) -> int:
user = update.message.from_user
logger.info("User %s did not send a photo.", user.first_name)
update.message.reply_text('I bet you look great! Now, send me your location please, '
'or send /skip.')
update.message.reply_text(
'I bet you look great! Now, send me your location please, or send /skip.'
)
return LOCATION
def location(update, context):
def location(update: Update, _: CallbackContext) -> int:
user = update.message.from_user
user_location = update.message.location
logger.info("Location of %s: %f / %f", user.first_name, user_location.latitude,
user_location.longitude)
update.message.reply_text('Maybe I can visit you sometime! '
'At last, tell me something about yourself.')
logger.info(
"Location of %s: %f / %f", user.first_name, user_location.latitude, user_location.longitude
)
update.message.reply_text(
'Maybe I can visit you sometime! At last, tell me something about yourself.'
)
return BIO
def skip_location(update, context):
def skip_location(update: Update, _: CallbackContext) -> int:
user = update.message.from_user
logger.info("User %s did not send a location.", user.first_name)
update.message.reply_text('You seem a bit paranoid! '
'At last, tell me something about yourself.')
update.message.reply_text(
'You seem a bit paranoid! At last, tell me something about yourself.'
)
return BIO
def bio(update, context):
def bio(update: Update, _: CallbackContext) -> int:
user = update.message.from_user
logger.info("Bio of %s: %s", user.first_name, update.message.text)
update.message.reply_text('Thank you! I hope we can talk again some day.')
@@ -99,52 +114,39 @@ def bio(update, context):
return ConversationHandler.END
def cancel(update, context):
def cancel(update: Update, _: CallbackContext) -> int:
user = update.message.from_user
logger.info("User %s canceled the conversation.", user.first_name)
update.message.reply_text('Bye! I hope we can talk again some day.',
reply_markup=ReplyKeyboardRemove())
update.message.reply_text(
'Bye! I hope we can talk again some day.', reply_markup=ReplyKeyboardRemove()
)
return ConversationHandler.END
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
def main() -> None:
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
dispatcher = updater.dispatcher
# Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
GENDER: [MessageHandler(Filters.regex('^(Boy|Girl|Other)$'), gender)],
PHOTO: [MessageHandler(Filters.photo, photo),
CommandHandler('skip', skip_photo)],
LOCATION: [MessageHandler(Filters.location, location),
CommandHandler('skip', skip_location)],
BIO: [MessageHandler(Filters.text, bio)]
PHOTO: [MessageHandler(Filters.photo, photo), CommandHandler('skip', skip_photo)],
LOCATION: [
MessageHandler(Filters.location, location),
CommandHandler('skip', skip_location),
],
BIO: [MessageHandler(Filters.text & ~Filters.command, bio)],
},
fallbacks=[CommandHandler('cancel', cancel)]
fallbacks=[CommandHandler('cancel', cancel)],
)
dp.add_handler(conv_handler)
# log all errors
dp.add_error_handler(error)
dispatcher.add_handler(conv_handler)
# Start the Bot
updater.start_polling()
+63 -58
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
"""
@@ -15,128 +15,133 @@ bot.
"""
import logging
from typing import Dict
from telegram import ReplyKeyboardMarkup
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters,
ConversationHandler)
from telegram import ReplyKeyboardMarkup, Update
from telegram.ext import (
Updater,
CommandHandler,
MessageHandler,
Filters,
ConversationHandler,
CallbackContext,
)
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3)
reply_keyboard = [['Age', 'Favourite colour'],
['Number of siblings', 'Something else...'],
['Done']]
reply_keyboard = [
['Age', 'Favourite colour'],
['Number of siblings', 'Something else...'],
['Done'],
]
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
def facts_to_str(user_data):
def facts_to_str(user_data: Dict[str, str]) -> str:
facts = list()
for key, value in user_data.items():
facts.append('{} - {}'.format(key, value))
facts.append(f'{key} - {value}')
return "\n".join(facts).join(['\n', '\n'])
def start(update, context):
def start(update: Update, _: CallbackContext) -> int:
update.message.reply_text(
"Hi! My name is Doctor Botter. I will hold a more complex conversation with you. "
"Why don't you tell me something about yourself?",
reply_markup=markup)
reply_markup=markup,
)
return CHOOSING
def regular_choice(update, context):
def regular_choice(update: Update, context: CallbackContext) -> int:
text = update.message.text
context.user_data['choice'] = text
update.message.reply_text(
'Your {}? Yes, I would love to hear about that!'.format(text.lower()))
update.message.reply_text(f'Your {text.lower()}? Yes, I would love to hear about that!')
return TYPING_REPLY
def custom_choice(update, context):
update.message.reply_text('Alright, please send me the category first, '
'for example "Most impressive skill"')
def custom_choice(update: Update, _: CallbackContext) -> int:
update.message.reply_text(
'Alright, please send me the category first, for example "Most impressive skill"'
)
return TYPING_CHOICE
def received_information(update, context):
def received_information(update: Update, context: CallbackContext) -> int:
user_data = context.user_data
text = update.message.text
category = user_data['choice']
user_data[category] = text
del user_data['choice']
update.message.reply_text("Neat! Just so you know, this is what you already told me:"
"{} You can tell me more, or change your opinion"
" on something.".format(facts_to_str(user_data)),
reply_markup=markup)
update.message.reply_text(
"Neat! Just so you know, this is what you already told me:"
f"{facts_to_str(user_data)} You can tell me more, or change your opinion"
" on something.",
reply_markup=markup,
)
return CHOOSING
def done(update, context):
def done(update: Update, context: CallbackContext) -> int:
user_data = context.user_data
if 'choice' in user_data:
del user_data['choice']
update.message.reply_text("I learned these facts about you:"
"{}"
"Until next time!".format(facts_to_str(user_data)))
update.message.reply_text(
f"I learned these facts about you: {facts_to_str(user_data)} Until next time!"
)
user_data.clear()
return ConversationHandler.END
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
def main() -> None:
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
dispatcher = updater.dispatcher
# Add conversation handler with the states CHOOSING, TYPING_CHOICE and TYPING_REPLY
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
CHOOSING: [MessageHandler(Filters.regex('^(Age|Favourite colour|Number of siblings)$'),
regular_choice),
MessageHandler(Filters.regex('^Something else...$'),
custom_choice)
],
TYPING_CHOICE: [MessageHandler(Filters.text,
regular_choice)
],
TYPING_REPLY: [MessageHandler(Filters.text,
received_information),
],
CHOOSING: [
MessageHandler(
Filters.regex('^(Age|Favourite colour|Number of siblings)$'), regular_choice
),
MessageHandler(Filters.regex('^Something else...$'), custom_choice),
],
TYPING_CHOICE: [
MessageHandler(
Filters.text & ~(Filters.command | Filters.regex('^Done$')), regular_choice
)
],
TYPING_REPLY: [
MessageHandler(
Filters.text & ~(Filters.command | Filters.regex('^Done$')),
received_information,
)
],
},
fallbacks=[MessageHandler(Filters.regex('^Done$'), done)]
fallbacks=[MessageHandler(Filters.regex('^Done$'), done)],
)
dp.add_handler(conv_handler)
# log all errors
dp.add_error_handler(error)
dispatcher.add_handler(conv_handler)
# Start the Bot
updater.start_polling()
+75 -41
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
"""Bot that explains Telegram's "Deep Linking Parameters" functionality.
@@ -19,92 +20,125 @@ bot.
import logging
from telegram import ParseMode, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import Updater, CommandHandler, Filters
from telegram import ParseMode, InlineKeyboardMarkup, InlineKeyboardButton, Update
from telegram.ext import (
Updater,
CommandHandler,
CallbackQueryHandler,
Filters,
CallbackContext,
)
# Enable logging
from telegram.utils import helpers
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
logger = logging.getLogger(__name__)
# Define constants that will allow us to reuse the deep-linking parameters.
CHECK_THIS_OUT = 'check-this-out'
USING_ENTITIES = 'using-entities-here'
SO_COOL = 'so-cool'
CHECK_THIS_OUT = "check-this-out"
USING_ENTITIES = "using-entities-here"
USING_KEYBOARD = "using-keyboard-here"
SO_COOL = "so-cool"
# Callback data to pass in 3rd level deeplinking
KEYBOARD_CALLBACKDATA = "keyboard-callback-data"
def start(update, context):
def start(update: Update, context: CallbackContext) -> None:
"""Send a deep-linked URL when the command /start is issued."""
bot = context.bot
url = helpers.create_deep_linked_url(bot.get_me().username, CHECK_THIS_OUT, group=True)
url = helpers.create_deep_linked_url(bot.username, CHECK_THIS_OUT, group=True)
text = "Feel free to tell your friends about it:\n\n" + url
update.message.reply_text(text)
def deep_linked_level_1(update, context):
def deep_linked_level_1(update: Update, context: CallbackContext) -> None:
"""Reached through the CHECK_THIS_OUT payload"""
bot = context.bot
url = helpers.create_deep_linked_url(bot.get_me().username, SO_COOL)
text = "Awesome, you just accessed hidden functionality! " \
" Now let's get back to the private chat."
url = helpers.create_deep_linked_url(bot.username, SO_COOL)
text = (
"Awesome, you just accessed hidden functionality! "
" Now let's get back to the private chat."
)
keyboard = InlineKeyboardMarkup.from_button(
InlineKeyboardButton(text='Continue here!', url=url)
InlineKeyboardButton(text="Continue here!", url=url)
)
update.message.reply_text(text, reply_markup=keyboard)
def deep_linked_level_2(update, context):
def deep_linked_level_2(update: Update, context: CallbackContext) -> None:
"""Reached through the SO_COOL payload"""
bot = context.bot
url = helpers.create_deep_linked_url(bot.get_me().username, USING_ENTITIES)
text = "You can also mask the deep-linked URLs as links: " \
"[▶️ CLICK HERE]({0}).".format(url)
url = helpers.create_deep_linked_url(bot.username, USING_ENTITIES)
text = f"You can also mask the deep-linked URLs as links: [▶️ CLICK HERE]({url})."
update.message.reply_text(text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True)
def deep_linked_level_3(update, context):
def deep_linked_level_3(update: Update, _: CallbackContext) -> None:
"""Reached through the USING_ENTITIES payload"""
update.message.reply_text(
"It is also possible to make deep-linking using InlineKeyboardButtons.",
reply_markup=InlineKeyboardMarkup(
[[InlineKeyboardButton(text="Like this!", callback_data=KEYBOARD_CALLBACKDATA)]]
),
)
def deep_link_level_3_callback(update: Update, context: CallbackContext) -> None:
"""Answers CallbackQuery with deeplinking url."""
bot = context.bot
url = helpers.create_deep_linked_url(bot.username, USING_KEYBOARD)
update.callback_query.answer(url=url)
def deep_linked_level_4(update: Update, context: CallbackContext) -> None:
"""Reached through the USING_KEYBOARD payload"""
payload = context.args
update.message.reply_text("Congratulations! This is as deep as it gets 👏🏻\n\n"
"The payload was: {0}".format(payload))
update.message.reply_text(
f"Congratulations! This is as deep as it gets 👏🏻\n\nThe payload was: {payload}"
)
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
def main() -> None:
"""Start the bot."""
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN", use_context=True)
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
dispatcher = updater.dispatcher
# More info on what deep linking actually is (read this first if it's unclear to you):
# https://core.telegram.org/bots#deep-linking
# Register a deep-linking handler
dp.add_handler(CommandHandler("start", deep_linked_level_1, Filters.regex(CHECK_THIS_OUT)))
dispatcher.add_handler(
CommandHandler("start", deep_linked_level_1, Filters.regex(CHECK_THIS_OUT))
)
# This one works with a textual link instead of an URL
dp.add_handler(CommandHandler("start", deep_linked_level_2, Filters.regex(SO_COOL)))
dispatcher.add_handler(CommandHandler("start", deep_linked_level_2, Filters.regex(SO_COOL)))
# We can also pass on the deep-linking payload
dp.add_handler(CommandHandler("start",
deep_linked_level_3,
Filters.regex(USING_ENTITIES),
pass_args=True))
dispatcher.add_handler(
CommandHandler("start", deep_linked_level_3, Filters.regex(USING_ENTITIES), pass_args=True)
)
# Possible with inline keyboard buttons aswell
dispatcher.add_handler(
CommandHandler("start", deep_linked_level_4, Filters.regex(USING_KEYBOARD))
)
# register callback handler for inline keyboard button
dispatcher.add_handler(
CallbackQueryHandler(deep_link_level_3_callback, pattern=KEYBOARD_CALLBACKDATA)
)
# Make sure the deep-linking handlers occur *before* the normal /start handler.
dp.add_handler(CommandHandler("start", start))
# log all errors
dp.add_error_handler(error)
dispatcher.add_handler(CommandHandler("start", start))
# Start the Bot
updater.start_polling()
@@ -115,5 +149,5 @@ def main():
updater.idle()
if __name__ == '__main__':
if __name__ == "__main__":
main()
+58 -41
View File
@@ -1,55 +1,72 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Simple Bot to reply to Telegram messages.
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
This is built on the API wrapper, see echobot2.py to see the same example built
on the telegram.ext bot framework.
This program is dedicated to the public domain under the CC0 license.
"""
Simple Bot to reply to Telegram messages.
First, a few handler functions are defined. Then, those functions are passed to
the Dispatcher and registered at their respective places.
Then, the bot is started and runs until we press Ctrl-C on the command line.
Usage:
Basic Echobot example, repeats messages.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
import logging
import telegram
from telegram.error import NetworkError, Unauthorized
from time import sleep
from telegram import Update
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackContext
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
update_id = None
# Define a few command handlers. These usually take the two arguments update and
# context. Error handlers also receive the raised TelegramError object in error.
def start(update: Update, _: CallbackContext) -> None:
"""Send a message when the command /start is issued."""
update.message.reply_text('Hi!')
def main():
"""Run the bot."""
global update_id
# Telegram Bot Authorization Token
bot = telegram.Bot('TOKEN')
# get the first pending update_id, this is so we can skip over it in case
# we get an "Unauthorized" exception.
try:
update_id = bot.get_updates()[0].update_id
except IndexError:
update_id = None
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
while True:
try:
echo(bot)
except NetworkError:
sleep(1)
except Unauthorized:
# The user has removed or blocked the bot.
update_id += 1
def help_command(update: Update, _: CallbackContext) -> None:
"""Send a message when the command /help is issued."""
update.message.reply_text('Help!')
def echo(bot):
"""Echo the message the user sent."""
global update_id
# Request updates after the last update_id
for update in bot.get_updates(offset=update_id, timeout=10):
update_id = update.update_id + 1
def echo(update: Update, _: CallbackContext) -> None:
"""Echo the user message."""
update.message.reply_text(update.message.text)
if update.message: # your bot can receive updates without messages
# Reply to the message
update.message.reply_text(update.message.text)
def main() -> None:
"""Start the bot."""
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dispatcher = updater.dispatcher
# on different commands - answer in Telegram
dispatcher.add_handler(CommandHandler("start", start))
dispatcher.add_handler(CommandHandler("help", help_command))
# on noncommand i.e message - echo the message on Telegram
dispatcher.add_handler(MessageHandler(Filters.text & ~Filters.command, echo))
# Start the Bot
updater.start_polling()
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
-81
View File
@@ -1,81 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This program is dedicated to the public domain under the CC0 license.
"""
Simple Bot to reply to Telegram messages.
First, a few handler functions are defined. Then, those functions are passed to
the Dispatcher and registered at their respective places.
Then, the bot is started and runs until we press Ctrl-C on the command line.
Usage:
Basic Echobot example, repeats messages.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
import logging
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
# Define a few command handlers. These usually take the two arguments update and
# context. Error handlers also receive the raised TelegramError object in error.
def start(update, context):
"""Send a message when the command /start is issued."""
update.message.reply_text('Hi!')
def help(update, context):
"""Send a message when the command /help is issued."""
update.message.reply_text('Help!')
def echo(update, context):
"""Echo the user message."""
update.message.reply_text(update.message.text)
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
"""Start the bot."""
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
# Get the dispatcher to register handlers
dp = updater.dispatcher
# on different commands - answer in Telegram
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", help))
# on noncommand i.e message - echo the message on Telegram
dp.add_handler(MessageHandler(Filters.text, echo))
# log all errors
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
+92
View File
@@ -0,0 +1,92 @@
#!/usr/bin/env python
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
"""
This is a very simple example on how one could implement a custom error handler
"""
import html
import json
import logging
import traceback
from telegram import Update, ParseMode
from telegram.ext import Updater, CallbackContext, CommandHandler
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
# The token you got from @botfather when you created the bot
BOT_TOKEN = "TOKEN"
# This can be your own ID, or one for a developer group/channel.
# You can use the /start command of this bot to see your chat id.
DEVELOPER_CHAT_ID = 123456789
def error_handler(update: object, context: CallbackContext) -> None:
"""Log the error and send a telegram message to notify the developer."""
# Log the error before we do anything else, so we can see it even if something breaks.
logger.error(msg="Exception while handling an update:", exc_info=context.error)
# traceback.format_exception returns the usual python message about an exception, but as a
# list of strings rather than a single string, so we have to join them together.
tb_list = traceback.format_exception(None, context.error, context.error.__traceback__)
tb_string = ''.join(tb_list)
# Build the message with some markup and additional information about what happened.
# You might need to add some logic to deal with messages longer than the 4096 character limit.
update_str = update.to_dict() if isinstance(update, Update) else str(update)
message = (
f'An exception was raised while handling an update\n'
f'<pre>update = {html.escape(json.dumps(update_str, indent=2, ensure_ascii=False))}'
'</pre>\n\n'
f'<pre>context.chat_data = {html.escape(str(context.chat_data))}</pre>\n\n'
f'<pre>context.user_data = {html.escape(str(context.user_data))}</pre>\n\n'
f'<pre>{html.escape(tb_string)}</pre>'
)
# Finally, send the message
context.bot.send_message(chat_id=DEVELOPER_CHAT_ID, text=message, parse_mode=ParseMode.HTML)
def bad_command(_: Update, context: CallbackContext) -> None:
"""Raise an error to trigger the error handler."""
context.bot.wrong_method_name() # type: ignore[attr-defined]
def start(update: Update, _: CallbackContext) -> None:
update.effective_message.reply_html(
'Use /bad_command to cause an error.\n'
f'Your chat id is <code>{update.effective_chat.id}</code>.'
)
def main() -> None:
# Create the Updater and pass it your bot's token.
updater = Updater(BOT_TOKEN)
# Get the dispatcher to register handlers
dispatcher = updater.dispatcher
# Register the commands...
dispatcher.add_handler(CommandHandler('start', start))
dispatcher.add_handler(CommandHandler('bad_command', bad_command))
# ...and the error handler
dispatcher.add_error_handler(error_handler)
# Start the Bot
updater.start_polling()
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
+27 -34
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
"""
@@ -15,78 +15,71 @@ bot.
import logging
from uuid import uuid4
from telegram import InlineQueryResultArticle, ParseMode, \
InputTextMessageContent
from telegram.ext import Updater, InlineQueryHandler, CommandHandler
from telegram import InlineQueryResultArticle, ParseMode, InputTextMessageContent, Update
from telegram.ext import Updater, InlineQueryHandler, CommandHandler, CallbackContext
from telegram.utils.helpers import escape_markdown
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
# Define a few command handlers. These usually take the two arguments update and
# context. Error handlers also receive the raised TelegramError object in error.
def start(update, context):
def start(update: Update, _: CallbackContext) -> None:
"""Send a message when the command /start is issued."""
update.message.reply_text('Hi!')
def help(update, context):
def help_command(update: Update, _: CallbackContext) -> None:
"""Send a message when the command /help is issued."""
update.message.reply_text('Help!')
def inlinequery(update, context):
def inlinequery(update: Update, _: CallbackContext) -> None:
"""Handle the inline query."""
query = update.inline_query.query
results = [
InlineQueryResultArticle(
id=uuid4(),
id=str(uuid4()),
title="Caps",
input_message_content=InputTextMessageContent(
query.upper())),
input_message_content=InputTextMessageContent(query.upper()),
),
InlineQueryResultArticle(
id=uuid4(),
id=str(uuid4()),
title="Bold",
input_message_content=InputTextMessageContent(
"*{}*".format(escape_markdown(query)),
parse_mode=ParseMode.MARKDOWN)),
f"*{escape_markdown(query)}*", parse_mode=ParseMode.MARKDOWN
),
),
InlineQueryResultArticle(
id=uuid4(),
id=str(uuid4()),
title="Italic",
input_message_content=InputTextMessageContent(
"_{}_".format(escape_markdown(query)),
parse_mode=ParseMode.MARKDOWN))]
f"_{escape_markdown(query)}_", parse_mode=ParseMode.MARKDOWN
),
),
]
update.inline_query.answer(results)
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
def main() -> None:
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
dispatcher = updater.dispatcher
# on different commands - answer in Telegram
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", help))
dispatcher.add_handler(CommandHandler("start", start))
dispatcher.add_handler(CommandHandler("help", help_command))
# on noncommand i.e message - echo the message on Telegram
dp.add_handler(InlineQueryHandler(inlinequery))
# log all errors
dp.add_error_handler(error)
dispatcher.add_handler(InlineQueryHandler(inlinequery))
# Start the Bot
updater.start_polling()
+24 -24
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
"""
@@ -7,50 +7,50 @@ Basic example for a bot that uses inline keyboards.
"""
import logging
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
def start(update, context):
keyboard = [[InlineKeyboardButton("Option 1", callback_data='1'),
InlineKeyboardButton("Option 2", callback_data='2')],
[InlineKeyboardButton("Option 3", callback_data='3')]]
def start(update: Update, _: CallbackContext) -> None:
keyboard = [
[
InlineKeyboardButton("Option 1", callback_data='1'),
InlineKeyboardButton("Option 2", callback_data='2'),
],
[InlineKeyboardButton("Option 3", callback_data='3')],
]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('Please choose:', reply_markup=reply_markup)
def button(update, context):
def button(update: Update, _: CallbackContext) -> None:
query = update.callback_query
query.edit_message_text(text="Selected option: {}".format(query.data))
# CallbackQueries need to be answered, even if no notification to the user is needed
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
query.answer()
query.edit_message_text(text=f"Selected option: {query.data}")
def help(update, context):
def help_command(update: Update, _: CallbackContext) -> None:
update.message.reply_text("Use /start to test this bot.")
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
def main() -> None:
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
updater = Updater("TOKEN")
updater.dispatcher.add_handler(CommandHandler('start', start))
updater.dispatcher.add_handler(CallbackQueryHandler(button))
updater.dispatcher.add_handler(CommandHandler('help', help))
updater.dispatcher.add_error_handler(error)
updater.dispatcher.add_handler(CommandHandler('help', help_command))
# Start the Bot
updater.start_polling()
+87 -93
View File
@@ -1,5 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
"""Simple inline keyboard bot with multiple CallbackQueryHandlers.
This Bot uses the Updater class to handle the bot.
@@ -12,13 +14,20 @@ ConversationHandler.
Send /start to initiate the conversation.
Press Ctrl-C on the command line to stop the bot.
"""
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, ConversationHandler
import logging
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import (
Updater,
CommandHandler,
CallbackQueryHandler,
ConversationHandler,
CallbackContext,
)
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
@@ -28,7 +37,7 @@ FIRST, SECOND = range(2)
ONE, TWO, THREE, FOUR = range(4)
def start(update, context):
def start(update: Update, _: CallbackContext) -> int:
"""Send message on `/start`."""
# Get user that sent /start and log his name
user = update.message.from_user
@@ -38,139 +47,123 @@ def start(update, context):
# The keyboard is a list of button rows, where each row is in turn
# a list (hence `[[...]]`).
keyboard = [
[InlineKeyboardButton("1", callback_data=str(ONE)),
InlineKeyboardButton("2", callback_data=str(TWO))]
[
InlineKeyboardButton("1", callback_data=str(ONE)),
InlineKeyboardButton("2", callback_data=str(TWO)),
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
# Send message with text and appended InlineKeyboard
update.message.reply_text(
"Start handler, Choose a route",
reply_markup=reply_markup
)
update.message.reply_text("Start handler, Choose a route", reply_markup=reply_markup)
# Tell ConversationHandler that we're in state `FIRST` now
return FIRST
def start_over(update, context):
def start_over(update: Update, _: CallbackContext) -> int:
"""Prompt same text & keyboard as `start` does but not as new message"""
# Get CallbackQuery from Update
query = update.callback_query
# Get Bot from CallbackContext
bot = context.bot
# CallbackQueries need to be answered, even if no notification to the user is needed
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
query.answer()
keyboard = [
[InlineKeyboardButton("1", callback_data=str(ONE)),
InlineKeyboardButton("2", callback_data=str(TWO))]
[
InlineKeyboardButton("1", callback_data=str(ONE)),
InlineKeyboardButton("2", callback_data=str(TWO)),
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
# Instead of sending a new message, edit the message that
# originated the CallbackQuery. This gives the feeling of an
# interactive menu.
bot.edit_message_text(
chat_id=query.message.chat_id,
message_id=query.message.message_id,
text="Start handler, Choose a route",
reply_markup=reply_markup
query.edit_message_text(text="Start handler, Choose a route", reply_markup=reply_markup)
return FIRST
def one(update: Update, _: CallbackContext) -> int:
"""Show new choice of buttons"""
query = update.callback_query
query.answer()
keyboard = [
[
InlineKeyboardButton("3", callback_data=str(THREE)),
InlineKeyboardButton("4", callback_data=str(FOUR)),
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
query.edit_message_text(
text="First CallbackQueryHandler, Choose a route", reply_markup=reply_markup
)
return FIRST
def one(update, context):
def two(update: Update, _: CallbackContext) -> int:
"""Show new choice of buttons"""
query = update.callback_query
bot = context.bot
query.answer()
keyboard = [
[InlineKeyboardButton("3", callback_data=str(THREE)),
InlineKeyboardButton("4", callback_data=str(FOUR))]
[
InlineKeyboardButton("1", callback_data=str(ONE)),
InlineKeyboardButton("3", callback_data=str(THREE)),
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
bot.edit_message_text(
chat_id=query.message.chat_id,
message_id=query.message.message_id,
text="First CallbackQueryHandler, Choose a route",
reply_markup=reply_markup
query.edit_message_text(
text="Second CallbackQueryHandler, Choose a route", reply_markup=reply_markup
)
return FIRST
def two(update, context):
def three(update: Update, _: CallbackContext) -> int:
"""Show new choice of buttons"""
query = update.callback_query
bot = context.bot
query.answer()
keyboard = [
[InlineKeyboardButton("1", callback_data=str(ONE)),
InlineKeyboardButton("3", callback_data=str(THREE))]
[
InlineKeyboardButton("Yes, let's do it again!", callback_data=str(ONE)),
InlineKeyboardButton("Nah, I've had enough ...", callback_data=str(TWO)),
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
bot.edit_message_text(
chat_id=query.message.chat_id,
message_id=query.message.message_id,
text="Second CallbackQueryHandler, Choose a route",
reply_markup=reply_markup
)
return FIRST
def three(update, context):
"""Show new choice of buttons"""
query = update.callback_query
bot = context.bot
keyboard = [
[InlineKeyboardButton("Yes, let's do it again!", callback_data=str(ONE)),
InlineKeyboardButton("Nah, I've had enough ...", callback_data=str(TWO))]
]
reply_markup = InlineKeyboardMarkup(keyboard)
bot.edit_message_text(
chat_id=query.message.chat_id,
message_id=query.message.message_id,
text="Third CallbackQueryHandler. Do want to start over?",
reply_markup=reply_markup
query.edit_message_text(
text="Third CallbackQueryHandler. Do want to start over?", reply_markup=reply_markup
)
# Transfer to conversation state `SECOND`
return SECOND
def four(update, context):
def four(update: Update, _: CallbackContext) -> int:
"""Show new choice of buttons"""
query = update.callback_query
bot = context.bot
query.answer()
keyboard = [
[InlineKeyboardButton("2", callback_data=str(TWO)),
InlineKeyboardButton("4", callback_data=str(FOUR))]
[
InlineKeyboardButton("2", callback_data=str(TWO)),
InlineKeyboardButton("4", callback_data=str(FOUR)),
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
bot.edit_message_text(
chat_id=query.message.chat_id,
message_id=query.message.message_id,
text="Fourth CallbackQueryHandler, Choose a route",
reply_markup=reply_markup
query.edit_message_text(
text="Fourth CallbackQueryHandler, Choose a route", reply_markup=reply_markup
)
return FIRST
def end(update, context):
def end(update: Update, _: CallbackContext) -> int:
"""Returns `ConversationHandler.END`, which tells the
ConversationHandler that the conversation is over"""
query = update.callback_query
bot = context.bot
bot.edit_message_text(
chat_id=query.message.chat_id,
message_id=query.message.message_id,
text="See you next time!"
)
query.answer()
query.edit_message_text(text="See you next time!")
return ConversationHandler.END
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
def main() -> None:
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN", use_context=True)
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
dispatcher = updater.dispatcher
# Setup conversation handler with the states FIRST and SECOND
# Use the pattern parameter to pass CallbackQueries with specific
@@ -181,22 +174,23 @@ def main():
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
FIRST: [CallbackQueryHandler(one, pattern='^' + str(ONE) + '$'),
CallbackQueryHandler(two, pattern='^' + str(TWO) + '$'),
CallbackQueryHandler(three, pattern='^' + str(THREE) + '$'),
CallbackQueryHandler(four, pattern='^' + str(FOUR) + '$')],
SECOND: [CallbackQueryHandler(start_over, pattern='^' + str(ONE) + '$'),
CallbackQueryHandler(end, pattern='^' + str(TWO) + '$')]
FIRST: [
CallbackQueryHandler(one, pattern='^' + str(ONE) + '$'),
CallbackQueryHandler(two, pattern='^' + str(TWO) + '$'),
CallbackQueryHandler(three, pattern='^' + str(THREE) + '$'),
CallbackQueryHandler(four, pattern='^' + str(FOUR) + '$'),
],
SECOND: [
CallbackQueryHandler(start_over, pattern='^' + str(ONE) + '$'),
CallbackQueryHandler(end, pattern='^' + str(TWO) + '$'),
],
},
fallbacks=[CommandHandler('start', start)]
fallbacks=[CommandHandler('start', start)],
)
# Add ConversationHandler to dispatcher that will be used for handling
# updates
dp.add_handler(conv_handler)
# log all errors
dp.add_error_handler(error)
dispatcher.add_handler(conv_handler)
# Start the Bot
updater.start_polling()
+154 -125
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
"""
@@ -15,14 +15,23 @@ bot.
"""
import logging
from typing import Tuple, Dict, Any
from telegram import (InlineKeyboardMarkup, InlineKeyboardButton)
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters,
ConversationHandler, CallbackQueryHandler)
from telegram import InlineKeyboardMarkup, InlineKeyboardButton, Update
from telegram.ext import (
Updater,
CommandHandler,
MessageHandler,
Filters,
ConversationHandler,
CallbackQueryHandler,
CallbackContext,
)
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
@@ -38,59 +47,79 @@ STOPPING, SHOWING = map(chr, range(8, 10))
END = ConversationHandler.END
# Different constants for this example
(PARENTS, CHILDREN, SELF, GENDER, MALE, FEMALE, AGE, NAME, START_OVER, FEATURES,
CURRENT_FEATURE, CURRENT_LEVEL) = map(chr, range(10, 22))
(
PARENTS,
CHILDREN,
SELF,
GENDER,
MALE,
FEMALE,
AGE,
NAME,
START_OVER,
FEATURES,
CURRENT_FEATURE,
CURRENT_LEVEL,
) = map(chr, range(10, 22))
# Helper
def _name_switcher(level):
def _name_switcher(level: str) -> Tuple[str, str]:
if level == PARENTS:
return ('Father', 'Mother')
elif level == CHILDREN:
return ('Brother', 'Sister')
return 'Father', 'Mother'
return 'Brother', 'Sister'
# Top level conversation callbacks
def start(update, context):
def start(update: Update, context: CallbackContext) -> str:
"""Select an action: Adding parent/child or show data."""
text = 'You may add a familiy member, yourself show the gathered data or end the ' \
'conversation. To abort, simply type /stop.'
buttons = [[
InlineKeyboardButton(text='Add family member', callback_data=str(ADDING_MEMBER)),
InlineKeyboardButton(text='Add yourself', callback_data=str(ADDING_SELF))
], [
InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)),
InlineKeyboardButton(text='Done', callback_data=str(END))
]]
text = (
'You may add a familiy member, yourself show the gathered data or end the '
'conversation. To abort, simply type /stop.'
)
buttons = [
[
InlineKeyboardButton(text='Add family member', callback_data=str(ADDING_MEMBER)),
InlineKeyboardButton(text='Add yourself', callback_data=str(ADDING_SELF)),
],
[
InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)),
InlineKeyboardButton(text='Done', callback_data=str(END)),
],
]
keyboard = InlineKeyboardMarkup(buttons)
# If we're starting over we don't need do send a new message
if context.user_data.get(START_OVER):
update.callback_query.answer()
update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
else:
update.message.reply_text('Hi, I\'m FamiliyBot and here to help you gather information'
'about your family.')
update.message.reply_text(
'Hi, I\'m FamiliyBot and here to help you gather information' 'about your family.'
)
update.message.reply_text(text=text, reply_markup=keyboard)
context.user_data[START_OVER] = False
return SELECTING_ACTION
def adding_self(update, context):
def adding_self(update: Update, context: CallbackContext) -> str:
"""Add information about youself."""
context.user_data[CURRENT_LEVEL] = SELF
text = 'Okay, please tell me about yourself.'
button = InlineKeyboardButton(text='Add info', callback_data=str(MALE))
keyboard = InlineKeyboardMarkup.from_button(button)
update.callback_query.answer()
update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
return DESCRIBING_SELF
def show_data(update, context):
def show_data(update: Update, context: CallbackContext) -> str:
"""Pretty print gathered data."""
def prettyprint(user_data, level):
def prettyprint(user_data: Dict[str, Any], level: str) -> str:
people = user_data.get(level)
if not people:
return '\nNo information yet.'
@@ -98,41 +127,41 @@ def show_data(update, context):
text = ''
if level == SELF:
for person in user_data[level]:
text += '\nName: {0}, Age: {1}'.format(person.get(NAME, '-'), person.get(AGE, '-'))
text += f"\nName: {person.get(NAME, '-')}, Age: {person.get(AGE, '-')}"
else:
male, female = _name_switcher(level)
for person in user_data[level]:
gender = female if person[GENDER] == FEMALE else male
text += '\n{0}: Name: {1}, Age: {2}'.format(gender, person.get(NAME, '-'),
person.get(AGE, '-'))
text += f"\n{gender}: Name: {person.get(NAME, '-')}, Age: {person.get(AGE, '-')}"
return text
ud = context.user_data
text = 'Yourself:' + prettyprint(ud, SELF)
text += '\n\nParents:' + prettyprint(ud, PARENTS)
text += '\n\nChildren:' + prettyprint(ud, CHILDREN)
user_data = context.user_data
text = 'Yourself:' + prettyprint(user_data, SELF)
text += '\n\nParents:' + prettyprint(user_data, PARENTS)
text += '\n\nChildren:' + prettyprint(user_data, CHILDREN)
buttons = [[
InlineKeyboardButton(text='Back', callback_data=str(END))
]]
buttons = [[InlineKeyboardButton(text='Back', callback_data=str(END))]]
keyboard = InlineKeyboardMarkup(buttons)
update.callback_query.answer()
update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
ud[START_OVER] = True
user_data[START_OVER] = True
return SHOWING
def stop(update, context):
def stop(update: Update, _: CallbackContext) -> int:
"""End Conversation by command."""
update.message.reply_text('Okay, bye.')
return END
def end(update, context):
def end(update: Update, _: CallbackContext) -> int:
"""End conversation from InlineKeyboardButton."""
update.callback_query.answer()
text = 'See you around!'
update.callback_query.edit_message_text(text=text)
@@ -140,23 +169,28 @@ def end(update, context):
# Second level conversation callbacks
def select_level(update, context):
def select_level(update: Update, _: CallbackContext) -> str:
"""Choose to add a parent or a child."""
text = 'You may add a parent or a child. Also you can show the gathered data or go back.'
buttons = [[
InlineKeyboardButton(text='Add parent', callback_data=str(PARENTS)),
InlineKeyboardButton(text='Add child', callback_data=str(CHILDREN))
], [
InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)),
InlineKeyboardButton(text='Back', callback_data=str(END))
]]
buttons = [
[
InlineKeyboardButton(text='Add parent', callback_data=str(PARENTS)),
InlineKeyboardButton(text='Add child', callback_data=str(CHILDREN)),
],
[
InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)),
InlineKeyboardButton(text='Back', callback_data=str(END)),
],
]
keyboard = InlineKeyboardMarkup(buttons)
update.callback_query.answer()
update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
return SELECTING_LEVEL
def select_gender(update, context):
def select_gender(update: Update, context: CallbackContext) -> str:
"""Choose to add mother or father."""
level = update.callback_query.data
context.user_data[CURRENT_LEVEL] = level
@@ -165,21 +199,25 @@ def select_gender(update, context):
male, female = _name_switcher(level)
buttons = [[
InlineKeyboardButton(text='Add ' + male, callback_data=str(MALE)),
InlineKeyboardButton(text='Add ' + female, callback_data=str(FEMALE))
], [
InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)),
InlineKeyboardButton(text='Back', callback_data=str(END))
]]
buttons = [
[
InlineKeyboardButton(text='Add ' + male, callback_data=str(MALE)),
InlineKeyboardButton(text='Add ' + female, callback_data=str(FEMALE)),
],
[
InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)),
InlineKeyboardButton(text='Back', callback_data=str(END)),
],
]
keyboard = InlineKeyboardMarkup(buttons)
update.callback_query.answer()
update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
return SELECTING_GENDER
def end_second_level(update, context):
def end_second_level(update: Update, context: CallbackContext) -> int:
"""Return to top level conversation."""
context.user_data[START_OVER] = True
start(update, context)
@@ -188,19 +226,23 @@ def end_second_level(update, context):
# Third level callbacks
def select_feature(update, context):
def select_feature(update: Update, context: CallbackContext) -> str:
"""Select a feature to update for the person."""
buttons = [[
InlineKeyboardButton(text='Name', callback_data=str(NAME)),
InlineKeyboardButton(text='Age', callback_data=str(AGE)),
InlineKeyboardButton(text='Done', callback_data=str(END)),
]]
buttons = [
[
InlineKeyboardButton(text='Name', callback_data=str(NAME)),
InlineKeyboardButton(text='Age', callback_data=str(AGE)),
InlineKeyboardButton(text='Done', callback_data=str(END)),
]
]
keyboard = InlineKeyboardMarkup(buttons)
# If we collect features for a new person, clear the cache and save the gender
if not context.user_data.get(START_OVER):
context.user_data[FEATURES] = {GENDER: update.callback_query.data}
text = 'Please select a feature to update.'
update.callback_query.answer()
update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
# But after we do that, we need to send a new message
else:
@@ -211,36 +253,38 @@ def select_feature(update, context):
return SELECTING_FEATURE
def ask_for_input(update, context):
def ask_for_input(update: Update, context: CallbackContext) -> str:
"""Prompt user to input data for selected feature."""
context.user_data[CURRENT_FEATURE] = update.callback_query.data
text = 'Okay, tell me.'
update.callback_query.answer()
update.callback_query.edit_message_text(text=text)
return TYPING
def save_input(update, context):
def save_input(update: Update, context: CallbackContext) -> str:
"""Save input for feature and return to feature selection."""
ud = context.user_data
ud[FEATURES][ud[CURRENT_FEATURE]] = update.message.text
user_data = context.user_data
user_data[FEATURES][user_data[CURRENT_FEATURE]] = update.message.text
ud[START_OVER] = True
user_data[START_OVER] = True
return select_feature(update, context)
def end_describing(update, context):
def end_describing(update: Update, context: CallbackContext) -> int:
"""End gathering of features and return to parent conversation."""
ud = context.user_data
level = ud[CURRENT_LEVEL]
if not ud.get(level):
ud[level] = []
ud[level].append(ud[FEATURES])
user_data = context.user_data
level = user_data[CURRENT_LEVEL]
if not user_data.get(level):
user_data[level] = []
user_data[level].append(user_data[FEATURES])
# Print upper level menu
if level == SELF:
ud[START_OVER] = True
user_data[START_OVER] = True
start(update, context)
else:
select_level(update, context)
@@ -248,70 +292,59 @@ def end_describing(update, context):
return END
def stop_nested(update, context):
def stop_nested(update: Update, _: CallbackContext) -> str:
"""Completely end conversation from within nested conversation."""
update.message.reply_text('Okay, bye.')
return STOPPING
# Error handler
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
def main() -> None:
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
dispatcher = updater.dispatcher
# Set up third level ConversationHandler (collecting features)
description_conv = ConversationHandler(
entry_points=[CallbackQueryHandler(select_feature,
pattern='^' + str(MALE) + '$|^' + str(FEMALE) + '$')],
entry_points=[
CallbackQueryHandler(
select_feature, pattern='^' + str(MALE) + '$|^' + str(FEMALE) + '$'
)
],
states={
SELECTING_FEATURE: [CallbackQueryHandler(ask_for_input,
pattern='^(?!' + str(END) + ').*$')],
TYPING: [MessageHandler(Filters.text, save_input)],
SELECTING_FEATURE: [
CallbackQueryHandler(ask_for_input, pattern='^(?!' + str(END) + ').*$')
],
TYPING: [MessageHandler(Filters.text & ~Filters.command, save_input)],
},
fallbacks=[
CallbackQueryHandler(end_describing, pattern='^' + str(END) + '$'),
CommandHandler('stop', stop_nested)
CommandHandler('stop', stop_nested),
],
map_to_parent={
# Return to second level menu
END: SELECTING_LEVEL,
# End conversation alltogether
STOPPING: STOPPING,
}
},
)
# Set up second level ConversationHandler (adding a person)
add_member_conv = ConversationHandler(
entry_points=[CallbackQueryHandler(select_level,
pattern='^' + str(ADDING_MEMBER) + '$')],
entry_points=[CallbackQueryHandler(select_level, pattern='^' + str(ADDING_MEMBER) + '$')],
states={
SELECTING_LEVEL: [CallbackQueryHandler(select_gender,
pattern='^{0}$|^{1}$'.format(str(PARENTS),
str(CHILDREN)))],
SELECTING_GENDER: [description_conv]
SELECTING_LEVEL: [
CallbackQueryHandler(select_gender, pattern=f'^{PARENTS}$|^{CHILDREN}$')
],
SELECTING_GENDER: [description_conv],
},
fallbacks=[
CallbackQueryHandler(show_data, pattern='^' + str(SHOWING) + '$'),
CallbackQueryHandler(end_second_level, pattern='^' + str(END) + '$'),
CommandHandler('stop', stop_nested)
CommandHandler('stop', stop_nested),
],
map_to_parent={
# After showing data return to top level menu
SHOWING: SHOWING,
@@ -319,35 +352,31 @@ def main():
END: SELECTING_ACTION,
# End conversation alltogether
STOPPING: END,
}
},
)
# Set up top level ConversationHandler (selecting action)
# Because the states of the third level conversation map to the ones of the econd level
# conversation, we need to make sure the top level conversation can also handle them
selection_handlers = [
add_member_conv,
CallbackQueryHandler(show_data, pattern='^' + str(SHOWING) + '$'),
CallbackQueryHandler(adding_self, pattern='^' + str(ADDING_SELF) + '$'),
CallbackQueryHandler(end, pattern='^' + str(END) + '$'),
]
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
SHOWING: [CallbackQueryHandler(start, pattern='^' + str(END) + '$')],
SELECTING_ACTION: [
add_member_conv,
CallbackQueryHandler(show_data, pattern='^' + str(SHOWING) + '$'),
CallbackQueryHandler(adding_self, pattern='^' + str(ADDING_SELF) + '$'),
CallbackQueryHandler(end, pattern='^' + str(END) + '$'),
],
SELECTING_ACTION: selection_handlers,
SELECTING_LEVEL: selection_handlers,
DESCRIBING_SELF: [description_conv],
STOPPING: [CommandHandler('start', start)],
},
fallbacks=[CommandHandler('stop', stop)],
)
# Because the states of the third level conversation map to the ones of the
# second level conversation, we need to be a bit hacky about that:
conv_handler.states[SELECTING_LEVEL] = conv_handler.states[SELECTING_ACTION]
conv_handler.states[STOPPING] = conv_handler.entry_points
dp.add_handler(conv_handler)
# log all errors
dp.add_error_handler(error)
dispatcher.add_handler(conv_handler)
# Start the Bot
updater.start_polling()
+47 -37
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
"""
@@ -12,16 +12,18 @@ See https://git.io/fAvYd for how to use Telegram Passport properly with python-t
"""
import logging
from telegram.ext import Updater, MessageHandler, Filters
from telegram import Update
from telegram.ext import Updater, MessageHandler, Filters, CallbackContext
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.DEBUG)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.DEBUG
)
logger = logging.getLogger(__name__)
def msg(bot, update):
def msg(update: Update, _: CallbackContext) -> None:
# If we received any passport data
passport_data = update.message.passport_data
if passport_data:
@@ -39,37 +41,53 @@ def msg(bot, update):
print('Phone: ', data.phone_number)
elif data.type == 'email':
print('Email: ', data.email)
if data.type in ('personal_details', 'passport', 'driver_license', 'identity_card',
'internal_passport', 'address'):
if data.type in (
'personal_details',
'passport',
'driver_license',
'identity_card',
'internal_passport',
'address',
):
print(data.type, data.data)
if data.type in ('utility_bill', 'bank_statement', 'rental_agreement',
'passport_registration', 'temporary_registration'):
if data.type in (
'utility_bill',
'bank_statement',
'rental_agreement',
'passport_registration',
'temporary_registration',
):
print(data.type, len(data.files), 'files')
for file in data.files:
actual_file = file.get_file()
print(actual_file)
actual_file.download()
if data.type in ('passport', 'driver_license', 'identity_card',
'internal_passport'):
if data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport'):
if data.front_side:
file = data.front_side.get_file()
print(data.type, file)
file.download()
front_file = data.front_side.get_file()
print(data.type, front_file)
front_file.download()
if data.type in ('driver_license' and 'identity_card'):
if data.reverse_side:
file = data.reverse_side.get_file()
print(data.type, file)
file.download()
if data.type in ('passport', 'driver_license', 'identity_card',
'internal_passport'):
reverse_file = data.reverse_side.get_file()
print(data.type, reverse_file)
reverse_file.download()
if data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport'):
if data.selfie:
file = data.selfie.get_file()
print(data.type, file)
file.download()
if data.type in ('passport', 'driver_license', 'identity_card',
'internal_passport', 'utility_bill', 'bank_statement',
'rental_agreement', 'passport_registration',
'temporary_registration'):
selfie_file = data.selfie.get_file()
print(data.type, selfie_file)
selfie_file.download()
if data.type in (
'passport',
'driver_license',
'identity_card',
'internal_passport',
'utility_bill',
'bank_statement',
'rental_agreement',
'passport_registration',
'temporary_registration',
):
print(data.type, len(data.translation), 'translation')
for file in data.translation:
actual_file = file.get_file()
@@ -77,24 +95,16 @@ def msg(bot, update):
actual_file.download()
def error(bot, update, error):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
def main():
def main() -> None:
"""Start the bot."""
# Create the Updater and pass it your token and private key
updater = Updater("TOKEN", private_key=open('private.key', 'rb').read())
# Get the dispatcher to register handlers
dp = updater.dispatcher
dispatcher = updater.dispatcher
# On messages that include passport data call msg
dp.add_handler(MessageHandler(Filters.passport_data, msg))
# log all errors
dp.add_error_handler(error)
dispatcher.add_handler(MessageHandler(Filters.passport_data, msg))
# Start the Bot
updater.start_polling()
+57 -47
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
"""
@@ -8,29 +8,32 @@ Basic example for a bot that can receive payment from user.
import logging
from telegram import (LabeledPrice, ShippingOption)
from telegram.ext import (Updater, CommandHandler, MessageHandler,
Filters, PreCheckoutQueryHandler, ShippingQueryHandler)
from telegram import LabeledPrice, ShippingOption, Update
from telegram.ext import (
Updater,
CommandHandler,
MessageHandler,
Filters,
PreCheckoutQueryHandler,
ShippingQueryHandler,
CallbackContext,
)
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def start_callback(update, context):
def start_callback(update: Update, _: CallbackContext) -> None:
msg = "Use /shipping to get an invoice for shipping-payment, "
msg += "or /noshipping for an invoice without shipping."
update.message.reply_text(msg)
def start_with_shipping_callback(update, context):
def start_with_shipping_callback(update: Update, context: CallbackContext) -> None:
chat_id = update.message.chat_id
title = "Payment Example"
description = "Payment Example using python-telegram-bot"
@@ -42,19 +45,30 @@ def start_with_shipping_callback(update, context):
currency = "USD"
# price in dollars
price = 1
# price * 100 so as to include 2 d.p.
# price * 100 so as to include 2 decimal points
# check https://core.telegram.org/bots/payments#supported-currencies for more details
prices = [LabeledPrice("Test", price * 100)]
# optionally pass need_name=True, need_phone_number=True,
# need_email=True, need_shipping_address=True, is_flexible=True
context.bot.send_invoice(chat_id, title, description, payload,
provider_token, start_parameter, currency, prices,
need_name=True, need_phone_number=True,
need_email=True, need_shipping_address=True, is_flexible=True)
context.bot.send_invoice(
chat_id,
title,
description,
payload,
provider_token,
start_parameter,
currency,
prices,
need_name=True,
need_phone_number=True,
need_email=True,
need_shipping_address=True,
is_flexible=True,
)
def start_without_shipping_callback(update, context):
def start_without_shipping_callback(update: Update, context: CallbackContext) -> None:
chat_id = update.message.chat_id
title = "Payment Example"
description = "Payment Example using python-telegram-bot"
@@ -66,34 +80,35 @@ def start_without_shipping_callback(update, context):
currency = "USD"
# price in dollars
price = 1
# price * 100 so as to include 2 d.p.
# price * 100 so as to include 2 decimal points
prices = [LabeledPrice("Test", price * 100)]
# optionally pass need_name=True, need_phone_number=True,
# need_email=True, need_shipping_address=True, is_flexible=True
context.bot.send_invoice(chat_id, title, description, payload,
provider_token, start_parameter, currency, prices)
context.bot.send_invoice(
chat_id, title, description, payload, provider_token, start_parameter, currency, prices
)
def shipping_callback(update, context):
def shipping_callback(update: Update, _: CallbackContext) -> None:
query = update.shipping_query
# check the payload, is this from your bot?
if query.invoice_payload != 'Custom-Payload':
# answer False pre_checkout_query
query.answer(ok=False, error_message="Something went wrong...")
return
else:
options = list()
# a single LabeledPrice
options.append(ShippingOption('1', 'Shipping Option A', [LabeledPrice('A', 100)]))
# an array of LabeledPrice objects
price_list = [LabeledPrice('B1', 150), LabeledPrice('B2', 200)]
options.append(ShippingOption('2', 'Shipping Option B', price_list))
query.answer(ok=True, shipping_options=options)
options = list()
# a single LabeledPrice
options.append(ShippingOption('1', 'Shipping Option A', [LabeledPrice('A', 100)]))
# an array of LabeledPrice objects
price_list = [LabeledPrice('B1', 150), LabeledPrice('B2', 200)]
options.append(ShippingOption('2', 'Shipping Option B', price_list))
query.answer(ok=True, shipping_options=options)
# after (optional) shipping, it's the pre-checkout
def precheckout_callback(update, context):
def precheckout_callback(update: Update, _: CallbackContext) -> None:
query = update.pre_checkout_query
# check the payload, is this from your bot?
if query.invoice_payload != 'Custom-Payload':
@@ -104,38 +119,33 @@ def precheckout_callback(update, context):
# finally, after contacting the payment provider...
def successful_payment_callback(update, context):
def successful_payment_callback(update: Update, _: CallbackContext) -> None:
# do something after successfully receiving payment?
update.message.reply_text("Thank you for your payment!")
def main():
def main() -> None:
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
dispatcher = updater.dispatcher
# simple start function
dp.add_handler(CommandHandler("start", start_callback))
dispatcher.add_handler(CommandHandler("start", start_callback))
# Add command handler to start the payment invoice
dp.add_handler(CommandHandler("shipping", start_with_shipping_callback))
dp.add_handler(CommandHandler("noshipping", start_without_shipping_callback))
dispatcher.add_handler(CommandHandler("shipping", start_with_shipping_callback))
dispatcher.add_handler(CommandHandler("noshipping", start_without_shipping_callback))
# Optional handler if your product requires shipping
dp.add_handler(ShippingQueryHandler(shipping_callback))
dispatcher.add_handler(ShippingQueryHandler(shipping_callback))
# Pre-checkout handler to final check
dp.add_handler(PreCheckoutQueryHandler(precheckout_callback))
dispatcher.add_handler(PreCheckoutQueryHandler(precheckout_callback))
# Success! Notify your user!
dp.add_handler(MessageHandler(Filters.successful_payment, successful_payment_callback))
# log all errors
dp.add_error_handler(error)
dispatcher.add_handler(MessageHandler(Filters.successful_payment, successful_payment_callback))
# Start the Bot
updater.start_polling()
+82 -67
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
"""
@@ -14,143 +14,158 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram import ReplyKeyboardMarkup
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters,
ConversationHandler, PicklePersistence)
import logging
from typing import Dict
from telegram import ReplyKeyboardMarkup, Update
from telegram.ext import (
Updater,
CommandHandler,
MessageHandler,
Filters,
ConversationHandler,
PicklePersistence,
CallbackContext,
)
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3)
reply_keyboard = [['Age', 'Favourite colour'],
['Number of siblings', 'Something else...'],
['Done']]
reply_keyboard = [
['Age', 'Favourite colour'],
['Number of siblings', 'Something else...'],
['Done'],
]
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
def facts_to_str(user_data):
def facts_to_str(user_data: Dict[str, str]) -> str:
facts = list()
for key, value in user_data.items():
facts.append('{} - {}'.format(key, value))
facts.append(f'{key} - {value}')
return "\n".join(facts).join(['\n', '\n'])
def start(update, context):
def start(update: Update, context: CallbackContext) -> int:
reply_text = "Hi! My name is Doctor Botter."
if context.user_data:
reply_text += " You already told me your {}. Why don't you tell me something more " \
"about yourself? Or change enything I " \
"already know.".format(", ".join(context.user_data.keys()))
reply_text += (
f" You already told me your {', '.join(context.user_data.keys())}. Why don't you "
f"tell me something more about yourself? Or change anything I already know."
)
else:
reply_text += " I will hold a more complex conversation with you. Why don't you tell me " \
"something about yourself?"
reply_text += (
" I will hold a more complex conversation with you. Why don't you tell me "
"something about yourself?"
)
update.message.reply_text(reply_text, reply_markup=markup)
return CHOOSING
def regular_choice(update, context):
def regular_choice(update: Update, context: CallbackContext) -> int:
text = update.message.text.lower()
context.user_data['choice'] = text
if context.user_data.get(text):
reply_text = 'Your {}, I already know the following ' \
'about that: {}'.format(text, context.user_data[text])
reply_text = (
f'Your {text}, I already know the following about that: {context.user_data[text]}'
)
else:
reply_text = 'Your {}? Yes, I would love to hear about that!'.format(text)
reply_text = f'Your {text}? Yes, I would love to hear about that!'
update.message.reply_text(reply_text)
return TYPING_REPLY
def custom_choice(update, context):
update.message.reply_text('Alright, please send me the category first, '
'for example "Most impressive skill"')
def custom_choice(update: Update, _: CallbackContext) -> int:
update.message.reply_text(
'Alright, please send me the category first, ' 'for example "Most impressive skill"'
)
return TYPING_CHOICE
def received_information(update, context):
def received_information(update: Update, context: CallbackContext) -> int:
text = update.message.text
category = context.user_data['choice']
context.user_data[category] = text.lower()
del context.user_data['choice']
update.message.reply_text("Neat! Just so you know, this is what you already told me:"
"{}"
"You can tell me more, or change your opinion on "
"something.".format(facts_to_str(context.user_data)),
reply_markup=markup)
update.message.reply_text(
"Neat! Just so you know, this is what you already told me:"
f"{facts_to_str(context.user_data)}"
"You can tell me more, or change your opinion on "
"something.",
reply_markup=markup,
)
return CHOOSING
def show_data(update, context):
update.message.reply_text("This is what you already told me:"
"{}".format(facts_to_str(context.user_data)))
def show_data(update: Update, context: CallbackContext) -> None:
update.message.reply_text(
f"This is what you already told me: {facts_to_str(context.user_data)}"
)
def done(update, context):
def done(update: Update, context: CallbackContext) -> int:
if 'choice' in context.user_data:
del context.user_data['choice']
update.message.reply_text("I learned these facts about you:"
"{}"
"Until next time!".format(facts_to_str(context.user_data)))
update.message.reply_text(
"I learned these facts about you:" f"{facts_to_str(context.user_data)} Until next time!"
)
return ConversationHandler.END
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
def main() -> None:
# Create the Updater and pass it your bot's token.
pp = PicklePersistence(filename='conversationbot')
updater = Updater("TOKEN", persistence=pp, use_context=True)
persistence = PicklePersistence(filename='conversationbot')
updater = Updater("TOKEN", persistence=persistence)
# Get the dispatcher to register handlers
dp = updater.dispatcher
dispatcher = updater.dispatcher
# Add conversation handler with the states CHOOSING, TYPING_CHOICE and TYPING_REPLY
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
CHOOSING: [MessageHandler(Filters.regex('^(Age|Favourite colour|Number of siblings)$'),
regular_choice),
MessageHandler(Filters.regex('^Something else...$'),
custom_choice),
],
TYPING_CHOICE: [MessageHandler(Filters.text,
regular_choice),
],
TYPING_REPLY: [MessageHandler(Filters.text,
received_information),
],
CHOOSING: [
MessageHandler(
Filters.regex('^(Age|Favourite colour|Number of siblings)$'), regular_choice
),
MessageHandler(Filters.regex('^Something else...$'), custom_choice),
],
TYPING_CHOICE: [
MessageHandler(
Filters.text & ~(Filters.command | Filters.regex('^Done$')), regular_choice
)
],
TYPING_REPLY: [
MessageHandler(
Filters.text & ~(Filters.command | Filters.regex('^Done$')),
received_information,
)
],
},
fallbacks=[MessageHandler(Filters.regex('^Done$'), done)],
name="my_conversation",
persistent=True
persistent=True,
)
dp.add_handler(conv_handler)
dispatcher.add_handler(conv_handler)
show_data_handler = CommandHandler('show_data', show_data)
dp.add_handler(show_data_handler)
# log all errors
dp.add_error_handler(error)
dispatcher.add_handler(show_data_handler)
# Start the Bot
updater.start_polling()
+175
View File
@@ -0,0 +1,175 @@
#!/usr/bin/env python
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
"""
Basic example for a bot that works with polls. Only 3 people are allowed to interact with each
poll/quiz the bot generates. The preview command generates a closed poll/quiz, excatly like the
one the user sends the bot
"""
import logging
from telegram import (
Poll,
ParseMode,
KeyboardButton,
KeyboardButtonPollType,
ReplyKeyboardMarkup,
ReplyKeyboardRemove,
Update,
)
from telegram.ext import (
Updater,
CommandHandler,
PollAnswerHandler,
PollHandler,
MessageHandler,
Filters,
CallbackContext,
)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
def start(update: Update, _: CallbackContext) -> None:
"""Inform user about what this bot can do"""
update.message.reply_text(
'Please select /poll to get a Poll, /quiz to get a Quiz or /preview'
' to generate a preview for your poll'
)
def poll(update: Update, context: CallbackContext) -> None:
"""Sends a predefined poll"""
questions = ["Good", "Really good", "Fantastic", "Great"]
message = context.bot.send_poll(
update.effective_chat.id,
"How are you?",
questions,
is_anonymous=False,
allows_multiple_answers=True,
)
# Save some info about the poll the bot_data for later use in receive_poll_answer
payload = {
message.poll.id: {
"questions": questions,
"message_id": message.message_id,
"chat_id": update.effective_chat.id,
"answers": 0,
}
}
context.bot_data.update(payload)
def receive_poll_answer(update: Update, context: CallbackContext) -> None:
"""Summarize a users poll vote"""
answer = update.poll_answer
poll_id = answer.poll_id
try:
questions = context.bot_data[poll_id]["questions"]
# this means this poll answer update is from an old poll, we can't do our answering then
except KeyError:
return
selected_options = answer.option_ids
answer_string = ""
for question_id in selected_options:
if question_id != selected_options[-1]:
answer_string += questions[question_id] + " and "
else:
answer_string += questions[question_id]
context.bot.send_message(
context.bot_data[poll_id]["chat_id"],
f"{update.effective_user.mention_html()} feels {answer_string}!",
parse_mode=ParseMode.HTML,
)
context.bot_data[poll_id]["answers"] += 1
# Close poll after three participants voted
if context.bot_data[poll_id]["answers"] == 3:
context.bot.stop_poll(
context.bot_data[poll_id]["chat_id"], context.bot_data[poll_id]["message_id"]
)
def quiz(update: Update, context: CallbackContext) -> None:
"""Send a predefined poll"""
questions = ["1", "2", "4", "20"]
message = update.effective_message.reply_poll(
"How many eggs do you need for a cake?", questions, type=Poll.QUIZ, correct_option_id=2
)
# Save some info about the poll the bot_data for later use in receive_quiz_answer
payload = {
message.poll.id: {"chat_id": update.effective_chat.id, "message_id": message.message_id}
}
context.bot_data.update(payload)
def receive_quiz_answer(update: Update, context: CallbackContext) -> None:
"""Close quiz after three participants took it"""
# the bot can receive closed poll updates we don't care about
if update.poll.is_closed:
return
if update.poll.total_voter_count == 3:
try:
quiz_data = context.bot_data[update.poll.id]
# this means this poll answer update is from an old poll, we can't stop it then
except KeyError:
return
context.bot.stop_poll(quiz_data["chat_id"], quiz_data["message_id"])
def preview(update: Update, _: CallbackContext) -> None:
"""Ask user to create a poll and display a preview of it"""
# using this without a type lets the user chooses what he wants (quiz or poll)
button = [[KeyboardButton("Press me!", request_poll=KeyboardButtonPollType())]]
message = "Press the button to let the bot generate a preview for your poll"
# using one_time_keyboard to hide the keyboard
update.effective_message.reply_text(
message, reply_markup=ReplyKeyboardMarkup(button, one_time_keyboard=True)
)
def receive_poll(update: Update, _: CallbackContext) -> None:
"""On receiving polls, reply to it by a closed poll copying the received poll"""
actual_poll = update.effective_message.poll
# Only need to set the question and options, since all other parameters don't matter for
# a closed poll
update.effective_message.reply_poll(
question=actual_poll.question,
options=[o.text for o in actual_poll.options],
# with is_closed true, the poll/quiz is immediately closed
is_closed=True,
reply_markup=ReplyKeyboardRemove(),
)
def help_handler(update: Update, _: CallbackContext) -> None:
"""Display a help message"""
update.message.reply_text("Use /quiz, /poll or /preview to test this bot.")
def main() -> None:
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
dispatcher = updater.dispatcher
dispatcher.add_handler(CommandHandler('start', start))
dispatcher.add_handler(CommandHandler('poll', poll))
dispatcher.add_handler(PollAnswerHandler(receive_poll_answer))
dispatcher.add_handler(CommandHandler('quiz', quiz))
dispatcher.add_handler(PollHandler(receive_quiz_answer))
dispatcher.add_handler(CommandHandler('preview', preview))
dispatcher.add_handler(MessageHandler(Filters.poll, receive_poll))
dispatcher.add_handler(CommandHandler('help', help_handler))
# Start the Bot
updater.start_polling()
# Run the bot until the user presses Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT
updater.idle()
if __name__ == '__main__':
main()
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python
# pylint: disable=W0603
"""Simple Bot to reply to Telegram messages.
This is built on the API wrapper, see echobot.py to see the same example built
on the telegram.ext bot framework.
This program is dedicated to the public domain under the CC0 license.
"""
import logging
from typing import NoReturn
from time import sleep
import telegram
from telegram.error import NetworkError, Unauthorized
UPDATE_ID = None
def main() -> NoReturn:
"""Run the bot."""
global UPDATE_ID
# Telegram Bot Authorization Token
bot = telegram.Bot('TOKEN')
# get the first pending update_id, this is so we can skip over it in case
# we get an "Unauthorized" exception.
try:
UPDATE_ID = bot.get_updates()[0].update_id
except IndexError:
UPDATE_ID = None
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
while True:
try:
echo(bot)
except NetworkError:
sleep(1)
except Unauthorized:
# The user has removed or blocked the bot.
UPDATE_ID += 1
def echo(bot: telegram.Bot) -> None:
"""Echo the message the user sent."""
global UPDATE_ID
# Request updates after the last update_id
for update in bot.get_updates(offset=UPDATE_ID, timeout=10):
UPDATE_ID = update.update_id + 1
if update.message: # your bot can receive updates without messages
if update.message.text: # not all messages contain text
# Reply to the message
update.message.reply_text(update.message.text)
if __name__ == '__main__':
main()
+37 -44
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable=C0116
# This program is dedicated to the public domain under the CC0 license.
"""
@@ -20,28 +20,40 @@ bot.
import logging
from telegram.ext import Updater, CommandHandler
from telegram import Update
from telegram.ext import Updater, CommandHandler, CallbackContext
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
# Define a few command handlers. These usually take the two arguments update and
# context. Error handlers also receive the raised TelegramError object in error.
def start(update, context):
def start(update: Update, _: CallbackContext) -> None:
update.message.reply_text('Hi! Use /set <seconds> to set a timer')
def alarm(context):
def alarm(context: CallbackContext) -> None:
"""Send the alarm message."""
job = context.job
context.bot.send_message(job.context, text='Beep!')
def set_timer(update, context):
def remove_job_if_exists(name: str, context: CallbackContext) -> bool:
"""Remove job with given name. Returns whether job was removed."""
current_jobs = context.job_queue.get_jobs_by_name(name)
if not current_jobs:
return False
for job in current_jobs:
job.schedule_removal()
return True
def set_timer(update: Update, context: CallbackContext) -> None:
"""Add a job to the queue."""
chat_id = update.message.chat_id
try:
@@ -51,58 +63,39 @@ def set_timer(update, context):
update.message.reply_text('Sorry we can not go back to future!')
return
# Add job to queue and stop current one if there is a timer already
if 'job' in context.chat_data:
old_job = context.chat_data['job']
old_job.schedule_removal()
new_job = context.job_queue.run_once(alarm, due, context=chat_id)
context.chat_data['job'] = new_job
job_removed = remove_job_if_exists(str(chat_id), context)
context.job_queue.run_once(alarm, due, context=chat_id, name=str(chat_id))
update.message.reply_text('Timer successfully set!')
text = 'Timer successfully set!'
if job_removed:
text += ' Old one was removed.'
update.message.reply_text(text)
except (IndexError, ValueError):
update.message.reply_text('Usage: /set <seconds>')
def unset(update, context):
def unset(update: Update, context: CallbackContext) -> None:
"""Remove the job if the user changed their mind."""
if 'job' not in context.chat_data:
update.message.reply_text('You have no active timer')
return
job = context.chat_data['job']
job.schedule_removal()
del context.chat_data['job']
update.message.reply_text('Timer successfully unset!')
chat_id = update.message.chat_id
job_removed = remove_job_if_exists(str(chat_id), context)
text = 'Timer successfully cancelled!' if job_removed else 'You have no active timer.'
update.message.reply_text(text)
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
def main() -> None:
"""Run bot."""
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("TOKEN", use_context=True)
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
dispatcher = updater.dispatcher
# on different commands - answer in Telegram
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", start))
dp.add_handler(CommandHandler("set", set_timer,
pass_args=True,
pass_job_queue=True,
pass_chat_data=True))
dp.add_handler(CommandHandler("unset", unset, pass_chat_data=True))
# log all errors
dp.add_error_handler(error)
dispatcher.add_handler(CommandHandler("start", start))
dispatcher.add_handler(CommandHandler("help", start))
dispatcher.add_handler(CommandHandler("set", set_timer))
dispatcher.add_handler(CommandHandler("unset", unset))
# Start the Bot
updater.start_polling()
+11
View File
@@ -0,0 +1,11 @@
[tool.black]
line-length = 99
target-version = ['py36']
skip-string-normalization = true
# We need to force-exclude the negated include pattern
# so that pre-commit run --all-files does the correct thing
# see https://github.com/psf/black/issues/1778
force-exclude = '^(?!/(telegram|examples|tests)/).*\.py$'
include = '(telegram|examples|tests)/.*\.py$'
exclude = 'telegram/vendor'
+13 -7
View File
@@ -1,11 +1,17 @@
flake8
pep257
pylint
flaky
yapf
# cryptography is an optional dependency, but running the tests properly requires it
cryptography!=3.4,!=3.4.1,!=3.4.2,!=3.4.3
pre-commit
# Make sure that the versions specified here match the pre-commit settings!
black==20.8b1
flake8==3.8.4
pylint==2.7.2
mypy==0.812
pyupgrade==2.10.0
pytest==6.2.2
flaky
beautifulsoup4
pytest==4.2.0
pytest-timeout
wheel
attrs==19.1.0
+5 -2
View File
@@ -1,4 +1,7 @@
future>=0.16.0
# Make sure to install those as additional_dependencies in the
# pre-commit hooks for pylint & mypy
certifi
# only telegram.ext: # Keep this line here; used in setup(-raw).py
tornado>=5.1
cryptography
APScheduler==3.6.3
pytz>=2018.6
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env python
"""The setup and build script for the python-telegram-bot-raw library."""
from setuptools import setup
from setup import get_setup_kwargs
setup(**get_setup_kwargs(raw=True))
+39 -9
View File
@@ -1,6 +1,3 @@
[wheel]
universal = 1
[metadata]
license_file = LICENSE.dual
@@ -15,12 +12,14 @@ upload-dir = docs/build/html
[flake8]
max-line-length = 99
ignore = W503, W605
exclude = setup.py, docs/source/conf.py
extend-ignore = E203
exclude = setup.py, setup-raw.py docs/source/conf.py, telegram/vendor
[yapf]
based_on_style = google
split_before_logical_operator = True
column_limit = 99
[pylint]
ignore=vendor
[pylint.message-control]
disable = C0330,R0801,R0913,R0904,R0903,R0902,W0511,C0116,C0115,W0703,R0914,R0914,C0302,R0912,R0915,R0401
[tool:pytest]
testpaths = tests
@@ -28,7 +27,9 @@ addopts = --no-success-flaky-report -rsxX
filterwarnings =
error
ignore::DeprecationWarning
ignore::telegram.utils.deprecate.TelegramDeprecationWarning
; Unfortunately due to https://github.com/pytest-dev/pytest/issues/8343 we can't have this here
; and instead do a trick directly in tests/conftest.py
; ignore::telegram.utils.deprecate.TelegramDeprecationWarning
[coverage:run]
branch = True
@@ -40,3 +41,32 @@ omit =
telegram/__main__.py
telegram/vendor/*
[coverage:report]
exclude_lines =
if TYPE_CHECKING:
[mypy]
warn_unused_ignores = True
warn_unused_configs = True
disallow_untyped_defs = True
disallow_incomplete_defs = True
disallow_untyped_decorators = True
show_error_codes = True
[mypy-telegram.vendor.*]
ignore_errors = True
# Disable strict optional for telegram objects with class methods
# We don't want to clutter the code with 'if self.bot is None: raise RuntimeError()'
[mypy-telegram.callbackquery,telegram.chat,telegram.message,telegram.user,telegram.files.*,telegram.inline.inlinequery,telegram.payment.precheckoutquery,telegram.payment.shippingquery,telegram.passport.passportdata,telegram.passport.credentials,telegram.passport.passportfile,telegram.ext.filters]
strict_optional = False
# type hinting for asyncio in webhookhandler is a bit tricky because it depends on the OS
[mypy-telegram.ext.utils.webhookhandler]
warn_unused_ignores = False
[mypy-urllib3.*]
ignore_missing_imports = True
[mypy-apscheduler.*]
ignore_missing_imports = True
+102 -38
View File
@@ -3,58 +3,122 @@
import codecs
import os
import subprocess
import sys
from setuptools import setup, find_packages
UPSTREAM_URLLIB3_FLAG = '--with-upstream-urllib3'
def requirements():
def get_requirements(raw=False):
"""Build the requirements list for this project"""
requirements_list = []
with open('requirements.txt') as requirements:
for install in requirements:
with open('requirements.txt') as reqs:
for install in reqs:
if install.startswith('# only telegram.ext:'):
if raw:
break
continue
requirements_list.append(install.strip())
return requirements_list
packages = find_packages(exclude=['tests*'])
def get_packages_requirements(raw=False):
"""Build the package & requirements list for this project"""
reqs = get_requirements(raw=raw)
exclude = ['tests*']
if raw:
exclude.append('telegram.ext*')
packs = find_packages(exclude=exclude)
# Allow for a package install to not use the vendored urllib3
if UPSTREAM_URLLIB3_FLAG in sys.argv:
sys.argv.remove(UPSTREAM_URLLIB3_FLAG)
reqs.append('urllib3 >= 1.19.1')
packs = [x for x in packs if not x.startswith('telegram.vendor.ptb_urllib3')]
return packs, reqs
def get_setup_kwargs(raw=False):
"""Builds a dictionary of kwargs for the setup function"""
packages, requirements = get_packages_requirements(raw=raw)
raw_ext = "-raw" if raw else ""
readme = f'README{"_RAW" if raw else ""}.rst'
with codecs.open('README.rst', 'r', 'utf-8') as fd:
fn = os.path.join('telegram', 'version.py')
with open(fn) as fh:
code = compile(fh.read(), fn, 'exec')
exec(code)
setup(name='python-telegram-bot',
version=__version__,
author='Leandro Toledo',
author_email='devs@python-telegram-bot.org',
license='LGPLv3',
url='https://python-telegram-bot.org/',
keywords='python telegram bot api wrapper',
description="We have made you a wrapper you can't refuse",
long_description=fd.read(),
packages=packages,
install_requires=requirements(),
extras_require={
'json': 'ujson',
'socks': 'PySocks'
},
include_package_data=True,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)',
'Operating System :: OS Independent',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Communications :: Chat',
'Topic :: Internet',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7'
],)
with open(readme, 'r', encoding='utf-8') as fd:
kwargs = dict(
script_name=f'setup{raw_ext}.py',
name=f'python-telegram-bot{raw_ext}',
version=locals()['__version__'],
author='Leandro Toledo',
author_email='devs@python-telegram-bot.org',
license='LGPLv3',
url='https://python-telegram-bot.org/',
# Keywords supported by PyPI can be found at https://git.io/JtLIZ
project_urls={
"Documentation": "https://python-telegram-bot.readthedocs.io",
"Bug Tracker": "https://github.com/python-telegram-bot/python-telegram-bot/issues",
"Source Code": "https://github.com/python-telegram-bot/python-telegram-bot",
"News": "https://t.me/pythontelegrambotchannel",
"Changelog": "https://python-telegram-bot.readthedocs.io/en/stable/changelog.html",
},
download_url=f'https://pypi.org/project/python-telegram-bot{raw_ext}/',
keywords='python telegram bot api wrapper',
description="We have made you a wrapper you can't refuse",
long_description=fd.read(),
long_description_content_type='text/x-rst',
packages=packages,
install_requires=requirements,
extras_require={
'json': 'ujson',
'socks': 'PySocks',
# 3.4-3.4.3 contained some cyclical import bugs
'passport': 'cryptography!=3.4,!=3.4.1,!=3.4.2,!=3.4.3',
},
include_package_data=True,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)',
'Operating System :: OS Independent',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Communications :: Chat',
'Topic :: Internet',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
],
python_requires='>=3.6'
)
return kwargs
def main():
# If we're building, build ptb-raw as well
if set(sys.argv[1:]) in [{'bdist_wheel'}, {'sdist'}, {'sdist', 'bdist_wheel'}]:
args = ['python', 'setup-raw.py']
args.extend(sys.argv[1:])
subprocess.run(args, check=True, capture_output=True)
setup(**get_setup_kwargs(raw=False))
if __name__ == '__main__':
main()
+183 -57
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,10 +19,14 @@
"""A library that provides a Python interface to the Telegram Bot API"""
from .base import TelegramObject
from .botcommand import BotCommand
from .user import User
from .files.chatphoto import ChatPhoto
from .chat import Chat
from .chatlocation import ChatLocation
from .chatinvitelink import ChatInviteLink
from .chatmember import ChatMember
from .chatmemberupdated import ChatMemberUpdated
from .chatpermissions import ChatPermissions
from .files.photosize import PhotoSize
from .files.audio import Audio
@@ -36,7 +40,9 @@ from .files.location import Location
from .files.venue import Venue
from .files.videonote import VideoNote
from .chataction import ChatAction
from .dice import Dice
from .userprofilephotos import UserProfilePhotos
from .keyboardbuttonpolltype import KeyboardButtonPollType
from .keyboardbutton import KeyboardButton
from .replymarkup import ReplyMarkup
from .replykeyboardmarkup import ReplyKeyboardMarkup
@@ -47,9 +53,12 @@ from .files.inputfile import InputFile
from .files.file import File
from .parsemode import ParseMode
from .messageentity import MessageEntity
from .messageid import MessageId
from .games.game import Game
from .poll import Poll, PollOption
from .poll import Poll, PollOption, PollAnswer
from .voicechat import VoiceChatStarted, VoiceChatEnded, VoiceChatParticipantsInvited
from .loginurl import LoginUrl
from .proximityalerttriggered import ProximityAlertTriggered
from .games.callbackgame import CallbackGame
from .payment.shippingaddress import ShippingAddress
from .payment.orderinfo import OrderInfo
@@ -62,6 +71,7 @@ from .passport.encryptedpassportelement import EncryptedPassportElement
from .passport.passportdata import PassportData
from .inline.inlinekeyboardbutton import InlineKeyboardButton
from .inline.inlinekeyboardmarkup import InlineKeyboardMarkup
from .messageautodeletetimerchanged import MessageAutoDeleteTimerChanged
from .message import Message
from .callbackquery import CallbackQuery
from .choseninlineresult import ChosenInlineResult
@@ -99,62 +109,178 @@ from .payment.shippingquery import ShippingQuery
from .webhookinfo import WebhookInfo
from .games.gamehighscore import GameHighScore
from .update import Update
from .files.inputmedia import (InputMedia, InputMediaVideo, InputMediaPhoto, InputMediaAnimation,
InputMediaAudio, InputMediaDocument)
from .files.inputmedia import (
InputMedia,
InputMediaVideo,
InputMediaPhoto,
InputMediaAnimation,
InputMediaAudio,
InputMediaDocument,
)
from .constants import (
MAX_MESSAGE_LENGTH,
MAX_CAPTION_LENGTH,
SUPPORTED_WEBHOOK_PORTS,
MAX_FILESIZE_DOWNLOAD,
MAX_FILESIZE_UPLOAD,
MAX_MESSAGES_PER_SECOND_PER_CHAT,
MAX_MESSAGES_PER_SECOND,
MAX_MESSAGES_PER_MINUTE_PER_GROUP,
)
from .passport.passportelementerrors import (
PassportElementError,
PassportElementErrorDataField,
PassportElementErrorFile,
PassportElementErrorFiles,
PassportElementErrorFrontSide,
PassportElementErrorReverseSide,
PassportElementErrorSelfie,
PassportElementErrorTranslationFile,
PassportElementErrorTranslationFiles,
PassportElementErrorUnspecified,
)
from .passport.credentials import (
Credentials,
DataCredentials,
SecureData,
SecureValue,
FileCredentials,
TelegramDecryptionError,
)
from .bot import Bot
from .constants import (MAX_MESSAGE_LENGTH, MAX_CAPTION_LENGTH, SUPPORTED_WEBHOOK_PORTS,
MAX_FILESIZE_DOWNLOAD, MAX_FILESIZE_UPLOAD,
MAX_MESSAGES_PER_SECOND_PER_CHAT, MAX_MESSAGES_PER_SECOND,
MAX_MESSAGES_PER_MINUTE_PER_GROUP)
from .passport.passportelementerrors import (PassportElementError,
PassportElementErrorDataField,
PassportElementErrorFile,
PassportElementErrorFiles,
PassportElementErrorFrontSide,
PassportElementErrorReverseSide,
PassportElementErrorSelfie,
PassportElementErrorTranslationFile,
PassportElementErrorTranslationFiles,
PassportElementErrorUnspecified)
from .passport.credentials import (Credentials,
DataCredentials,
SecureData,
FileCredentials,
TelegramDecryptionError)
from .version import __version__ # noqa: F401
from .version import __version__, bot_api_version # noqa: F401
__author__ = 'devs@python-telegram-bot.org'
__all__ = [
'Audio', 'Bot', 'Chat', 'ChatMember', 'ChatPermissions', 'ChatAction', 'ChosenInlineResult',
'CallbackQuery', 'Contact', 'Document', 'File', 'ForceReply', 'InlineKeyboardButton',
'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult', 'InlineQueryResult',
'InlineQueryResultArticle', 'InlineQueryResultAudio', 'InlineQueryResultCachedAudio',
'InlineQueryResultCachedDocument', 'InlineQueryResultCachedGif',
'InlineQueryResultCachedMpeg4Gif', 'InlineQueryResultCachedPhoto',
'InlineQueryResultCachedSticker', 'InlineQueryResultCachedVideo',
'InlineQueryResultCachedVoice', 'InlineQueryResultContact', 'InlineQueryResultDocument',
'InlineQueryResultGif', 'InlineQueryResultLocation', 'InlineQueryResultMpeg4Gif',
'InlineQueryResultPhoto', 'InlineQueryResultVenue', 'InlineQueryResultVideo',
'InlineQueryResultVoice', 'InlineQueryResultGame', 'InputContactMessageContent', 'InputFile',
'InputLocationMessageContent', 'InputMessageContent', 'InputTextMessageContent',
'InputVenueMessageContent', 'KeyboardButton', 'Location', 'EncryptedCredentials',
'PassportFile', 'EncryptedPassportElement', 'PassportData', 'Message', 'MessageEntity',
'ParseMode', 'PhotoSize', 'ReplyKeyboardRemove', 'ReplyKeyboardMarkup', 'ReplyMarkup',
'Sticker', 'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue',
'Video', 'Voice', 'MAX_MESSAGE_LENGTH', 'MAX_CAPTION_LENGTH', 'SUPPORTED_WEBHOOK_PORTS',
'MAX_FILESIZE_DOWNLOAD', 'MAX_FILESIZE_UPLOAD', 'MAX_MESSAGES_PER_SECOND_PER_CHAT',
'MAX_MESSAGES_PER_SECOND', 'MAX_MESSAGES_PER_MINUTE_PER_GROUP', 'WebhookInfo', 'Animation',
'Game', 'GameHighScore', 'VideoNote', 'LabeledPrice', 'SuccessfulPayment', 'ShippingOption',
'ShippingAddress', 'PreCheckoutQuery', 'OrderInfo', 'Invoice', 'ShippingQuery', 'ChatPhoto',
'StickerSet', 'MaskPosition', 'CallbackGame', 'InputMedia', 'InputMediaPhoto',
'InputMediaVideo', 'PassportElementError', 'PassportElementErrorFile',
'PassportElementErrorReverseSide', 'PassportElementErrorFrontSide',
'PassportElementErrorFiles', 'PassportElementErrorDataField', 'PassportElementErrorFile',
'Credentials', 'DataCredentials', 'SecureData', 'FileCredentials', 'IdDocumentData',
'PersonalDetails', 'ResidentialAddress', 'InputMediaVideo', 'InputMediaAnimation',
'InputMediaAudio', 'InputMediaDocument', 'TelegramDecryptionError',
'PassportElementErrorSelfie', 'PassportElementErrorTranslationFile',
'PassportElementErrorTranslationFiles', 'PassportElementErrorUnspecified', 'Poll',
'PollOption', 'LoginUrl'
]
__all__ = ( # Keep this alphabetically ordered
'Animation',
'Audio',
'Bot',
'BotCommand',
'CallbackGame',
'CallbackQuery',
'Chat',
'ChatAction',
'ChatInviteLink',
'ChatLocation',
'ChatMember',
'ChatMemberUpdated',
'ChatPermissions',
'ChatPhoto',
'ChosenInlineResult',
'Contact',
'Credentials',
'DataCredentials',
'Dice',
'Document',
'EncryptedCredentials',
'EncryptedPassportElement',
'File',
'FileCredentials',
'ForceReply',
'Game',
'GameHighScore',
'IdDocumentData',
'InlineKeyboardButton',
'InlineKeyboardMarkup',
'InlineQuery',
'InlineQueryResult',
'InlineQueryResultArticle',
'InlineQueryResultAudio',
'InlineQueryResultCachedAudio',
'InlineQueryResultCachedDocument',
'InlineQueryResultCachedGif',
'InlineQueryResultCachedMpeg4Gif',
'InlineQueryResultCachedPhoto',
'InlineQueryResultCachedSticker',
'InlineQueryResultCachedVideo',
'InlineQueryResultCachedVoice',
'InlineQueryResultContact',
'InlineQueryResultDocument',
'InlineQueryResultGame',
'InlineQueryResultGif',
'InlineQueryResultLocation',
'InlineQueryResultMpeg4Gif',
'InlineQueryResultPhoto',
'InlineQueryResultVenue',
'InlineQueryResultVideo',
'InlineQueryResultVoice',
'InputContactMessageContent',
'InputFile',
'InputLocationMessageContent',
'InputMedia',
'InputMediaAnimation',
'InputMediaAudio',
'InputMediaDocument',
'InputMediaPhoto',
'InputMediaVideo',
'InputMessageContent',
'InputTextMessageContent',
'InputVenueMessageContent',
'Invoice',
'KeyboardButton',
'KeyboardButtonPollType',
'LabeledPrice',
'Location',
'LoginUrl',
'MAX_CAPTION_LENGTH',
'MAX_FILESIZE_DOWNLOAD',
'MAX_FILESIZE_UPLOAD',
'MAX_MESSAGES_PER_MINUTE_PER_GROUP',
'MAX_MESSAGES_PER_SECOND',
'MAX_MESSAGES_PER_SECOND_PER_CHAT',
'MAX_MESSAGE_LENGTH',
'MaskPosition',
'Message',
'MessageAutoDeleteTimerChanged',
'MessageEntity',
'MessageId',
'OrderInfo',
'ParseMode',
'PassportData',
'PassportElementError',
'PassportElementErrorDataField',
'PassportElementErrorFile',
'PassportElementErrorFiles',
'PassportElementErrorFrontSide',
'PassportElementErrorReverseSide',
'PassportElementErrorSelfie',
'PassportElementErrorTranslationFile',
'PassportElementErrorTranslationFiles',
'PassportElementErrorUnspecified',
'PassportFile',
'PersonalDetails',
'PhotoSize',
'Poll',
'PollAnswer',
'PollOption',
'PreCheckoutQuery',
'ProximityAlertTriggered',
'ReplyKeyboardMarkup',
'ReplyKeyboardRemove',
'ReplyMarkup',
'ResidentialAddress',
'SUPPORTED_WEBHOOK_PORTS',
'SecureData',
'SecureValue',
'ShippingAddress',
'ShippingOption',
'ShippingQuery',
'Sticker',
'StickerSet',
'SuccessfulPayment',
'TelegramDecryptionError',
'TelegramError',
'TelegramObject',
'Update',
'User',
'UserProfilePhotos',
'Venue',
'Video',
'VideoNote',
'Voice',
'VoiceChatStarted',
'VoiceChatEnded',
'VoiceChatParticipantsInvited',
'WebhookInfo',
)
+16 -14
View File
@@ -1,7 +1,7 @@
# !/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,35 +16,37 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import sys
# pylint: disable=C0114
import subprocess
import sys
from typing import Optional
import certifi
import future
from . import __version__ as telegram_ver
from .constants import BOT_API_VERSION
def _git_revision():
def _git_revision() -> Optional[str]:
try:
output = subprocess.check_output(["git", "describe", "--long", "--tags"],
stderr=subprocess.STDOUT)
output = subprocess.check_output(
["git", "describe", "--long", "--tags"], stderr=subprocess.STDOUT
)
except (subprocess.SubprocessError, OSError):
return None
return output.decode().strip()
def print_ver_info():
def print_ver_info() -> None:
git_revision = _git_revision()
print('python-telegram-bot {0}'.format(telegram_ver) + (' ({0})'.format(git_revision)
if git_revision else ''))
print('certifi {0}'.format(certifi.__version__))
print('future {0}'.format(future.__version__))
print('Python {0}'.format(sys.version.replace('\n', ' ')))
print(f'python-telegram-bot {telegram_ver}' + (f' ({git_revision})' if git_revision else ''))
print(f'Bot API {BOT_API_VERSION}')
print(f'certifi {certifi.__version__}') # type: ignore[attr-defined]
sys_version = sys.version.replace('\n', ' ')
print(f'Python {sys_version}')
def main():
def main() -> None:
print_ver_info()
+49 -25
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -17,37 +17,56 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Base class for Telegram Objects."""
try:
import ujson as json
except ImportError:
import json
import json # type: ignore[no-redef]
from abc import ABCMeta
import warnings
from typing import TYPE_CHECKING, List, Optional, Tuple, Type, TypeVar
from telegram.utils.types import JSONDict
if TYPE_CHECKING:
from telegram import Bot
TO = TypeVar('TO', bound='TelegramObject', covariant=True)
class TelegramObject(object):
class TelegramObject:
"""Base class for most telegram objects."""
__metaclass__ = ABCMeta
_id_attrs = ()
_id_attrs: Tuple[object, ...] = ()
def __str__(self):
def __str__(self) -> str:
return str(self.to_dict())
def __getitem__(self, item):
def __getitem__(self, item: str) -> object:
return self.__dict__[item]
@staticmethod
def parse_data(data: Optional[JSONDict]) -> Optional[JSONDict]:
return None if data is None else data.copy()
@classmethod
def de_json(cls, data, bot):
if not data:
def de_json(cls: Type[TO], data: Optional[JSONDict], bot: 'Bot') -> Optional[TO]:
data = cls.parse_data(data)
if data is None:
return None
data = data.copy()
if cls == TelegramObject:
return cls()
return cls(bot=bot, **data) # type: ignore[call-arg]
return data
@classmethod
def de_list(cls: Type[TO], data: Optional[List[JSONDict]], bot: 'Bot') -> List[Optional[TO]]:
if not data:
return []
def to_json(self):
return [cls.de_json(d, bot) for d in data]
def to_json(self) -> str:
"""
Returns:
:obj:`str`
@@ -56,16 +75,11 @@ class TelegramObject(object):
return json.dumps(self.to_dict())
def to_dict(self):
def to_dict(self) -> JSONDict:
data = dict()
for key in iter(self.__dict__):
if key in ('bot',
'_id_attrs',
'_credentials',
'_decrypted_credentials',
'_decrypted_data',
'_decrypted_secret'):
if key == 'bot' or key.startswith('_'):
continue
value = self.__dict__[key]
@@ -79,12 +93,22 @@ class TelegramObject(object):
data['from'] = data.pop('from_user', None)
return data
def __eq__(self, other):
def __eq__(self, other: object) -> bool:
if isinstance(other, self.__class__):
if self._id_attrs == ():
warnings.warn(
f"Objects of type {self.__class__.__name__} can not be meaningfully tested for"
" equivalence."
)
if other._id_attrs == ():
warnings.warn(
f"Objects of type {other.__class__.__name__} can not be meaningfully tested"
" for equivalence."
)
return self._id_attrs == other._id_attrs
return super(TelegramObject, self).__eq__(other) # pylint: disable=no-member
return super().__eq__(other) # pylint: disable=no-member
def __hash__(self):
def __hash__(self) -> int:
if self._id_attrs:
return hash((self.__class__, self._id_attrs)) # pylint: disable=no-member
return super(TelegramObject, self).__hash__()
return super().__hash__()
+3080 -1449
View File
File diff suppressed because it is too large Load Diff
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env python
# pylint: disable=R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Bot Command."""
from typing import Any
from telegram import TelegramObject
class BotCommand(TelegramObject):
"""
This object represents a bot command.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`command` and :attr:`description` are equal.
Args:
command (:obj:`str`): Text of the command, 1-32 characters. Can contain only lowercase
English letters, digits and underscores.
description (:obj:`str`): Description of the command, 3-256 characters.
Attributes:
command (:obj:`str`): Text of the command.
description (:obj:`str`): Description of the command.
"""
def __init__(self, command: str, description: str, **_kwargs: Any):
self.command = command
self.description = description
self._id_attrs = (self.command, self.description)
+526 -83
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,9 +16,23 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
# pylint: disable=W0622
"""This module contains an object that represents a Telegram CallbackQuery"""
from typing import TYPE_CHECKING, Any, List, Optional, Union, Tuple, ClassVar
from telegram import TelegramObject, Message, User
from telegram import Message, TelegramObject, User, Location, ReplyMarkup, constants
from telegram.utils.helpers import DEFAULT_NONE
from telegram.utils.types import JSONDict, ODVInput, DVInput
if TYPE_CHECKING:
from telegram import (
Bot,
GameHighScore,
InlineKeyboardMarkup,
MessageId,
InputMedia,
MessageEntity,
)
class CallbackQuery(TelegramObject):
@@ -29,58 +43,62 @@ class CallbackQuery(TelegramObject):
:attr:`message` will be present. If the button was attached to a message sent via the bot (in
inline mode), the field :attr:`inline_message_id` will be present.
Note:
* In Python `from` is a reserved word, use `from_user` instead.
* Exactly one of the fields :attr:`data` or :attr:`game_short_name` will be present.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`id` is equal.
Attributes:
id (:obj:`str`): Unique identifier for this query.
from_user (:class:`telegram.User`): Sender.
message (:class:`telegram.Message`): Optional. Message with the callback button that
originated the query.
inline_message_id (:obj:`str`): Optional. Identifier of the message sent via the bot in
inline mode, that originated the query.
chat_instance (:obj:`str`): Optional. Global identifier, uniquely corresponding to the chat
to which the message with the callback button was sent.
data (:obj:`str`): Optional. Data associated with the callback button.
game_short_name (:obj:`str`): Optional. Short name of a Game to be returned.
Note:
* In Python ``from`` is a reserved word, use ``from_user`` instead.
* Exactly one of the fields :attr:`data` or :attr:`game_short_name` will be present.
* After the user presses an inline button, Telegram clients will display a progress bar
until you call :attr:`answer`. It is, therefore, necessary to react
by calling :attr:`telegram.Bot.answer_callback_query` even if no notification to the user
is needed (e.g., without specifying any of the optional parameters).
Args:
id (:obj:`str`): Unique identifier for this query.
from_user (:class:`telegram.User`): Sender.
chat_instance (:obj:`str`): Global identifier, uniquely corresponding to the chat to which
the message with the callback button was sent. Useful for high scores in games.
message (:class:`telegram.Message`, optional): Message with the callback button that
originated the query. Note that message content and message date will not be available
if the message is too old.
inline_message_id (:obj:`str`, optional): Identifier of the message sent via the bot in
inline mode, that originated the query.
chat_instance (:obj:`str`, optional): Global identifier, uniquely corresponding to the chat
to which the message with the callback button was sent. Useful for high scores in
games.
data (:obj:`str`, optional): Data associated with the callback button. Be aware that a bad
client can send arbitrary data in this field.
inline_message_id (:obj:`str`, optional): Identifier of the message sent via the bot in
inline mode, that originated the query.
game_short_name (:obj:`str`, optional): Short name of a Game to be returned, serves as
the unique identifier for the game
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
Note:
After the user presses an inline button, Telegram clients will display a progress bar
until you call :attr:`answer`. It is, therefore, necessary to react
by calling :attr:`telegram.Bot.answer_callback_query` even if no notification to the user
is needed (e.g., without specifying any of the optional parameters).
Attributes:
id (:obj:`str`): Unique identifier for this query.
from_user (:class:`telegram.User`): Sender.
chat_instance (:obj:`str`): Global identifier, uniquely corresponding to the chat to which
the message with the callback button was sent.
message (:class:`telegram.Message`): Optional. Message with the callback button that
originated the query.
data (:obj:`str`): Optional. Data associated with the callback button.
inline_message_id (:obj:`str`): Optional. Identifier of the message sent via the bot in
inline mode, that originated the query.
game_short_name (:obj:`str`): Optional. Short name of a Game to be returned.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
"""
def __init__(self,
id,
from_user,
chat_instance,
message=None,
data=None,
inline_message_id=None,
game_short_name=None,
bot=None,
**kwargs):
def __init__(
self,
id: str, # pylint: disable=W0622
from_user: User,
chat_instance: str,
message: Message = None,
data: str = None,
inline_message_id: str = None,
game_short_name: str = None,
bot: 'Bot' = None,
**_kwargs: Any,
):
# Required
self.id = id
self.id = id # pylint: disable=C0103
self.from_user = from_user
self.chat_instance = chat_instance
# Optionals
@@ -94,59 +112,109 @@ class CallbackQuery(TelegramObject):
self._id_attrs = (self.id,)
@classmethod
def de_json(cls, data, bot):
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['CallbackQuery']:
data = cls.parse_data(data)
if not data:
return None
data = super(CallbackQuery, cls).de_json(data, bot)
data['from_user'] = User.de_json(data.get('from'), bot)
data['message'] = Message.de_json(data.get('message'), bot)
return cls(bot=bot, **data)
def answer(self, *args, **kwargs):
def answer(
self,
text: str = None,
show_alert: bool = False,
url: str = None,
cache_time: int = None,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
bot.answer_callback_query(update.callback_query.id, *args, **kwargs)
For the documentation of the arguments, please see
:meth:`telegram.Bot.answer_callback_query`.
Returns:
:obj:`bool`: On success, ``True`` is returned.
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.bot.answerCallbackQuery(self.id, *args, **kwargs)
return self.bot.answer_callback_query(
callback_query_id=self.id,
text=text,
show_alert=show_alert,
url=url,
cache_time=cache_time,
timeout=timeout,
api_kwargs=api_kwargs,
)
def edit_message_text(self, text, *args, **kwargs):
def edit_message_text(
self,
text: str,
parse_mode: ODVInput[str] = DEFAULT_NONE,
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
reply_markup: 'InlineKeyboardMarkup' = None,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
) -> Union[Message, bool]:
"""Shortcut for either::
bot.edit_message_text(text, chat_id=update.callback_query.message.chat_id,
message_id=update.callback_query.message.message_id,
*args, **kwargs)
update.callback_query.message.edit_text(text, *args, **kwargs)
or::
bot.edit_message_text(text, inline_message_id=update.callback_query.inline_message_id,
*args, **kwargs)
For the documentation of the arguments, please see
:meth:`telegram.Bot.edit_message_text`.
Returns:
:class:`telegram.Message`: On success, if edited message is sent by the bot, the
edited Message is returned, otherwise ``True`` is returned.
edited Message is returned, otherwise :obj:`True` is returned.
"""
if self.inline_message_id:
return self.bot.edit_message_text(text, inline_message_id=self.inline_message_id,
*args, **kwargs)
else:
return self.bot.edit_message_text(text, chat_id=self.message.chat_id,
message_id=self.message.message_id, *args, **kwargs)
return self.bot.edit_message_text(
inline_message_id=self.inline_message_id,
text=text,
parse_mode=parse_mode,
disable_web_page_preview=disable_web_page_preview,
reply_markup=reply_markup,
timeout=timeout,
api_kwargs=api_kwargs,
entities=entities,
chat_id=None,
message_id=None,
)
return self.message.edit_text(
text=text,
parse_mode=parse_mode,
disable_web_page_preview=disable_web_page_preview,
reply_markup=reply_markup,
timeout=timeout,
api_kwargs=api_kwargs,
entities=entities,
)
def edit_message_caption(self, caption, *args, **kwargs):
def edit_message_caption(
self,
caption: str = None,
reply_markup: 'InlineKeyboardMarkup' = None,
timeout: ODVInput[float] = DEFAULT_NONE,
parse_mode: ODVInput[str] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None,
) -> Union[Message, bool]:
"""Shortcut for either::
bot.edit_message_caption(caption=caption,
chat_id=update.callback_query.message.chat_id,
message_id=update.callback_query.message.message_id,
*args, **kwargs)
update.callback_query.message.edit_caption(caption, *args, **kwargs)
or::
@@ -154,45 +222,420 @@ class CallbackQuery(TelegramObject):
inline_message_id=update.callback_query.inline_message_id,
*args, **kwargs)
For the documentation of the arguments, please see
:meth:`telegram.Bot.edit_message_caption`.
Returns:
:class:`telegram.Message`: On success, if edited message is sent by the bot, the
edited Message is returned, otherwise ``True`` is returned.
edited Message is returned, otherwise :obj:`True` is returned.
"""
if self.inline_message_id:
return self.bot.edit_message_caption(caption=caption,
inline_message_id=self.inline_message_id,
*args, **kwargs)
else:
return self.bot.edit_message_caption(caption=caption, chat_id=self.message.chat_id,
message_id=self.message.message_id,
*args, **kwargs)
return self.bot.edit_message_caption(
caption=caption,
inline_message_id=self.inline_message_id,
reply_markup=reply_markup,
timeout=timeout,
parse_mode=parse_mode,
api_kwargs=api_kwargs,
caption_entities=caption_entities,
chat_id=None,
message_id=None,
)
return self.message.edit_caption(
caption=caption,
reply_markup=reply_markup,
timeout=timeout,
parse_mode=parse_mode,
api_kwargs=api_kwargs,
caption_entities=caption_entities,
)
def edit_message_reply_markup(self, reply_markup, *args, **kwargs):
def edit_message_reply_markup(
self,
reply_markup: Optional['InlineKeyboardMarkup'] = None,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> Union[Message, bool]:
"""Shortcut for either::
bot.edit_message_replyMarkup(chat_id=update.callback_query.message.chat_id,
message_id=update.callback_query.message.message_id,
reply_markup=reply_markup,
*args, **kwargs)
update.callback_query.message.edit_reply_markup(
reply_markup=reply_markup,
*args,
**kwargs
)
or::
bot.edit_message_reply_markup(inline_message_id=update.callback_query.inline_message_id,
reply_markup=reply_markup,
*args, **kwargs)
bot.edit_message_reply_markup
inline_message_id=update.callback_query.inline_message_id,
reply_markup=reply_markup,
*args,
**kwargs
)
For the documentation of the arguments, please see
:meth:`telegram.Bot.edit_message_reply_markup`.
Returns:
:class:`telegram.Message`: On success, if edited message is sent by the bot, the
edited Message is returned, otherwise ``True`` is returned.
edited Message is returned, otherwise :obj:`True` is returned.
"""
if self.inline_message_id:
return self.bot.edit_message_reply_markup(reply_markup=reply_markup,
inline_message_id=self.inline_message_id,
*args, **kwargs)
else:
return self.bot.edit_message_reply_markup(reply_markup=reply_markup,
chat_id=self.message.chat_id,
message_id=self.message.message_id,
*args, **kwargs)
return self.bot.edit_message_reply_markup(
reply_markup=reply_markup,
inline_message_id=self.inline_message_id,
timeout=timeout,
api_kwargs=api_kwargs,
chat_id=None,
message_id=None,
)
return self.message.edit_reply_markup(
reply_markup=reply_markup,
timeout=timeout,
api_kwargs=api_kwargs,
)
def edit_message_media(
self,
media: 'InputMedia' = None,
reply_markup: 'InlineKeyboardMarkup' = None,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> Union[Message, bool]:
"""Shortcut for either::
update.callback_query.message.edit_media(*args, **kwargs)
or::
bot.edit_message_media(inline_message_id=update.callback_query.inline_message_id,
*args, **kwargs)
For the documentation of the arguments, please see
:meth:`telegram.Bot.edit_message_media`.
Returns:
:class:`telegram.Message`: On success, if edited message is sent by the bot, the
edited Message is returned, otherwise :obj:`True` is returned.
"""
if self.inline_message_id:
return self.bot.edit_message_media(
inline_message_id=self.inline_message_id,
media=media,
reply_markup=reply_markup,
timeout=timeout,
api_kwargs=api_kwargs,
chat_id=None,
message_id=None,
)
return self.message.edit_media(
media=media,
reply_markup=reply_markup,
timeout=timeout,
api_kwargs=api_kwargs,
)
def edit_message_live_location(
self,
latitude: float = None,
longitude: float = None,
location: Location = None,
reply_markup: 'InlineKeyboardMarkup' = None,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
horizontal_accuracy: float = None,
heading: int = None,
proximity_alert_radius: int = None,
) -> Union[Message, bool]:
"""Shortcut for either::
update.callback_query.message.edit_live_location(*args, **kwargs)
or::
bot.edit_message_live_location(
inline_message_id=update.callback_query.inline_message_id,
*args, **kwargs
)
For the documentation of the arguments, please see
:meth:`telegram.Bot.edit_message_live_location`.
Returns:
:class:`telegram.Message`: On success, if edited message is sent by the bot, the
edited Message is returned, otherwise :obj:`True` is returned.
"""
if self.inline_message_id:
return self.bot.edit_message_live_location(
inline_message_id=self.inline_message_id,
latitude=latitude,
longitude=longitude,
location=location,
reply_markup=reply_markup,
timeout=timeout,
api_kwargs=api_kwargs,
horizontal_accuracy=horizontal_accuracy,
heading=heading,
proximity_alert_radius=proximity_alert_radius,
chat_id=None,
message_id=None,
)
return self.message.edit_live_location(
latitude=latitude,
longitude=longitude,
location=location,
reply_markup=reply_markup,
timeout=timeout,
api_kwargs=api_kwargs,
horizontal_accuracy=horizontal_accuracy,
heading=heading,
proximity_alert_radius=proximity_alert_radius,
)
def stop_message_live_location(
self,
reply_markup: 'InlineKeyboardMarkup' = None,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> Union[Message, bool]:
"""Shortcut for either::
update.callback_query.message.stop_live_location(*args, **kwargs)
or::
bot.stop_message_live_location(
inline_message_id=update.callback_query.inline_message_id,
*args, **kwargs
)
For the documentation of the arguments, please see
:meth:`telegram.Bot.stop_message_live_location`.
Returns:
:class:`telegram.Message`: On success, if edited message is sent by the bot, the
edited Message is returned, otherwise :obj:`True` is returned.
"""
if self.inline_message_id:
return self.bot.stop_message_live_location(
inline_message_id=self.inline_message_id,
reply_markup=reply_markup,
timeout=timeout,
api_kwargs=api_kwargs,
chat_id=None,
message_id=None,
)
return self.message.stop_live_location(
reply_markup=reply_markup,
timeout=timeout,
api_kwargs=api_kwargs,
)
def set_game_score(
self,
user_id: Union[int, str],
score: int,
force: bool = None,
disable_edit_message: bool = None,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> Union[Message, bool]:
"""Shortcut for either::
update.callback_query.message.set_game_score(*args, **kwargs)
or::
bot.set_game_score(inline_message_id=update.callback_query.inline_message_id,
*args, **kwargs)
For the documentation of the arguments, please see
:meth:`telegram.Bot.set_game_score`.
Returns:
:class:`telegram.Message`: On success, if edited message is sent by the bot, the
edited Message is returned, otherwise :obj:`True` is returned.
"""
if self.inline_message_id:
return self.bot.set_game_score(
inline_message_id=self.inline_message_id,
user_id=user_id,
score=score,
force=force,
disable_edit_message=disable_edit_message,
timeout=timeout,
api_kwargs=api_kwargs,
chat_id=None,
message_id=None,
)
return self.message.set_game_score(
user_id=user_id,
score=score,
force=force,
disable_edit_message=disable_edit_message,
timeout=timeout,
api_kwargs=api_kwargs,
)
def get_game_high_scores(
self,
user_id: Union[int, str],
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> List['GameHighScore']:
"""Shortcut for either::
update.callback_query.message.get_game_high_score(*args, **kwargs)
or::
bot.get_game_high_scores(inline_message_id=update.callback_query.inline_message_id,
*args, **kwargs)
For the documentation of the arguments, please see
:meth:`telegram.Bot.get_game_high_scores`.
Returns:
List[:class:`telegram.GameHighScore`]
"""
if self.inline_message_id:
return self.bot.get_game_high_scores(
inline_message_id=self.inline_message_id,
user_id=user_id,
timeout=timeout,
api_kwargs=api_kwargs,
chat_id=None,
message_id=None,
)
return self.message.get_game_high_scores(
user_id=user_id,
timeout=timeout,
api_kwargs=api_kwargs,
)
def delete_message(
self,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
update.callback_query.message.delete(*args, **kwargs)
For the documentation of the arguments, please see
:meth:`telegram.Bot.delete_message`.
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.message.delete(
timeout=timeout,
api_kwargs=api_kwargs,
)
def pin_message(
self,
disable_notification: ODVInput[bool] = DEFAULT_NONE,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
bot.pin_chat_message(chat_id=message.chat_id,
message_id=message.message_id,
*args,
**kwargs)
For the documentation of the arguments, please see
:meth:`telegram.Bot.pin_chat_message`.
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.message.pin(
disable_notification=disable_notification,
timeout=timeout,
api_kwargs=api_kwargs,
)
def unpin_message(
self,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> bool:
"""Shortcut for::
bot.unpin_chat_message(chat_id=message.chat_id,
message_id=message.message_id,
*args,
**kwargs)
For the documentation of the arguments, please see
:meth:`telegram.Bot.unpin_chat_message`.
Returns:
:obj:`bool`: On success, :obj:`True` is returned.
"""
return self.message.unpin(
timeout=timeout,
api_kwargs=api_kwargs,
)
def copy_message(
self,
chat_id: Union[int, str],
caption: str = None,
parse_mode: ODVInput[str] = DEFAULT_NONE,
caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None,
disable_notification: DVInput[bool] = DEFAULT_NONE,
reply_to_message_id: int = None,
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
reply_markup: ReplyMarkup = None,
timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict = None,
) -> 'MessageId':
"""Shortcut for::
update.callback_query.message.copy(
chat_id,
from_chat_id=update.message.chat_id,
message_id=update.message.message_id,
*args,
**kwargs)
For the documentation of the arguments, please see
:meth:`telegram.Bot.copy_message`.
Returns:
:class:`telegram.MessageId`: On success, returns the MessageId of the sent message.
"""
return self.message.copy(
chat_id=chat_id,
caption=caption,
parse_mode=parse_mode,
caption_entities=caption_entities,
disable_notification=disable_notification,
reply_to_message_id=reply_to_message_id,
allow_sending_without_reply=allow_sending_without_reply,
reply_markup=reply_markup,
timeout=timeout,
api_kwargs=api_kwargs,
)
MAX_ANSWER_TEXT_LENGTH: ClassVar[int] = constants.MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH
"""
:const:`telegram.constants.MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH`
.. versionadded:: 13.2
"""
+1285 -137
View File
File diff suppressed because it is too large Load Diff
+25 -23
View File
@@ -2,7 +2,7 @@
# pylint: disable=R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -18,28 +18,30 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ChatAction."""
from typing import ClassVar
from telegram import constants
class ChatAction(object):
"""Helper class to provide constants for different chatactions."""
class ChatAction:
"""Helper class to provide constants for different chat actions."""
FIND_LOCATION = 'find_location'
""":obj:`str`: 'find_location'"""
RECORD_AUDIO = 'record_audio'
""":obj:`str`: 'record_audio'"""
RECORD_VIDEO = 'record_video'
""":obj:`str`: 'record_video'"""
RECORD_VIDEO_NOTE = 'record_video_note'
""":obj:`str`: 'record_video_note'"""
TYPING = 'typing'
""":obj:`str`: 'typing'"""
UPLOAD_AUDIO = 'upload_audio'
""":obj:`str`: 'upload_audio'"""
UPLOAD_DOCUMENT = 'upload_document'
""":obj:`str`: 'upload_document'"""
UPLOAD_PHOTO = 'upload_photo'
""":obj:`str`: 'upload_photo'"""
UPLOAD_VIDEO = 'upload_video'
""":obj:`str`: 'upload_video'"""
UPLOAD_VIDEO_NOTE = 'upload_video_note'
""":obj:`str`: 'upload_video_note'"""
FIND_LOCATION: ClassVar[str] = constants.CHATACTION_FIND_LOCATION
""":const:`telegram.constants.CHATACTION_FIND_LOCATION`"""
RECORD_AUDIO: ClassVar[str] = constants.CHATACTION_RECORD_AUDIO
""":const:`telegram.constants.CHATACTION_RECORD_AUDIO`"""
RECORD_VIDEO: ClassVar[str] = constants.CHATACTION_RECORD_VIDEO
""":const:`telegram.constants.CHATACTION_RECORD_VIDEO`"""
RECORD_VIDEO_NOTE: ClassVar[str] = constants.CHATACTION_RECORD_VIDEO_NOTE
""":const:`telegram.constants.CHATACTION_RECORD_VIDEO_NOTE`"""
TYPING: ClassVar[str] = constants.CHATACTION_TYPING
""":const:`telegram.constants.CHATACTION_TYPING`"""
UPLOAD_AUDIO: ClassVar[str] = constants.CHATACTION_UPLOAD_AUDIO
""":const:`telegram.constants.CHATACTION_UPLOAD_AUDIO`"""
UPLOAD_DOCUMENT: ClassVar[str] = constants.CHATACTION_UPLOAD_DOCUMENT
""":const:`telegram.constants.CHATACTION_UPLOAD_DOCUMENT`"""
UPLOAD_PHOTO: ClassVar[str] = constants.CHATACTION_UPLOAD_PHOTO
""":const:`telegram.constants.CHATACTION_UPLOAD_PHOTO`"""
UPLOAD_VIDEO: ClassVar[str] = constants.CHATACTION_UPLOAD_VIDEO
""":const:`telegram.constants.CHATACTION_UPLOAD_VIDEO`"""
UPLOAD_VIDEO_NOTE: ClassVar[str] = constants.CHATACTION_UPLOAD_VIDEO_NOTE
""":const:`telegram.constants.CHATACTION_UPLOAD_VIDEO_NOTE`"""
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents an invite link for a chat."""
import datetime
from typing import TYPE_CHECKING, Any, Optional
from telegram import TelegramObject, User
from telegram.utils.helpers import from_timestamp, to_timestamp
from telegram.utils.types import JSONDict
if TYPE_CHECKING:
from telegram import Bot
class ChatInviteLink(TelegramObject):
"""This object represents an invite link for a chat.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`invite_link`, :attr:`creator`, :attr:`is_primary` and
:attr:`is_revoked` are equal.
.. versionadded:: 13.4
Args:
invite_link (:obj:`str`): The invite link.
creator (:class:`telegram.User`): Creator of the link.
is_primary (:obj:`bool`): :obj:`True`, if the link is primary.
is_revoked (:obj:`bool`): :obj:`True`, if the link is revoked.
expire_date (:class:`datetime.datetime`, optional): Date when the link will expire or
has been expired.
member_limit (:obj:`int`, optional): Maximum number of users that can be members of the
chat simultaneously after joining the chat via this invite link; 1-99999.
Attributes:
invite_link (:obj:`str`): The invite link. If the link was created by another chat
administrator, then the second part of the link will be replaced with ``''``.
creator (:class:`telegram.User`): Creator of the link.
is_primary (:obj:`bool`): :obj:`True`, if the link is primary.
is_revoked (:obj:`bool`): :obj:`True`, if the link is revoked.
expire_date (:class:`datetime.datetime`): Optional. Date when the link will expire or
has been expired.
member_limit (:obj:`int`): Optional. Maximum number of users that can be members
of the chat simultaneously after joining the chat via this invite link; 1-99999.
"""
def __init__(
self,
invite_link: str,
creator: User,
is_primary: bool,
is_revoked: bool,
expire_date: datetime.datetime = None,
member_limit: int = None,
**_kwargs: Any,
):
# Required
self.invite_link = invite_link
self.creator = creator
self.is_primary = is_primary
self.is_revoked = is_revoked
# Optionals
self.expire_date = expire_date
self.member_limit = int(member_limit) if member_limit is not None else None
self._id_attrs = (self.invite_link, self.creator, self.is_primary, self.is_revoked)
@classmethod
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatInviteLink']:
data = cls.parse_data(data)
if not data:
return None
data['creator'] = User.de_json(data.get('creator'), bot)
data['expire_date'] = from_timestamp(data.get('expire_date', None))
return cls(**data)
def to_dict(self) -> JSONDict:
data = super().to_dict()
data['expire_date'] = to_timestamp(self.expire_date)
return data
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a location to which a chat is connected."""
from typing import TYPE_CHECKING, Any, Optional
from telegram import TelegramObject
from telegram.utils.types import JSONDict
from .files.location import Location
if TYPE_CHECKING:
from telegram import Bot
class ChatLocation(TelegramObject):
"""This object represents a location to which a chat is connected.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`location` is equal.
Args:
location (:class:`telegram.Location`): The location to which the supergroup is connected.
Can't be a live location.
address (:obj:`str`): Location address; 1-64 characters, as defined by the chat owner
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
Attributes:
location (:class:`telegram.Location`): The location to which the supergroup is connected.
address (:obj:`str`): Location address, as defined by the chat owner
"""
def __init__(
self,
location: Location,
address: str,
**_kwargs: Any,
):
self.location = location
self.address = address
self._id_attrs = (self.location,)
@classmethod
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatLocation']:
data = cls.parse_data(data)
if not data:
return None
data['location'] = Location.de_json(data.get('location'), bot)
return cls(bot=bot, **data)
+135 -71
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -17,21 +17,99 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ChatMember."""
import datetime
from typing import TYPE_CHECKING, Any, Optional, ClassVar
from telegram import User, TelegramObject
from telegram.utils.helpers import to_timestamp, from_timestamp
from telegram import TelegramObject, User, constants
from telegram.utils.helpers import from_timestamp, to_timestamp
from telegram.utils.types import JSONDict
if TYPE_CHECKING:
from telegram import Bot
class ChatMember(TelegramObject):
"""This object contains information about one member of the chat.
"""This object contains information about one member of a chat.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`user` and :attr:`status` are equal.
Args:
user (:class:`telegram.User`): Information about the user.
status (:obj:`str`): The member's status in the chat. Can be 'creator', 'administrator',
'member', 'restricted', 'left' or 'kicked'.
custom_title (:obj:`str`, optional): Owner and administrators only.
Custom title for this user.
is_anonymous (:obj:`bool`, optional): Owner and administrators only. :obj:`True`, if the
user's presence in the chat is hidden.
until_date (:class:`datetime.datetime`, optional): Restricted and kicked only. Date when
restrictions will be lifted for this user.
can_be_edited (:obj:`bool`, optional): Administrators only. :obj:`True`, if the bot is
allowed to edit administrator privileges of that user.
can_manage_chat (:obj:`bool`, optional): Administrators only. :obj:`True`, if the
administrator can access the chat event log, chat statistics, message statistics in
channels, see channel members, see anonymous administrators in supergroups and ignore
slow mode. Implied by any other administrator privilege.
.. versionadded:: 13.4
can_manage_voice_chats (:obj:`bool`, optional): Administrators only. :obj:`True`, if the
administrator can manage voice chats.
.. versionadded:: 13.4
can_change_info (:obj:`bool`, optional): Administrators and restricted only. :obj:`True`,
if the user can change the chat title, photo and other settings.
can_post_messages (:obj:`bool`, optional): Administrators only. :obj:`True`, if the
administrator can post in the channel, channels only.
can_edit_messages (:obj:`bool`, optional): Administrators only. :obj:`True`, if the
administrator can edit messages of other users and can pin messages; channels only.
can_delete_messages (:obj:`bool`, optional): Administrators only. :obj:`True`, if the
administrator can delete messages of other users.
can_invite_users (:obj:`bool`, optional): Administrators and restricted only. :obj:`True`,
if the user can invite new users to the chat.
can_restrict_members (:obj:`bool`, optional): Administrators only. :obj:`True`, if the
administrator can restrict, ban or unban chat members.
can_pin_messages (:obj:`bool`, optional): Administrators and restricted only. :obj:`True`,
if the user can pin messages, groups and supergroups only.
can_promote_members (:obj:`bool`, optional): Administrators only. :obj:`True`, if the
administrator can add new administrators with a subset of his own privileges or demote
administrators that he has promoted, directly or indirectly (promoted by administrators
that were appointed by the user).
is_member (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user is a member of
the chat at the moment of the request.
can_send_messages (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user can
send text messages, contacts, locations and venues.
can_send_media_messages (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user
can send audios, documents, photos, videos, video notes and voice notes.
can_send_polls (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user is
allowed to send polls.
can_send_other_messages (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user
can send animations, games, stickers and use inline bots.
can_add_web_page_previews (:obj:`bool`, optional): Restricted only. :obj:`True`, if user
may add web page previews to his messages.
Attributes:
user (:class:`telegram.User`): Information about the user.
status (:obj:`str`): The member's status in the chat.
custom_title (:obj:`str`): Optional. Custom title for owner and administrators.
is_anonymous (:obj:`bool`): Optional. :obj:`True`, if the user's presence in the chat is
hidden.
until_date (:class:`datetime.datetime`): Optional. Date when restrictions will be lifted
for this user.
can_be_edited (:obj:`bool`): Optional. If the bot is allowed to edit administrator
privileges of that user.
can_manage_chat (:obj:`bool`): Optional. If the administrator can access the chat event
log, chat statistics, message statistics in channels, see channel members, see
anonymous administrators in supergroups and ignore slow mode.
.. versionadded:: 13.4
can_manage_voice_chats (:obj:`bool`): Optional. if the administrator can manage
voice chats.
.. versionadded:: 13.4
can_change_info (:obj:`bool`): Optional. If the user can change the chat title, photo and
other settings.
can_post_messages (:obj:`bool`): Optional. If the administrator can post in the channel.
@@ -45,83 +123,67 @@ class ChatMember(TelegramObject):
can_pin_messages (:obj:`bool`): Optional. If the user can pin messages.
can_promote_members (:obj:`bool`): Optional. If the administrator can add new
administrators.
is_member (:obj:`bool`): Optional. Restricted only. True, if the user is a member of the
chat at the moment of the request.
is_member (:obj:`bool`): Optional. Restricted only. :obj:`True`, if the user is a member of
the chat at the moment of the request.
can_send_messages (:obj:`bool`): Optional. If the user can send text messages, contacts,
locations and venues.
can_send_media_messages (:obj:`bool`): Optional. If the user can send media messages,
implies can_send_messages.
can_send_polls (:obj:`bool`): Optional. True, if the user is allowed to
can_send_polls (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to
send polls.
can_send_other_messages (:obj:`bool`): Optional. If the user can send animations, games,
stickers and use inline bots, implies can_send_media_messages.
can_add_web_page_previews (:obj:`bool`): Optional. If user may add web page previews to his
messages, implies can_send_media_messages
Args:
user (:class:`telegram.User`): Information about the user.
status (:obj:`str`): The member's status in the chat. Can be 'creator', 'administrator',
'member', 'restricted', 'left' or 'kicked'.
until_date (:class:`datetime.datetime`, optional): Restricted and kicked only. Date when
restrictions will be lifted for this user.
can_be_edited (:obj:`bool`, optional): Administrators only. True, if the bot is allowed to
edit administrator privileges of that user.
can_change_info (:obj:`bool`, optional): Administrators and restricted only. True, if the
user can change the chat title, photo and other settings.
can_post_messages (:obj:`bool`, optional): Administrators only. True, if the administrator
can post in the channel, channels only.
can_edit_messages (:obj:`bool`, optional): Administrators only. True, if the administrator
can edit messages of other users, channels only.
can_delete_messages (:obj:`bool`, optional): Administrators only. True, if the
administrator can delete messages of other user.
can_invite_users (:obj:`bool`, optional): Administrators and restricted only. True, if the
user can invite new users to the chat.
can_restrict_members (:obj:`bool`, optional): Administrators only. True, if the
administrator can restrict, ban or unban chat members.
can_pin_messages (:obj:`bool`, optional): Administrators and restricted only. True, if the
user can pin messages, supergroups only.
can_promote_members (:obj:`bool`, optional): Administrators only. True, if the
administrator can add new administrators with a subset of his own privileges or demote
administrators that he has promoted, directly or indirectly (promoted by administrators
that were appointed by the user).
is_member (:obj:`bool`, optional): Restricted only. True, if the user is a member of the
chat at the moment of the request.
can_send_messages (:obj:`bool`, optional): Restricted only. True, if the user can send text
messages, contacts, locations and venues.
can_send_media_messages (:obj:`bool`, optional): Restricted only. True, if the user can
send audios, documents, photos, videos, video notes and voice notes, implies
can_send_messages.
can_send_polls (:obj:`bool`, optional): Restricted only. True, if the user is allowed to
send polls.
can_send_other_messages (:obj:`bool`, optional): Restricted only. True, if the user can
send animations, games, stickers and use inline bots, implies can_send_media_messages.
can_add_web_page_previews (:obj:`bool`, optional): Restricted only. True, if user may add
web page previews to his messages, implies can_send_media_messages.
"""
ADMINISTRATOR = 'administrator'
""":obj:`str`: 'administrator'"""
CREATOR = 'creator'
""":obj:`str`: 'creator'"""
KICKED = 'kicked'
""":obj:`str`: 'kicked'"""
LEFT = 'left'
""":obj:`str`: 'left'"""
MEMBER = 'member'
""":obj:`str`: 'member'"""
RESTRICTED = 'restricted'
""":obj:`str`: 'restricted'"""
def __init__(self, user, status, until_date=None, can_be_edited=None,
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, can_send_messages=None,
can_send_media_messages=None, can_send_polls=None, can_send_other_messages=None,
can_add_web_page_previews=None, is_member=None, **kwargs):
ADMINISTRATOR: ClassVar[str] = constants.CHATMEMBER_ADMINISTRATOR
""":const:`telegram.constants.CHATMEMBER_ADMINISTRATOR`"""
CREATOR: ClassVar[str] = constants.CHATMEMBER_CREATOR
""":const:`telegram.constants.CHATMEMBER_CREATOR`"""
KICKED: ClassVar[str] = constants.CHATMEMBER_KICKED
""":const:`telegram.constants.CHATMEMBER_KICKED`"""
LEFT: ClassVar[str] = constants.CHATMEMBER_LEFT
""":const:`telegram.constants.CHATMEMBER_LEFT`"""
MEMBER: ClassVar[str] = constants.CHATMEMBER_MEMBER
""":const:`telegram.constants.CHATMEMBER_MEMBER`"""
RESTRICTED: ClassVar[str] = constants.CHATMEMBER_RESTRICTED
""":const:`telegram.constants.CHATMEMBER_RESTRICTED`"""
def __init__(
self,
user: User,
status: str,
until_date: datetime.datetime = None,
can_be_edited: bool = None,
can_change_info: bool = None,
can_post_messages: bool = None,
can_edit_messages: bool = None,
can_delete_messages: bool = None,
can_invite_users: bool = None,
can_restrict_members: bool = None,
can_pin_messages: bool = None,
can_promote_members: bool = None,
can_send_messages: bool = None,
can_send_media_messages: bool = None,
can_send_polls: bool = None,
can_send_other_messages: bool = None,
can_add_web_page_previews: bool = None,
is_member: bool = None,
custom_title: str = None,
is_anonymous: bool = None,
can_manage_chat: bool = None,
can_manage_voice_chats: bool = None,
**_kwargs: Any,
):
# Required
self.user = user
self.status = status
# Optionals
self.custom_title = custom_title
self.is_anonymous = is_anonymous
self.until_date = until_date
self.can_be_edited = can_be_edited
self.can_change_info = can_change_info
@@ -138,23 +200,25 @@ class ChatMember(TelegramObject):
self.can_send_other_messages = can_send_other_messages
self.can_add_web_page_previews = can_add_web_page_previews
self.is_member = is_member
self.can_manage_chat = can_manage_chat
self.can_manage_voice_chats = can_manage_voice_chats
self._id_attrs = (self.user, self.status)
@classmethod
def de_json(cls, data, bot):
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatMember']:
data = cls.parse_data(data)
if not data:
return None
data = super(ChatMember, cls).de_json(data, bot)
data['user'] = User.de_json(data.get('user'), bot)
data['until_date'] = from_timestamp(data.get('until_date', None))
return cls(**data)
def to_dict(self):
data = super(ChatMember, self).to_dict()
def to_dict(self) -> JSONDict:
data = super().to_dict()
data['until_date'] = to_timestamp(self.until_date)
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ChatMemberUpdated."""
import datetime
from typing import TYPE_CHECKING, Any, Optional
from telegram import TelegramObject, User, Chat, ChatMember, ChatInviteLink
from telegram.utils.helpers import from_timestamp, to_timestamp
from telegram.utils.types import JSONDict
if TYPE_CHECKING:
from telegram import Bot
class ChatMemberUpdated(TelegramObject):
"""This object represents changes in the status of a chat member.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`chat`, :attr:`from_user`, :attr:`date`,
:attr:`old_chat_member` and :attr:`new_chat_member` are equal.
.. versionadded:: 13.4
Note:
In Python ``from`` is a reserved word, use ``from_user`` instead.
Args:
chat (:class:`telegram.Chat`): Chat the user belongs to.
from_user (:class:`telegram.User`): Performer of the action, which resulted in the change.
date (:class:`datetime.datetime`): Date the change was done in Unix time. Converted to
:class:`datetime.datetime`.
old_chat_member (:class:`telegram.ChatMember`): Previous information about the chat member.
new_chat_member (:class:`telegram.ChatMember`): New information about the chat member.
invite_link (:class:`telegram.ChatInviteLink`, optional): Chat invite link, which was used
by the user to join the chat. For joining by invite link events only.
Attributes:
chat (:class:`telegram.Chat`): Chat the user belongs to.
from_user (:class:`telegram.User`): Performer of the action, which resulted in the change.
date (:class:`datetime.datetime`): Date the change was done in Unix time. Converted to
:class:`datetime.datetime`.
old_chat_member (:class:`telegram.ChatMember`): Previous information about the chat member.
new_chat_member (:class:`telegram.ChatMember`): New information about the chat member.
invite_link (:class:`telegram.ChatInviteLink`): Optional. Chat invite link, which was used
by the user to join the chat.
"""
def __init__(
self,
chat: Chat,
from_user: User,
date: datetime.datetime,
old_chat_member: ChatMember,
new_chat_member: ChatMember,
invite_link: ChatInviteLink = None,
**_kwargs: Any,
):
# Required
self.chat = chat
self.from_user = from_user
self.date = date
self.old_chat_member = old_chat_member
self.new_chat_member = new_chat_member
# Optionals
self.invite_link = invite_link
self._id_attrs = (
self.chat,
self.from_user,
self.date,
self.old_chat_member,
self.new_chat_member,
)
@classmethod
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatMemberUpdated']:
data = cls.parse_data(data)
if not data:
return None
data['chat'] = Chat.de_json(data.get('chat'), bot)
data['from_user'] = User.de_json(data.get('from'), bot)
data['date'] = from_timestamp(data.get('date'))
data['old_chat_member'] = ChatMember.de_json(data.get('old_chat_member'), bot)
data['new_chat_member'] = ChatMember.de_json(data.get('new_chat_member'), bot)
data['invite_link'] = ChatInviteLink.de_json(data.get('invite_link'), bot)
return cls(**data)
def to_dict(self) -> JSONDict:
data = super().to_dict()
# Required
data['date'] = to_timestamp(self.date)
return data
+69 -44
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -18,57 +18,78 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ChatPermission."""
from typing import Any
from telegram import TelegramObject
class ChatPermissions(TelegramObject):
"""Describes actions that a non-administrator user is allowed to take in a chat.
Attributes:
can_send_messages (:obj:`bool`): Optional. True, if the user is allowed to send text
messages, contacts, locations and venues.
can_send_media_messages (:obj:`bool`): Optional. True, if the user is allowed to send
audios, documents, photos, videos, video notes and voice notes, implies
:attr:`can_send_messages`.
can_send_polls (:obj:`bool`): Optional. True, if the user is allowed to send polls, implies
:attr:`can_send_messages`.
can_send_other_messages (:obj:`bool`): Optional. True, if the user is allowed to send
animations, games, stickers and use inline bots, implies
:attr:`can_send_media_messages`.
can_add_web_page_previews (:obj:`bool`): Optional. True, if the user is allowed to add web
page previews to their messages, implies :attr:`can_send_media_messages`.
can_change_info (:obj:`bool`): Optional. True, if the user is allowed to change the chat
title, photo and other settings. Ignored in public supergroups.
can_invite_users (:obj:`bool`): Optional. True, if the user is allowed to invite new users
to the chat.
can_pin_messages (:obj:`bool`): Optional. True, if the user is allowed to pin messages.
Ignored in public supergroups.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`can_send_messages`, :attr:`can_send_media_messages`,
:attr:`can_send_polls`, :attr:`can_send_other_messages`, :attr:`can_add_web_page_previews`,
:attr:`can_change_info`, :attr:`can_invite_users` and :attr:`can_pin_message` are equal.
Note:
Though not stated explicitly in the official docs, Telegram changes not only the
permissions that are set, but also sets all the others to :obj:`False`. However, since not
documented, this behaviour may change unbeknown to PTB.
Args:
can_send_messages (:obj:`bool`, optional): True, if the user is allowed to send text
can_send_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to send text
messages, contacts, locations and venues.
can_send_media_messages (:obj:`bool`, optional): True, if the user is allowed to send
audios, documents, photos, videos, video notes and voice notes, implies
can_send_media_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to
send audios, documents, photos, videos, video notes and voice notes, implies
:attr:`can_send_messages`.
can_send_polls (:obj:`bool`, optional): True, if the user is allowed to send polls, implies
:attr:`can_send_messages`.
can_send_other_messages (:obj:`bool`, optional): True, if the user is allowed to send
animations, games, stickers and use inline bots, implies
can_send_polls (:obj:`bool`, optional): :obj:`True`, if the user is allowed to send polls,
implies :attr:`can_send_messages`.
can_send_other_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to
send animations, games, stickers and use inline bots, implies
:attr:`can_send_media_messages`.
can_add_web_page_previews (:obj:`bool`, optional): True, if the user is allowed to add web
page previews to their messages, implies :attr:`can_send_media_messages`.
can_change_info (:obj:`bool`, optional): True, if the user is allowed to change the chat
title, photo and other settings. Ignored in public supergroups.
can_invite_users (:obj:`bool`, optional): True, if the user is allowed to invite new users
to the chat.
can_pin_messages (:obj:`bool`, optional): True, if the user is allowed to pin messages.
Ignored in public supergroups.
can_add_web_page_previews (:obj:`bool`, optional): :obj:`True`, if the user is allowed to
add web page previews to their messages, implies :attr:`can_send_media_messages`.
can_change_info (:obj:`bool`, optional): :obj:`True`, if the user is allowed to change the
chat title, photo and other settings. Ignored in public supergroups.
can_invite_users (:obj:`bool`, optional): :obj:`True`, if the user is allowed to invite new
users to the chat.
can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to pin
messages. Ignored in public supergroups.
Attributes:
can_send_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to send text
messages, contacts, locations and venues.
can_send_media_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to
send audios, documents, photos, videos, video notes and voice notes, implies
:attr:`can_send_messages`.
can_send_polls (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to send polls,
implies :attr:`can_send_messages`.
can_send_other_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to
send animations, games, stickers and use inline bots, implies
:attr:`can_send_media_messages`.
can_add_web_page_previews (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to
add web page previews to their messages, implies :attr:`can_send_media_messages`.
can_change_info (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to change the
chat title, photo and other settings. Ignored in public supergroups.
can_invite_users (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to invite
new users to the chat.
can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to pin
messages. Ignored in public supergroups.
"""
def __init__(self, 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, **kwargs):
def __init__(
self,
can_send_messages: bool = None,
can_send_media_messages: bool = None,
can_send_polls: bool = None,
can_send_other_messages: bool = None,
can_add_web_page_previews: bool = None,
can_change_info: bool = None,
can_invite_users: bool = None,
can_pin_messages: bool = None,
**_kwargs: Any,
):
# Required
self.can_send_messages = can_send_messages
self.can_send_media_messages = can_send_media_messages
@@ -79,9 +100,13 @@ class ChatPermissions(TelegramObject):
self.can_invite_users = can_invite_users
self.can_pin_messages = can_pin_messages
@classmethod
def de_json(cls, data, bot):
if not data:
return None
return cls(**data)
self._id_attrs = (
self.can_send_messages,
self.can_send_media_messages,
self.can_send_polls,
self.can_send_other_messages,
self.can_add_web_page_previews,
self.can_change_info,
self.can_invite_users,
self.can_pin_messages,
)
+34 -20
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env python
# pylint: disable=R0902,R0912,R0913
# pylint: disable=R0902,R0913
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,7 +19,13 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ChosenInlineResult."""
from telegram import TelegramObject, User, Location
from typing import TYPE_CHECKING, Any, Optional
from telegram import Location, TelegramObject, User
from telegram.utils.types import JSONDict
if TYPE_CHECKING:
from telegram import Bot
class ChosenInlineResult(TelegramObject):
@@ -27,15 +33,13 @@ class ChosenInlineResult(TelegramObject):
Represents a result of an inline query that was chosen by the user and sent to their chat
partner.
Note:
In Python `from` is a reserved word, use `from_user` instead.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`result_id` is equal.
Attributes:
result_id (:obj:`str`): The unique identifier for the result that was chosen.
from_user (:class:`telegram.User`): The user that chose the result.
location (:class:`telegram.Location`): Optional. Sender location.
inline_message_id (:obj:`str`): Optional. Identifier of the sent inline message.
query (:obj:`str`): The query that was used to obtain the result.
Note:
* In Python ``from`` is a reserved word, use ``from_user`` instead.
* It is necessary to enable inline feedback via `@Botfather <https://t.me/BotFather>`_ in
order to receive these objects in updates.
Args:
result_id (:obj:`str`): The unique identifier for the result that was chosen.
@@ -48,15 +52,24 @@ class ChosenInlineResult(TelegramObject):
query (:obj:`str`): The query that was used to obtain the result.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
Attributes:
result_id (:obj:`str`): The unique identifier for the result that was chosen.
from_user (:class:`telegram.User`): The user that chose the result.
location (:class:`telegram.Location`): Optional. Sender location.
inline_message_id (:obj:`str`): Optional. Identifier of the sent inline message.
query (:obj:`str`): The query that was used to obtain the result.
"""
def __init__(self,
result_id,
from_user,
query,
location=None,
inline_message_id=None,
**kwargs):
def __init__(
self,
result_id: str,
from_user: User,
query: str,
location: Location = None,
inline_message_id: str = None,
**_kwargs: Any,
):
# Required
self.result_id = result_id
self.from_user = from_user
@@ -68,11 +81,12 @@ class ChosenInlineResult(TelegramObject):
self._id_attrs = (self.result_id,)
@classmethod
def de_json(cls, data, bot):
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChosenInlineResult']:
data = cls.parse_data(data)
if not data:
return None
data = super(ChosenInlineResult, cls).de_json(data, bot)
# Required
data['from_user'] = User.de_json(data.pop('from'), bot)
# Optionals
+209 -12
View File
@@ -1,5 +1,5 @@
# python-telegram-bot - a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# by the python-telegram-bot contributors <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -17,38 +17,235 @@
"""Constants in the Telegram network.
The following constants were extracted from the
`Telegram Bots FAQ <https://core.telegram.org/bots/faq>`_.
`Telegram Bots FAQ <https://core.telegram.org/bots/faq>`_ and
`Telegram Bots API <https://core.telegram.org/bots/api>`_.
Attributes:
BOT_API_VERSION (:obj:`str`): `5.1`. Telegram Bot API version supported by this
version of `python-telegram-bot`. Also available as ``telegram.bot_api_version``.
.. versionadded:: 13.4
MAX_MESSAGE_LENGTH (:obj:`int`): 4096
MAX_CAPTION_LENGTH (:obj:`int`): 1024
SUPPORTED_WEBHOOK_PORTS (List[:obj:`int`]): [443, 80, 88, 8443]
MAX_FILESIZE_DOWNLOAD (:obj:`int`): In bytes (20MB)
MAX_FILESIZE_UPLOAD (:obj:`int`): In bytes (50MB)
MAX_PHOTOSIZE_UPLOAD (:obj:`int`): In bytes (10MB)
MAX_MESSAGES_PER_SECOND_PER_CHAT (:obj:`int`): `1`. Telegram may allow short bursts that go
over this limit, but eventually you'll begin receiving 429 errors.
MAX_MESSAGES_PER_SECOND (:obj:`int`): 30
MAX_MESSAGES_PER_MINUTE_PER_GROUP (:obj:`int`): 20
MAX_INLINE_QUERY_RESULTS (:obj:`int`): 50
MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH (:obj:`int`): 200
.. versionadded:: 13.2
The following constant have been found by experimentation:
Attributes:
MAX_MESSAGE_ENTITIES (:obj:`int`): 100 (Beyond this cap telegram will simply ignore further
formatting styles)
ANONYMOUS_ADMIN_ID (:obj:`int`): ``1087968824`` (User id in groups for anonymous admin)
SERVICE_CHAT_ID (:obj:`int`): ``777000`` (Telegram service chat, that also acts as sender of
channel posts forwarded to discussion groups)
The following constants are related to specific classes and are also available
as attributes of those classes:
:class:`telegram.Chat`:
Attributes:
CHAT_PRIVATE (:obj:`str`): 'private'
CHAT_GROUP (:obj:`str`): 'group'
CHAT_SUPERGROUP (:obj:`str`): 'supergroup'
CHAT_CHANNEL (:obj:`str`): 'channel'
:class:`telegram.ChatAction`:
Attributes:
CHATACTION_FIND_LOCATION (:obj:`str`): 'find_location'
CHATACTION_RECORD_AUDIO (:obj:`str`): 'record_audio'
CHATACTION_RECORD_VIDEO (:obj:`str`): 'record_video'
CHATACTION_RECORD_VIDEO_NOTE (:obj:`str`): 'record_video_note'
CHATACTION_TYPING (:obj:`str`): 'typing'
CHATACTION_UPLOAD_AUDIO (:obj:`str`): 'upload_audio'
CHATACTION_UPLOAD_DOCUMENT (:obj:`str`): 'upload_document'
CHATACTION_UPLOAD_PHOTO (:obj:`str`): 'upload_photo'
CHATACTION_UPLOAD_VIDEO (:obj:`str`): 'upload_video'
CHATACTION_UPLOAD_VIDEO_NOTE (:obj:`str`): 'upload_video_note'
:class:`telegram.ChatMember`:
Attributes:
CHATMEMBER_ADMINISTRATOR (:obj:`str`): 'administrator'
CHATMEMBER_CREATOR (:obj:`str`): 'creator'
CHATMEMBER_KICKED (:obj:`str`): 'kicked'
CHATMEMBER_LEFT (:obj:`str`): 'left'
CHATMEMBER_MEMBER (:obj:`str`): 'member'
CHATMEMBER_RESTRICTED (:obj:`str`): 'restricted'
:class:`telegram.Dice`:
Attributes:
DICE_DICE (:obj:`str`): '🎲'
DICE_DARTS (:obj:`str`): '🎯'
DICE_BASKETBALL (:obj:`str`): '🏀'
DICE_FOOTBALL (:obj:`str`): ''
DICE_SLOT_MACHINE (:obj:`str`): '🎰'
DICE_BOWLING (:obj:`str`): '🎳'
.. versionadded:: 13.4
DICE_ALL_EMOJI (List[:obj:`str`]): List of all supported base emoji.
.. versionchanged:: 13.4
Added :attr:`DICE_BOWLING`
:class:`telegram.MessageEntity`:
Attributes:
MESSAGEENTITY_MENTION (:obj:`str`): 'mention'
MESSAGEENTITY_HASHTAG (:obj:`str`): 'hashtag'
MESSAGEENTITY_CASHTAG (:obj:`str`): 'cashtag'
MESSAGEENTITY_PHONE_NUMBER (:obj:`str`): 'phone_number'
MESSAGEENTITY_BOT_COMMAND (:obj:`str`): 'bot_command'
MESSAGEENTITY_URL (:obj:`str`): 'url'
MESSAGEENTITY_EMAIL (:obj:`str`): 'email'
MESSAGEENTITY_BOLD (:obj:`str`): 'bold'
MESSAGEENTITY_ITALIC (:obj:`str`): 'italic'
MESSAGEENTITY_CODE (:obj:`str`): 'code'
MESSAGEENTITY_PRE (:obj:`str`): 'pre'
MESSAGEENTITY_TEXT_LINK (:obj:`str`): 'text_link'
MESSAGEENTITY_TEXT_MENTION (:obj:`str`): 'text_mention'
MESSAGEENTITY_UNDERLINE (:obj:`str`): 'underline'
MESSAGEENTITY_STRIKETHROUGH (:obj:`str`): 'strikethrough'
MESSAGEENTITY_ALL_TYPES (List[:obj:`str`]): List of all the types of message entity.
:class:`telegram.ParseMode`:
Attributes:
PARSEMODE_MARKDOWN (:obj:`str`): 'Markdown'
PARSEMODE_MARKDOWN_V2 (:obj:`str`): 'MarkdownV2'
PARSEMODE_HTML (:obj:`str`): 'HTML'
:class:`telegram.Poll`:
Attributes:
POLL_REGULAR (:obj:`str`): 'regular'
POLL_QUIZ (:obj:`str`): 'quiz'
MAX_POLL_QUESTION_LENGTH (:obj:`int`): 300
MAX_POLL_OPTION_LENGTH (:obj:`int`): 100
:class:`telegram.files.MaskPosition`:
Attributes:
STICKER_FOREHEAD (:obj:`str`): 'forehead'
STICKER_EYES (:obj:`str`): 'eyes'
STICKER_MOUTH (:obj:`str`): 'mouth'
STICKER_CHIN (:obj:`str`): 'chin'
"""
from typing import List
MAX_MESSAGE_LENGTH = 4096
MAX_CAPTION_LENGTH = 1024
BOT_API_VERSION: str = '5.1'
MAX_MESSAGE_LENGTH: int = 4096
MAX_CAPTION_LENGTH: int = 1024
ANONYMOUS_ADMIN_ID: int = 1087968824
SERVICE_CHAT_ID: int = 777000
# constants above this line are tested
SUPPORTED_WEBHOOK_PORTS = [443, 80, 88, 8443]
MAX_FILESIZE_DOWNLOAD = int(20E6) # (20MB)
MAX_FILESIZE_UPLOAD = int(50E6) # (50MB)
MAX_MESSAGES_PER_SECOND_PER_CHAT = 1
MAX_MESSAGES_PER_SECOND = 30
MAX_MESSAGES_PER_MINUTE_PER_GROUP = 20
MAX_MESSAGE_ENTITIES = 100
MAX_INLINE_QUERY_RESULTS = 50
SUPPORTED_WEBHOOK_PORTS: List[int] = [443, 80, 88, 8443]
MAX_FILESIZE_DOWNLOAD: int = int(20e6) # (20MB)
MAX_FILESIZE_UPLOAD: int = int(50e6) # (50MB)
MAX_PHOTOSIZE_UPLOAD: int = int(10e6) # (10MB)
MAX_MESSAGES_PER_SECOND_PER_CHAT: int = 1
MAX_MESSAGES_PER_SECOND: int = 30
MAX_MESSAGES_PER_MINUTE_PER_GROUP: int = 20
MAX_MESSAGE_ENTITIES: int = 100
MAX_INLINE_QUERY_RESULTS: int = 50
MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH: int = 200
CHAT_PRIVATE: str = 'private'
CHAT_GROUP: str = 'group'
CHAT_SUPERGROUP: str = 'supergroup'
CHAT_CHANNEL: str = 'channel'
CHATACTION_FIND_LOCATION: str = 'find_location'
CHATACTION_RECORD_AUDIO: str = 'record_audio'
CHATACTION_RECORD_VIDEO: str = 'record_video'
CHATACTION_RECORD_VIDEO_NOTE: str = 'record_video_note'
CHATACTION_TYPING: str = 'typing'
CHATACTION_UPLOAD_AUDIO: str = 'upload_audio'
CHATACTION_UPLOAD_DOCUMENT: str = 'upload_document'
CHATACTION_UPLOAD_PHOTO: str = 'upload_photo'
CHATACTION_UPLOAD_VIDEO: str = 'upload_video'
CHATACTION_UPLOAD_VIDEO_NOTE: str = 'upload_video_note'
CHATMEMBER_ADMINISTRATOR: str = 'administrator'
CHATMEMBER_CREATOR: str = 'creator'
CHATMEMBER_KICKED: str = 'kicked'
CHATMEMBER_LEFT: str = 'left'
CHATMEMBER_MEMBER: str = 'member'
CHATMEMBER_RESTRICTED: str = 'restricted'
DICE_DICE: str = '🎲'
DICE_DARTS: str = '🎯'
DICE_BASKETBALL: str = '🏀'
DICE_FOOTBALL: str = ''
DICE_SLOT_MACHINE: str = '🎰'
DICE_BOWLING: str = '🎳'
DICE_ALL_EMOJI: List[str] = [
DICE_DICE,
DICE_DARTS,
DICE_BASKETBALL,
DICE_FOOTBALL,
DICE_SLOT_MACHINE,
DICE_BOWLING,
]
MESSAGEENTITY_MENTION: str = 'mention'
MESSAGEENTITY_HASHTAG: str = 'hashtag'
MESSAGEENTITY_CASHTAG: str = 'cashtag'
MESSAGEENTITY_PHONE_NUMBER: str = 'phone_number'
MESSAGEENTITY_BOT_COMMAND: str = 'bot_command'
MESSAGEENTITY_URL: str = 'url'
MESSAGEENTITY_EMAIL: str = 'email'
MESSAGEENTITY_BOLD: str = 'bold'
MESSAGEENTITY_ITALIC: str = 'italic'
MESSAGEENTITY_CODE: str = 'code'
MESSAGEENTITY_PRE: str = 'pre'
MESSAGEENTITY_TEXT_LINK: str = 'text_link'
MESSAGEENTITY_TEXT_MENTION: str = 'text_mention'
MESSAGEENTITY_UNDERLINE: str = 'underline'
MESSAGEENTITY_STRIKETHROUGH: str = 'strikethrough'
MESSAGEENTITY_ALL_TYPES: List[str] = [
MESSAGEENTITY_MENTION,
MESSAGEENTITY_HASHTAG,
MESSAGEENTITY_CASHTAG,
MESSAGEENTITY_PHONE_NUMBER,
MESSAGEENTITY_BOT_COMMAND,
MESSAGEENTITY_URL,
MESSAGEENTITY_EMAIL,
MESSAGEENTITY_BOLD,
MESSAGEENTITY_ITALIC,
MESSAGEENTITY_CODE,
MESSAGEENTITY_PRE,
MESSAGEENTITY_TEXT_LINK,
MESSAGEENTITY_TEXT_MENTION,
MESSAGEENTITY_UNDERLINE,
MESSAGEENTITY_STRIKETHROUGH,
]
PARSEMODE_MARKDOWN: str = 'Markdown'
PARSEMODE_MARKDOWN_V2: str = 'MarkdownV2'
PARSEMODE_HTML: str = 'HTML'
POLL_REGULAR: str = 'regular'
POLL_QUIZ: str = 'quiz'
MAX_POLL_QUESTION_LENGTH: int = 300
MAX_POLL_OPTION_LENGTH: int = 100
STICKER_FOREHEAD: str = 'forehead'
STICKER_EYES: str = 'eyes'
STICKER_MOUTH: str = 'mouth'
STICKER_CHIN: str = 'chin'
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env python
# pylint: disable=R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Dice."""
from typing import Any, List, ClassVar
from telegram import TelegramObject, constants
class Dice(TelegramObject):
"""
This object represents an animated emoji with a random value for currently supported base
emoji. (The singular form of "dice" is "die". However, PTB mimics the Telegram API, which uses
the term "dice".)
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`value` and :attr:`emoji` are equal.
Note:
If :attr:`emoji` is "🎯", a value of 6 currently represents a bullseye, while a value of 1
indicates that the dartboard was missed. However, this behaviour is undocumented and might
be changed by Telegram.
If :attr:`emoji` is "🏀", a value of 4 or 5 currently score a basket, while a value of 1 to
3 indicates that the basket was missed. However, this behaviour is undocumented and might
be changed by Telegram.
If :attr:`emoji` is "", a value of 4 to 5 currently scores a goal, while a value of 1 to
3 indicates that the goal was missed. However, this behaviour is undocumented and might
be changed by Telegram.
If :attr:`emoji` is "🎳", a value of 6 knocks all the pins, while a value of 1 means all
the pins were missed. However, this behaviour is undocumented and might be changed by
Telegram.
If :attr:`emoji` is "🎰", each value corresponds to a unique combination of symbols, which
can be found at our `wiki <https://git.io/JkeC6>`_. However, this behaviour is undocumented
and might be changed by Telegram.
Args:
value (:obj:`int`): Value of the dice. 1-6 for dice, darts and bowling balls, 1-5 for
basketball and football/soccer ball, 1-64 for slot machine.
emoji (:obj:`str`): Emoji on which the dice throw animation is based.
Attributes:
value (:obj:`int`): Value of the dice.
emoji (:obj:`str`): Emoji on which the dice throw animation is based.
"""
def __init__(self, value: int, emoji: str, **_kwargs: Any):
self.value = value
self.emoji = emoji
self._id_attrs = (self.value, self.emoji)
DICE: ClassVar[str] = constants.DICE_DICE
""":const:`telegram.constants.DICE_DICE`"""
DARTS: ClassVar[str] = constants.DICE_DARTS
""":const:`telegram.constants.DICE_DARTS`"""
BASKETBALL: ClassVar[str] = constants.DICE_BASKETBALL
""":const:`telegram.constants.DICE_BASKETBALL`"""
FOOTBALL: ClassVar[str] = constants.DICE_FOOTBALL
""":const:`telegram.constants.DICE_FOOTBALL`"""
SLOT_MACHINE: ClassVar[str] = constants.DICE_SLOT_MACHINE
""":const:`telegram.constants.DICE_SLOT_MACHINE`"""
BOWLING: ClassVar[str] = constants.DICE_BOWLING
"""
:const:`telegram.constants.DICE_BOWLING`
.. versionadded:: 13.4
"""
ALL_EMOJI: ClassVar[List[str]] = constants.DICE_ALL_EMOJI
""":const:`telegram.constants.DICE_ALL_EMOJI`"""
+40 -25
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,29 +16,31 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
# pylint: disable=C0115
"""This module contains an object that represents Telegram errors."""
from typing import Tuple
def _lstrip_str(in_s, lstr):
def _lstrip_str(in_s: str, lstr: str) -> str:
"""
Args:
in_s (:obj:`str`): in string
lstr (:obj:`str`): substr to strip from left side
Returns:
str:
:obj:`str`: The stripped string.
"""
if in_s.startswith(lstr):
res = in_s[len(lstr):]
res = in_s[len(lstr) :]
else:
res = in_s
return res
class TelegramError(Exception):
def __init__(self, message):
super(TelegramError, self).__init__()
def __init__(self, message: str):
super().__init__()
msg = _lstrip_str(message, 'Error: ')
msg = _lstrip_str(msg, '[Error]: ')
@@ -48,8 +50,11 @@ class TelegramError(Exception):
msg = msg.capitalize()
self.message = msg
def __str__(self):
return '%s' % (self.message)
def __str__(self) -> str:
return '%s' % self.message
def __reduce__(self) -> Tuple[type, Tuple[str]]:
return self.__class__, (self.message,)
class Unauthorized(TelegramError):
@@ -57,8 +62,11 @@ class Unauthorized(TelegramError):
class InvalidToken(TelegramError):
def __init__(self):
super(InvalidToken, self).__init__('Invalid token')
def __init__(self) -> None:
super().__init__('Invalid token')
def __reduce__(self) -> Tuple[type, Tuple]: # type: ignore[override]
return self.__class__, ()
class NetworkError(TelegramError):
@@ -70,44 +78,51 @@ class BadRequest(NetworkError):
class TimedOut(NetworkError):
def __init__(self):
super(TimedOut, self).__init__('Timed out')
def __init__(self) -> None:
super().__init__('Timed out')
def __reduce__(self) -> Tuple[type, Tuple]: # type: ignore[override]
return self.__class__, ()
class ChatMigrated(TelegramError):
"""
Args:
new_chat_id (:obj:`int`):
new_chat_id (:obj:`int`): The new chat id of the group.
"""
def __init__(self, new_chat_id):
super(ChatMigrated,
self).__init__('Group migrated to supergroup. New chat id: {}'.format(new_chat_id))
def __init__(self, new_chat_id: int):
super().__init__(f'Group migrated to supergroup. New chat id: {new_chat_id}')
self.new_chat_id = new_chat_id
def __reduce__(self) -> Tuple[type, Tuple[int]]: # type: ignore[override]
return self.__class__, (self.new_chat_id,)
class RetryAfter(TelegramError):
"""
Args:
retry_after (:obj:`int`):
retry_after (:obj:`int`): Time in seconds, after which the bot can retry the request.
"""
def __init__(self, retry_after):
super(RetryAfter,
self).__init__('Flood control exceeded. Retry in {} seconds'.format(retry_after))
def __init__(self, retry_after: int):
super().__init__(f'Flood control exceeded. Retry in {float(retry_after)} seconds')
self.retry_after = float(retry_after)
def __reduce__(self) -> Tuple[type, Tuple[float]]: # type: ignore[override]
return self.__class__, (self.retry_after,)
class Conflict(TelegramError):
"""
Raised when a long poll or webhook conflicts with another one.
Raised when a long poll or webhook conflicts with another one.
Args:
msg (:obj:`str`): The message from telegrams server.
Args:
msg (:obj:`str`): The message from telegrams server.
"""
def __init__(self, msg):
super(Conflict, self).__init__(msg)
def __reduce__(self) -> Tuple[type, Tuple[str]]:
return self.__class__, (self.message,)
+42 -9
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -29,7 +29,7 @@ from .updater import Updater
from .callbackqueryhandler import CallbackQueryHandler
from .choseninlineresulthandler import ChosenInlineResultHandler
from .inlinequeryhandler import InlineQueryHandler
from .filters import BaseFilter, Filters
from .filters import BaseFilter, MessageFilter, UpdateFilter, Filters
from .messagehandler import MessageHandler
from .commandhandler import CommandHandler, PrefixHandler
from .regexhandler import RegexHandler
@@ -41,11 +41,44 @@ from .precheckoutqueryhandler import PreCheckoutQueryHandler
from .shippingqueryhandler import ShippingQueryHandler
from .messagequeue import MessageQueue
from .messagequeue import DelayQueue
from .pollanswerhandler import PollAnswerHandler
from .pollhandler import PollHandler
from .chatmemberhandler import ChatMemberHandler
from .defaults import Defaults
__all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
'ChosenInlineResultHandler', 'CommandHandler', 'Handler', 'InlineQueryHandler',
'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler',
'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue',
'DispatcherHandlerStop', 'run_async', 'CallbackContext', 'BasePersistence',
'PicklePersistence', 'DictPersistence', 'PrefixHandler')
__all__ = (
'Dispatcher',
'JobQueue',
'Job',
'Updater',
'CallbackQueryHandler',
'ChosenInlineResultHandler',
'CommandHandler',
'Handler',
'InlineQueryHandler',
'MessageHandler',
'BaseFilter',
'MessageFilter',
'UpdateFilter',
'Filters',
'RegexHandler',
'StringCommandHandler',
'StringRegexHandler',
'TypeHandler',
'ConversationHandler',
'PreCheckoutQueryHandler',
'ShippingQueryHandler',
'MessageQueue',
'DelayQueue',
'DispatcherHandlerStop',
'run_async',
'CallbackContext',
'BasePersistence',
'PicklePersistence',
'DictPersistence',
'PrefixHandler',
'PollAnswerHandler',
'PollHandler',
'ChatMemberHandler',
'Defaults',
)
+305 -44
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -17,64 +17,314 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the BasePersistence class."""
import warnings
from abc import ABC, abstractmethod
from copy import copy
from typing import DefaultDict, Dict, Optional, Tuple, cast, ClassVar
from telegram import Bot
from telegram.utils.types import ConversationDict
class BasePersistence(object):
class BasePersistence(ABC):
"""Interface class for adding persistence to your bot.
Subclass this object for different implementations of a persistent bot.
All relevant methods must be overwritten. This means:
All relevant methods must be overwritten. This includes:
* If :attr:`store_chat_data` is ``True`` you must overwrite :meth:`get_chat_data` and
:meth:`update_chat_data`.
* If :attr:`store_user_data` is ``True`` you must overwrite :meth:`get_user_data` and
:meth:`update_user_data`.
* If you want to store conversation data with :class:`telegram.ext.ConversationHandler`, you
must overwrite :meth:`get_conversations` and :meth:`update_conversation`.
* :meth:`flush` will be called when the bot is shutdown.
* :meth:`get_bot_data`
* :meth:`update_bot_data`
* :meth:`get_chat_data`
* :meth:`update_chat_data`
* :meth:`get_user_data`
* :meth:`update_user_data`
* :meth:`get_conversations`
* :meth:`update_conversation`
* :meth:`flush`
If you don't actually need one of those methods, a simple ``pass`` is enough. For example, if
``store_bot_data=False``, you don't need :meth:`get_bot_data` and :meth:`update_bot_data`.
Warning:
Persistence will try to replace :class:`telegram.Bot` instances by :attr:`REPLACED_BOT` and
insert the bot set with :meth:`set_bot` upon loading of the data. This is to ensure that
changes to the bot apply to the saved objects, too. If you change the bots token, this may
lead to e.g. ``Chat not found`` errors. For the limitations on replacing bots see
:meth:`replace_bot` and :meth:`insert_bot`.
Note:
:meth:`replace_bot` and :meth:`insert_bot` are used *independently* of the implementation
of the :meth:`update/get_*` methods, i.e. you don't need to worry about it while
implementing a custom persistence subclass.
Args:
store_user_data (:obj:`bool`, optional): Whether user_data should be saved by this
persistence class. Default is :obj:`True`.
store_chat_data (:obj:`bool`, optional): Whether chat_data should be saved by this
persistence class. Default is :obj:`True` .
store_bot_data (:obj:`bool`, optional): Whether bot_data should be saved by this
persistence class. Default is :obj:`True` .
Attributes:
store_user_data (:obj:`bool`): Optional, Whether user_data should be saved by this
persistence class.
store_chat_data (:obj:`bool`): Optional. Whether chat_data should be saved by this
persistence class.
Args:
store_user_data (:obj:`bool`, optional): Whether user_data should be saved by this
persistence class. Default is ``True``.
store_chat_data (:obj:`bool`, optional): Whether chat_data should be saved by this
persistence class. Default is ``True`` .
store_bot_data (:obj:`bool`): Optional. Whether bot_data should be saved by this
persistence class.
"""
def __init__(self, store_user_data=True, store_chat_data=True):
def __new__(
cls, *args: object, **kwargs: object # pylint: disable=W0613
) -> 'BasePersistence':
instance = super().__new__(cls)
get_user_data = instance.get_user_data
get_chat_data = instance.get_chat_data
get_bot_data = instance.get_bot_data
update_user_data = instance.update_user_data
update_chat_data = instance.update_chat_data
update_bot_data = instance.update_bot_data
def get_user_data_insert_bot() -> DefaultDict[int, Dict[object, object]]:
return instance.insert_bot(get_user_data())
def get_chat_data_insert_bot() -> DefaultDict[int, Dict[object, object]]:
return instance.insert_bot(get_chat_data())
def get_bot_data_insert_bot() -> Dict[object, object]:
return instance.insert_bot(get_bot_data())
def update_user_data_replace_bot(user_id: int, data: Dict) -> None:
return update_user_data(user_id, instance.replace_bot(data))
def update_chat_data_replace_bot(chat_id: int, data: Dict) -> None:
return update_chat_data(chat_id, instance.replace_bot(data))
def update_bot_data_replace_bot(data: Dict) -> None:
return update_bot_data(instance.replace_bot(data))
instance.get_user_data = get_user_data_insert_bot
instance.get_chat_data = get_chat_data_insert_bot
instance.get_bot_data = get_bot_data_insert_bot
instance.update_user_data = update_user_data_replace_bot
instance.update_chat_data = update_chat_data_replace_bot
instance.update_bot_data = update_bot_data_replace_bot
return instance
def __init__(
self,
store_user_data: bool = True,
store_chat_data: bool = True,
store_bot_data: bool = True,
):
self.store_user_data = store_user_data
self.store_chat_data = store_chat_data
self.store_bot_data = store_bot_data
self.bot: Bot = None # type: ignore[assignment]
def get_user_data(self):
""""Will be called by :class:`telegram.ext.Dispatcher` upon creation with a
persistence object. It should return the user_data if stored, or an empty
def set_bot(self, bot: Bot) -> None:
"""Set the Bot to be used by this persistence instance.
Args:
bot (:class:`telegram.Bot`): The bot.
"""
self.bot = bot
@classmethod
def replace_bot(cls, obj: object) -> object:
"""
Replaces all instances of :class:`telegram.Bot` that occur within the passed object with
:attr:`REPLACED_BOT`. Currently, this handles objects of type ``list``, ``tuple``, ``set``,
``frozenset``, ``dict``, ``defaultdict`` and objects that have a ``__dict__`` or
``__slot__`` attribute, excluding objects that can't be copied with `copy.copy`.
Args:
obj (:obj:`object`): The object
Returns:
:obj:`obj`: Copy of the object with Bot instances replaced.
"""
return cls._replace_bot(obj, {})
@classmethod
def _replace_bot(cls, obj: object, memo: Dict[int, object]) -> object: # pylint: disable=R0911
obj_id = id(obj)
if obj_id in memo:
return memo[obj_id]
if isinstance(obj, Bot):
memo[obj_id] = cls.REPLACED_BOT
return cls.REPLACED_BOT
if isinstance(obj, (list, set)):
# We copy the iterable here for thread safety, i.e. make sure the object we iterate
# over doesn't change its length during the iteration
temp_iterable = obj.copy()
new_iterable = obj.__class__(cls._replace_bot(item, memo) for item in temp_iterable)
memo[obj_id] = new_iterable
return new_iterable
if isinstance(obj, (tuple, frozenset)):
# tuples and frozensets are immutable so we don't need to worry about thread safety
new_immutable = obj.__class__(cls._replace_bot(item, memo) for item in obj)
memo[obj_id] = new_immutable
return new_immutable
try:
new_obj = copy(obj)
memo[obj_id] = new_obj
except Exception:
warnings.warn(
'BasePersistence.replace_bot does not handle objects that can not be copied. See '
'the docs of BasePersistence.replace_bot for more information.',
RuntimeWarning,
)
memo[obj_id] = obj
return obj
if isinstance(obj, dict):
# We handle dicts via copy(obj) so we don't have to make a
# difference between dict and defaultdict
new_obj = cast(dict, new_obj)
# We can't iterate over obj.items() due to thread safety, i.e. the dicts length may
# change during the iteration
temp_dict = new_obj.copy()
new_obj.clear()
for k, val in temp_dict.items():
new_obj[cls._replace_bot(k, memo)] = cls._replace_bot(val, memo)
memo[obj_id] = new_obj
return new_obj
if hasattr(obj, '__dict__'):
for attr_name, attr in new_obj.__dict__.items():
setattr(new_obj, attr_name, cls._replace_bot(attr, memo))
memo[obj_id] = new_obj
return new_obj
if hasattr(obj, '__slots__'):
for attr_name in new_obj.__slots__:
setattr(
new_obj,
attr_name,
cls._replace_bot(cls._replace_bot(getattr(new_obj, attr_name), memo), memo),
)
memo[obj_id] = new_obj
return new_obj
return obj
def insert_bot(self, obj: object) -> object:
"""
Replaces all instances of :attr:`REPLACED_BOT` that occur within the passed object with
:attr:`bot`. Currently, this handles objects of type ``list``, ``tuple``, ``set``,
``frozenset``, ``dict``, ``defaultdict`` and objects that have a ``__dict__`` or
``__slot__`` attribute, excluding objects that can't be copied with `copy.copy`.
Args:
obj (:obj:`object`): The object
Returns:
:obj:`obj`: Copy of the object with Bot instances inserted.
"""
return self._insert_bot(obj, {})
def _insert_bot(self, obj: object, memo: Dict[int, object]) -> object: # pylint: disable=R0911
obj_id = id(obj)
if obj_id in memo:
return memo[obj_id]
if isinstance(obj, Bot):
memo[obj_id] = self.bot
return self.bot
if isinstance(obj, str) and obj == self.REPLACED_BOT:
memo[obj_id] = self.bot
return self.bot
if isinstance(obj, (list, set)):
# We copy the iterable here for thread safety, i.e. make sure the object we iterate
# over doesn't change its length during the iteration
temp_iterable = obj.copy()
new_iterable = obj.__class__(self._insert_bot(item, memo) for item in temp_iterable)
memo[obj_id] = new_iterable
return new_iterable
if isinstance(obj, (tuple, frozenset)):
# tuples and frozensets are immutable so we don't need to worry about thread safety
new_immutable = obj.__class__(self._insert_bot(item, memo) for item in obj)
memo[obj_id] = new_immutable
return new_immutable
try:
new_obj = copy(obj)
except Exception:
warnings.warn(
'BasePersistence.insert_bot does not handle objects that can not be copied. See '
'the docs of BasePersistence.insert_bot for more information.',
RuntimeWarning,
)
memo[obj_id] = obj
return obj
if isinstance(obj, dict):
# We handle dicts via copy(obj) so we don't have to make a
# difference between dict and defaultdict
new_obj = cast(dict, new_obj)
# We can't iterate over obj.items() due to thread safety, i.e. the dicts length may
# change during the iteration
temp_dict = new_obj.copy()
new_obj.clear()
for k, val in temp_dict.items():
new_obj[self._insert_bot(k, memo)] = self._insert_bot(val, memo)
memo[obj_id] = new_obj
return new_obj
if hasattr(obj, '__dict__'):
for attr_name, attr in new_obj.__dict__.items():
setattr(new_obj, attr_name, self._insert_bot(attr, memo))
memo[obj_id] = new_obj
return new_obj
if hasattr(obj, '__slots__'):
for attr_name in obj.__slots__:
setattr(
new_obj,
attr_name,
self._insert_bot(self._insert_bot(getattr(new_obj, attr_name), memo), memo),
)
memo[obj_id] = new_obj
return new_obj
return obj
@abstractmethod
def get_user_data(self) -> DefaultDict[int, Dict[object, object]]:
"""Will be called by :class:`telegram.ext.Dispatcher` upon creation with a
persistence object. It should return the ``user_data`` if stored, or an empty
``defaultdict(dict)``.
Returns:
:obj:`defaultdict`: The restored user data.
"""
raise NotImplementedError
def get_chat_data(self):
""""Will be called by :class:`telegram.ext.Dispatcher` upon creation with a
persistence object. It should return the chat_data if stored, or an empty
@abstractmethod
def get_chat_data(self) -> DefaultDict[int, Dict[object, object]]:
"""Will be called by :class:`telegram.ext.Dispatcher` upon creation with a
persistence object. It should return the ``chat_data`` if stored, or an empty
``defaultdict(dict)``.
Returns:
:obj:`defaultdict`: The restored chat data.
"""
raise NotImplementedError
def get_conversations(self, name):
""""Will be called by :class:`telegram.ext.Dispatcher` when a
@abstractmethod
def get_bot_data(self) -> Dict[object, object]:
"""Will be called by :class:`telegram.ext.Dispatcher` upon creation with a
persistence object. It should return the ``bot_data`` if stored, or an empty
:obj:`dict`.
Returns:
:obj:`dict`: The restored bot data.
"""
@abstractmethod
def get_conversations(self, name: str) -> ConversationDict:
"""Will be called by :class:`telegram.ext.Dispatcher` when a
:class:`telegram.ext.ConversationHandler` is added if
:attr:`telegram.ext.ConversationHandler.persistent` is ``True``.
It should return the conversations for the handler with `name` or an empty ``dict``
:attr:`telegram.ext.ConversationHandler.persistent` is :obj:`True`.
It should return the conversations for the handler with `name` or an empty :obj:`dict`
Args:
name (:obj:`str`): The handlers name.
@@ -82,20 +332,22 @@ class BasePersistence(object):
Returns:
:obj:`dict`: The restored conversations for the handler.
"""
raise NotImplementedError
def update_conversation(self, name, key, new_state):
@abstractmethod
def update_conversation(
self, name: str, key: Tuple[int, ...], new_state: Optional[object]
) -> None:
"""Will be called when a :attr:`telegram.ext.ConversationHandler.update_state`
is called. this allows the storeage of the new state in the persistence.
is called. This allows the storage of the new state in the persistence.
Args:
name (:obj:`str`): The handlers name.
name (:obj:`str`): The handler's name.
key (:obj:`tuple`): The key the state is changed for.
new_state (:obj:`tuple` | :obj:`any`): The new state for the given key.
"""
raise NotImplementedError
def update_user_data(self, user_id, data):
@abstractmethod
def update_user_data(self, user_id: int, data: Dict) -> None:
"""Will be called by the :class:`telegram.ext.Dispatcher` after a handler has
handled an update.
@@ -103,21 +355,30 @@ class BasePersistence(object):
user_id (:obj:`int`): The user the data might have been changed for.
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.user_data` [user_id].
"""
raise NotImplementedError
def update_chat_data(self, chat_id, data):
@abstractmethod
def update_chat_data(self, chat_id: int, data: Dict) -> None:
"""Will be called by the :class:`telegram.ext.Dispatcher` after a handler has
handled an update.
Args:
chat_id (:obj:`int`): The chat the data might have been changed for.
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.chat_data` [user_id].
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.chat_data` [chat_id].
"""
raise NotImplementedError
def flush(self):
"""Will be called by :class:`telegram.ext.Updater` upon receiving a stop signal. Gives the
persistence a chance to finish up saving or close a database connection gracefully. If this
is not of any importance just pass will be sufficient.
@abstractmethod
def update_bot_data(self, data: Dict) -> None:
"""Will be called by the :class:`telegram.ext.Dispatcher` after a handler has
handled an update.
Args:
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.bot_data` .
"""
pass
def flush(self) -> None:
"""Will be called by :class:`telegram.ext.Updater` upon receiving a stop signal. Gives the
persistence a chance to finish up saving or close a database connection gracefully.
"""
REPLACED_BOT: ClassVar[str] = 'bot_instance_replaced_by_ptb_persistence'
""":obj:`str`: Placeholder for :class:`telegram.Bot` instances replaced in saved data."""
+93 -40
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -16,12 +16,19 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
# pylint: disable=R0201
"""This module contains the CallbackContext class."""
from queue import Queue
from typing import TYPE_CHECKING, Dict, List, Match, NoReturn, Optional, Tuple, Union
from telegram import Update
if TYPE_CHECKING:
from telegram import Bot
from telegram.ext import Dispatcher, Job, JobQueue
class CallbackContext(object):
class CallbackContext:
"""
This is a context object passed to the callback called by :class:`telegram.ext.Handler`
or by the :class:`telegram.ext.Dispatcher` in an error handler added by
@@ -38,101 +45,147 @@ class CallbackContext(object):
use a fairly unique name for the attributes.
Warning:
Do not combine custom attributes and @run_async. Due to how @run_async works, it will
Do not combine custom attributes and ``@run_async``/
:meth:`telegram.ext.Disptacher.run_async`. Due to how ``run_async`` works, it will
almost certainly execute the callbacks for an update out of order, and the attributes
that you think you added will not be present.
Attributes:
chat_data (:obj:`dict`, optional): A dict that can be used to keep any data in. For each
update from the same chat it will be the same ``dict``.
user_data (:obj:`dict`, optional): A dict that can be used to keep any data in. For each
bot_data (:obj:`dict`): Optional. A dict that can be used to keep any data in. For each
update it will be the same ``dict``.
chat_data (:obj:`dict`): Optional. A dict that can be used to keep any data in. For each
update from the same chat id it will be the same ``dict``.
Warning:
When a group chat migrates to a supergroup, its chat id will change and the
``chat_data`` needs to be transferred. For details see our `wiki page
<https://github.com/python-telegram-bot/python-telegram-bot/wiki/
Storing-user--and-chat-related-data#chat-migration>`_.
user_data (:obj:`dict`): Optional. A dict that can be used to keep any data in. For each
update from the same user it will be the same ``dict``.
matches (List[:obj:`re match object`], optional): If the associated update originated from
matches (List[:obj:`re match object`]): Optional. If the associated update originated from
a regex-supported handler or had a :class:`Filters.regex`, this will contain a list of
match objects for every pattern where ``re.search(pattern, string)`` returned a match.
Note that filters short circuit, so combined regex filters will not always
be evaluated.
args (List[:obj:`str`], optional): Arguments passed to a command if the associated update
args (List[:obj:`str`]): Optional. Arguments passed to a command if the associated update
is handled by :class:`telegram.ext.CommandHandler`, :class:`telegram.ext.PrefixHandler`
or :class:`telegram.ext.StringCommandHandler`. It contains a list of the words in the
text after the command, using any whitespace string as a delimiter.
error (:class:`telegram.TelegramError`, optional): The Telegram error that was raised.
Only present when passed to a error handler registered with
:attr:`telegram.ext.Dispatcher.add_error_handler`.
job (:class:`telegram.ext.Job`): The job that that originated this callback.
error (:obj:`Exception`): Optional. The error that was raised. Only present when passed
to a error handler registered with :attr:`telegram.ext.Dispatcher.add_error_handler`.
async_args (List[:obj:`object`]): Optional. Positional arguments of the function that
raised the error. Only present when the raising function was run asynchronously using
:meth:`telegram.ext.Dispatcher.run_async`.
async_kwargs (Dict[:obj:`str`, :obj:`object`]): Optional. Keyword arguments of the function
that raised the error. Only present when the raising function was run asynchronously
using :meth:`telegram.ext.Dispatcher.run_async`.
job (:class:`telegram.ext.Job`): Optional. The job which originated this callback.
Only present when passed to the callback of :class:`telegram.ext.Job`.
"""
def __init__(self, dispatcher):
def __init__(self, dispatcher: 'Dispatcher'):
"""
Args:
dispatcher (:class:`telegram.ext.Dispatcher`):
"""
if not dispatcher.use_context:
raise ValueError('CallbackContext should not be used with a non context aware '
'dispatcher!')
raise ValueError(
'CallbackContext should not be used with a non context aware ' 'dispatcher!'
)
self._dispatcher = dispatcher
self._chat_data = None
self._user_data = None
self.args = None
self.matches = None
self.error = None
self.job = None
self._bot_data = dispatcher.bot_data
self._chat_data: Optional[Dict[object, object]] = None
self._user_data: Optional[Dict[object, object]] = None
self.args: Optional[List[str]] = None
self.matches: Optional[List[Match]] = None
self.error: Optional[Exception] = None
self.job: Optional['Job'] = None
self.async_args: Optional[Union[List, Tuple]] = None
self.async_kwargs: Optional[Dict[str, object]] = None
@property
def chat_data(self):
def dispatcher(self) -> 'Dispatcher':
""":class:`telegram.ext.Dispatcher`: The dispatcher associated with this context."""
return self._dispatcher
@property
def bot_data(self) -> Dict:
return self._bot_data
@bot_data.setter
def bot_data(self, value: object) -> NoReturn:
raise AttributeError(
"You can not assign a new value to bot_data, see https://git.io/Jt6ic"
)
@property
def chat_data(self) -> Optional[Dict]:
return self._chat_data
@chat_data.setter
def chat_data(self, value):
raise AttributeError("You can not assign a new value to chat_data, see "
"https://git.io/fjxKe")
def chat_data(self, value: object) -> NoReturn:
raise AttributeError(
"You can not assign a new value to chat_data, see https://git.io/Jt6ic"
)
@property
def user_data(self):
def user_data(self) -> Optional[Dict]:
return self._user_data
@user_data.setter
def user_data(self, value):
raise AttributeError("You can not assign a new value to user_data, see "
"https://git.io/fjxKe")
def user_data(self, value: object) -> NoReturn:
raise AttributeError(
"You can not assign a new value to user_data, see https://git.io/Jt6ic"
)
@classmethod
def from_error(cls, update, error, dispatcher):
def from_error(
cls,
update: object,
error: Exception,
dispatcher: 'Dispatcher',
async_args: Union[List, Tuple] = None,
async_kwargs: Dict[str, object] = None,
) -> 'CallbackContext':
self = cls.from_update(update, dispatcher)
self.error = error
self.async_args = async_args
self.async_kwargs = async_kwargs
return self
@classmethod
def from_update(cls, update, dispatcher):
def from_update(cls, update: object, dispatcher: 'Dispatcher') -> 'CallbackContext':
self = cls(dispatcher)
if update is not None and isinstance(update, Update):
chat = update.effective_chat
user = update.effective_user
if chat:
self._chat_data = dispatcher.chat_data[chat.id]
self._chat_data = dispatcher.chat_data[chat.id] # pylint: disable=W0212
if user:
self._user_data = dispatcher.user_data[user.id]
self._user_data = dispatcher.user_data[user.id] # pylint: disable=W0212
return self
@classmethod
def from_job(cls, job, dispatcher):
def from_job(cls, job: 'Job', dispatcher: 'Dispatcher') -> 'CallbackContext':
self = cls(dispatcher)
self.job = job
return self
def update(self, data):
def update(self, data: Dict[str, object]) -> None:
self.__dict__.update(data)
@property
def bot(self):
def bot(self) -> 'Bot':
""":class:`telegram.Bot`: The bot associated with this context."""
return self._dispatcher.bot
@property
def job_queue(self):
def job_queue(self) -> Optional['JobQueue']:
"""
:class:`telegram.ext.JobQueue`: The ``JobQueue`` used by the
:class:`telegram.ext.Dispatcher` and (usually) the :class:`telegram.ext.Updater`
@@ -142,7 +195,7 @@ class CallbackContext(object):
return self._dispatcher.job_queue
@property
def update_queue(self):
def update_queue(self) -> Queue:
"""
:class:`queue.Queue`: The ``Queue`` instance used by the
:class:`telegram.ext.Dispatcher` and (usually) the :class:`telegram.ext.Updater`
@@ -152,13 +205,13 @@ class CallbackContext(object):
return self._dispatcher.update_queue
@property
def match(self):
def match(self) -> Optional[Match[str]]:
"""
`Regex match type`: The first match from :attr:`matches`.
Useful if you are only filtering using a single regex filter.
Returns `None` if :attr:`matches` is empty.
"""
try:
return self.matches[0] # pylint: disable=unsubscriptable-object
return self.matches[0] # type: ignore[index] # pylint: disable=unsubscriptable-object
except (IndexError, TypeError):
return None
+109 -69
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,18 +19,86 @@
"""This module contains the CallbackQueryHandler class."""
import re
from future.utils import string_types
from typing import (
TYPE_CHECKING,
Callable,
Dict,
Match,
Optional,
Pattern,
TypeVar,
Union,
cast,
)
from telegram import Update
from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE
from .handler import Handler
if TYPE_CHECKING:
from telegram.ext import CallbackContext, Dispatcher
class CallbackQueryHandler(Handler):
RT = TypeVar('RT')
class CallbackQueryHandler(Handler[Update]):
"""Handler class to handle Telegram callback queries. Optionally based on a regex.
Read the documentation of the ``re`` module for more information.
Note:
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
can use to keep any data in will be sent to the :attr:`callback` function. Related to
either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/fxJuV for more info.
Warning:
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info.
Args:
callback (:obj:`callable`): The callback function for this handler. Will be called when
:attr:`check_update` has determined that an update should be processed by this handler.
Callback signature for context based API:
``def callback(update: Update, context: CallbackContext)``
The return value of the callback is usually ignored except for the special case of
:class:`telegram.ext.ConversationHandler`.
pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher`
that contains new updates which can be used to insert updates. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pattern (:obj:`str` | `Pattern`, optional): Regex pattern. If not :obj:`None`, ``re.match``
is used on :attr:`telegram.CallbackQuery.data` to determine if an update should be
handled by this handler.
pass_groups (:obj:`bool`, optional): If the callback should be passed the result of
``re.match(pattern, data).groups()`` as a keyword argument called ``groups``.
Default is :obj:`False`
DEPRECATED: Please switch to context based callbacks.
pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of
``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``.
Default is :obj:`False`
DEPRECATED: Please switch to context based callbacks.
pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``user_data`` will be passed to the callback function. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``chat_data`` will be passed to the callback function. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
run_async (:obj:`bool`): Determines whether the callback will run asynchronously.
Defaults to :obj:`False`.
Attributes:
callback (:obj:`callable`): The callback function for this handler.
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
@@ -47,83 +115,43 @@ class CallbackQueryHandler(Handler):
the callback function.
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
Note:
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
can use to keep any data in will be sent to the :attr:`callback` function. Related to
either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/fxJuV for more info.
Args:
callback (:obj:`callable`): The callback function for this handler. Will be called when
:attr:`check_update` has determined that an update should be processed by this handler.
Callback signature for context based API:
``def callback(update: Update, context: CallbackContext)``
The return value of the callback is usually ignored except for the special case of
:class:`telegram.ext.ConversationHandler`.
pass_update_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher`
that contains new updates which can be used to insert updates. Default is ``False``.
DEPRECATED: Please switch to context based callbacks.
pass_job_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``.
DEPRECATED: Please switch to context based callbacks.
pattern (:obj:`str` | `Pattern`, optional): Regex pattern. If not ``None``, ``re.match``
is used on :attr:`telegram.CallbackQuery.data` to determine if an update should be
handled by this handler.
pass_groups (:obj:`bool`, optional): If the callback should be passed the result of
``re.match(pattern, data).groups()`` as a keyword argument called ``groups``.
Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of
``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``.
Default is ``False``
DEPRECATED: Please switch to context based callbacks.
pass_user_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. Default is ``False``.
DEPRECATED: Please switch to context based callbacks.
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. Default is ``False``.
DEPRECATED: Please switch to context based callbacks.
run_async (:obj:`bool`): Determines whether the callback will run asynchronously.
"""
def __init__(self,
callback,
pass_update_queue=False,
pass_job_queue=False,
pattern=None,
pass_groups=False,
pass_groupdict=False,
pass_user_data=False,
pass_chat_data=False):
super(CallbackQueryHandler, self).__init__(
def __init__(
self,
callback: Callable[[Update, 'CallbackContext'], RT],
pass_update_queue: bool = False,
pass_job_queue: bool = False,
pattern: Union[str, Pattern] = None,
pass_groups: bool = False,
pass_groupdict: bool = False,
pass_user_data: bool = False,
pass_chat_data: bool = False,
run_async: Union[bool, DefaultValue] = DEFAULT_FALSE,
):
super().__init__(
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
pass_chat_data=pass_chat_data,
run_async=run_async,
)
if isinstance(pattern, string_types):
if isinstance(pattern, str):
pattern = re.compile(pattern)
self.pattern = pattern
self.pass_groups = pass_groups
self.pass_groupdict = pass_groupdict
def check_update(self, update):
def check_update(self, update: object) -> Optional[Union[bool, object]]:
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
update (:class:`telegram.Update` | :obj:`object`): Incoming update.
Returns:
:obj:`bool`
@@ -137,18 +165,30 @@ class CallbackQueryHandler(Handler):
return match
else:
return True
return None
def collect_optional_args(self, dispatcher, update=None, check_result=None):
optional_args = super(CallbackQueryHandler, self).collect_optional_args(dispatcher,
update,
check_result)
def collect_optional_args(
self,
dispatcher: 'Dispatcher',
update: Update = None,
check_result: Union[bool, Match] = None,
) -> Dict[str, object]:
optional_args = super().collect_optional_args(dispatcher, update, check_result)
if self.pattern:
check_result = cast(Match, check_result)
if self.pass_groups:
optional_args['groups'] = check_result.groups()
if self.pass_groupdict:
optional_args['groupdict'] = check_result.groupdict()
return optional_args
def collect_additional_context(self, context, update, dispatcher, check_result):
def collect_additional_context(
self,
context: 'CallbackContext',
update: Update,
dispatcher: 'Dispatcher',
check_result: Union[bool, Match],
) -> None:
if self.pattern:
check_result = cast(Match, check_result)
context.matches = [check_result]
+146
View File
@@ -0,0 +1,146 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2019-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the ChatMemberHandler classes."""
from typing import ClassVar, TypeVar, Union, Callable, TYPE_CHECKING
from telegram import Update
from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE
from .handler import Handler
if TYPE_CHECKING:
from telegram.ext import CallbackContext
RT = TypeVar('RT')
class ChatMemberHandler(Handler[Update]):
"""Handler class to handle Telegram updates that contain a chat member update.
.. versionadded:: 13.4
Note:
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
can use to keep any data in will be sent to the :attr:`callback` function. Related to
either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/fxJuV for more info.
Warning:
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info.
Args:
callback (:obj:`callable`): The callback function for this handler. Will be called when
:attr:`check_update` has determined that an update should be processed by this handler.
Callback signature for context based API:
``def callback(update: Update, context: CallbackContext)``
The return value of the callback is usually ignored except for the special case of
:class:`telegram.ext.ConversationHandler`.
chat_member_types (:obj:`int`, optional): Pass one of :attr:`MY_CHAT_MEMBER`,
:attr:`CHAT_MEMBER` or :attr:`ANY_CHAT_MEMBER` to specify if this handler should handle
only updates with :attr:`telegram.Update.my_chat_member`,
:attr:`telegram.Update.chat_member` or both. Defaults to :attr:`MY_CHAT_MEMBER`.
pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher`
that contains new updates which can be used to insert updates. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``user_data`` will be passed to the callback function. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``chat_data`` will be passed to the callback function. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
run_async (:obj:`bool`): Determines whether the callback will run asynchronously.
Defaults to :obj:`False`.
Attributes:
callback (:obj:`callable`): The callback function for this handler.
chat_member_types (:obj:`int`, optional): Specifies if this handler should handle
only updates with :attr:`telegram.Update.my_chat_member`,
:attr:`telegram.Update.chat_member` or both.
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
the callback function.
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
run_async (:obj:`bool`): Determines whether the callback will run asynchronously.
"""
MY_CHAT_MEMBER: ClassVar[int] = -1
""":obj:`int`: Used as a constant to handle only :attr:`telegram.Update.my_chat_member`."""
CHAT_MEMBER: ClassVar[int] = 0
""":obj:`int`: Used as a constant to handle only :attr:`telegram.Update.chat_member`."""
ANY_CHAT_MEMBER: ClassVar[int] = 1
""":obj:`int`: Used as a constant to handle bot :attr:`telegram.Update.my_chat_member`
and :attr:`telegram.Update.chat_member`."""
def __init__(
self,
callback: Callable[[Update, 'CallbackContext'], RT],
chat_member_types: int = MY_CHAT_MEMBER,
pass_update_queue: bool = False,
pass_job_queue: bool = False,
pass_user_data: bool = False,
pass_chat_data: bool = False,
run_async: Union[bool, DefaultValue] = DEFAULT_FALSE,
):
super().__init__(
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data,
run_async=run_async,
)
self.chat_member_types = chat_member_types
def check_update(self, update: object) -> bool:
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update` | :obj:`object`): Incoming update.
Returns:
:obj:`bool`
"""
if isinstance(update, Update):
if not (update.my_chat_member or update.chat_member):
return False
if self.chat_member_types == self.ANY_CHAT_MEMBER:
return True
if self.chat_member_types == self.CHAT_MEMBER:
return bool(update.chat_member)
return bool(update.my_chat_member)
return False
+51 -39
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -18,13 +18,59 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the ChosenInlineResultHandler class."""
from typing import Optional, TypeVar, Union
from telegram import Update
from .handler import Handler
RT = TypeVar('RT')
class ChosenInlineResultHandler(Handler):
class ChosenInlineResultHandler(Handler[Update]):
"""Handler class to handle Telegram updates that contain a chosen inline result.
Note:
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
can use to keep any data in will be sent to the :attr:`callback` function. Related to
either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/fxJuV for more info.
Warning:
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info.
Args:
callback (:obj:`callable`): The callback function for this handler. Will be called when
:attr:`check_update` has determined that an update should be processed by this handler.
Callback signature for context based API:
``def callback(update: Update, context: CallbackContext)``
The return value of the callback is usually ignored except for the special case of
:class:`telegram.ext.ConversationHandler`.
pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher`
that contains new updates which can be used to insert updates. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``user_data`` will be passed to the callback function. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``chat_data`` will be passed to the callback function. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
run_async (:obj:`bool`): Determines whether the callback will run asynchronously.
Defaults to :obj:`False`.
Attributes:
callback (:obj:`callable`): The callback function for this handler.
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
@@ -35,49 +81,15 @@ class ChosenInlineResultHandler(Handler):
the callback function.
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
Note:
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
can use to keep any data in will be sent to the :attr:`callback` function. Related to
either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/fxJuV for more info.
Args:
callback (:obj:`callable`): The callback function for this handler. Will be called when
:attr:`check_update` has determined that an update should be processed by this handler.
Callback signature for context based API:
``def callback(update: Update, context: CallbackContext)``
The return value of the callback is usually ignored except for the special case of
:class:`telegram.ext.ConversationHandler`.
pass_update_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher`
that contains new updates which can be used to insert updates. Default is ``False``.
DEPRECATED: Please switch to context based callbacks.
pass_job_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``.
DEPRECATED: Please switch to context based callbacks.
pass_user_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. Default is ``False``.
DEPRECATED: Please switch to context based callbacks.
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. Default is ``False``.
DEPRECATED: Please switch to context based callbacks.
run_async (:obj:`bool`): Determines whether the callback will run asynchronously.
"""
def check_update(self, update):
def check_update(self, update: object) -> Optional[Union[bool, object]]:
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
update (:class:`telegram.Update` | :obj:`object`): Incoming update.
Returns:
:obj:`bool`
+227 -123
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2021
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,17 +19,23 @@
"""This module contains the CommandHandler and PrefixHandler classes."""
import re
import warnings
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple, TypeVar, Union
from future.utils import string_types
from telegram.ext import Filters
from telegram import MessageEntity, Update
from telegram.ext import BaseFilter, Filters
from telegram.utils.deprecate import TelegramDeprecationWarning
from telegram.utils.types import SLT
from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE
from telegram import Update, MessageEntity
from .handler import Handler
if TYPE_CHECKING:
from telegram.ext import CallbackContext, Dispatcher
class CommandHandler(Handler):
RT = TypeVar('RT')
class CommandHandler(Handler[Update]):
"""Handler class to handle Telegram commands.
Commands are Telegram messages that start with ``/``, optionally followed by an ``@`` and the
@@ -40,39 +46,26 @@ class CommandHandler(Handler):
By default the handler listens to messages as well as edited messages. To change this behavior
use ``~Filters.update.edited_message`` in the filter argument.
Attributes:
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
should listen for. Limitations are the same as described here
https://core.telegram.org/bots#commands
callback (:obj:`callable`): The callback function for this handler.
filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these
Filters.
allow_edited (:obj:`bool`): Determines Whether the handler should also accept
edited messages.
pass_args (:obj:`bool`): Determines whether the handler should be passed
``args``.
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
the callback function.
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
Note:
:class:`telegram.ext.CommandHandler` does *not* handle (edited) channel posts.
Note:
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a :obj:`dict` you
can use to keep any data in will be sent to the :attr:`callback` function. Related to
either the user or the chat that the update was sent in. For each update from the same user
or in the same chat, it will be the same ``dict``.
or in the same chat, it will be the same :obj:`dict`.
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/fxJuV for more info.
Warning:
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info.
Args:
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
should listen for. Limitations are the same as described here
https://core.telegram.org/bots#commands
command (:class:`telegram.utils.types.SLT[str]`):
The command or list of commands this handler should listen for.
Limitations are the same as described here https://core.telegram.org/bots#commands
callback (:obj:`callable`): The callback function for this handler. Will be called when
:attr:`check_update` has determined that an update should be processed by this handler.
Callback signature for context based API:
@@ -86,53 +79,81 @@ class CommandHandler(Handler):
:class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise
operators (& for and, | for or, ~ for not).
allow_edited (:obj:`bool`, optional): Determines whether the handler should also accept
edited messages. Default is ``False``.
edited messages. Default is :obj:`False`.
DEPRECATED: Edited is allowed by default. To change this behavior use
``~Filters.update.edited_message``.
pass_args (:obj:`bool`, optional): Determines whether the handler should be passed the
arguments passed to the command as a keyword argument called ``args``. It will contain
a list of strings, which is the text following the command split on single or
consecutive whitespace characters. Default is ``False``
consecutive whitespace characters. Default is :obj:`False`
DEPRECATED: Please switch to context based callbacks.
pass_update_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher`
that contains new updates which can be used to insert updates. Default is ``False``.
that contains new updates which can be used to insert updates. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_job_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``.
which can be used to schedule new jobs. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_user_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. Default is ``False``.
pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``user_data`` will be passed to the callback function. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. Default is ``False``.
pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``chat_data`` will be passed to the callback function. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
run_async (:obj:`bool`): Determines whether the callback will run asynchronously.
Defaults to :obj:`False`.
Raises:
ValueError - when command is too long or has illegal chars.
Attributes:
command (:class:`telegram.utils.types.SLT[str]`):
The command or list of commands this handler should listen for.
Limitations are the same as described here https://core.telegram.org/bots#commands
callback (:obj:`callable`): The callback function for this handler.
filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these
Filters.
allow_edited (:obj:`bool`): Determines whether the handler should also accept
edited messages.
pass_args (:obj:`bool`): Determines whether the handler should be passed
``args``.
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
the callback function.
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
run_async (:obj:`bool`): Determines whether the callback will run asynchronously.
"""
def __init__(self,
command,
callback,
filters=None,
allow_edited=None,
pass_args=False,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(CommandHandler, self).__init__(
def __init__(
self,
command: SLT[str],
callback: Callable[[Update, 'CallbackContext'], RT],
filters: BaseFilter = None,
allow_edited: bool = None,
pass_args: bool = False,
pass_update_queue: bool = False,
pass_job_queue: bool = False,
pass_user_data: bool = False,
pass_chat_data: bool = False,
run_async: Union[bool, DefaultValue] = DEFAULT_FALSE,
):
super().__init__(
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
pass_chat_data=pass_chat_data,
run_async=run_async,
)
if isinstance(command, string_types):
if isinstance(command, str):
self.command = [command.lower()]
else:
self.command = [x.lower() for x in command]
@@ -146,53 +167,76 @@ class CommandHandler(Handler):
self.filters = Filters.update.messages
if allow_edited is not None:
warnings.warn('allow_edited is deprecated. See https://git.io/fxJuV for more info',
TelegramDeprecationWarning,
stacklevel=2)
warnings.warn(
'allow_edited is deprecated. See https://git.io/fxJuV for more info',
TelegramDeprecationWarning,
stacklevel=2,
)
if not allow_edited:
self.filters &= ~Filters.update.edited_message
self.pass_args = pass_args
def check_update(self, update):
def check_update(
self, update: object
) -> Optional[Union[bool, Tuple[List[str], Optional[Union[bool, Dict]]]]]:
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
update (:class:`telegram.Update` | :obj:`object`): Incoming update.
Returns:
:obj:`list`: The list of args for the handler
:obj:`list`: The list of args for the handler.
"""
if isinstance(update, Update) and update.effective_message:
message = update.effective_message
if (message.entities and message.entities[0].type == MessageEntity.BOT_COMMAND
and message.entities[0].offset == 0):
command = message.text[1:message.entities[0].length]
if (
message.entities
and message.entities[0].type == MessageEntity.BOT_COMMAND
and message.entities[0].offset == 0
and message.text
and message.bot
):
command = message.text[1 : message.entities[0].length]
args = message.text.split()[1:]
command = command.split('@')
command.append(message.bot.username)
command_parts = command.split('@')
command_parts.append(message.bot.username)
if not (command[0].lower() in self.command
and command[1].lower() == message.bot.username.lower()):
if not (
command_parts[0].lower() in self.command
and command_parts[1].lower() == message.bot.username.lower()
):
return None
filter_result = self.filters(update)
if filter_result:
return args, filter_result
else:
return False
return False
return None
def collect_optional_args(self, dispatcher, update=None, check_result=None):
optional_args = super(CommandHandler, self).collect_optional_args(dispatcher, update)
if self.pass_args:
def collect_optional_args(
self,
dispatcher: 'Dispatcher',
update: Update = None,
check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]] = None,
) -> Dict[str, object]:
optional_args = super().collect_optional_args(dispatcher, update)
if self.pass_args and isinstance(check_result, tuple):
optional_args['args'] = check_result[0]
return optional_args
def collect_additional_context(self, context, update, dispatcher, check_result):
context.args = check_result[0]
if isinstance(check_result[1], dict):
context.update(check_result[1])
def collect_additional_context(
self,
context: 'CallbackContext',
update: Update,
dispatcher: 'Dispatcher',
check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]],
) -> None:
if isinstance(check_result, tuple):
context.args = check_result[0]
if isinstance(check_result[1], dict):
context.update(check_result[1])
class PrefixHandler(CommandHandler):
@@ -215,7 +259,7 @@ class PrefixHandler(CommandHandler):
PrefixHandler(['!', '#'], 'test', callback) will respond to '!test' and
'#test'.
Miltiple prefixes and commands:
Multiple prefixes and commands:
PrefixHandler(['!', '#'], ['test', 'help`], callback) will respond to '!test',
'#test', '!help' and '#help'.
@@ -225,9 +269,6 @@ class PrefixHandler(CommandHandler):
use ~``Filters.update.edited_message``.
Attributes:
prefix (:obj:`str` | List[:obj:`str`]): The prefix(es) that will precede :attr:`command`.
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
should listen for.
callback (:obj:`callable`): The callback function for this handler.
filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these
Filters.
@@ -241,6 +282,7 @@ class PrefixHandler(CommandHandler):
the callback function.
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
the callback function.
run_async (:obj:`bool`): Determines whether the callback will run asynchronously.
Note:
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
@@ -251,10 +293,15 @@ class PrefixHandler(CommandHandler):
Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/fxJuV for more info.
Warning:
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info.
Args:
prefix (:obj:`str` | List[:obj:`str`]): The prefix(es) that will precede :attr:`command`.
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
should listen for.
prefix (:class:`telegram.utils.types.SLT[str]`):
The prefix(es) that will precede :attr:`command`.
command (:class:`telegram.utils.types.SLT[str]`):
The command or list of commands this handler should listen for.
callback (:obj:`callable`): The callback function for this handler. Will be called when
:attr:`check_update` has determined that an update should be processed by this handler.
Callback signature for context based API:
@@ -270,63 +317,113 @@ class PrefixHandler(CommandHandler):
pass_args (:obj:`bool`, optional): Determines whether the handler should be passed the
arguments passed to the command as a keyword argument called ``args``. It will contain
a list of strings, which is the text following the command split on single or
consecutive whitespace characters. Default is ``False``
consecutive whitespace characters. Default is :obj:`False`
DEPRECATED: Please switch to context based callbacks.
pass_update_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher`
that contains new updates which can be used to insert updates. Default is ``False``.
that contains new updates which can be used to insert updates. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_job_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
which can be used to schedule new jobs. Default is ``False``.
which can be used to schedule new jobs. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_user_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. Default is ``False``.
pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``user_data`` will be passed to the callback function. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. Default is ``False``.
pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
``chat_data`` will be passed to the callback function. Default is :obj:`False`.
DEPRECATED: Please switch to context based callbacks.
run_async (:obj:`bool`): Determines whether the callback will run asynchronously.
Defaults to :obj:`False`.
"""
def __init__(self,
prefix,
command,
callback,
filters=None,
pass_args=False,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
def __init__(
self,
prefix: SLT[str],
command: SLT[str],
callback: Callable[[Update, 'CallbackContext'], RT],
filters: BaseFilter = None,
pass_args: bool = False,
pass_update_queue: bool = False,
pass_job_queue: bool = False,
pass_user_data: bool = False,
pass_chat_data: bool = False,
run_async: Union[bool, DefaultValue] = DEFAULT_FALSE,
):
super(PrefixHandler, self).__init__(
'nocommand', callback, filters=filters, allow_edited=None, pass_args=pass_args,
self._prefix: List[str] = list()
self._command: List[str] = list()
self._commands: List[str] = list()
super().__init__(
'nocommand',
callback,
filters=filters,
allow_edited=None,
pass_args=pass_args,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
pass_chat_data=pass_chat_data,
run_async=run_async,
)
if isinstance(prefix, string_types):
self.prefix = [prefix.lower()]
else:
self.prefix = prefix
if isinstance(command, string_types):
self.command = [command.lower()]
else:
self.command = command
self.command = [x.lower() + y.lower() for x in self.prefix for y in self.command]
self.prefix = prefix # type: ignore[assignment]
self.command = command # type: ignore[assignment]
self._build_commands()
def check_update(self, update):
@property
def prefix(self) -> List[str]:
"""
The prefixes that will precede :attr:`command`.
Returns:
List[:obj:`str`]
"""
return self._prefix
@prefix.setter
def prefix(self, prefix: Union[str, List[str]]) -> None:
if isinstance(prefix, str):
self._prefix = [prefix.lower()]
else:
self._prefix = prefix
self._build_commands()
@property # type: ignore[override]
def command(self) -> List[str]: # type: ignore[override]
"""
The list of commands this handler should listen for.
Returns:
List[:obj:`str`]
"""
return self._command
@command.setter
def command(self, command: Union[str, List[str]]) -> None:
if isinstance(command, str):
self._command = [command.lower()]
else:
self._command = command
self._build_commands()
def _build_commands(self) -> None:
self._commands = [x.lower() + y.lower() for x in self.prefix for y in self.command]
def check_update(
self, update: object
) -> Optional[Union[bool, Tuple[List[str], Optional[Union[bool, Dict]]]]]:
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
update (:class:`telegram.Update` | :obj:`object`): Incoming update.
Returns:
:obj:`list`: The list of args for the handler
:obj:`list`: The list of args for the handler.
"""
if isinstance(update, Update) and update.effective_message:
@@ -334,15 +431,22 @@ class PrefixHandler(CommandHandler):
if message.text:
text_list = message.text.split()
if text_list[0].lower() not in self.command:
if text_list[0].lower() not in self._commands:
return None
filter_result = self.filters(update)
if filter_result:
return text_list[1:], filter_result
else:
return False
return False
return None
def collect_additional_context(self, context, update, dispatcher, check_result):
context.args = check_result[0]
if isinstance(check_result[1], dict):
context.update(check_result[1])
def collect_additional_context(
self,
context: 'CallbackContext',
update: Update,
dispatcher: 'Dispatcher',
check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]],
) -> None:
if isinstance(check_result, tuple):
context.args = check_result[0]
if isinstance(check_result[1], dict):
context.update(check_result[1])

Some files were not shown because too many files have changed in this diff Show More