Compare commits

...

94 Commits

Author SHA1 Message Date
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
272 changed files with 7819 additions and 2174 deletions
+12 -2
View File
@@ -43,6 +43,8 @@ If you have an idea for something to do, first check if it's already been filed
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.
That being said, we want to mention that we are very hesistant 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
#####################################
@@ -111,6 +113,14 @@ Here's how to make a one-off code change.
$ pytest -v
To run ``test_official`` (particularly useful if you made API changes), run
.. code-block::
$ export TEST_OFFICIAL=true
prior to running the tests.
- To actually make the commit (this will trigger tests for yapf, lint and pep8 automatically):
.. code-block:: bash
@@ -238,6 +248,6 @@ break the API classes. For example:
.. _`developers' mailing list`: mailto:devs@python-telegram-bot.org
.. _`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
+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
+2 -2
View File
@@ -2,7 +2,7 @@
name: Question
about: Get help with errors or general questions
title: "[QUESTION]"
labels: 'question :question:'
labels: question
assignees: ''
---
@@ -10,7 +10,7 @@ assignees: ''
<!--
Hey there, you have a question? We are happy to answer. Please make sure no similar question was opened already.
The following template is a suggestion how you can report an issue you run into whilst using our library. If you just want to ask a question, feel free to delete everything; just make sure you have a describing title :)
To make it easier for us to help you, please 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.
-->
+14
View File
@@ -0,0 +1,14 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 5
# 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.
+9 -6
View File
@@ -1,10 +1,13 @@
name: Testing your PR
name: GitHub Actions
on:
pull_request:
branches:
- master
schedule:
- cron: 7 3 * * *
push:
branches:
- master
jobs:
pytest:
@@ -12,7 +15,7 @@ jobs:
runs-on: ${{matrix.os}}
strategy:
matrix:
python-version: [2.7, 3.5, 3.6, 3.7]
python-version: [3.5, 3.6, 3.7, 3.8]
os: [ubuntu-latest, windows-latest]
include:
- os: ubuntu-latest
@@ -23,7 +26,7 @@ jobs:
test-build: True
fail-fast: False
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: Initialize vendored libs
run:
git submodule update --init --recursive
@@ -48,7 +51,7 @@ jobs:
exit ${global_exit}
env:
JOB_INDEX: ${{ strategy.job-index }}
BOTS: W3sidG9rZW4iOiAiNjk2MTg4NzMyOkFBR1Z3RUtmSEhsTmpzY3hFRE5LQXdraEdzdFpfa28xbUMwIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WldGaU1UUmxNbVF5TnpNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMi43IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzI3X2JvdCJ9LCB7InRva2VuIjogIjY3MTQ2ODg4NjpBQUdQR2ZjaVJJQlVORmU4MjR1SVZkcTdKZTNfWW5BVE5HdyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpHWXdPVGxrTXpNeE4yWTIiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNCIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zNF9ib3QifSwgeyJ0b2tlbiI6ICI2MjkzMjY1Mzg6QUFGUnJaSnJCN29CM211ekdzR0pYVXZHRTVDUXpNNUNVNG8iLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpNbU01WVdKaFl6a3hNMlUxIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgQ1B5dGhvbiAzLjUiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX2NweXRob25fMzVfYm90In0sIHsidG9rZW4iOiAiNjQwMjA4OTQzOkFBRmhCalFwOXFtM1JUeFN6VXBZekJRakNsZS1Kano1aGNrIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WXpoa1pUZzFOamMxWXpWbCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMy42IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzM2X2JvdCJ9LCB7InRva2VuIjogIjY5NTEwNDA4ODpBQUhmenlsSU9qU0lJUy1lT25JMjB5MkUyMEhvZEhzZnotMCIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOk9HUTFNRGd3WmpJd1pqRmwiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNyIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zN19ib3QifSwgeyJ0b2tlbiI6ICI2OTE0MjM1NTQ6QUFGOFdrakNaYm5IcVBfaTZHaFRZaXJGRWxackdhWU9oWDAiLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpZamM1TlRoaU1tUXlNV1ZoIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgUHlQeSAyLjciLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX3B5cHlfMjdfYm90In0sIHsidG9rZW4iOiAiNjg0MzM5OTg0OkFBRk1nRUVqcDAxcjVyQjAwN3lDZFZOc2c4QWxOc2FVLWNjIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TVRBek1UWTNNR1V5TmpnMCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIFB5UHkgMy41IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19weXB5XzM1X2JvdCJ9LCB7InRva2VuIjogIjY5MDA5MTM0NzpBQUZMbVI1cEFCNVljcGVfbU9oN3pNNEpGQk9oMHozVDBUbyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpEaGxOekU1TURrd1lXSmkiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIEFwcFZleW9yIHVzaW5nIENQeXRob24gMy40IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX2FwcHZleW9yX2NweXRob25fMzRfYm90In0sIHsidG9rZW4iOiAiNjk0MzA4MDUyOkFBRUIyX3NvbkNrNTVMWTlCRzlBTy1IOGp4aVBTNTVvb0JBIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WW1aaVlXWm1NakpoWkdNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gQXBwVmV5b3IgdXNpbmcgQ1B5dGhvbiAyLjciLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfYXBwdmV5b3JfY3B5dGhvbl8yN19ib3QifV0=
BOTS: W3sidG9rZW4iOiAiNjk2MTg4NzMyOkFBR1Z3RUtmSEhsTmpzY3hFRE5LQXdraEdzdFpfa28xbUMwIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WldGaU1UUmxNbVF5TnpNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMi43IiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxMzkwOTgzOTk3IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzI3X2JvdCJ9LCB7InRva2VuIjogIjY3MTQ2ODg4NjpBQUdQR2ZjaVJJQlVORmU4MjR1SVZkcTdKZTNfWW5BVE5HdyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpHWXdPVGxrTXpNeE4yWTIiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNCIsICJzdXBlcl9ncm91cF9pZCI6ICItMTAwMTQ0NjAyMjUyMiIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zNF9ib3QifSwgeyJ0b2tlbiI6ICI2MjkzMjY1Mzg6QUFGUnJaSnJCN29CM211ekdzR0pYVXZHRTVDUXpNNUNVNG8iLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpNbU01WVdKaFl6a3hNMlUxIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgQ1B5dGhvbiAzLjUiLCAic3VwZXJfZ3JvdXBfaWQiOiAiLTEwMDE0OTY5MTc3NTAiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX2NweXRob25fMzVfYm90In0sIHsidG9rZW4iOiAiNjQwMjA4OTQzOkFBRmhCalFwOXFtM1JUeFN6VXBZekJRakNsZS1Kano1aGNrIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WXpoa1pUZzFOamMxWXpWbCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMy42IiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxMzMzODcxNDYxIiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzM2X2JvdCJ9LCB7InRva2VuIjogIjY5NTEwNDA4ODpBQUhmenlsSU9qU0lJUy1lT25JMjB5MkUyMEhvZEhzZnotMCIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOk9HUTFNRGd3WmpJd1pqRmwiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNyIsICJzdXBlcl9ncm91cF9pZCI6ICItMTAwMTQ3ODI5MzcxNCIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zN19ib3QifSwgeyJ0b2tlbiI6ICI2OTE0MjM1NTQ6QUFGOFdrakNaYm5IcVBfaTZHaFRZaXJGRWxackdhWU9oWDAiLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpZamM1TlRoaU1tUXlNV1ZoIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgUHlQeSAyLjciLCAic3VwZXJfZ3JvdXBfaWQiOiAiLTEwMDEzNjM5MzI1NzMiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX3B5cHlfMjdfYm90In0sIHsidG9rZW4iOiAiNjg0MzM5OTg0OkFBRk1nRUVqcDAxcjVyQjAwN3lDZFZOc2c4QWxOc2FVLWNjIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TVRBek1UWTNNR1V5TmpnMCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIFB5UHkgMy41IiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxNDA3ODM2NjA1IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19weXB5XzM1X2JvdCJ9LCB7InRva2VuIjogIjY5MDA5MTM0NzpBQUZMbVI1cEFCNVljcGVfbU9oN3pNNEpGQk9oMHozVDBUbyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpEaGxOekU1TURrd1lXSmkiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIEFwcFZleW9yIHVzaW5nIENQeXRob24gMy40IiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxMjc5NjAwMDI2IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX2FwcHZleW9yX2NweXRob25fMzRfYm90In0sIHsidG9rZW4iOiAiNjk0MzA4MDUyOkFBRUIyX3NvbkNrNTVMWTlCRzlBTy1IOGp4aVBTNTVvb0JBIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WW1aaVlXWm1NakpoWkdNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gQXBwVmV5b3IgdXNpbmcgQ1B5dGhvbiAyLjciLCAic3VwZXJfZ3JvdXBfaWQiOiAiLTEwMDEyOTMwNzkxNjUiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfYXBwdmV5b3JfY3B5dGhvbl8yN19ib3QifSwgeyJ0b2tlbiI6ICIxMDU1Mzk3NDcxOkFBRzE4bkJfUzJXQXd1SjNnN29oS0JWZ1hYY2VNbklPeVNjIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TmpBd056QXpZalZpTkdOayIsICJuYW1lIjogIlBUQiB0ZXN0cyBbMF0iLCAic3VwZXJfZ3JvdXBfaWQiOiAiLTEwMDExODU1MDk2MzYiLCAidXNlcm5hbWUiOiAicHRiXzBfYm90In0sIHsidG9rZW4iOiAiMTA0NzMyNjc3MTpBQUY4bk90ODFGcFg4bGJidno4VWV3UVF2UmZUYkZmQnZ1SSIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOllUVTFOVEk0WkdSallqbGkiLCAibmFtZSI6ICJQVEIgdGVzdHMgWzFdIiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxNDg0Nzk3NjEyIiwgInVzZXJuYW1lIjogInB0Yl8xX2JvdCJ9LCB7InRva2VuIjogIjk3MTk5Mjc0NTpBQUdPa09hVzBOSGpnSXY1LTlqUWJPajR2R3FkaFNGLVV1cyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOk5XWmtNV1ZoWWpsallqVTUiLCAibmFtZSI6ICJQVEIgdGVzdHMgWzJdIiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxNDAyMjU1MDcwIiwgInVzZXJuYW1lIjogInB0Yl8yX2JvdCJ9XQ==
TEST_BUILD: ${{ matrix.test-build }}
TEST_PRE_COMMIT: ${{ matrix.test-pre-commit }}
shell: bash --noprofile --norc {0}
@@ -70,7 +73,7 @@ jobs:
os: [ubuntu-latest]
fail-fast: False
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: Initialize vendored libs
run:
git submodule update --init --recursive
@@ -99,7 +102,7 @@ jobs:
os: [ubuntu-latest]
fail-fast: False
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: Initialize vendored libs
run:
git submodule update --init --recursive
+4 -4
View File
@@ -1,17 +1,17 @@
repos:
- repo: git://github.com/python-telegram-bot/mirrors-yapf
sha: 5769e088ef6e0a0d1eb63bd6d0c1fe9f3606d6c8
rev: 5769e088ef6e0a0d1eb63bd6d0c1fe9f3606d6c8
hooks:
- id: yapf
files: ^(telegram|tests)/.*\.py$
args:
- --diff
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: 0b70e285e369bcb24b57b74929490ea7be9c4b19
- repo: https://gitlab.com/pycqa/flake8
rev: 3.7.1
hooks:
- id: flake8
- repo: git://github.com/pre-commit/mirrors-pylint
sha: 9d8dcbc2b86c796275680f239c1e90dcd50bd398
rev: 9d8dcbc2b86c796275680f239c1e90dcd50bd398
hooks:
- id: pylint
files: ^telegram/.*\.py$
-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
+7
View File
@@ -18,6 +18,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 +27,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>`_
@@ -35,6 +38,8 @@ The following wonderful people contributed directly or indirectly to this projec
- `Evan Haberecht <https://github.com/habereet>`_
- `evgfilim1 <https://github.com/evgfilim1>`_
- `franciscod <https://github.com/franciscod>`_
- `gamgi <https://github.com/gamgi>`_
- `Harshil <https://github.com/harshil21>`_
- `Hugo Damer <https://github.com/HakimusGIT>`_
- `ihoru <https://github.com/ihoru>`_
- `Jasmin Bom <https://github.com/jsmnbom>`_
@@ -67,6 +72,8 @@ The following wonderful people contributed directly or indirectly to this projec
- `Pieter Schutz <https://github.com/eldinnie>`_
- `Poolitzer <https://github.com/Poolitzer>`_
- `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>`_
+228 -2
View File
@@ -2,6 +2,232 @@
Changelog
=========
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`_)
**New Features:**
- New method ``run_mothly`` 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:**
- Added ``tzinfo`` argument to ``helpers.from_timestamp`` (`#1621`_)
- 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*
@@ -15,7 +241,7 @@ Version 12.3.0
- Fix inconsistent handling of naive datetimes (`#1506`_).
**Minor changes, CI improvments or bug fixes:**
**Minor changes, CI improvements or bug fixes:**
- Documentation fixes (`#1558`_, `#1569`_, `#1579`_, `#1572`_, `#1566`_, `#1577`_, `#1656`_).
- Add mutex protection on `ConversationHandler` (`#1533`_).
@@ -27,7 +253,7 @@ Version 12.3.0
- 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/1558
.. _`#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
+6 -6
View File
@@ -29,7 +29,7 @@ We have a vibrant community of developers helping each other in our `Telegram gr
:target: https://www.gnu.org/licenses/lgpl-3.0.html
:alt: LGPLv3 License
.. image:: https://github.com/python-telegram-bot/python-telegram-bot/workflows/Testing%20your%20PR/badge.svg?event=schedule
.. image:: https://github.com/python-telegram-bot/python-telegram-bot/workflows/GitHub%20Actions/badge.svg?event=schedule
:target: https://github.com/python-telegram-bot/python-telegram-bot/
:alt: Github Actions workflow
@@ -83,7 +83,7 @@ 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.5+ and `PyPy <http://pypy.org/>`_.
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
@@ -93,7 +93,7 @@ make the development of bots easy and straightforward. These classes are contain
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 **4.8** are supported.
==========
Installing
@@ -137,9 +137,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 ``echobot2``, 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.
-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"
+3 -3
View File
@@ -50,7 +50,7 @@ master_doc = 'index'
# General information about the project.
project = u'Python Telegram Bot'
copyright = u'2015-2018, Leandro Toledo'
copyright = u'2015-2020, Leandro Toledo'
author = u'Leandro Toledo'
# The version info for the project you're documenting, acts as replacement for
@@ -58,9 +58,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '12.3' # telegram.__version__[:3]
version = '12.7' # telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = '12.3.0' # telegram.__version__
release = '12.7' # 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.Dice
=============
.. autoclass:: telegram.Dice
: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:
+4
View File
@@ -5,12 +5,14 @@ telegram.ext package
telegram.ext.updater
telegram.ext.dispatcher
telegram.ext.dispatcherhandlerstop
telegram.ext.filters
telegram.ext.job
telegram.ext.jobqueue
telegram.ext.messagequeue
telegram.ext.delayqueue
telegram.ext.callbackcontext
telegram.ext.defaults
Handlers
--------
@@ -24,6 +26,8 @@ Handlers
telegram.ext.commandhandler
telegram.ext.inlinequeryhandler
telegram.ext.messagehandler
telegram.ext.pollanswerhandler
telegram.ext.pollhandler
telegram.ext.precheckoutqueryhandler
telegram.ext.prefixhandler
telegram.ext.regexhandler
@@ -0,0 +1,6 @@
telegram.KeyboardButtonPollType
===============================
.. autoclass:: telegram.KeyboardButtonPollType
:members:
:show-inheritance:
+6
View File
@@ -0,0 +1,6 @@
telegram.PollAnswer
===================
.. autoclass:: telegram.PollAnswer
:members:
:show-inheritance:
+4
View File
@@ -9,6 +9,7 @@ telegram package
telegram.animation
telegram.audio
telegram.bot
telegram.botcommand
telegram.callbackquery
telegram.chat
telegram.chataction
@@ -17,6 +18,7 @@ telegram package
telegram.chatphoto
telegram.constants
telegram.contact
telegram.dice
telegram.document
telegram.error
telegram.file
@@ -31,6 +33,7 @@ telegram package
telegram.inputmediaphoto
telegram.inputmediavideo
telegram.keyboardbutton
telegram.keyboardbuttonpolltype
telegram.location
telegram.loginurl
telegram.message
@@ -38,6 +41,7 @@ telegram package
telegram.parsemode
telegram.photosize
telegram.poll
telegram.pollanswer
telegram.polloption
telegram.replykeyboardremove
telegram.replykeyboardmarkup
+4
View File
@@ -29,6 +29,10 @@ def start(update, context):
def button(update, context):
query = update.callback_query
# 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="Selected option: {}".format(query.data))
+14 -25
View File
@@ -55,8 +55,9 @@ def start_over(update, context):
"""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))]
@@ -65,9 +66,7 @@ def start_over(update, context):
# 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,
query.edit_message_text(
text="Start handler, Choose a route",
reply_markup=reply_markup
)
@@ -77,15 +76,13 @@ def start_over(update, context):
def one(update, context):
"""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))]
]
reply_markup = InlineKeyboardMarkup(keyboard)
bot.edit_message_text(
chat_id=query.message.chat_id,
message_id=query.message.message_id,
query.edit_message_text(
text="First CallbackQueryHandler, Choose a route",
reply_markup=reply_markup
)
@@ -95,15 +92,13 @@ def one(update, context):
def two(update, context):
"""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))]
]
reply_markup = InlineKeyboardMarkup(keyboard)
bot.edit_message_text(
chat_id=query.message.chat_id,
message_id=query.message.message_id,
query.edit_message_text(
text="Second CallbackQueryHandler, Choose a route",
reply_markup=reply_markup
)
@@ -113,15 +108,13 @@ def two(update, context):
def three(update, context):
"""Show new choice of buttons"""
query = update.callback_query
bot = context.bot
query.answer()
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,
query.edit_message_text(
text="Third CallbackQueryHandler. Do want to start over?",
reply_markup=reply_markup
)
@@ -132,15 +125,13 @@ def three(update, context):
def four(update, context):
"""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))]
]
reply_markup = InlineKeyboardMarkup(keyboard)
bot.edit_message_text(
chat_id=query.message.chat_id,
message_id=query.message.message_id,
query.edit_message_text(
text="Fourth CallbackQueryHandler, Choose a route",
reply_markup=reply_markup
)
@@ -151,10 +142,8 @@ def end(update, context):
"""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,
query.answer()
query.edit_message_text(
text="See you next time!"
)
return ConversationHandler.END
+13 -1
View File
@@ -66,6 +66,7 @@ def start(update, context):
# 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'
@@ -83,6 +84,7 @@ def adding_self(update, context):
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
@@ -118,6 +120,7 @@ def show_data(update, context):
]]
keyboard = InlineKeyboardMarkup(buttons)
update.callback_query.answer()
update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
ud[START_OVER] = True
@@ -133,6 +136,8 @@ def stop(update, context):
def end(update, context):
"""End conversation from InlineKeyboardButton."""
update.callback_query.answer()
text = 'See you around!'
update.callback_query.edit_message_text(text=text)
@@ -151,6 +156,8 @@ def select_level(update, context):
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
@@ -172,8 +179,9 @@ def select_gender(update, context):
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
@@ -201,6 +209,8 @@ def select_feature(update, context):
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:
@@ -215,6 +225,8 @@ def ask_for_input(update, context):
"""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
+1 -1
View File
@@ -47,7 +47,7 @@ def start(update, context):
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 " \
"about yourself? Or change anything I " \
"already know.".format(", ".join(context.user_data.keys()))
else:
reply_text += " I will hold a more complex conversation with you. Why don't you tell me " \
+147
View File
@@ -0,0 +1,147 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 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)
from telegram.ext import (Updater, CommandHandler, PollAnswerHandler, PollHandler, MessageHandler,
Filters)
from telegram.utils.helpers import mention_html
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
def start(update, context):
"""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, context):
"""Sends a predefined poll"""
questions = ["Good", "Really good", "Fantastic", "Great"]
message = context.bot.send_poll(update.effective_user.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, context):
"""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]
user_mention = mention_html(update.effective_user.id, update.effective_user.full_name)
context.bot.send_message(context.bot_data[poll_id]["chat_id"],
"{} feels {}!".format(user_mention, 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, context):
"""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, context):
"""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, context):
"""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, context):
"""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, context):
"""Display a help message"""
update.message.reply_text("Use /quiz, /poll or /preview to test this "
"bot.")
def main():
# 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)
dp = updater.dispatcher
dp.add_handler(CommandHandler('start', start))
dp.add_handler(CommandHandler('poll', poll))
dp.add_handler(PollAnswerHandler(receive_poll_answer))
dp.add_handler(CommandHandler('quiz', quiz))
dp.add_handler(PollHandler(receive_quiz_answer))
dp.add_handler(CommandHandler('preview', preview))
dp.add_handler(MessageHandler(Filters.poll, receive_poll))
dp.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()
+1
View File
@@ -2,3 +2,4 @@ future>=0.16.0
certifi
tornado>=5.1
cryptography
decorator>=4.4.0
+13 -5
View File
@@ -3,6 +3,8 @@
import codecs
import os
import sys
from setuptools import setup, find_packages
@@ -18,6 +20,14 @@ def requirements():
packages = find_packages(exclude=['tests*'])
requirements = requirements()
# Allow for a package install to not use the vendored urllib3
UPSTREAM_URLLIB3_FLAG = '--with-upstream-urllib3'
if UPSTREAM_URLLIB3_FLAG in sys.argv:
sys.argv.remove(UPSTREAM_URLLIB3_FLAG)
requirements.append('urllib3 >= 1.19.1')
packages = [x for x in packages if not x.startswith('telegram.vendor.ptb_urllib3')]
with codecs.open('README.rst', 'r', 'utf-8') as fd:
fn = os.path.join('telegram', 'version.py')
@@ -35,7 +45,7 @@ with codecs.open('README.rst', 'r', 'utf-8') as fd:
description="We have made you a wrapper you can't refuse",
long_description=fd.read(),
packages=packages,
install_requires=requirements(),
install_requires=requirements,
extras_require={
'json': 'ujson',
'socks': 'PySocks'
@@ -50,11 +60,9 @@ with codecs.open('README.rst', 'r', 'utf-8') as fd:
'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'
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
],)
+8 -4
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,6 +19,7 @@
"""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
@@ -36,8 +37,10 @@ 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 .keyboardbutton import KeyboardButton
from .keyboardbuttonpolltype import KeyboardButtonPollType
from .replymarkup import ReplyMarkup
from .replykeyboardmarkup import ReplyKeyboardMarkup
from .replykeyboardremove import ReplyKeyboardRemove
@@ -48,7 +51,7 @@ from .files.file import File
from .parsemode import ParseMode
from .messageentity import MessageEntity
from .games.game import Game
from .poll import Poll, PollOption
from .poll import Poll, PollOption, PollAnswer
from .loginurl import LoginUrl
from .games.callbackgame import CallbackGame
from .payment.shippingaddress import ShippingAddress
@@ -138,7 +141,7 @@ __all__ = [
'InlineQueryResultPhoto', 'InlineQueryResultVenue', 'InlineQueryResultVideo',
'InlineQueryResultVoice', 'InlineQueryResultGame', 'InputContactMessageContent', 'InputFile',
'InputLocationMessageContent', 'InputMessageContent', 'InputTextMessageContent',
'InputVenueMessageContent', 'KeyboardButton', 'Location', 'EncryptedCredentials',
'InputVenueMessageContent', 'Location', 'EncryptedCredentials',
'PassportFile', 'EncryptedPassportElement', 'PassportData', 'Message', 'MessageEntity',
'ParseMode', 'PhotoSize', 'ReplyKeyboardRemove', 'ReplyKeyboardMarkup', 'ReplyMarkup',
'Sticker', 'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue',
@@ -156,5 +159,6 @@ __all__ = [
'InputMediaAudio', 'InputMediaDocument', 'TelegramDecryptionError',
'PassportElementErrorSelfie', 'PassportElementErrorTranslationFile',
'PassportElementErrorTranslationFiles', 'PassportElementErrorUnspecified', 'Poll',
'PollOption', 'LoginUrl'
'PollOption', 'PollAnswer', 'LoginUrl', 'KeyboardButton', 'KeyboardButtonPollType', 'Dice',
'BotCommand'
]
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+1 -4
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -23,13 +23,10 @@ try:
except ImportError:
import json
from abc import ABCMeta
class TelegramObject(object):
"""Base class for most telegram objects."""
__metaclass__ = ABCMeta
_id_attrs = ()
def __str__(self):
+684 -227
View File
File diff suppressed because it is too large Load Diff
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env python
# pylint: disable=R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2020
# 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 telegram import TelegramObject
class BotCommand(TelegramObject):
"""
This object represents a bot command.
Attributes:
command (:obj:`str`): Text of the command.
description (:obj:`str`): Description of the command.
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.
"""
def __init__(self, command, description, **kwargs):
self.command = command
self.description = description
@classmethod
def de_json(cls, data, bot):
if not data:
return None
return cls(**data)
+14 -10
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -36,30 +36,31 @@ class CallbackQuery(TelegramObject):
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.
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.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
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
@@ -101,7 +102,10 @@ class CallbackQuery(TelegramObject):
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)
message = data.get('message')
if message:
message['default_quote'] = data.get('default_quote')
data['message'] = Message.de_json(message, bot)
return cls(bot=bot, **data)
+22 -2
View File
@@ -2,7 +2,7 @@
# pylint: disable=C0103,W0622
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -40,6 +40,8 @@ class Chat(TelegramObject):
Returned only in get_chat.
permissions (:class:`telegram.ChatPermission`): Optional. Default chat member permissions,
for groups and supergroups. Returned only in getChat.
slow_mode_delay (:obj:`int`): Optional. For supergroups, the minimum allowed delay between
consecutive messages sent by each unpriviledged user. Returned only in getChat.
sticker_set_name (:obj:`str`): Optional. For supergroups, name of Group sticker set.
can_set_sticker_set (:obj:`bool`): Optional. ``True``, if the bot can change group the
sticker set.
@@ -65,6 +67,8 @@ class Chat(TelegramObject):
Returned only in get_chat.
permissions (:class:`telegram.ChatPermission`): Optional. Default chat member permissions,
for groups and supergroups. Returned only in getChat.
slow_mode_delay (:obj:`int`, optional): For supergroups, the minimum allowed delay between
consecutive messages sent by each unpriviledged user. Returned only in getChat.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
sticker_set_name (:obj:`str`, optional): For supergroups, name of Group sticker set.
Returned only in get_chat.
@@ -98,6 +102,7 @@ class Chat(TelegramObject):
permissions=None,
sticker_set_name=None,
can_set_sticker_set=None,
slow_mode_delay=None,
**kwargs):
# Required
self.id = int(id)
@@ -114,6 +119,7 @@ class Chat(TelegramObject):
self.invite_link = invite_link
self.pinned_message = pinned_message
self.permissions = permissions
self.slow_mode_delay = slow_mode_delay
self.sticker_set_name = sticker_set_name
self.can_set_sticker_set = can_set_sticker_set
@@ -135,7 +141,10 @@ class Chat(TelegramObject):
data['photo'] = ChatPhoto.de_json(data.get('photo'), bot)
from telegram import Message
data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot)
pinned_message = data.get('pinned_message')
if pinned_message:
pinned_message['default_quote'] = data.get('default_quote')
data['pinned_message'] = Message.de_json(pinned_message, bot)
data['permissions'] = ChatPermissions.de_json(data.get('permissions'), bot)
return cls(bot=bot, **data)
@@ -237,6 +246,17 @@ class Chat(TelegramObject):
"""
return self.bot.set_chat_permissions(self.id, *args, **kwargs)
def set_administrator_custom_title(self, *args, **kwargs):
"""Shortcut for::
bot.set_chat_administrator_custom_title(update.message.chat.id, *args, **kwargs)
Returns:
:obj:`bool`: If the action was sent successfully.
"""
return self.bot.set_chat_administrator_custom_title(self.id, *args, **kwargs)
def send_message(self, *args, **kwargs):
"""Shortcut for::
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+6 -2
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -28,6 +28,7 @@ class ChatMember(TelegramObject):
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.
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
@@ -62,6 +63,8 @@ class ChatMember(TelegramObject):
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.
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
@@ -118,10 +121,11 @@ class ChatMember(TelegramObject):
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):
can_add_web_page_previews=None, is_member=None, custom_title=None, **kwargs):
# Required
self.user = user
self.status = status
self.custom_title = custom_title
self.until_date = until_date
self.can_be_edited = can_be_edited
self.can_change_info = can_change_info
+6 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -24,6 +24,11 @@ from telegram import TelegramObject
class ChatPermissions(TelegramObject):
"""Describes actions that a non-administrator user is allowed to take in a chat.
Note:
Though not stated explicitly in the offical 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.
Attributes:
can_send_messages (:obj:`bool`): Optional. True, if the user is allowed to send text
messages, contacts, locations and venues.
+1 -1
View File
@@ -2,7 +2,7 @@
# pylint: disable=R0902,R0912,R0913
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+1 -1
View File
@@ -1,5 +1,5 @@
# python-telegram-bot - a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2020
# by the python-telegram-bot contributors <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env python
# pylint: disable=R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2020
# 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 telegram import TelegramObject
class Dice(TelegramObject):
"""
This object represents a dice with random value from 1 to 6 for currently supported base eomji.
(The singular form of "dice" is "die". However, PTB mimics the Telegram API, which uses the
term "dice".)
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.
Attributes:
value (:obj:`int`): Value of the dice.
emoji (:obj:`str`): Emoji on which the dice throw animation is based.
Args:
value (:obj:`int`): Value of the dice, 1-6.
emoji (:obj:`str`): Emoji on which the dice throw animation is based.
"""
def __init__(self, value, emoji, **kwargs):
self.value = value
self.emoji = emoji
@classmethod
def de_json(cls, data, bot):
if not data:
return None
return cls(**data)
DICE = '🎲'
""":obj:`str`: '🎲'"""
DARTS = '🎯'
""":obj:`str`: '🎯'"""
ALL_EMOJI = [DICE, DARTS]
"""List[:obj:`str`]: List of all supported base emoji. Currently :attr:`DICE` and
:attr:`DARTS`."""
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+6 -2
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -41,6 +41,9 @@ 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 .defaults import Defaults
__all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
'ChosenInlineResultHandler', 'CommandHandler', 'Handler', 'InlineQueryHandler',
@@ -48,4 +51,5 @@ __all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue',
'DispatcherHandlerStop', 'run_async', 'CallbackContext', 'BasePersistence',
'PicklePersistence', 'DictPersistence', 'PrefixHandler')
'PicklePersistence', 'DictPersistence', 'PrefixHandler', 'PollAnswerHandler',
'PollHandler', 'Defaults')
+38 -10
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -18,13 +18,17 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the BasePersistence class."""
from abc import ABC, abstractmethod
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:
* If :attr:`store_bot_data` is ``True`` you must overwrite :meth:`get_bot_data` and
:meth:`update_bot_data`.
* 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
@@ -38,18 +42,24 @@ class BasePersistence(object):
persistence class.
store_chat_data (:obj:`bool`): Optional. Whether chat_data should be saved by this
persistence class.
store_bot_data (:obj:`bool`): Optional. Whether bot_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. Default is ``True`` .
"""
def __init__(self, store_user_data=True, store_chat_data=True):
def __init__(self, store_user_data=True, store_chat_data=True, store_bot_data=True):
self.store_user_data = store_user_data
self.store_chat_data = store_chat_data
self.store_bot_data = store_bot_data
@abstractmethod
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
@@ -58,8 +68,8 @@ class BasePersistence(object):
Returns:
:obj:`defaultdict`: The restored user data.
"""
raise NotImplementedError
@abstractmethod
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
@@ -68,8 +78,18 @@ class BasePersistence(object):
Returns:
:obj:`defaultdict`: The restored chat data.
"""
raise NotImplementedError
@abstractmethod
def get_bot_data(self):
""""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
``dict``.
Returns:
:obj:`defaultdict`: The restored bot data.
"""
@abstractmethod
def get_conversations(self, name):
""""Will be called by :class:`telegram.ext.Dispatcher` when a
:class:`telegram.ext.ConversationHandler` is added if
@@ -82,8 +102,8 @@ class BasePersistence(object):
Returns:
:obj:`dict`: The restored conversations for the handler.
"""
raise NotImplementedError
@abstractmethod
def update_conversation(self, name, key, new_state):
"""Will be called when a :attr:`telegram.ext.ConversationHandler.update_state`
is called. this allows the storeage of the new state in the persistence.
@@ -93,8 +113,8 @@ class BasePersistence(object):
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
@abstractmethod
def update_user_data(self, user_id, data):
"""Will be called by the :class:`telegram.ext.Dispatcher` after a handler has
handled an update.
@@ -103,17 +123,25 @@ 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
@abstractmethod
def update_chat_data(self, chat_id, data):
"""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].
"""
@abstractmethod
def update_bot_data(self, data):
"""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` .
"""
raise NotImplementedError
def flush(self):
"""Will be called by :class:`telegram.ext.Updater` upon receiving a stop signal. Gives the
+27 -2
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -43,8 +43,17 @@ class CallbackContext(object):
that you think you added will not be present.
Attributes:
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 it will be the same ``dict``.
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
@@ -73,6 +82,7 @@ class CallbackContext(object):
raise ValueError('CallbackContext should not be used with a non context aware '
'dispatcher!')
self._dispatcher = dispatcher
self._bot_data = dispatcher.bot_data
self._chat_data = None
self._user_data = None
self.args = None
@@ -80,6 +90,20 @@ class CallbackContext(object):
self.error = None
self.job = None
@property
def dispatcher(self):
""":class:`telegram.ext.Dispatcher`: The dispatcher associated with this context."""
return self._dispatcher
@property
def bot_data(self):
return self._bot_data
@bot_data.setter
def bot_data(self, value):
raise AttributeError("You can not assign a new value to bot_data, see "
"https://git.io/fjxKe")
@property
def chat_data(self):
return self._chat_data
@@ -107,6 +131,7 @@ class CallbackContext(object):
@classmethod
def from_update(cls, update, dispatcher):
self = cls(dispatcher)
if update is not None and isinstance(update, Update):
chat = update.effective_chat
user = update.effective_user
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+32 -7
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -302,6 +302,10 @@ class PrefixHandler(CommandHandler):
pass_user_data=False,
pass_chat_data=False):
self._prefix = list()
self._command = list()
self._commands = list()
super(PrefixHandler, self).__init__(
'nocommand', callback, filters=filters, allow_edited=None, pass_args=pass_args,
pass_update_queue=pass_update_queue,
@@ -309,15 +313,36 @@ class PrefixHandler(CommandHandler):
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
self.prefix = prefix
self.command = command
self._build_commands()
@property
def prefix(self):
return self._prefix
@prefix.setter
def prefix(self, prefix):
if isinstance(prefix, string_types):
self.prefix = [prefix.lower()]
self._prefix = [prefix.lower()]
else:
self.prefix = prefix
self._prefix = prefix
self._build_commands()
@property
def command(self):
return self._command
@command.setter
def command(self, command):
if isinstance(command, string_types):
self.command = [command.lower()]
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._command = command
self._build_commands()
def _build_commands(self):
self._commands = [x.lower() + y.lower() for x in self.prefix for y in self.command]
def check_update(self, update):
"""Determines whether an update should be passed to this handlers :attr:`callback`.
@@ -334,7 +359,7 @@ 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:
+134 -23
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -29,10 +29,11 @@ from telegram.utils.promise import Promise
class _ConversationTimeoutContext(object):
def __init__(self, conversation_key, update, dispatcher):
def __init__(self, conversation_key, update, dispatcher, callback_context):
self.conversation_key = conversation_key
self.update = update
self.dispatcher = dispatcher
self.callback_context = callback_context
class ConversationHandler(Handler):
@@ -93,11 +94,12 @@ class ConversationHandler(Handler):
per_user (:obj:`bool`): If the conversationkey should contain the User's ID.
per_message (:obj:`bool`): If the conversationkey should contain the Message's
ID.
conversation_timeout (:obj:`float`|:obj:`datetime.timedelta`): Optional. When this handler
is inactive more than this timeout (in seconds), it will be automatically ended. If
this value is 0 (default), there will be no timeout. When it's triggered, the last
received update will be handled by ALL the handler's who's `check_update` method
returns True that are in the state :attr:`ConversationHandler.TIMEOUT`.
conversation_timeout (:obj:`float` | :obj:`datetime.timedelta`): Optional. When this
handler is inactive more than this timeout (in seconds), it will be automatically
ended. If this value is 0 (default), there will be no timeout. When it's triggered, the
last received update and the corresponding ``context`` will be handled by ALL the
handler's who's `check_update` method returns True that are in the state
:attr:`ConversationHandler.TIMEOUT`.
name (:obj:`str`): Optional. The name for this conversationhandler. Required for
persistence
persistent (:obj:`bool`): Optional. If the conversations dict for this handler should be
@@ -130,8 +132,9 @@ class ConversationHandler(Handler):
conversation_timeout (:obj:`float` | :obj:`datetime.timedelta`, optional): When this
handler is inactive more than this timeout (in seconds), it will be automatically
ended. If this value is 0 or None (default), there will be no timeout. The last
received update will be handled by ALL the handler's who's `check_update` method
returns True that are in the state :attr:`ConversationHandler.TIMEOUT`.
received update and the corresponding ``context`` will be handled by ALL the handler's
who's `check_update` method returns True that are in the state
:attr:`ConversationHandler.TIMEOUT`.
name (:obj:`str`, optional): The name for this conversationhandler. Required for
persistence
persistent (:obj:`bool`, optional): If the conversations dict for this handler should be
@@ -165,27 +168,27 @@ class ConversationHandler(Handler):
persistent=False,
map_to_parent=None):
self.entry_points = entry_points
self.states = states
self.fallbacks = fallbacks
self._entry_points = entry_points
self._states = states
self._fallbacks = fallbacks
self.allow_reentry = allow_reentry
self.per_user = per_user
self.per_chat = per_chat
self.per_message = per_message
self.conversation_timeout = conversation_timeout
self.name = name
self._allow_reentry = allow_reentry
self._per_user = per_user
self._per_chat = per_chat
self._per_message = per_message
self._conversation_timeout = conversation_timeout
self._name = name
if persistent and not self.name:
raise ValueError("Conversations can't be persistent when handler is unnamed.")
self.persistent = persistent
self.persistence = None
self._persistence = None
""":obj:`telegram.ext.BasePersistance`: The persistence used to store conversations.
Set by dispatcher"""
self.map_to_parent = map_to_parent
self._map_to_parent = map_to_parent
self.timeout_jobs = dict()
self._timeout_jobs_lock = Lock()
self.conversations = dict()
self._conversations = dict()
self._conversations_lock = Lock()
self.logger = logging.getLogger(__name__)
@@ -225,6 +228,113 @@ class ConversationHandler(Handler):
"since inline queries have no chat context.")
break
@property
def entry_points(self):
return self._entry_points
@entry_points.setter
def entry_points(self, value):
raise ValueError('You can not assign a new value to entry_points after initialization.')
@property
def states(self):
return self._states
@states.setter
def states(self, value):
raise ValueError('You can not assign a new value to states after initialization.')
@property
def fallbacks(self):
return self._fallbacks
@fallbacks.setter
def fallbacks(self, value):
raise ValueError('You can not assign a new value to fallbacks after initialization.')
@property
def allow_reentry(self):
return self._allow_reentry
@allow_reentry.setter
def allow_reentry(self, value):
raise ValueError('You can not assign a new value to allow_reentry after initialization.')
@property
def per_user(self):
return self._per_user
@per_user.setter
def per_user(self, value):
raise ValueError('You can not assign a new value to per_user after initialization.')
@property
def per_chat(self):
return self._per_chat
@per_chat.setter
def per_chat(self, value):
raise ValueError('You can not assign a new value to per_chat after initialization.')
@property
def per_message(self):
return self._per_message
@per_message.setter
def per_message(self, value):
raise ValueError('You can not assign a new value to per_message after initialization.')
@property
def conversation_timeout(self):
return self._conversation_timeout
@conversation_timeout.setter
def conversation_timeout(self, value):
raise ValueError('You can not assign a new value to conversation_timeout after '
'initialization.')
@property
def name(self):
return self._name
@name.setter
def name(self, value):
raise ValueError('You can not assign a new value to name after initialization.')
@property
def map_to_parent(self):
return self._map_to_parent
@map_to_parent.setter
def map_to_parent(self, value):
raise ValueError('You can not assign a new value to map_to_parent after initialization.')
@property
def persistence(self):
return self._persistence
@persistence.setter
def persistence(self, persistence):
self._persistence = persistence
# Set persistence for nested conversations
for handlers in self.states.values():
for handler in handlers:
if isinstance(handler, ConversationHandler):
handler.persistence = self.persistence
@property
def conversations(self):
return self._conversations
@conversations.setter
def conversations(self, value):
self._conversations = value
# Set conversations for nested conversations
for handlers in self.states.values():
for handler in handlers:
if isinstance(handler, ConversationHandler):
handler.conversations = self.persistence.get_conversations(handler.name)
def _get_key(self, update):
chat = update.effective_chat
user = update.effective_user
@@ -359,7 +469,8 @@ class ConversationHandler(Handler):
# Add the new timeout job
self.timeout_jobs[conversation_key] = dispatcher.job_queue.run_once(
self._trigger_timeout, self.conversation_timeout,
context=_ConversationTimeoutContext(conversation_key, update, dispatcher))
context=_ConversationTimeoutContext(conversation_key, update,
dispatcher, context))
if isinstance(self.map_to_parent, dict) and new_state in self.map_to_parent:
self.update_state(self.END, conversation_key)
@@ -396,9 +507,9 @@ class ConversationHandler(Handler):
callback_context = None
if isinstance(context, CallbackContext):
job = context.job
callback_context = context
context = job.context
callback_context = context.callback_context
with self._timeout_jobs_lock:
found_job = self.timeout_jobs[context.conversation_key]
+127
View File
@@ -0,0 +1,127 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2020
# 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 class Defaults, which allows to pass default values to Updater."""
from telegram.utils.helpers import DEFAULT_NONE
class Defaults:
"""Convenience Class to gather all parameters with a (user defined) default value
Attributes:
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width toxt or URLs in your bot's message.
disable_notification (:obj:`bool`): Optional. Sends the message silently. Users will
receive a notification with no sound.
disable_web_page_preview (:obj:`bool`): Optional. Disables link previews for links in this
message.
timeout (:obj:`int` | :obj:`float`): Optional. If this value is specified, use it as the
read timeout from the server (instead of the one specified during creation of the
connection pool).
quote (:obj:`bool`): Optional. If set to ``True``, the reply is sent as an actual reply to
the message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter will
be ignored. Default: ``True`` in group chats and ``False`` in private chats.
Parameters:
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width toxt or URLs in your bot's message.
disable_notification (:obj:`bool`, optional): Sends the message silently. Users will
receive a notification with no sound.
disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in this
message.
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as the
read timeout from the server (instead of the one specified during creation of the
connection pool).
quote (:obj:`bool`, opitonal): If set to ``True``, the reply is sent as an actual reply to
the message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter will
be ignored. Default: ``True`` in group chats and ``False`` in private chats.
"""
def __init__(self,
parse_mode=None,
disable_notification=None,
disable_web_page_preview=None,
# Timeout needs special treatment, since the bot methods have two different
# default values for timeout (None and 20s)
timeout=DEFAULT_NONE,
quote=None):
self._parse_mode = parse_mode
self._disable_notification = disable_notification
self._disable_web_page_preview = disable_web_page_preview
self._timeout = timeout
self._quote = quote
@property
def parse_mode(self):
return self._parse_mode
@parse_mode.setter
def parse_mode(self, value):
raise AttributeError("You can not assign a new value to defaults after because it would "
"not have any effect.")
@property
def disable_notification(self):
return self._disable_notification
@disable_notification.setter
def disable_notification(self, value):
raise AttributeError("You can not assign a new value to defaults after because it would "
"not have any effect.")
@property
def disable_web_page_preview(self):
return self._disable_web_page_preview
@disable_web_page_preview.setter
def disable_web_page_preview(self, value):
raise AttributeError("You can not assign a new value to defaults after because it would "
"not have any effect.")
@property
def timeout(self):
return self._timeout
@timeout.setter
def timeout(self, value):
raise AttributeError("You can not assign a new value to defaults after because it would "
"not have any effect.")
@property
def quote(self):
return self._quote
@quote.setter
def quote(self, value):
raise AttributeError("You can not assign a new value to defaults after because it would "
"not have any effect.")
def __hash__(self):
return hash((self._parse_mode,
self._disable_notification,
self._disable_web_page_preview,
self._timeout,
self._quote))
def __eq__(self, other):
if isinstance(other, Defaults):
return self.__dict__ == other.__dict__
return False
def __ne__(self, other):
return not self == other
+71 -8
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -20,7 +20,7 @@
from copy import deepcopy
from telegram.utils.helpers import decode_user_chat_data_from_json,\
decode_conversations_from_json, enocde_conversations_to_json
decode_conversations_from_json, encode_conversations_to_json
try:
import ujson as json
@@ -31,36 +31,51 @@ from telegram.ext import BasePersistence
class DictPersistence(BasePersistence):
"""Using python's dicts and json for making you bot persistent.
"""Using python's dicts and json for making your bot persistent.
Attributes:
store_user_data (:obj:`bool`): Whether user_data should be saved by this
persistence class.
store_chat_data (:obj:`bool`): Whether chat_data should be saved by this
persistence class.
store_bot_data (:obj:`bool`): Whether bot_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 user_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. Default is ``True`` .
user_data_json (:obj:`str`, optional): Json string that will be used to reconstruct
user_data on creating this persistence. Default is ``""``.
chat_data_json (:obj:`str`, optional): Json string that will be used to reconstruct
chat_data on creating this persistence. Default is ``""``.
bot_data_json (:obj:`str`, optional): Json string that will be used to reconstruct
bot_data on creating this persistence. Default is ``""``.
conversations_json (:obj:`str`, optional): Json string that will be used to reconstruct
conversation on creating this persistence. Default is ``""``.
"""
def __init__(self, store_user_data=True, store_chat_data=True, user_data_json='',
chat_data_json='', conversations_json=''):
self.store_user_data = store_user_data
self.store_chat_data = store_chat_data
def __init__(self,
store_user_data=True,
store_chat_data=True,
store_bot_data=True,
user_data_json='',
chat_data_json='',
bot_data_json='',
conversations_json=''):
super(DictPersistence, self).__init__(store_user_data=store_user_data,
store_chat_data=store_chat_data,
store_bot_data=store_bot_data)
self._user_data = None
self._chat_data = None
self._bot_data = None
self._conversations = None
self._user_data_json = None
self._chat_data_json = None
self._bot_data_json = None
self._conversations_json = None
if user_data_json:
try:
@@ -74,6 +89,14 @@ class DictPersistence(BasePersistence):
self._chat_data_json = chat_data_json
except (ValueError, AttributeError):
raise TypeError("Unable to deserialize chat_data_json. Not valid JSON")
if bot_data_json:
try:
self._bot_data = json.loads(bot_data_json)
self._bot_data_json = bot_data_json
except (ValueError, AttributeError):
raise TypeError("Unable to deserialize bot_data_json. Not valid JSON")
if not isinstance(self._bot_data, dict):
raise TypeError("bot_data_json must be serialized dict")
if conversations_json:
try:
@@ -108,6 +131,19 @@ class DictPersistence(BasePersistence):
else:
return json.dumps(self.chat_data)
@property
def bot_data(self):
""":obj:`dict`: The bot_data as a dict"""
return self._bot_data
@property
def bot_data_json(self):
""":obj:`str`: The bot_data serialized as a JSON-string."""
if self._bot_data_json:
return self._bot_data_json
else:
return json.dumps(self.bot_data)
@property
def conversations(self):
""":obj:`dict`: The conversations as a dict"""
@@ -119,7 +155,7 @@ class DictPersistence(BasePersistence):
if self._conversations_json:
return self._conversations_json
else:
return enocde_conversations_to_json(self.conversations)
return encode_conversations_to_json(self.conversations)
def get_user_data(self):
"""Returns the user_data created from the ``user_data_json`` or an empty defaultdict.
@@ -145,6 +181,18 @@ class DictPersistence(BasePersistence):
self._chat_data = defaultdict(dict)
return deepcopy(self.chat_data)
def get_bot_data(self):
"""Returns the bot_data created from the ``bot_data_json`` or an empty dict.
Returns:
:obj:`defaultdict`: The restored user data.
"""
if self.bot_data:
pass
else:
self._bot_data = {}
return deepcopy(self.bot_data)
def get_conversations(self, name):
"""Returns the conversations created from the ``conversations_json`` or an empty
defaultdict.
@@ -178,6 +226,8 @@ class DictPersistence(BasePersistence):
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].
"""
if self._user_data is None:
self._user_data = defaultdict(dict)
if self._user_data.get(user_id) == data:
return
self._user_data[user_id] = data
@@ -190,7 +240,20 @@ class DictPersistence(BasePersistence):
chat_id (:obj:`int`): The chat the data might have been changed for.
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.chat_data` [chat_id].
"""
if self._chat_data is None:
self._chat_data = defaultdict(dict)
if self._chat_data.get(chat_id) == data:
return
self._chat_data[chat_id] = data
self._chat_data_json = None
def update_bot_data(self, data):
"""Will update the bot_data (if changed).
Args:
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.bot_data`.
"""
if self._bot_data == data:
return
self._bot_data = data.copy()
self._bot_data_json = None
+68 -50
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -79,6 +79,7 @@ class Dispatcher(object):
decorator.
user_data (:obj:`defaultdict`): A dictionary handlers can use to store data for the user.
chat_data (:obj:`defaultdict`): A dictionary handlers can use to store data for the chat.
bot_data (:obj:`dict`): A dictionary handlers can use to store data for the bot.
persistence (:class:`telegram.ext.BasePersistence`): Optional. The persistence class to
store data that should be persistent over restarts
@@ -121,8 +122,8 @@ class Dispatcher(object):
TelegramDeprecationWarning, stacklevel=3)
self.user_data = defaultdict(dict)
""":obj:`dict`: A dictionary handlers can use to store data for the user."""
self.chat_data = defaultdict(dict)
self.bot_data = {}
if persistence:
if not isinstance(persistence, BasePersistence):
raise TypeError("persistence should be based on telegram.ext.BasePersistence")
@@ -135,11 +136,13 @@ class Dispatcher(object):
self.chat_data = self.persistence.get_chat_data()
if not isinstance(self.chat_data, defaultdict):
raise ValueError("chat_data must be of type defaultdict")
if self.persistence.store_bot_data:
self.bot_data = self.persistence.get_bot_data()
if not isinstance(self.bot_data, dict):
raise ValueError("bot_data must be of type dict")
else:
self.persistence = None
self.job_queue = job_queue
self.handlers = {}
"""Dict[:obj:`int`, List[:class:`telegram.ext.Handler`]]: Holds the handlers per group."""
self.groups = []
@@ -162,6 +165,10 @@ class Dispatcher(object):
else:
self._set_singleton(None)
@property
def exception_event(self):
return self.__exception_event
def _init_async_threads(self, base_name, workers):
base_name = '{}_'.format(base_name) if base_name else ''
@@ -316,42 +323,6 @@ class Dispatcher(object):
"""
def persist_update(update):
"""Persist a single update.
Args:
update (:class:`telegram.Update`):
The update to process.
"""
if self.persistence and isinstance(update, Update):
if self.persistence.store_chat_data and update.effective_chat:
chat_id = update.effective_chat.id
try:
self.persistence.update_chat_data(chat_id,
self.chat_data[chat_id])
except Exception as e:
try:
self.dispatch_error(update, e)
except Exception:
message = 'Saving chat data raised an error and an ' \
'uncaught error was raised while handling ' \
'the error with an error_handler'
self.logger.exception(message)
if self.persistence.store_user_data and update.effective_user:
user_id = update.effective_user.id
try:
self.persistence.update_user_data(user_id,
self.user_data[user_id])
except Exception as e:
try:
self.dispatch_error(update, e)
except Exception:
message = 'Saving user data raised an error and an ' \
'uncaught error was raised while handling ' \
'the error with an error_handler'
self.logger.exception(message)
# An error happened while polling
if isinstance(update, TelegramError):
try:
@@ -370,13 +341,13 @@ class Dispatcher(object):
if not context and self.use_context:
context = CallbackContext.from_update(update, self)
handler.handle_update(update, self, check, context)
persist_update(update)
self.update_persistence(update=update)
break
# Stop processing with any other handler.
except DispatcherHandlerStop:
self.logger.debug('Stopping further handlers due to DispatcherHandlerStop')
persist_update(update)
self.update_persistence(update=update)
break
# Dispatch any error.
@@ -395,7 +366,8 @@ class Dispatcher(object):
def add_handler(self, handler, group=DEFAULT_GROUP):
"""Register a handler.
TL;DR: Order and priority counts. 0 or 1 handlers per group will be used.
TL;DR: Order and priority counts. 0 or 1 handlers per group will be used. End handling of
update with :class:`telegram.ext.DispatcherHandlerStop`.
A handler must be an instance of a subclass of :class:`telegram.ext.Handler`. All handlers
are organized in groups with a numeric value. The default group is 0. All groups will be
@@ -428,8 +400,8 @@ class Dispatcher(object):
raise ValueError(
"Conversationhandler {} can not be persistent if dispatcher has no "
"persistence".format(handler.name))
handler.conversations = self.persistence.get_conversations(handler.name)
handler.persistence = self.persistence
handler.conversations = self.persistence.get_conversations(handler.name)
if group not in self.handlers:
self.handlers[group] = list()
@@ -452,16 +424,62 @@ class Dispatcher(object):
del self.handlers[group]
self.groups.remove(group)
def update_persistence(self):
"""Update :attr:`user_data` and :attr:`chat_data` in :attr:`persistence`.
def update_persistence(self, update=None):
"""Update :attr:`user_data`, :attr:`chat_data` and :attr:`bot_data` in :attr:`persistence`.
Args:
update (:class:`telegram.Update`, optional): The update to process. If passed, only the
corresponding ``user_data`` and ``chat_data`` will be updated.
"""
if self.persistence:
chat_ids = self.chat_data.keys()
user_ids = self.user_data.keys()
if isinstance(update, Update):
if update.effective_chat:
chat_ids = [update.effective_chat.id]
else:
chat_ids = []
if update.effective_user:
user_ids = [update.effective_user.id]
else:
user_ids = []
if self.persistence.store_bot_data:
try:
self.persistence.update_bot_data(self.bot_data)
except Exception as e:
try:
self.dispatch_error(update, e)
except Exception:
message = 'Saving bot data raised an error and an ' \
'uncaught error was raised while handling ' \
'the error with an error_handler'
self.logger.exception(message)
if self.persistence.store_chat_data:
for chat_id in self.chat_data:
self.persistence.update_chat_data(chat_id, self.chat_data[chat_id])
for chat_id in chat_ids:
try:
self.persistence.update_chat_data(chat_id, self.chat_data[chat_id])
except Exception as e:
try:
self.dispatch_error(update, e)
except Exception:
message = 'Saving chat data raised an error and an ' \
'uncaught error was raised while handling ' \
'the error with an error_handler'
self.logger.exception(message)
if self.persistence.store_user_data:
for user_id in self.user_data:
self.persistence.update_user_data(user_id, self.user_data[user_id])
for user_id in user_ids:
try:
self.persistence.update_user_data(user_id, self.user_data[user_id])
except Exception as e:
try:
self.dispatch_error(update, e)
except Exception:
message = 'Saving user data raised an error and an ' \
'uncaught error was raised while handling ' \
'the error with an error_handler'
self.logger.exception(message)
def add_error_handler(self, callback):
"""Registers an error handler in the Dispatcher. This handler will receive every error
+154 -73
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -21,13 +21,14 @@
import re
from future.utils import string_types
from abc import ABC, abstractmethod
from telegram import Chat, Update
from telegram import Chat, Update, MessageEntity
__all__ = ['Filters', 'BaseFilter', 'InvertedFilter', 'MergedFilter']
class BaseFilter(object):
class BaseFilter(ABC):
"""Base class for all Message Filters.
Subclassing from this class filters to be combined using bitwise operators:
@@ -50,7 +51,7 @@ class BaseFilter(object):
>>> Filters.text & (~ Filters.forwarded)
Note:
Filters use the same short circuiting logic that pythons `and`, `or` and `not`.
Filters use the same short circuiting logic as python's `and`, `or` and `not`.
This means that for example:
>>> Filters.regex(r'(a?x)') | Filters.regex(r'(b?x)')
@@ -103,6 +104,7 @@ class BaseFilter(object):
self.name = self.__class__.__name__
return self.name
@abstractmethod
def filter(self, update):
"""This method must be overwritten.
@@ -118,8 +120,6 @@ class BaseFilter(object):
"""
raise NotImplementedError
class InvertedFilter(BaseFilter):
"""Represents a filter that has been inverted.
@@ -215,6 +215,38 @@ class MergedFilter(BaseFilter):
self.and_filter or self.or_filter)
class _DiceEmoji(BaseFilter):
def __init__(self, emoji=None, name=None):
self.name = 'Filters.dice.{}'.format(name) if name else 'Filters.dice'
self.emoji = emoji
class _DiceValues(BaseFilter):
def __init__(self, values, name, emoji=None):
self.values = [values] if isinstance(values, int) else values
self.emoji = emoji
self.name = '{}({})'.format(name, values)
def filter(self, message):
if bool(message.dice and message.dice.value in self.values):
if self.emoji:
return message.dice.emoji == self.emoji
return True
def __call__(self, update):
if isinstance(update, Update):
return self.filter(update.effective_message)
else:
return self._DiceValues(update, self.name, emoji=self.emoji)
def filter(self, message):
if bool(message.dice):
if self.emoji:
return message.dice.emoji == self.emoji
return True
class Filters(object):
"""Predefined filters for use as the `filter` argument of :class:`telegram.ext.MessageHandler`.
@@ -236,38 +268,35 @@ class Filters(object):
class _Text(BaseFilter):
name = 'Filters.text'
class _TextIterable(BaseFilter):
class _TextStrings(BaseFilter):
def __init__(self, iterable):
self.iterable = iterable
self.name = 'Filters.text({})'.format(iterable)
def __init__(self, strings):
self.strings = strings
self.name = 'Filters.text({})'.format(strings)
def filter(self, message):
if message.text and not message.text.startswith('/'):
return message.text in self.iterable
if message.text:
return message.text in self.strings
return False
def __call__(self, update):
if isinstance(update, Update):
if self.update_filter:
return self.filter(update)
else:
return self.filter(update.effective_message)
return self.filter(update.effective_message)
else:
return self._TextIterable(update)
return self._TextStrings(update)
def filter(self, message):
return bool(message.text and not message.text.startswith('/'))
return bool(message.text)
text = _Text()
"""Text Messages. If an iterable of strings is passed, it filters messages to only allow those
whose text is appearing in the given iterable.
"""Text Messages. If a list of strings is passed, it filters messages to only allow those
whose text is appearing in the given list.
Examples:
To allow any text message, simply use
``MessageHandler(Filters.text, callback_method)``.
A simple usecase for passing an iterable is to allow only messages that were send by a
A simple usecase for passing a list is to allow only messages that were send by a
custom :class:`telegram.ReplyKeyboardMarkup`::
buttons = ['Start', 'Settings', 'Back']
@@ -275,57 +304,94 @@ class Filters(object):
...
MessageHandler(Filters.text(buttons), callback_method)
Note:
* Dice messages don't have text. If you want to filter either text or dice messages, use
``Filters.text | Filters.dice``.
* Messages containing a command are accepted by this filter. Use
``Filters.text & (~Filters.command)``, if you want to filter only text messages without
commands.
Args:
update (Iterable[:obj:`str`], optional): Which messages to allow. Only exact matches
are allowed. If not specified, will allow any text message.
update (List[:obj:`str`] | Tuple[:obj:`str`], optional): Which messages to allow. Only
exact matches are allowed. If not specified, will allow any text message.
"""
class _Caption(BaseFilter):
name = 'Filters.caption'
class _CaptionIterable(BaseFilter):
class _CaptionStrings(BaseFilter):
def __init__(self, iterable):
self.iterable = iterable
self.name = 'Filters.caption({})'.format(iterable)
def __init__(self, strings):
self.strings = strings
self.name = 'Filters.caption({})'.format(strings)
def filter(self, message):
if message.caption:
return message.caption in self.iterable
return message.caption in self.strings
return False
def __call__(self, update):
if isinstance(update, Update):
if self.update_filter:
return self.filter(update)
else:
return self.filter(update.effective_message)
return self.filter(update.effective_message)
else:
return self._CaptionIterable(update)
return self._CaptionStrings(update)
def filter(self, message):
return bool(message.caption)
caption = _Caption()
"""Messages with a caption. If an iterable of strings is passed, it filters messages to only
allow those whose caption is appearing in the given iterable.
"""Messages with a caption. If a list of strings is passed, it filters messages to only
allow those whose caption is appearing in the given list.
Examples:
``MessageHandler(Filters.caption, callback_method)``
Args:
update (Iterable[:obj:`str`], optional): Which captions to allow. Only exact matches
are allowed. If not specified, will allow any message with a caption.
update (List[:obj:`str`] | Tuple[:obj:`str`], optional): Which captions to allow. Only
exact matches are allowed. If not specified, will allow any message with a caption.
"""
class _Command(BaseFilter):
name = 'Filters.command'
class _CommandOnlyStart(BaseFilter):
def __init__(self, only_start):
self.only_start = only_start
self.name = 'Filters.command({})'.format(only_start)
def filter(self, message):
return (message.entities
and any([e.type == MessageEntity.BOT_COMMAND for e in message.entities]))
def __call__(self, update):
if isinstance(update, Update):
return self.filter(update.effective_message)
else:
return self._CommandOnlyStart(update)
def filter(self, message):
return bool(message.text and message.text.startswith('/'))
return (message.entities and message.entities[0].type == MessageEntity.BOT_COMMAND
and message.entities[0].offset == 0)
command = _Command()
"""Messages starting with ``/``."""
"""
Messages with a :attr:`telegram.MessageEntity.BOT_COMMAND`. By default only allows
messages `starting` with a bot command. Pass ``False`` to also allow messages that contain a
bot command `anywhere` in the text.
Examples::
MessageHandler(Filters.command, command_at_start_callback)
MessageHandler(Filters.command(False), command_anywhere_callback)
Note:
``Filters.text`` also accepts messages containing a command.
Args:
update (:obj:`bool`, optional): Whether to only allow messages that `start` with a bot
command. Defaults to ``True``.
"""
class regex(BaseFilter):
"""
@@ -344,7 +410,7 @@ class Filters(object):
if you need to specify flags on your pattern.
Note:
Filters use the same short circuiting logic that pythons `and`, `or` and `not`.
Filters use the same short circuiting logic as python's `and`, `or` and `not`.
This means that for example:
>>> Filters.regex(r'(a?x)') | Filters.regex(r'(b?x)')
@@ -403,7 +469,7 @@ class Filters(object):
send media with wrong types that don't fit to this handler.
Example:
Filters.documents.category('audio/') returnes `True` for all types
Filters.documents.category('audio/') returns `True` for all types
of audio sent as file, for example 'audio/mpeg' or 'audio/x-wav'
"""
@@ -481,38 +547,6 @@ class Filters(object):
``Filters.document`` for all document messages.
Attributes:
category: This Filter filters documents by their category in the mime-type attribute.
Example:
``Filters.documents.category('audio/')`` filters all types
of audio sent as file, for example 'audio/mpeg' or 'audio/x-wav'. The following
attributes can be used as a shortcut like: ``Filters.document.audio``
application:
audio:
image:
video:
text:
mime_type: This Filter filters documents by their mime-type attribute.
Example:
``Filters.documents.mime_type('audio/mpeg')`` filters all audio in mp3 format. The
following attributes can be used as a shortcut like: ``Filters.document.jpg``
apk:
doc:
docx:
exe:
gif:
jpg:
mp3:
pdf:
py:
svg:
txt:
targz:
wav:
xml:
zip:
category: This Filter filters documents by their category in the mime-type attribute
Note:
@@ -956,6 +990,46 @@ officedocument.wordprocessingml.document")``-
passport_data = _PassportData()
"""Messages that contain a :class:`telegram.PassportData`"""
class _Poll(BaseFilter):
name = 'Filters.poll'
def filter(self, message):
return bool(message.poll)
poll = _Poll()
"""Messages that contain a :class:`telegram.Poll`."""
class _Dice(_DiceEmoji):
dice = _DiceEmoji('🎲', 'dice')
darts = _DiceEmoji('🎯', 'darts')
dice = _Dice()
"""Dice Messages. If an integer or a list of integers is passed, it filters messages to only
allow those whose dice value is appearing in the given list.
Examples:
To allow any dice message, simply use
``MessageHandler(Filters.dice, callback_method)``.
To allow only dice with value 6, use
``MessageHandler(Filters.dice(6), callback_method)``.
To allow only dice with value 5 `or` 6, use
``MessageHandler(Filters.dice([5, 6]), callback_method)``.
Args:
update (:obj:`int` | List[:obj:`int`], optional): Which values to allow. If not
specified, will allow any dice message.
Note:
Dice messages don't have text. If you want to filter either text or dice messages, use
``Filters.text | Filters.dice``.
Attributes:
dice: Dice messages with the emoji 🎲. Passing a list of integers is supported just as for
:attr:`Filters.dice`.
darts: Dice messages with the emoji 🎯. Passing a list of integers is supported just as for
:attr:`Filters.dice`.
"""
class language(BaseFilter):
"""Filters messages to only allow those which are from users with a certain language code.
@@ -987,8 +1061,10 @@ officedocument.wordprocessingml.document")``-
class _UpdateType(BaseFilter):
update_filter = True
name = 'Filters.update'
class _Message(BaseFilter):
name = 'Filters.update.message'
update_filter = True
def filter(self, update):
@@ -997,6 +1073,7 @@ officedocument.wordprocessingml.document")``-
message = _Message()
class _EditedMessage(BaseFilter):
name = 'Filters.update.edited_message'
update_filter = True
def filter(self, update):
@@ -1005,6 +1082,7 @@ officedocument.wordprocessingml.document")``-
edited_message = _EditedMessage()
class _Messages(BaseFilter):
name = 'Filters.update.messages'
update_filter = True
def filter(self, update):
@@ -1013,6 +1091,7 @@ officedocument.wordprocessingml.document")``-
messages = _Messages()
class _ChannelPost(BaseFilter):
name = 'Filters.update.channel_post'
update_filter = True
def filter(self, update):
@@ -1022,6 +1101,7 @@ officedocument.wordprocessingml.document")``-
class _EditedChannelPost(BaseFilter):
update_filter = True
name = 'Filters.update.edited_channel_post'
def filter(self, update):
return update.edited_channel_post is not None
@@ -1030,6 +1110,7 @@ officedocument.wordprocessingml.document")``-
class _ChannelPosts(BaseFilter):
update_filter = True
name = 'Filters.update.channel_posts'
def filter(self, update):
return update.channel_post is not None or update.edited_channel_post is not None
+5 -3
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -18,8 +18,10 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the base class for handlers as used by the Dispatcher."""
from abc import ABC, abstractmethod
class Handler(object):
class Handler(ABC):
"""The base class for all update handlers. Create custom handlers by inheriting from it.
Attributes:
@@ -82,6 +84,7 @@ class Handler(object):
self.pass_user_data = pass_user_data
self.pass_chat_data = pass_chat_data
@abstractmethod
def check_update(self, update):
"""
This method is called to determine if an update should be handled by
@@ -96,7 +99,6 @@ class Handler(object):
when the update gets handled.
"""
raise NotImplementedError
def handle_update(self, update, dispatcher, check_result, context=None):
"""
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+213 -31
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -18,6 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes JobQueue and Job."""
import calendar
import datetime
import logging
import time
@@ -29,7 +30,7 @@ from threading import Thread, Lock, Event
from telegram.ext.callbackcontext import CallbackContext
from telegram.utils.deprecate import TelegramDeprecationWarning
from telegram.utils.helpers import to_float_timestamp, _UTC
from telegram.utils.helpers import to_float_timestamp
class Days(object):
@@ -95,14 +96,16 @@ class JobQueue(object):
"""
# get time at which to run:
time_spec = time_spec or job.interval
if time_spec is None:
time_spec = job.interval
if time_spec is None:
raise ValueError("no time specification given for scheduling non-repeating job")
next_t = to_float_timestamp(time_spec, reference_timestamp=previous_t)
# enqueue:
self.logger.debug('Putting job %s with t=%f', job.name, time_spec)
self.logger.debug('Putting job %s with t=%s', job.name, time_spec)
self._queue.put((next_t, job))
job._set_next_t(next_t)
# Wake up the loop if this job should be executed next
self._set_next_peek(next_t)
@@ -112,9 +115,12 @@ class JobQueue(object):
Args:
callback (:obj:`callable`): The callback function that should be executed by the new
job. It should take ``bot, job`` as parameters, where ``job`` is the
:class:`telegram.ext.Job` instance. It can be used to access its
``job.context`` or change it to a repeating job.
job. Callback signature for context based API:
``def callback(CallbackContext)``
``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access
its ``job.context`` or change it to a repeating job.
when (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta` | \
:obj:`datetime.datetime` | :obj:`datetime.time`):
Time in or at which the job should run. This parameter will be interpreted
@@ -125,10 +131,14 @@ class JobQueue(object):
* :obj:`datetime.timedelta` will be interpreted as "time from now" in which the
job should run.
* :obj:`datetime.datetime` will be interpreted as a specific date and time at
which the job should run.
which the job should run. If the timezone (``datetime.tzinfo``) is ``None``, UTC
will be assumed.
* :obj:`datetime.time` will be interpreted as a specific time of day at which the
job should run. This could be either today or, if the time has already passed,
tomorrow.
tomorrow. If the timezone (``time.tzinfo``) is ``None``, UTC will be assumed.
If ``when`` is :obj:`datetime.datetime` or :obj:`datetime.time` type
then ``when.tzinfo`` will define ``Job.tzinfo``. Otherwise UTC will be assumed.
context (:obj:`object`, optional): Additional data needed for the callback function.
Can be accessed through ``job.context`` in the callback. Defaults to ``None``.
@@ -140,7 +150,14 @@ class JobQueue(object):
queue.
"""
job = Job(callback, repeat=False, context=context, name=name, job_queue=self)
tzinfo = when.tzinfo if isinstance(when, (datetime.datetime, datetime.time)) else None
job = Job(callback,
repeat=False,
context=context,
name=name,
job_queue=self,
tzinfo=tzinfo)
self._put(job, time_spec=when)
return job
@@ -149,9 +166,12 @@ class JobQueue(object):
Args:
callback (:obj:`callable`): The callback function that should be executed by the new
job. It should take ``bot, job`` as parameters, where ``job`` is the
:class:`telegram.ext.Job` instance. It can be used to access its
``Job.context`` or change it to a repeating job.
job. Callback signature for context based API:
``def callback(CallbackContext)``
``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access
its ``job.context`` or change it to a repeating job.
interval (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta`): The interval in which
the job will run. If it is an :obj:`int` or a :obj:`float`, it will be interpreted
as seconds.
@@ -165,10 +185,14 @@ class JobQueue(object):
* :obj:`datetime.timedelta` will be interpreted as "time from now" in which the
job should run.
* :obj:`datetime.datetime` will be interpreted as a specific date and time at
which the job should run.
which the job should run. If the timezone (``datetime.tzinfo``) is ``None``, UTC
will be assumed.
* :obj:`datetime.time` will be interpreted as a specific time of day at which the
job should run. This could be either today or, if the time has already passed,
tomorrow.
tomorrow. If the timezone (``time.tzinfo``) is ``None``, UTC will be assumed.
If ``first`` is :obj:`datetime.datetime` or :obj:`datetime.time` type
then ``first.tzinfo`` will define ``Job.tzinfo``. Otherwise UTC will be assumed.
Defaults to ``interval``
context (:obj:`object`, optional): Additional data needed for the callback function.
@@ -180,31 +204,145 @@ class JobQueue(object):
:class:`telegram.ext.Job`: The new ``Job`` instance that has been added to the job
queue.
Notes:
Note:
`interval` is always respected "as-is". That means that if DST changes during that
interval, the job might not run at the time one would expect. It is always recommended
to pin servers to UTC time, then time related behaviour can always be expected.
"""
tzinfo = first.tzinfo if isinstance(first, (datetime.datetime, datetime.time)) else None
job = Job(callback,
interval=interval,
repeat=True,
context=context,
name=name,
job_queue=self)
job_queue=self,
tzinfo=tzinfo)
self._put(job, time_spec=first)
return job
def run_monthly(self, callback, when, day, context=None, name=None, day_is_strict=True):
"""Creates a new ``Job`` that runs on a monthly basis and adds it to the queue.
Args:
callback (:obj:`callable`): The callback function that should be executed by the new
job. Callback signature for context based API:
``def callback(CallbackContext)``
``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access
its ``job.context`` or change it to a repeating job.
when (:obj:`datetime.time`): Time of day at which the job should run. If the timezone
(``when.tzinfo``) is ``None``, UTC will be assumed. This will also implicitly
define ``Job.tzinfo``.
day (:obj:`int`): Defines the day of the month whereby the job would run. It should
be within the range of 1 and 31, inclusive.
context (:obj:`object`, optional): Additional data needed for the callback function.
Can be accessed through ``job.context`` in the callback. Defaults to ``None``.
name (:obj:`str`, optional): The name of the new job. Defaults to
``callback.__name__``.
day_is_strict (:obj:`bool`, optional): If ``False`` and day > month.days, will pick
the last day in the month. Defaults to ``True``.
Returns:
:class:`telegram.ext.Job`: The new ``Job`` instance that has been added to the job
queue.
"""
tzinfo = when.tzinfo if isinstance(when, (datetime.datetime, datetime.time)) else None
if 1 <= day <= 31:
next_dt = self._get_next_month_date(day, day_is_strict, when, allow_now=True)
job = Job(callback, repeat=False, context=context, name=name, job_queue=self,
is_monthly=True, day_is_strict=day_is_strict, tzinfo=tzinfo)
self._put(job, time_spec=next_dt)
return job
else:
raise ValueError("The elements of the 'day' argument should be from 1 up to"
" and including 31")
def _get_next_month_date(self, day, day_is_strict, when, allow_now=False):
"""This method returns the date that the next monthly job should be scheduled.
Args:
day (:obj:`int`): The day of the month the job should run.
day_is_strict (:obj:`bool`):
Specification as to whether the specified day of job should be strictly
respected. If day_is_strict is ``True`` it ignores months whereby the
specified date does not exist (e.g February 31st). If it set to ``False``,
it returns the last valid date of the month instead. For example,
if the user runs a job on the 31st of every month, and sets
the day_is_strict variable to ``False``, April, for example,
the job would run on April 30th.
when (:obj:`datetime.time`): Time of day at which the job should run. If the
timezone (``time.tzinfo``) is ``None``, UTC will be assumed.
allow_now (:obj:`bool`): Whether executing the job right now is a feasible options.
For stability reasons, this defaults to :obj:`False`, but it needs to be :obj:`True`
on initializing a job.
"""
dt = datetime.datetime.now(tz=when.tzinfo or datetime.timezone.utc)
dt_time = dt.time().replace(tzinfo=when.tzinfo)
days_in_current_month = calendar.monthrange(dt.year, dt.month)[1]
days_till_months_end = days_in_current_month - dt.day
if days_in_current_month < day:
# if the day does not exist in the current month (e.g Feb 31st)
if day_is_strict is False:
# set day as last day of month instead
next_dt = dt + datetime.timedelta(days=days_till_months_end)
else:
# else set as day in subsequent month. Subsequent month is
# guaranteed to have the date, if current month does not have the date.
next_dt = dt + datetime.timedelta(days=days_till_months_end + day)
else:
# if the day exists in the current month
if dt.day < day:
# day is upcoming
next_dt = dt + datetime.timedelta(day - dt.day)
elif dt.day > day or (dt.day == day and ((not allow_now and dt_time >= when)
or (allow_now and dt_time > when))):
# run next month if day has already passed
next_year = dt.year + 1 if dt.month == 12 else dt.year
next_month = 1 if dt.month == 12 else dt.month + 1
days_in_next_month = calendar.monthrange(next_year, next_month)[1]
next_month_has_date = days_in_next_month >= day
if next_month_has_date:
next_dt = dt + datetime.timedelta(days=days_till_months_end + day)
elif day_is_strict:
# schedule the subsequent month if day is strict
next_dt = dt + datetime.timedelta(
days=days_till_months_end + days_in_next_month + day)
else:
# schedule in the next month last date if day is not strict
next_dt = dt + datetime.timedelta(days=days_till_months_end
+ days_in_next_month)
else:
# day is today but time has not yet come
next_dt = dt
# Set the correct time
next_dt = next_dt.replace(hour=when.hour, minute=when.minute, second=when.second,
microsecond=when.microsecond)
# fold is new in Py3.6
if hasattr(next_dt, 'fold'):
next_dt = next_dt.replace(fold=when.fold)
return next_dt
def run_daily(self, callback, time, days=Days.EVERY_DAY, context=None, name=None):
"""Creates a new ``Job`` that runs on a daily basis and adds it to the queue.
Args:
callback (:obj:`callable`): The callback function that should be executed by the new
job. It should take ``bot, job`` as parameters, where ``job`` is the
:class:`telegram.ext.Job` instance. It can be used to access its ``Job.context``
or change it to a repeating job.
job. Callback signature for context based API:
``def callback(CallbackContext)``
``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access
its ``job.context`` or change it to a repeating job.
time (:obj:`datetime.time`): Time of day at which the job should run. If the timezone
(``time.tzinfo``) is ``None``, UTC will be assumed.
``time.tzinfo`` will implicitly define ``Job.tzinfo``.
days (Tuple[:obj:`int`], optional): Defines on which days of the week the job should
run. Defaults to ``EVERY_DAY``
context (:obj:`object`, optional): Additional data needed for the callback function.
@@ -216,7 +354,7 @@ class JobQueue(object):
:class:`telegram.ext.Job`: The new ``Job`` instance that has been added to the job
queue.
Notes:
Note:
Daily is just an alias for "24 Hours". That means that if DST changes during that
interval, the job might not run at the time one would expect. It is always recommended
to pin servers to UTC time, then time related behaviour can always be expected.
@@ -275,9 +413,10 @@ class JobQueue(object):
if job.enabled:
try:
current_week_day = datetime.datetime.now(job.tzinfo).date().weekday()
if any(day == current_week_day for day in job.days):
if current_week_day in job.days:
self.logger.debug('Running job %s', job.name)
job.run(self._dispatcher)
self._dispatcher.update_persistence()
except Exception:
self.logger.exception('An uncaught error was raised while executing job %s',
@@ -287,7 +426,13 @@ class JobQueue(object):
if job.repeat and not job.removed:
self._put(job, previous_t=t)
elif job.is_monthly and not job.removed:
dt = datetime.datetime.now(tz=job.tzinfo)
dt_time = dt.time().replace(tzinfo=job.tzinfo)
self._put(job, time_spec=self._get_next_month_date(dt.day, job.day_is_strict,
dt_time))
else:
job._set_next_t(None)
self.logger.debug('Dropping non-repeating or removed job %s', job.name)
def start(self):
@@ -354,12 +499,17 @@ class Job(object):
callback (:obj:`callable`): The callback function that should be executed by the new job.
context (:obj:`object`): Optional. Additional data needed for the callback function.
name (:obj:`str`): Optional. The name of the new job.
is_monthly (:obj: `bool`): Optional. Indicates whether it is a monthly job.
day_is_strict (:obj: `bool`): Optional. Indicates whether the monthly jobs day is strict.
Args:
callback (:obj:`callable`): The callback function that should be executed by the new job.
It should take ``bot, job`` as parameters, where ``job`` is the
:class:`telegram.ext.Job` instance. It can be used to access it's :attr:`context`
or change it to a repeating job.
Callback signature for context based API:
``def callback(CallbackContext)``
a ``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access
its ``job.context`` or change it to a repeating job.
interval (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta`, optional): The time
interval between executions of the job. If it is an :obj:`int` or a :obj:`float`,
it will be interpreted as seconds. If you don't set this value, you must set
@@ -377,6 +527,11 @@ class Job(object):
tzinfo (:obj:`datetime.tzinfo`, optional): timezone associated to this job. Used when
checking the day of the week to determine whether a job should run (only relevant when
``days is not Days.EVERY_DAY``). Defaults to UTC.
is_monthly (:obj:`bool`, optional): If this job is supposed to be a monthly scheduled job.
Defaults to ``False``.
day_is_strict (:obj:`bool`, optional): If ``False`` and day > month.days, will pick the
last day in the month. Defaults to ``True``. Only relevant when ``is_monthly`` is
``True``.
"""
def __init__(self,
@@ -387,7 +542,9 @@ class Job(object):
days=Days.EVERY_DAY,
name=None,
job_queue=None,
tzinfo=_UTC):
tzinfo=None,
is_monthly=False,
day_is_strict=True):
self.callback = callback
self.context = context
@@ -396,11 +553,14 @@ class Job(object):
self._repeat = None
self._interval = None
self.interval = interval
self._next_t = None
self.repeat = repeat
self.is_monthly = is_monthly
self.day_is_strict = day_is_strict
self._days = None
self.days = days
self.tzinfo = tzinfo
self.tzinfo = tzinfo or datetime.timezone.utc
self._job_queue = weakref.proxy(job_queue) if job_queue is not None else None
@@ -422,6 +582,7 @@ class Job(object):
"""
self._remove.set()
self._next_t = None
@property
def removed(self):
@@ -455,8 +616,8 @@ class Job(object):
raise ValueError("The 'interval' can not be 'None' when 'repeat' is set to 'True'")
if not (interval is None or isinstance(interval, (Number, datetime.timedelta))):
raise ValueError("The 'interval' must be of type 'datetime.timedelta',"
" 'int' or 'float'")
raise TypeError("The 'interval' must be of type 'datetime.timedelta',"
" 'int' or 'float'")
self._interval = interval
@@ -469,6 +630,27 @@ class Job(object):
else:
return interval
@property
def next_t(self):
"""
:obj:`datetime.datetime`: Datetime for the next job execution.
Datetime is localized according to :attr:`tzinfo`.
If job is removed or already ran it equals to ``None``.
"""
return datetime.datetime.fromtimestamp(self._next_t, self.tzinfo) if self._next_t else None
def _set_next_t(self, next_t):
if isinstance(next_t, datetime.datetime):
# Set timezone to UTC in case datetime is in local timezone.
next_t = next_t.astimezone(datetime.timezone.utc)
next_t = to_float_timestamp(next_t)
elif not (isinstance(next_t, Number) or next_t is None):
raise TypeError("The 'next_t' argument should be one of the following types: "
"'float', 'int', 'datetime.datetime' or 'NoneType'")
self._next_t = next_t
@property
def repeat(self):
""":obj:`bool`: Optional. If this job should periodically execute its callback function."""
@@ -488,10 +670,10 @@ class Job(object):
@days.setter
def days(self, days):
if not isinstance(days, tuple):
raise ValueError("The 'days' argument should be of type 'tuple'")
raise TypeError("The 'days' argument should be of type 'tuple'")
if not all(isinstance(day, int) for day in days):
raise ValueError("The elements of the 'days' argument should be of type 'int'")
raise TypeError("The elements of the 'days' argument should be of type 'int'")
if not all(0 <= day <= 6 for day in days):
raise ValueError("The elements of the 'days' argument should be from 0 up to and "
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+2 -2
View File
@@ -4,7 +4,7 @@
# Tymofii A. Khodniev (thodnev) <thodnev@mail.ru>
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -249,7 +249,7 @@ class MessageQueue(object):
``DelayQueue`` (if set to ``False``), resulting in needed delays to avoid
hitting specified limits. Defaults to ``False``.
Notes:
Note:
Method is designed to accept ``telegram.utils.promise.Promise`` as ``promise``
argument, but other callables could be used too. For example, lambdas or simple
functions could be used to wrap original func to be called with needed args. In that
+65 -11
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -34,6 +34,8 @@ class PicklePersistence(BasePersistence):
persistence class.
store_chat_data (:obj:`bool`): Optional. Whether user_data should be saved by this
persistence class.
store_bot_data (:obj:`bool`): Optional. Whether bot_data should be saved by this
persistence class.
single_file (:obj:`bool`): Optional. When ``False`` will store 3 sperate files of
`filename_user_data`, `filename_chat_data` and `filename_conversations`. Default is
``True``.
@@ -48,6 +50,8 @@ class PicklePersistence(BasePersistence):
persistence class. Default is ``True``.
store_chat_data (:obj:`bool`, optional): Whether user_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. Default is ``True`` .
single_file (:obj:`bool`, optional): When ``False`` will store 3 sperate files of
`filename_user_data`, `filename_chat_data` and `filename_conversations`. Default is
``True``.
@@ -56,29 +60,38 @@ class PicklePersistence(BasePersistence):
on any transaction *and* on call fo :meth:`flush`. Default is ``False``.
"""
def __init__(self, filename, store_user_data=True, store_chat_data=True, single_file=True,
def __init__(self, filename,
store_user_data=True,
store_chat_data=True,
store_bot_data=True,
single_file=True,
on_flush=False):
super(PicklePersistence, self).__init__(store_user_data=store_user_data,
store_chat_data=store_chat_data,
store_bot_data=store_bot_data)
self.filename = filename
self.store_user_data = store_user_data
self.store_chat_data = store_chat_data
self.single_file = single_file
self.on_flush = on_flush
self.user_data = None
self.chat_data = None
self.bot_data = None
self.conversations = None
def load_singlefile(self):
try:
filename = self.filename
with open(self.filename, "rb") as f:
all = pickle.load(f)
self.user_data = defaultdict(dict, all['user_data'])
self.chat_data = defaultdict(dict, all['chat_data'])
self.conversations = all['conversations']
data = pickle.load(f)
self.user_data = defaultdict(dict, data['user_data'])
self.chat_data = defaultdict(dict, data['chat_data'])
# For backwards compatibility with files not containing bot data
self.bot_data = data.get('bot_data', {})
self.conversations = data['conversations']
except IOError:
self.conversations = {}
self.user_data = defaultdict(dict)
self.chat_data = defaultdict(dict)
self.bot_data = {}
except pickle.UnpicklingError:
raise TypeError("File {} does not contain valid pickle data".format(filename))
except Exception:
@@ -97,9 +110,9 @@ class PicklePersistence(BasePersistence):
def dump_singlefile(self):
with open(self.filename, "wb") as f:
all = {'conversations': self.conversations, 'user_data': self.user_data,
'chat_data': self.chat_data}
pickle.dump(all, f)
data = {'conversations': self.conversations, 'user_data': self.user_data,
'chat_data': self.chat_data, 'bot_data': self.bot_data}
pickle.dump(data, f)
def dump_file(self, filename, data):
with open(filename, "wb") as f:
@@ -145,6 +158,24 @@ class PicklePersistence(BasePersistence):
self.load_singlefile()
return deepcopy(self.chat_data)
def get_bot_data(self):
"""Returns the bot_data from the pickle file if it exsists or an empty dict.
Returns:
:obj:`defaultdict`: The restored bot data.
"""
if self.bot_data:
pass
elif not self.single_file:
filename = "{}_bot_data".format(self.filename)
data = self.load_file(filename)
if not data:
data = {}
self.bot_data = data
else:
self.load_singlefile()
return deepcopy(self.bot_data)
def get_conversations(self, name):
"""Returns the conversations from the pickle file if it exsists or an empty defaultdict.
@@ -193,6 +224,8 @@ class PicklePersistence(BasePersistence):
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].
"""
if self.user_data is None:
self.user_data = defaultdict(dict)
if self.user_data.get(user_id) == data:
return
self.user_data[user_id] = data
@@ -211,6 +244,8 @@ class PicklePersistence(BasePersistence):
chat_id (:obj:`int`): The chat the data might have been changed for.
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.chat_data` [chat_id].
"""
if self.chat_data is None:
self.chat_data = defaultdict(dict)
if self.chat_data.get(chat_id) == data:
return
self.chat_data[chat_id] = data
@@ -221,6 +256,23 @@ class PicklePersistence(BasePersistence):
else:
self.dump_singlefile()
def update_bot_data(self, data):
"""Will update the bot_data (if changed) and depending on :attr:`on_flush` save the
pickle file.
Args:
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.bot_data`.
"""
if self.bot_data == data:
return
self.bot_data = data.copy()
if not self.on_flush:
if not self.single_file:
filename = "{}_bot_data".format(self.filename)
self.dump_file(filename, self.bot_data)
else:
self.dump_singlefile()
def flush(self):
""" Will save all data in memory to pickle file(s).
"""
@@ -232,5 +284,7 @@ class PicklePersistence(BasePersistence):
self.dump_file("{}_user_data".format(self.filename), self.user_data)
if self.chat_data:
self.dump_file("{}_chat_data".format(self.filename), self.chat_data)
if self.bot_data:
self.dump_file("{}_bot_data".format(self.filename), self.bot_data)
if self.conversations:
self.dump_file("{}_conversations".format(self.filename), self.conversations)
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2019
# 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/].
from telegram import Update
from .handler import Handler
class PollAnswerHandler(Handler):
"""Handler class to handle Telegram updates that contain a poll answer.
Attributes:
callback (:obj:`callable`): The callback function for this handler.
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:
: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.
"""
def check_update(self, update):
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
Returns:
:obj:`bool`
"""
return isinstance(update, Update) and update.poll_answer
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2019
# 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/].
from telegram import Update
from .handler import Handler
class PollHandler(Handler):
"""Handler class to handle Telegram updates that contain a poll.
Attributes:
callback (:obj:`callable`): The callback function for this handler.
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:
: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.
"""
def check_update(self, update):
"""Determines whether an update should be passed to this handlers :attr:`callback`.
Args:
update (:class:`telegram.Update`): Incoming telegram update.
Returns:
:obj:`bool`
"""
return isinstance(update, Update) and update.poll
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+125 -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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -62,28 +62,38 @@ class Updater(object):
Args:
token (:obj:`str`, optional): The bot's token given by the @BotFather.
base_url (:obj:`str`, optional): Base_url for the bot.
base_file_url (:obj:`str`, optional): Base_file_url for the bot.
workers (:obj:`int`, optional): Amount of threads in the thread pool for functions
decorated with ``@run_async``.
bot (:class:`telegram.Bot`, optional): A pre-initialized bot instance. If a pre-initialized
bot is used, it is the user's responsibility to create it using a `Request`
instance with a large enough connection pool.
decorated with ``@run_async`` (ignored if `dispatcher` argument is used).
bot (:class:`telegram.Bot`, optional): A pre-initialized bot instance (ignored if
`dispatcher` argument is used). If a pre-initialized bot is used, it is the user's
responsibility to create it using a `Request` instance with a large enough connection
pool.
dispatcher (:class:`telegram.ext.Dispatcher`, optional): A pre-initialized dispatcher
instance. If a pre-initialized dispatcher is used, it is the user's responsibility to
create it with proper arguments.
private_key (:obj:`bytes`, optional): Private key for decryption of telegram passport data.
private_key_password (:obj:`bytes`, optional): Password for above private key.
user_sig_handler (:obj:`function`, optional): Takes ``signum, frame`` as positional
arguments. This will be called when a signal is received, defaults are (SIGINT,
SIGTERM, SIGABRT) setable with :attr:`idle`.
request_kwargs (:obj:`dict`, optional): Keyword args to control the creation of a
`telegram.utils.request.Request` object (ignored if `bot` argument is used). The
request_kwargs are very useful for the advanced users who would like to control the
default timeouts and/or control the proxy used for http communication.
use_context (:obj:`bool`, optional): If set to ``True`` Use the context based callback API.
During the deprecation period of the old API the default is ``False``. **New users**:
set this to ``True``.
`telegram.utils.request.Request` object (ignored if `bot` or `dispatcher` argument is
used). The request_kwargs are very useful for the advanced users who would like to
control the default timeouts and/or control the proxy used for http communication.
use_context (:obj:`bool`, optional): If set to ``True`` Use the context based callback API
(ignored if `dispatcher` argument is used). During the deprecation period of the old
API the default is ``False``. **New users**: set this to ``True``.
persistence (:class:`telegram.ext.BasePersistence`, optional): The persistence class to
store data that should be persistent over restarts.
store data that should be persistent over restarts (ignored if `dispatcher` argument is
used).
defaults (:class:`telegram.ext.Defaults`, optional): An object containing default values to
be used if not set explicitly in the bot methods.
Note:
You must supply either a :attr:`bot` or a :attr:`token` argument.
* You must supply either a :attr:`bot` or a :attr:`token` argument.
* If you supply a :attr:`bot`, you will need to pass :attr:`defaults` to *both* the bot and
the :class:`telegram.ext.Updater`.
Raises:
ValueError: If both :attr:`token` and :attr:`bot` are passed or none of them.
@@ -102,53 +112,85 @@ class Updater(object):
user_sig_handler=None,
request_kwargs=None,
persistence=None,
use_context=False):
defaults=None,
use_context=False,
dispatcher=None,
base_file_url=None):
if (token is None) and (bot is None):
raise ValueError('`token` or `bot` must be passed')
if (token is not None) and (bot is not None):
raise ValueError('`token` and `bot` are mutually exclusive')
if (private_key is not None) and (bot is not None):
raise ValueError('`bot` and `private_key` are mutually exclusive')
if dispatcher is None:
if (token is None) and (bot is None):
raise ValueError('`token` or `bot` must be passed')
if (token is not None) and (bot is not None):
raise ValueError('`token` and `bot` are mutually exclusive')
if (private_key is not None) and (bot is not None):
raise ValueError('`bot` and `private_key` are mutually exclusive')
else:
if bot is not None:
raise ValueError('`dispatcher` and `bot` are mutually exclusive')
if persistence is not None:
raise ValueError('`dispatcher` and `persistence` are mutually exclusive')
if workers is not None:
raise ValueError('`dispatcher` and `workers` are mutually exclusive')
if use_context != dispatcher.use_context:
raise ValueError('`dispatcher` and `use_context` are mutually exclusive')
self.logger = logging.getLogger(__name__)
con_pool_size = workers + 4
if dispatcher is None:
con_pool_size = workers + 4
if bot is not None:
self.bot = bot
if bot.request.con_pool_size < con_pool_size:
if bot is not None:
self.bot = bot
if bot.request.con_pool_size < con_pool_size:
self.logger.warning(
'Connection pool of Request object is smaller than optimal value (%s)',
con_pool_size)
else:
# we need a connection pool the size of:
# * for each of the workers
# * 1 for Dispatcher
# * 1 for polling Updater (even if webhook is used, we can spare a connection)
# * 1 for JobQueue
# * 1 for main thread
if request_kwargs is None:
request_kwargs = {}
if 'con_pool_size' not in request_kwargs:
request_kwargs['con_pool_size'] = con_pool_size
self._request = Request(**request_kwargs)
self.bot = Bot(token,
base_url,
base_file_url=base_file_url,
request=self._request,
private_key=private_key,
private_key_password=private_key_password,
defaults=defaults)
self.update_queue = Queue()
self.job_queue = JobQueue()
self.__exception_event = Event()
self.persistence = persistence
self.dispatcher = Dispatcher(self.bot,
self.update_queue,
job_queue=self.job_queue,
workers=workers,
exception_event=self.__exception_event,
persistence=persistence,
use_context=use_context)
self.job_queue.set_dispatcher(self.dispatcher)
else:
con_pool_size = dispatcher.workers + 4
self.bot = dispatcher.bot
if self.bot.request.con_pool_size < con_pool_size:
self.logger.warning(
'Connection pool of Request object is smaller than optimal value (%s)',
con_pool_size)
else:
# we need a connection pool the size of:
# * for each of the workers
# * 1 for Dispatcher
# * 1 for polling Updater (even if webhook is used, we can spare a connection)
# * 1 for JobQueue
# * 1 for main thread
if request_kwargs is None:
request_kwargs = {}
if 'con_pool_size' not in request_kwargs:
request_kwargs['con_pool_size'] = con_pool_size
self._request = Request(**request_kwargs)
self.bot = Bot(token, base_url, request=self._request, private_key=private_key,
private_key_password=private_key_password)
self.update_queue = dispatcher.update_queue
self.__exception_event = dispatcher.exception_event
self.persistence = dispatcher.persistence
self.job_queue = dispatcher.job_queue
self.dispatcher = dispatcher
self.user_sig_handler = user_sig_handler
self.update_queue = Queue()
self.job_queue = JobQueue()
self.__exception_event = Event()
self.persistence = persistence
self.dispatcher = Dispatcher(
self.bot,
self.update_queue,
job_queue=self.job_queue,
workers=workers,
exception_event=self.__exception_event,
persistence=persistence,
use_context=use_context)
self.job_queue.set_dispatcher(self.dispatcher)
self.last_update_id = 0
self.running = False
self.is_idle = False
@@ -156,9 +198,14 @@ class Updater(object):
self.__lock = Lock()
self.__threads = []
# Just for passing to WebhookAppClass
self._default_quote = defaults.quote if defaults else None
def _init_thread(self, target, name, *args, **kwargs):
thr = Thread(target=self._thread_wrapper, name="Bot:{}:{}".format(self.bot.id, name),
args=(target,) + args, kwargs=kwargs)
thr = Thread(target=self._thread_wrapper,
name="Bot:{}:{}".format(self.bot.id, name),
args=(target,) + args,
kwargs=kwargs)
thr.start()
self.__threads.append(thr)
@@ -288,9 +335,10 @@ class Updater(object):
self.logger.debug('Bootstrap done')
def polling_action_cb():
updates = self.bot.get_updates(
self.last_update_id, timeout=timeout, read_latency=read_latency,
allowed_updates=allowed_updates)
updates = self.bot.get_updates(self.last_update_id,
timeout=timeout,
read_latency=read_latency,
allowed_updates=allowed_updates)
if updates:
if not self.running:
@@ -370,7 +418,8 @@ class Updater(object):
url_path = '/{0}'.format(url_path)
# Create Tornado app instance
app = WebhookAppClass(url_path, self.bot, self.update_queue)
app = WebhookAppClass(url_path, self.bot, self.update_queue,
default_quote=self._default_quote)
# Form SSL Context
# An SSLError is raised if the private key does not match with the certificate
@@ -391,12 +440,11 @@ class Updater(object):
if not webhook_url:
webhook_url = self._gen_webhook_url(listen, port, url_path)
self._bootstrap(
max_retries=bootstrap_retries,
clean=clean,
webhook_url=webhook_url,
cert=open(cert, 'rb'),
allowed_updates=allowed_updates)
self._bootstrap(max_retries=bootstrap_retries,
clean=clean,
webhook_url=webhook_url,
cert=open(cert, 'rb'),
allowed_updates=allowed_updates)
elif clean:
self.logger.warning("cleaning updates is not supported if "
"SSL-termination happens elsewhere; skipping")
@@ -407,7 +455,12 @@ class Updater(object):
def _gen_webhook_url(listen, port, url_path):
return 'https://{listen}:{port}{path}'.format(listen=listen, port=port, path=url_path)
def _bootstrap(self, max_retries, clean, webhook_url, allowed_updates, cert=None,
def _bootstrap(self,
max_retries,
clean,
webhook_url,
allowed_updates,
cert=None,
bootstrap_interval=5):
retries = [0]
@@ -423,15 +476,16 @@ class Updater(object):
return False
def bootstrap_set_webhook():
self.bot.set_webhook(
url=webhook_url, certificate=cert, allowed_updates=allowed_updates)
self.bot.set_webhook(url=webhook_url,
certificate=cert,
allowed_updates=allowed_updates)
return False
def bootstrap_onerr_cb(exc):
if not isinstance(exc, Unauthorized) and (max_retries < 0 or retries[0] < max_retries):
retries[0] += 1
self.logger.warning('Failed bootstrap phase; try=%s max_retries=%s',
retries[0], max_retries)
self.logger.warning('Failed bootstrap phase; try=%s max_retries=%s', retries[0],
max_retries)
else:
self.logger.error('Failed bootstrap phase after %s retries (%s)', retries[0], exc)
raise exc
@@ -516,9 +570,9 @@ class Updater(object):
"""Blocks until one of the signals are received and stops the updater.
Args:
stop_signals (:obj:`iterable`): Iterable containing signals from the signal module that
should be subscribed to. Updater.stop() will be called on receiving one of those
signals. Defaults to (``SIGINT``, ``SIGTERM``, ``SIGABRT``).
stop_signals (:obj:`list` | :obj:`tuple`): List containing signals from the signal
module that should be subscribed to. Updater.stop() will be called on receiving one
of those signals. Defaults to (``SIGINT``, ``SIGTERM``, ``SIGABRT``).
"""
for sig in stop_signals:
+12 -4
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -25,7 +25,10 @@ class Animation(TelegramObject):
"""This object represents an animation file to be displayed in the message containing a game.
Attributes:
file_id (:obj:`str`): Unique file identifier.
file_id (:obj:`str`): File identifier.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Video width as defined by sender.
height (:obj:`int`): Video height as defined by sender.
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
@@ -37,7 +40,10 @@ class Animation(TelegramObject):
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique file identifier.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
width (:obj:`int`): Video width as defined by sender.
height (:obj:`int`): Video height as defined by sender.
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
@@ -52,6 +58,7 @@ class Animation(TelegramObject):
def __init__(self,
file_id,
file_unique_id,
width,
height,
duration,
@@ -63,6 +70,7 @@ class Animation(TelegramObject):
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.width = int(width)
self.height = int(height)
self.duration = duration
@@ -73,7 +81,7 @@ class Animation(TelegramObject):
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):
+11 -3
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -26,6 +26,9 @@ class Audio(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
duration (:obj:`int`): Duration of the audio in seconds.
performer (:obj:`str`): Optional. Performer of the audio as defined by sender or by audio
tags.
@@ -37,7 +40,10 @@ class Audio(TelegramObject):
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
duration (:obj:`int`): Duration of the audio in seconds as defined by sender.
performer (:obj:`str`, optional): Performer of the audio as defined by sender or by audio
tags.
@@ -53,6 +59,7 @@ class Audio(TelegramObject):
def __init__(self,
file_id,
file_unique_id,
duration,
performer=None,
title=None,
@@ -63,6 +70,7 @@ class Audio(TelegramObject):
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.duration = int(duration)
# Optionals
self.performer = performer
@@ -72,7 +80,7 @@ class Audio(TelegramObject):
self.thumb = thumb
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):
+32 -8
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -25,24 +25,48 @@ class ChatPhoto(TelegramObject):
Attributes:
small_file_id (:obj:`str`): File identifier of small (160x160) chat photo.
This file_id can be used only for photo download and only for as long
as the photo is not changed.
small_file_unique_id (:obj:`str`): Unique file identifier of small (160x160) chat photo,
which is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
big_file_id (:obj:`str`): File identifier of big (640x640) chat photo.
This file_id can be used only for photo download and only for as long as
the photo is not changed.
big_file_unique_id (:obj:`str`): Unique file identifier of big (640x640) chat photo,
which is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
Args:
small_file_id (:obj:`str`): File identifier of small (160x160) chat photo. This file_id can
be used only for photo download and only for as long as the photo is not changed.
big_file_id (:obj:`str`): File identifier of big (640x640) chat photo. This file_id can be
used only for photo download and only for as long as the photo is not changed.
small_file_id (:obj:`str`): Unique file identifier of small (160x160) chat photo. This
file_id can be used only for photo download and only for as long
as the photo is not changed.
small_file_unique_id (:obj:`str`): Unique file identifier of small (160x160) chat photo,
which is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
big_file_id (:obj:`str`): Unique file identifier of big (640x640) chat photo. This file_id
can be used only for photo download and only for as long as the photo is not changed.
big_file_unique_id (:obj:`str`): Unique file identifier of big (640x640) chat photo,
which is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
def __init__(self, small_file_id, big_file_id, bot=None, **kwargs):
def __init__(self,
small_file_id,
small_file_unique_id,
big_file_id,
big_file_unique_id,
bot=None, **kwargs):
self.small_file_id = small_file_id
self.small_file_unique_id = small_file_unique_id
self.big_file_id = big_file_id
self.big_file_unique_id = big_file_unique_id
self.bot = bot
self._id_attrs = (self.small_file_id, self.big_file_id)
self._id_attrs = (self.small_file_unique_id, self.big_file_unique_id,)
@classmethod
def de_json(cls, data, bot):
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+11 -3
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -26,6 +26,9 @@ class Document(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique file identifier.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
thumb (:class:`telegram.PhotoSize`): Optional. Document thumbnail.
file_name (:obj:`str`): Original filename.
mime_type (:obj:`str`): Optional. MIME type of the file.
@@ -33,7 +36,10 @@ class Document(TelegramObject):
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique file identifier
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
thumb (:class:`telegram.PhotoSize`, optional): Document thumbnail as defined by sender.
file_name (:obj:`str`, optional): Original filename as defined by sender.
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
@@ -46,6 +52,7 @@ class Document(TelegramObject):
def __init__(self,
file_id,
file_unique_id,
thumb=None,
file_name=None,
mime_type=None,
@@ -54,6 +61,7 @@ class Document(TelegramObject):
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
# Optionals
self.thumb = thumb
self.file_name = file_name
@@ -61,7 +69,7 @@ class Document(TelegramObject):
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):
+25 -10
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,6 +19,7 @@
"""This module contains an object that represents a Telegram File."""
from base64 import b64decode
from os.path import basename
import os
from future.backports.urllib import parse as urllib_parse
@@ -37,11 +38,17 @@ class File(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
file_size (:obj:`str`): Optional. File size.
file_path (:obj:`str`): Optional. File path. Use :attr:`download` to get the file.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
file_size (:obj:`int`, optional): Optional. File size, if known.
file_path (:obj:`str`, optional): File path. Use :attr:`download` to get the file.
bot (:obj:`telegram.Bot`, optional): Bot to use with shortcut method.
@@ -53,18 +60,23 @@ class File(TelegramObject):
"""
def __init__(self, file_id, bot=None, file_size=None, file_path=None, **kwargs):
def __init__(self,
file_id,
file_unique_id,
bot=None,
file_size=None,
file_path=None,
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
# Optionals
self.file_size = file_size
self.file_path = file_path
self.bot = bot
self._credentials = None
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):
@@ -76,9 +88,10 @@ class File(TelegramObject):
def download(self, custom_path=None, out=None, timeout=None):
"""
Download this file. By default, the file is saved in the current working directory with its
original filename as reported by Telegram. If a :attr:`custom_path` is supplied, it will be
saved to that path instead. If :attr:`out` is defined, the file contents will be saved to
that object using the ``out.write`` method.
original filename as reported by Telegram. If the file has no filename, it the file ID will
be used as filename. If a :attr:`custom_path` is supplied, it will be saved to that path
instead. If :attr:`out` is defined, the file contents will be saved to that object using
the ``out.write`` method.
Note:
:attr:`custom_path` and :attr:`out` are mutually exclusive.
@@ -116,8 +129,10 @@ class File(TelegramObject):
else:
if custom_path:
filename = custom_path
else:
elif self.file_path:
filename = basename(self.file_path)
else:
filename = os.path.join(os.getcwd(), self.file_id)
buf = self.bot.request.retrieve(url, timeout=timeout)
if self._credentials:
+1 -1
View File
@@ -2,7 +2,7 @@
# pylint: disable=W0622,E0611
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+46 -34
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,6 +19,7 @@
"""Base class for Telegram InputMedia Objects."""
from telegram import TelegramObject, InputFile, PhotoSize, Animation, Video, Audio, Document
from telegram.utils.helpers import DEFAULT_NONE
class InputMedia(TelegramObject):
@@ -44,9 +45,10 @@ class InputMediaAnimation(InputMedia):
Lastly you can pass an existing :class:`telegram.Animation` object to send.
thumb (`filelike object`): Optional. Thumbnail of the
file sent. The thumbnail should be in JPEG format and less than 200 kB in size.
A thumbnail's width and height should not exceed 90. Ignored if the file is not
A thumbnail's width and height should not exceed 320. Ignored if the file is not
is passed as a string or file_id.
caption (:obj:`str`): Optional. Caption of the animation to be sent, 0-1024 characters.
caption (:obj:`str`): Optional. Caption of the animation to be sent, 0-1024 characters
after entities parsing.
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -61,9 +63,10 @@ class InputMediaAnimation(InputMedia):
Lastly you can pass an existing :class:`telegram.Animation` object to send.
thumb (`filelike object`, optional): Thumbnail of the
file sent. The thumbnail should be in JPEG format and less than 200 kB in size.
A thumbnail's width and height should not exceed 90. Ignored if the file is not
A thumbnail's width and height should not exceed 320. Ignored if the file is not
is passed as a string or file_id.
caption (:obj:`str`, optional): Caption of the animation to be sent, 0-1024 characters.
caption (:obj:`str`, optional): Caption of the animation to be sent, 0-1024 characters
after entities parsing.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -77,7 +80,13 @@ class InputMediaAnimation(InputMedia):
arguments.
"""
def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None,
def __init__(self,
media,
thumb=None,
caption=None,
parse_mode=DEFAULT_NONE,
width=None,
height=None,
duration=None):
self.type = 'animation'
@@ -98,8 +107,7 @@ class InputMediaAnimation(InputMedia):
if caption:
self.caption = caption
if parse_mode:
self.parse_mode = parse_mode
self.parse_mode = parse_mode
if width:
self.width = width
if height:
@@ -118,7 +126,8 @@ class InputMediaPhoto(InputMedia):
(recommended), pass an HTTP URL as a String for Telegram to get a photo from the
Internet, or upload a new photo using multipart/form-data. Lastly you can pass
an existing :class:`telegram.PhotoSize` object to send.
caption (:obj:`str`): Optional. Caption of the photo to be sent, 0-1024 characters.
caption (:obj:`str`): Optional. Caption of the photo to be sent, 0-1024 characters after
entities parsing.
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -127,13 +136,14 @@ class InputMediaPhoto(InputMedia):
media (:obj:`str`): File to send. Pass a file_id to send a file that exists on the
Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the
Internet. Lastly you can pass an existing :class:`telegram.PhotoSize` object to send.
caption (:obj:`str`, optional ): Caption of the photo to be sent, 0-1024 characters.
caption (:obj:`str`, optional ): Caption of the photo to be sent, 0-1024 characters after
entities parsing.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
"""
def __init__(self, media, caption=None, parse_mode=None):
def __init__(self, media, caption=None, parse_mode=DEFAULT_NONE):
self.type = 'photo'
if isinstance(media, PhotoSize):
@@ -145,8 +155,7 @@ class InputMediaPhoto(InputMedia):
if caption:
self.caption = caption
if parse_mode:
self.parse_mode = parse_mode
self.parse_mode = parse_mode
class InputMediaVideo(InputMedia):
@@ -159,7 +168,8 @@ class InputMediaVideo(InputMedia):
(recommended), pass an HTTP URL as a String for Telegram to get an video file from
the Internet, or upload a new one using multipart/form-data. Lastly you can pass
an existing :class:`telegram.Video` object to send.
caption (:obj:`str`): Optional. Caption of the video to be sent, 0-1024 characters.
caption (:obj:`str`): Optional. Caption of the video to be sent, 0-1024 characters after
entities parsing.
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -170,14 +180,15 @@ class InputMediaVideo(InputMedia):
for streaming.
thumb (`filelike object`): Optional. Thumbnail of the
file sent. The thumbnail should be in JPEG format and less than 200 kB in size.
A thumbnail's width and height should not exceed 90. Ignored if the file is not
A thumbnail's width and height should not exceed 320. Ignored if the file is not
is passed as a string or file_id.
Args:
media (:obj:`str`): File to send. Pass a file_id to send a file that exists on the Telegram
servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet.
Lastly you can pass an existing :class:`telegram.Video` object to send.
caption (:obj:`str`, optional): Caption of the video to be sent, 0-1024 characters.
caption (:obj:`str`, optional): Caption of the video to be sent, 0-1024 characters after
entities parsing.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -188,7 +199,7 @@ class InputMediaVideo(InputMedia):
for streaming.
thumb (`filelike object`, optional): Thumbnail of the
file sent. The thumbnail should be in JPEG format and less than 200 kB in size.
A thumbnail's width and height should not exceed 90. Ignored if the file is not
A thumbnail's width and height should not exceed 320. Ignored if the file is not
is passed as a string or file_id.
Note:
@@ -198,7 +209,7 @@ class InputMediaVideo(InputMedia):
"""
def __init__(self, media, caption=None, width=None, height=None, duration=None,
supports_streaming=None, parse_mode=None, thumb=None):
supports_streaming=None, parse_mode=DEFAULT_NONE, thumb=None):
self.type = 'video'
if isinstance(media, Video):
@@ -218,8 +229,7 @@ class InputMediaVideo(InputMedia):
if caption:
self.caption = caption
if parse_mode:
self.parse_mode = parse_mode
self.parse_mode = parse_mode
if width:
self.width = width
if height:
@@ -240,7 +250,8 @@ class InputMediaAudio(InputMedia):
(recommended), pass an HTTP URL as a String for Telegram to get an audio file from
the Internet, or upload a new one using multipart/form-data. Lastly you can pass
an existing :class:`telegram.Audio` object to send.
caption (:obj:`str`): Optional. Caption of the audio to be sent, 0-1024 characters.
caption (:obj:`str`): Optional. Caption of the audio to be sent, 0-1024 characters after
entities parsing.
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -250,14 +261,15 @@ class InputMediaAudio(InputMedia):
title (:obj:`str`): Optional. Title of the audio as defined by sender or by audio tags.
thumb (`filelike object`): Optional. Thumbnail of the
file sent. The thumbnail should be in JPEG format and less than 200 kB in size.
A thumbnail's width and height should not exceed 90. Ignored if the file is not
A thumbnail's width and height should not exceed 320. Ignored if the file is not
is passed as a string or file_id.
Args:
media (:obj:`str`): File to send. Pass a file_id to send a file that exists on the Telegram
servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet.
Lastly you can pass an existing :class:`telegram.Document` object to send.
caption (:obj:`str`, optional): Caption of the audio to be sent, 0-1024 characters.
caption (:obj:`str`, optional): Caption of the audio to be sent, 0-1024 characters after
entities parsing.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -267,7 +279,7 @@ class InputMediaAudio(InputMedia):
title (:obj:`str`, optional): Title of the audio as defined by sender or by audio tags.
thumb (`filelike object`, optional): Thumbnail of the
file sent. The thumbnail should be in JPEG format and less than 200 kB in size.
A thumbnail's width and height should not exceed 90. Ignored if the file is not
A thumbnail's width and height should not exceed 320. Ignored if the file is not
is passed as a string or file_id.
Note:
@@ -276,7 +288,7 @@ class InputMediaAudio(InputMedia):
optional arguments.
"""
def __init__(self, media, thumb=None, caption=None, parse_mode=None,
def __init__(self, media, thumb=None, caption=None, parse_mode=DEFAULT_NONE,
duration=None, performer=None, title=None):
self.type = 'audio'
@@ -297,8 +309,7 @@ class InputMediaAudio(InputMedia):
if caption:
self.caption = caption
if parse_mode:
self.parse_mode = parse_mode
self.parse_mode = parse_mode
if duration:
self.duration = duration
if performer:
@@ -317,30 +328,32 @@ class InputMediaDocument(InputMedia):
(recommended), pass an HTTP URL as a String for Telegram to get a file from the
Internet, or upload a new one using multipart/form-data. Lastly you can pass
an existing :class:`telegram.Document` object to send.
caption (:obj:`str`): Optional. Caption of the document to be sent, 0-1024 characters.
caption (:obj:`str`): Optional. Caption of the document to be sent, 0-1024 characters after
entities parsing.
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
thumb (`filelike object`): Optional. Thumbnail of the
file sent. The thumbnail should be in JPEG format and less than 200 kB in size.
A thumbnail's width and height should not exceed 90. Ignored if the file is not
A thumbnail's width and height should not exceed 320. Ignored if the file is not
is passed as a string or file_id.
Args:
media (:obj:`str`): File to send. Pass a file_id to send a file that exists on the Telegram
servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet.
Lastly you can pass an existing :class:`telegram.Document` object to send.
caption (:obj:`str`, optional): Caption of the document to be sent, 0-1024 characters.
caption (:obj:`str`, optional): Caption of the document to be sent, 0-1024 characters after
entities parsing.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
thumb (`filelike object`, optional): Thumbnail of the
file sent. The thumbnail should be in JPEG format and less than 200 kB in size.
A thumbnail's width and height should not exceed 90. Ignored if the file is not
A thumbnail's width and height should not exceed 320. Ignored if the file is not
is passed as a string or file_id.
"""
def __init__(self, media, thumb=None, caption=None, parse_mode=None):
def __init__(self, media, thumb=None, caption=None, parse_mode=DEFAULT_NONE):
self.type = 'document'
if isinstance(media, Document):
@@ -357,5 +370,4 @@ class InputMediaDocument(InputMedia):
if caption:
self.caption = caption
if parse_mode:
self.parse_mode = parse_mode
self.parse_mode = parse_mode
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+18 -4
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -26,13 +26,19 @@ class PhotoSize(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Photo width.
height (:obj:`int`): Photo height.
file_size (:obj:`int`): Optional. File size.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
width (:obj:`int`): Photo width.
height (:obj:`int`): Photo height.
file_size (:obj:`int`, optional): File size.
@@ -41,16 +47,24 @@ class PhotoSize(TelegramObject):
"""
def __init__(self, file_id, width, height, file_size=None, bot=None, **kwargs):
def __init__(self,
file_id,
file_unique_id,
width,
height,
file_size=None,
bot=None,
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.width = int(width)
self.height = int(height)
# Optionals
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):
+21 -5
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -26,6 +26,9 @@ class Sticker(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Sticker width.
height (:obj:`int`): Sticker height.
is_animated (:obj:`bool`): True, if the sticker is animated.
@@ -39,7 +42,10 @@ class Sticker(TelegramObject):
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
width (:obj:`int`): Sticker width.
height (:obj:`int`): Sticker height.
is_animated (:obj:`bool`): True, if the sticker is animated.
@@ -58,6 +64,7 @@ class Sticker(TelegramObject):
def __init__(self,
file_id,
file_unique_id,
width,
height,
is_animated,
@@ -70,6 +77,7 @@ class Sticker(TelegramObject):
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.width = int(width)
self.height = int(height)
self.is_animated = is_animated
@@ -81,7 +89,7 @@ class Sticker(TelegramObject):
self.mask_position = mask_position
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):
@@ -130,6 +138,8 @@ class StickerSet(TelegramObject):
is_animated (:obj:`bool`): True, if the sticker set contains animated stickers.
contains_masks (:obj:`bool`): True, if the sticker set contains masks.
stickers (List[:class:`telegram.Sticker`]): List of all set stickers.
thumb (:class:`telegram.PhotoSize`): Optional. Sticker set thumbnail in the .WEBP or .TGS
format
Args:
name (:obj:`str`): Sticker set name.
@@ -137,15 +147,20 @@ class StickerSet(TelegramObject):
is_animated (:obj:`bool`): True, if the sticker set contains animated stickers.
contains_masks (:obj:`bool`): True, if the sticker set contains masks.
stickers (List[:class:`telegram.Sticker`]): List of all set stickers.
thumb (:class:`telegram.PhotoSize`, optional): Sticker set thumbnail in the .WEBP or .TGS
format
"""
def __init__(self, name, title, is_animated, contains_masks, stickers, bot=None, **kwargs):
def __init__(self, name, title, is_animated, contains_masks, stickers, bot=None, thumb=None,
**kwargs):
self.name = name
self.title = title
self.is_animated = is_animated
self.contains_masks = contains_masks
self.stickers = stickers
# Optionals
self.thumb = thumb
self._id_attrs = (self.name,)
@@ -156,6 +171,7 @@ class StickerSet(TelegramObject):
data = super(StickerSet, StickerSet).de_json(data, bot)
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
data['stickers'] = Sticker.de_list(data.get('stickers'), bot)
return StickerSet(bot=bot, **data)
@@ -179,7 +195,7 @@ class MaskPosition(TelegramObject):
size, from top to bottom.
scale (:obj:`float`): Mask scaling coefficient. For example, 2.0 means double size.
Notes:
Note:
:attr:`type` should be one of the following: `forehead`, `eyes`, `mouth` or `chin`. You can
use the classconstants for those.
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+11 -3
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -26,6 +26,9 @@ class Video(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
width (:obj:`int`): Video width as defined by sender.
height (:obj:`int`): Video height as defined by sender.
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
@@ -35,7 +38,10 @@ class Video(TelegramObject):
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
width (:obj:`int`): Video width as defined by sender.
height (:obj:`int`): Video height as defined by sender.
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
@@ -49,6 +55,7 @@ class Video(TelegramObject):
def __init__(self,
file_id,
file_unique_id,
width,
height,
duration,
@@ -59,6 +66,7 @@ class Video(TelegramObject):
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.width = int(width)
self.height = int(height)
self.duration = int(duration)
@@ -68,7 +76,7 @@ class Video(TelegramObject):
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):
+19 -4
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -26,6 +26,9 @@ class VideoNote(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
length (:obj:`int`): Video width and height as defined by sender.
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
thumb (:class:`telegram.PhotoSize`): Optional. Video thumbnail.
@@ -33,7 +36,10 @@ class VideoNote(TelegramObject):
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
length (:obj:`int`): Video width and height as defined by sender.
duration (:obj:`int`): Duration of the video in seconds as defined by sender.
thumb (:class:`telegram.PhotoSize`, optional): Video thumbnail.
@@ -43,9 +49,18 @@ class VideoNote(TelegramObject):
"""
def __init__(self, file_id, length, duration, thumb=None, file_size=None, bot=None, **kwargs):
def __init__(self,
file_id,
file_unique_id,
length,
duration,
thumb=None,
file_size=None,
bot=None,
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.length = int(length)
self.duration = int(duration)
# Optionals
@@ -53,7 +68,7 @@ class VideoNote(TelegramObject):
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):
+18 -4
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -26,13 +26,19 @@ class Voice(TelegramObject):
Attributes:
file_id (:obj:`str`): Unique identifier for this file.
file_unique_id (:obj:`str`): Unique identifier for this file, which
is supposed to be the same over time and for different bots.
Can't be used to download or reuse the file.
duration (:obj:`int`): Duration of the audio in seconds as defined by sender.
mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender.
file_size (:obj:`int`): Optional. File size.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
file_id (:obj:`str`): Unique identifier for this file.
file_id (:obj:`str`): Identifier for this file, which can be used to download
or reuse the file.
file_unique_id (:obj:`str`): Unique and the same over time and
for different bots file identifier.
duration (:obj:`int`, optional): Duration of the audio in seconds as defined by sender.
mime_type (:obj:`str`, optional): MIME type of the file as defined by sender.
file_size (:obj:`int`, optional): File size.
@@ -41,16 +47,24 @@ class Voice(TelegramObject):
"""
def __init__(self, file_id, duration, mime_type=None, file_size=None, bot=None, **kwargs):
def __init__(self,
file_id,
file_unique_id,
duration,
mime_type=None,
file_size=None,
bot=None,
**kwargs):
# Required
self.file_id = str(file_id)
self.file_unique_id = str(file_unique_id)
self.duration = int(duration)
# Optionals
self.mime_type = mime_type
self.file_size = file_size
self.bot = bot
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+9 -8
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -30,27 +30,28 @@ class InlineKeyboardButton(TelegramObject):
Attributes:
text (:obj:`str`): Label text on the button.
url (:obj:`str`): Optional. HTTP url to be opened when button is pressed.
url (:obj:`str`): Optional. HTTP or tg:// url to be opened when button is pressed.
login_url (:class:`telegram.LoginUrl`) Optional. An HTTP URL used to automatically
authorize the user.
authorize the user. Can be used as a replacement for the Telegram Login Widget.
callback_data (:obj:`str`): Optional. Data to be sent in a callback query to the bot when
button is pressed, UTF-8 1-64 bytes.
switch_inline_query (:obj:`str`): Optional. Will prompt the user to select one of their
chats, open that chat and insert the bot's username and the specified inline query in
the input field.
the input field. Can be empty, in which case just the bots username will be inserted.
switch_inline_query_current_chat (:obj:`str`): Optional. Will insert the bot's username and
the specified inline query in the current chat's input field.
the specified inline query in the current chat's input field. Can be empty, in which
case just the bots username will be inserted.
callback_game (:class:`telegram.CallbackGame`): Optional. Description of the game that will
be launched when the user presses the button.
pay (:obj:`bool`): Optional. Specify True, to send a Pay button.
Args:
text (:obj:`str`): Label text on the button.
url (:obj:`str`): HTTP url to be opened when button is pressed.
url (:obj:`str`): HTTP or tg:// url to be opened when button is pressed.
login_url (:class:`telegram.LoginUrl`, optional) An HTTP URL used to automatically
authorize the user.
authorize the user. Can be used as a replacement for the Telegram Login Widget.
callback_data (:obj:`str`, optional): Data to be sent in a callback query to the bot when
button is pressed, 1-64 UTF-8 bytes.
button is pressed, UTF-8 1-64 bytes.
switch_inline_query (:obj:`str`, optional): If set, pressing the button will prompt the
user to select one of their chats, open that chat and insert the bot's username and the
specified inline query in the input field. Can be empty, in which case just the bot's
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+3 -3
View File
@@ -2,7 +2,7 @@
# pylint: disable=R0902,R0912,R0913
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2018
# Copyright (C) 2015-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -35,7 +35,7 @@ class InlineQuery(TelegramObject):
from_user (:class:`telegram.User`): Sender.
location (:class:`telegram.Location`): Optional. Sender location, only for bots that
request user location.
query (:obj:`str`): Text of the query (up to 512 characters).
query (:obj:`str`): Text of the query (up to 256 characters).
offset (:obj:`str`): Offset of the results to be returned, can be controlled by the bot.
Args:
@@ -43,7 +43,7 @@ class InlineQuery(TelegramObject):
from_user (:class:`telegram.User`): Sender.
location (:class:`telegram.Location`, optional): Sender location, only for bots that
request user location.
query (:obj:`str`): Text of the query (up to 512 characters).
query (:obj:`str`): Text of the query (up to 256 characters).
offset (:obj:`str`): Offset of the results to be returned, can be controlled by the bot.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
+9 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -41,3 +41,11 @@ class InlineQueryResult(TelegramObject):
self.id = str(id)
self._id_attrs = (self.id,)
@property
def _has_parse_mode(self):
return hasattr(self, 'parse_mode')
@property
def _has_input_message_content(self):
return hasattr(self, 'input_message_content')
+1 -1
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
+9 -8
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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,6 +19,7 @@
"""This module contains the classes that represent Telegram InlineQueryResultAudio."""
from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE
class InlineQueryResultAudio(InlineQueryResult):
@@ -32,9 +33,9 @@ class InlineQueryResultAudio(InlineQueryResult):
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
audio_url (:obj:`str`): A valid URL for the audio file.
title (:obj:`str`): Title.
performer (:obj:`str`): Optional. Caption, 0-200 characters.
audio_duration (:obj:`str`): Optional. Performer.
caption (:obj:`str`): Optional. Audio duration in seconds.
performer (:obj:`str`): Optional. Performer.
audio_duration (:obj:`str`): Optional. Audio duration in seconds.
caption (:obj:`str`): Optional. Caption, 0-1024 characters after entities parsing.
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -47,9 +48,9 @@ class InlineQueryResultAudio(InlineQueryResult):
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
audio_url (:obj:`str`): A valid URL for the audio file.
title (:obj:`str`): Title.
performer (:obj:`str`, optional): Caption, 0-200 characters.
audio_duration (:obj:`str`, optional): Performer.
caption (:obj:`str`, optional): Audio duration in seconds.
performer (:obj:`str`, optional): Performer.
audio_duration (:obj:`str`, optional): Audio duration in seconds.
caption (:obj:`str`, optional): Caption, 0-1024 characters after entities parsing.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -70,7 +71,7 @@ class InlineQueryResultAudio(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
@@ -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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,19 +19,20 @@
"""This module contains the classes that represent Telegram InlineQueryResultCachedAudio."""
from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE
class InlineQueryResultCachedAudio(InlineQueryResult):
"""
Represents a link to an mp3 audio file stored on the Telegram servers. By default, this audio
file will be sent by the user. Alternatively, you can use :attr:`input_message_content` to
send amessage with the specified content instead of the audio.
send a message with the specified content instead of the audio.
Attributes:
type (:obj:`str`): 'audio'.
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
audio_file_id (:obj:`str`): A valid file identifier for the audio file.
caption (:obj:`str`): Optional. Caption, 0-1024 characters
caption (:obj:`str`): Optional. Caption, 0-1024 characters after entities parsing.
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -43,7 +44,7 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
Args:
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
audio_file_id (:obj:`str`): A valid file identifier for the audio file.
caption (:obj:`str`, optional): Caption, 0-1024 characters
caption (:obj:`str`, optional): Caption, 0-1024 characters after entities parsing.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -61,7 +62,7 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
super(InlineQueryResultCachedAudio, self).__init__('audio', id)
@@ -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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,6 +19,7 @@
"""This module contains the classes that represent Telegram InlineQueryResultCachedDocument."""
from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE
class InlineQueryResultCachedDocument(InlineQueryResult):
@@ -33,7 +34,8 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
title (:obj:`str`): Title for the result.
document_file_id (:obj:`str`): A valid file identifier for the file.
description (:obj:`str`): Optional. Short description of the result.
caption (:obj:`str`): Optional. Caption, 0-1024 characters
caption (:obj:`str`): Optional. Caption of the document to be sent, 0-1024 characters
after entities parsing.
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -47,7 +49,8 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
title (:obj:`str`): Title for the result.
document_file_id (:obj:`str`): A valid file identifier for the file.
description (:obj:`str`, optional): Short description of the result.
caption (:obj:`str`, optional): Caption, 0-1024 characters
caption (:obj:`str`, optional): Caption of the document to be sent, 0-1024 characters
after entities parsing.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -67,7 +70,7 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
super(InlineQueryResultCachedDocument, self).__init__('document', id)
@@ -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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,6 +19,7 @@
"""This module contains the classes that represent Telegram InlineQueryResultCachedGif."""
from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE
class InlineQueryResultCachedGif(InlineQueryResult):
@@ -33,7 +34,8 @@ class InlineQueryResultCachedGif(InlineQueryResult):
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
gif_file_id (:obj:`str`): A valid file identifier for the GIF file.
title (:obj:`str`): Optional. Title for the result.
caption (:obj:`str`): Optional. Caption, 0-1024 characters
caption (:obj:`str`): Optional. Caption of the GIF file to be sent, 0-1024 characters
after entities parsing.
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -46,7 +48,8 @@ class InlineQueryResultCachedGif(InlineQueryResult):
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
gif_file_id (:obj:`str`): A valid file identifier for the GIF file.
title (:obj:`str`, optional): Title for the result.caption (:obj:`str`, optional):
caption (:obj:`str`, optional): Caption, 0-1024 characters
caption (:obj:`str`, optional): Caption of the GIF file to be sent, 0-1024 characters
after entities parsing.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -65,7 +68,7 @@ class InlineQueryResultCachedGif(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
super(InlineQueryResultCachedGif, self).__init__('gif', id)
@@ -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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,6 +19,7 @@
"""This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif."""
from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE
class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
@@ -33,7 +34,8 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
mpeg4_file_id (:obj:`str`): A valid file identifier for the MP4 file.
title (:obj:`str`): Optional. Title for the result.
caption (:obj:`str`): Optional. Caption, 0-1024 characters
caption (:obj:`str`): Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters
after entities parsing.
parse_mode (:obj:`str`): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -46,7 +48,8 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
mpeg4_file_id (:obj:`str`): A valid file identifier for the MP4 file.
title (:obj:`str`, optional): Title for the result.
caption (:obj:`str`, optional): Caption, 0-1024 characters
caption (:obj:`str`, optional): Caption of the MPEG-4 file to be sent, 0-1024 characters
after entities parsing.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -65,7 +68,7 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
super(InlineQueryResultCachedMpeg4Gif, self).__init__('mpeg4_gif', id)
@@ -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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -19,6 +19,7 @@
"""This module contains the classes that represent Telegram InlineQueryResultPhoto"""
from telegram import InlineQueryResult
from telegram.utils.helpers import DEFAULT_NONE
class InlineQueryResultCachedPhoto(InlineQueryResult):
@@ -34,7 +35,8 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
photo_file_id (:obj:`str`): A valid file identifier of the photo.
title (:obj:`str`): Optional. Title for the result.
description (:obj:`str`): Optional. Short description of the result.
caption (:obj:`str`): Optional. Caption, 0-1024 characters
caption (:obj:`str`): Optional. Caption of the photo to be sent, 0-1024 characters after
entities parsing.
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -48,7 +50,8 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
photo_file_id (:obj:`str`): A valid file identifier of the photo.
title (:obj:`str`, optional): Title for the result.
description (:obj:`str`, optional): Short description of the result.
caption (:obj:`str`, optional): Caption, 0-1024 characters
caption (:obj:`str`, optional): Caption of the photo to be sent, 0-1024 characters after
entities parsing.
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
in :class:`telegram.ParseMode` for the available modes.
@@ -68,7 +71,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
parse_mode=None,
parse_mode=DEFAULT_NONE,
**kwargs):
# Required
super(InlineQueryResultCachedPhoto, self).__init__('photo', id)
@@ -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-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
@@ -37,8 +37,8 @@ class InlineQueryResultCachedSticker(InlineQueryResult):
message to be sent instead of the sticker.
Args:
id (:obj:`str`):
sticker_file_id (:obj:`str`):
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
sticker_file_id (:obj:`str`): A valid file identifier of the sticker.
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
to the message.
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the

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