Compare commits

...

20 Commits

Author SHA1 Message Date
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
134 changed files with 2962 additions and 1026 deletions
+1 -1
View File
@@ -117,7 +117,7 @@ Here's how to make a one-off code change.
.. code-block::
$ export TEST_OFFICIAL=True
$ export TEST_OFFICIAL=true
prior to running the tests.
+1 -1
View File
@@ -2,7 +2,7 @@
name: Question
about: Get help with errors or general questions
title: "[QUESTION]"
labels: 'question :question:'
labels: question
assignees: ''
---
+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.
+2 -3
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ${{matrix.os}}
strategy:
matrix:
python-version: [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
@@ -51,7 +51,7 @@ jobs:
exit ${global_exit}
env:
JOB_INDEX: ${{ strategy.job-index }}
BOTS: W3sidG9rZW4iOiAiNjk2MTg4NzMyOkFBR1Z3RUtmSEhsTmpzY3hFRE5LQXdraEdzdFpfa28xbUMwIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WldGaU1UUmxNbVF5TnpNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMi43IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzI3X2JvdCJ9LCB7InRva2VuIjogIjY3MTQ2ODg4NjpBQUdQR2ZjaVJJQlVORmU4MjR1SVZkcTdKZTNfWW5BVE5HdyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpHWXdPVGxrTXpNeE4yWTIiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNCIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zNF9ib3QifSwgeyJ0b2tlbiI6ICI2MjkzMjY1Mzg6QUFGUnJaSnJCN29CM211ekdzR0pYVXZHRTVDUXpNNUNVNG8iLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpNbU01WVdKaFl6a3hNMlUxIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgQ1B5dGhvbiAzLjUiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX2NweXRob25fMzVfYm90In0sIHsidG9rZW4iOiAiNjQwMjA4OTQzOkFBRmhCalFwOXFtM1JUeFN6VXBZekJRakNsZS1Kano1aGNrIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WXpoa1pUZzFOamMxWXpWbCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMy42IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzM2X2JvdCJ9LCB7InRva2VuIjogIjY5NTEwNDA4ODpBQUhmenlsSU9qU0lJUy1lT25JMjB5MkUyMEhvZEhzZnotMCIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOk9HUTFNRGd3WmpJd1pqRmwiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNyIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zN19ib3QifSwgeyJ0b2tlbiI6ICI2OTE0MjM1NTQ6QUFGOFdrakNaYm5IcVBfaTZHaFRZaXJGRWxackdhWU9oWDAiLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpZamM1TlRoaU1tUXlNV1ZoIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgUHlQeSAyLjciLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX3B5cHlfMjdfYm90In0sIHsidG9rZW4iOiAiNjg0MzM5OTg0OkFBRk1nRUVqcDAxcjVyQjAwN3lDZFZOc2c4QWxOc2FVLWNjIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TVRBek1UWTNNR1V5TmpnMCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIFB5UHkgMy41IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19weXB5XzM1X2JvdCJ9LCB7InRva2VuIjogIjY5MDA5MTM0NzpBQUZMbVI1cEFCNVljcGVfbU9oN3pNNEpGQk9oMHozVDBUbyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpEaGxOekU1TURrd1lXSmkiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIEFwcFZleW9yIHVzaW5nIENQeXRob24gMy40IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX2FwcHZleW9yX2NweXRob25fMzRfYm90In0sIHsidG9rZW4iOiAiNjk0MzA4MDUyOkFBRUIyX3NvbkNrNTVMWTlCRzlBTy1IOGp4aVBTNTVvb0JBIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WW1aaVlXWm1NakpoWkdNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gQXBwVmV5b3IgdXNpbmcgQ1B5dGhvbiAyLjciLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfYXBwdmV5b3JfY3B5dGhvbl8yN19ib3QifV0=
BOTS: W3sidG9rZW4iOiAiNjk2MTg4NzMyOkFBR1Z3RUtmSEhsTmpzY3hFRE5LQXdraEdzdFpfa28xbUMwIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WldGaU1UUmxNbVF5TnpNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMi43IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzI3X2JvdCJ9LCB7InRva2VuIjogIjY3MTQ2ODg4NjpBQUdQR2ZjaVJJQlVORmU4MjR1SVZkcTdKZTNfWW5BVE5HdyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpHWXdPVGxrTXpNeE4yWTIiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNCIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zNF9ib3QifSwgeyJ0b2tlbiI6ICI2MjkzMjY1Mzg6QUFGUnJaSnJCN29CM211ekdzR0pYVXZHRTVDUXpNNUNVNG8iLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpNbU01WVdKaFl6a3hNMlUxIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgQ1B5dGhvbiAzLjUiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX2NweXRob25fMzVfYm90In0sIHsidG9rZW4iOiAiNjQwMjA4OTQzOkFBRmhCalFwOXFtM1JUeFN6VXBZekJRakNsZS1Kano1aGNrIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WXpoa1pUZzFOamMxWXpWbCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMy42IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzM2X2JvdCJ9LCB7InRva2VuIjogIjY5NTEwNDA4ODpBQUhmenlsSU9qU0lJUy1lT25JMjB5MkUyMEhvZEhzZnotMCIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOk9HUTFNRGd3WmpJd1pqRmwiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNyIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zN19ib3QifSwgeyJ0b2tlbiI6ICI2OTE0MjM1NTQ6QUFGOFdrakNaYm5IcVBfaTZHaFRZaXJGRWxackdhWU9oWDAiLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpZamM1TlRoaU1tUXlNV1ZoIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgUHlQeSAyLjciLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX3B5cHlfMjdfYm90In0sIHsidG9rZW4iOiAiNjg0MzM5OTg0OkFBRk1nRUVqcDAxcjVyQjAwN3lDZFZOc2c4QWxOc2FVLWNjIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TVRBek1UWTNNR1V5TmpnMCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIFB5UHkgMy41IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19weXB5XzM1X2JvdCJ9LCB7InRva2VuIjogIjY5MDA5MTM0NzpBQUZMbVI1cEFCNVljcGVfbU9oN3pNNEpGQk9oMHozVDBUbyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpEaGxOekU1TURrd1lXSmkiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIEFwcFZleW9yIHVzaW5nIENQeXRob24gMy40IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX2FwcHZleW9yX2NweXRob25fMzRfYm90In0sIHsidG9rZW4iOiAiNjk0MzA4MDUyOkFBRUIyX3NvbkNrNTVMWTlCRzlBTy1IOGp4aVBTNTVvb0JBIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WW1aaVlXWm1NakpoWkdNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gQXBwVmV5b3IgdXNpbmcgQ1B5dGhvbiAyLjciLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfYXBwdmV5b3JfY3B5dGhvbl8yN19ib3QifSwgeyJ0b2tlbiI6ICIxMDU1Mzk3NDcxOkFBRzE4bkJfUzJXQXd1SjNnN29oS0JWZ1hYY2VNbklPeVNjIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TmpBd056QXpZalZpTkdOayIsICJuYW1lIjogIlBUQiB0ZXN0cyBbMF0iLCAidXNlcm5hbWUiOiAicHRiXzBfYm90In0sIHsidG9rZW4iOiAiMTA0NzMyNjc3MTpBQUY4bk90ODFGcFg4bGJidno4VWV3UVF2UmZUYkZmQnZ1SSIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOllUVTFOVEk0WkdSallqbGkiLCAibmFtZSI6ICJQVEIgdGVzdHMgWzFdIiwgInVzZXJuYW1lIjogInB0Yl8xX2JvdCJ9LCB7InRva2VuIjogIjk3MTk5Mjc0NTpBQUdPa09hVzBOSGpnSXY1LTlqUWJPajR2R3FkaFNGLVV1cyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOk5XWmtNV1ZoWWpsallqVTUiLCAibmFtZSI6ICJQVEIgdGVzdHMgWzJdIiwgInVzZXJuYW1lIjogInB0Yl8yX2JvdCJ9XQ==
TEST_BUILD: ${{ matrix.test-build }}
TEST_PRE_COMMIT: ${{ matrix.test-pre-commit }}
shell: bash --noprofile --norc {0}
@@ -90,7 +90,6 @@ jobs:
run: |
pytest -v tests/test_official.py
exit $?
continue-on-error: True
env:
TEST_OFFICIAL: "true"
shell: bash --noprofile --norc {0}
+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
+1
View File
@@ -36,6 +36,7 @@ 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>`_
- `Hugo Damer <https://github.com/HakimusGIT>`_
- `ihoru <https://github.com/ihoru>`_
- `Jasmin Bom <https://github.com/jsmnbom>`_
+45
View File
@@ -2,6 +2,51 @@
Changelog
=========
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*
+1 -1
View File
@@ -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.6** are supported.
==========
Installing
-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"
+2 -2
View File
@@ -58,9 +58,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '12.4' # telegram.__version__[:3]
version = '12.5' # telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = '12.4.2' # telegram.__version__
release = '12.5' # telegram.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -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:
+2
View File
@@ -31,6 +31,7 @@ telegram package
telegram.inputmediaphoto
telegram.inputmediavideo
telegram.keyboardbutton
telegram.keyboardbuttonpolltype
telegram.location
telegram.loginurl
telegram.message
@@ -38,6 +39,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
+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()
+2 -1
View File
@@ -63,5 +63,6 @@ with codecs.open('README.rst', 'r', 'utf-8') as fd:
'Programming Language :: Python :: 3',
'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',
],)
+4 -3
View File
@@ -38,6 +38,7 @@ from .files.videonote import VideoNote
from .chataction import ChatAction
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 +49,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 +139,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 +157,5 @@ __all__ = [
'InputMediaAudio', 'InputMediaDocument', 'TelegramDecryptionError',
'PassportElementErrorSelfie', 'PassportElementErrorTranslationFile',
'PassportElementErrorTranslationFiles', 'PassportElementErrorUnspecified', 'Poll',
'PollOption', 'LoginUrl'
'PollOption', 'PollAnswer', 'LoginUrl', 'KeyboardButton', 'KeyboardButtonPollType',
]
+105 -9
View File
@@ -223,6 +223,34 @@ class Bot(TelegramObject):
return self.bot.username
@property
@info
def link(self):
""":obj:`str`: Convenience property. Returns the t.me link of the bot."""
return "https://t.me/{}".format(self.username)
@property
@info
def can_join_groups(self):
""":obj:`str`: Bot's can_join_groups attribute."""
return self.bot.can_join_groups
@property
@info
def can_read_all_group_messages(self):
""":obj:`str`: Bot's can_read_all_group_messages attribute."""
return self.bot.can_read_all_group_messages
@property
@info
def supports_inline_queries(self):
""":obj:`str`: Bot's supports_inline_queries attribute."""
return self.bot.supports_inline_queries
@property
def name(self):
""":obj:`str`: Bot's @username."""
@@ -419,7 +447,7 @@ class Bot(TelegramObject):
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): Photo caption (may also be used when resending photos
by file_id), 0-1024 characters.
by file_id), 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.
@@ -493,7 +521,8 @@ class Bot(TelegramObject):
(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): Audio caption, 0-1024 characters.
caption (:obj:`str`, optional): Audio 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.
@@ -579,7 +608,7 @@ class Bot(TelegramObject):
filename (:obj:`str`, optional): File name that shows in telegram message (it is useful
when you send file generated by temp module, for example). Undocumented.
caption (:obj:`str`, optional): Document caption (may also be used when resending
documents by file_id), 0-1024 characters.
documents by file_id), 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.
@@ -715,7 +744,7 @@ class Bot(TelegramObject):
width (:obj:`int`, optional): Video width.
height (:obj:`int`, optional): Video height.
caption (:obj:`str`, optional): Video caption (may also be used when resending videos
by file_id), 0-1024 characters.
by file_id), 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.
@@ -877,7 +906,7 @@ class Bot(TelegramObject):
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): Animation caption (may also be used when resending
animations by file_id), 0-1024 characters.
animations by file_id), 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.
@@ -955,7 +984,8 @@ class Bot(TelegramObject):
(recommended), pass an HTTP URL as a String for Telegram to get an voice file from
the Internet, or upload a new one using multipart/form-data. Lastly you can pass
an existing :class:`telegram.Voice` object to send.
caption (:obj:`str`, optional): Voice message caption, 0-1024 characters.
caption (:obj:`str`, optional): Voice message 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.
@@ -1795,7 +1825,7 @@ class Bot(TelegramObject):
Identifier of the sent message.
inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not
specified. Identifier of the inline message.
text (:obj:`str`): New text of the message.
text (:obj:`str`): New text of the message, 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 your bot's message. See the
constants in :class:`telegram.ParseMode` for the available modes.
@@ -1932,7 +1962,7 @@ class Bot(TelegramObject):
if inline_message_id is None and (chat_id is None or message_id is None):
raise ValueError(
'edit_message_caption: Both chat_id and message_id are required when '
'edit_message_media: Both chat_id and message_id are required when '
'inline_message_id is not specified')
url = '{0}/editMessageMedia'.format(self.base_url)
@@ -2932,6 +2962,44 @@ class Bot(TelegramObject):
return result
@log
def set_chat_administrator_custom_title(self,
chat_id,
user_id,
custom_title,
timeout=None,
**kwargs):
"""
Use this method to set a custom title for administrators promoted by the bot in a
supergroup. The bot must be an administrator for this to work. Returns True on success.
Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username of
the target supergroup (in the format `@supergroupusername`).
user_id (:obj:`int`): Unique identifier of the target administrator.
custom_title (:obj:`str`) New custom title for the administrator. It must be a string
with len 0-16 characters, emoji are not allowed.
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).
**kwargs (:obj:`dict`): Arbitrary keyword arguments
Returns:
:obj:`bool`: Returns True on success.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/setChatAdministratorCustomTitle'.format(self.base_url)
data = {'chat_id': chat_id, 'user_id': user_id, 'custom_title': custom_title}
data.update(kwargs)
result = self._request.post(url, data, timeout=timeout)
return result
@log
def export_chat_invite_link(self, chat_id, timeout=None, **kwargs):
"""
@@ -3445,18 +3513,33 @@ class Bot(TelegramObject):
chat_id,
question,
options,
is_anonymous=True,
type=Poll.REGULAR,
allows_multiple_answers=False,
correct_option_id=None,
is_closed=None,
disable_notification=None,
reply_to_message_id=None,
reply_markup=None,
timeout=None,
**kwargs):
"""
Use this method to send a native poll. A native poll can't be sent to a private chat.
Use this method to send a native poll.
Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target private chat.
question (:obj:`str`): Poll question, 1-255 characters.
options (List[:obj:`str`]): List of answer options, 2-10 strings 1-100 characters each.
is_anonymous (:obj:`bool`, optional): True, if the poll needs to be anonymous,
defaults to True.
type (:obj:`str`, optional): Poll type, :attr:`telegram.Poll.QUIZ` or
:attr:`telegram.Poll.REGULAR`, defaults to :attr:`telegram.Poll.REGULAR`.
allows_multiple_answers (:obj:`bool`, optional): True, if the poll allows multiple
answers, ignored for polls in quiz mode, defaults to False
correct_option_id (:obj:`int`, optional): 0-based identifier of the correct answer
option, required for polls in quiz mode
is_closed (:obj:`bool`, optional): Pass True, if the poll needs to be immediately
closed. This can be useful for poll preview.
disable_notification (:obj:`bool`, optional): Sends the message silently. Users will
receive a notification with no sound.
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
@@ -3484,6 +3567,17 @@ class Bot(TelegramObject):
'options': options
}
if not is_anonymous:
data['is_anonymous'] = is_anonymous
if type:
data['type'] = type
if allows_multiple_answers:
data['allows_multiple_answers'] = allows_multiple_answers
if correct_option_id is not None:
data['correct_option_id'] = correct_option_id
if is_closed:
data['is_closed'] = is_closed
return self._message(url, data, timeout=timeout, disable_notification=disable_notification,
reply_to_message_id=reply_to_message_id, reply_markup=reply_markup,
**kwargs)
@@ -3646,6 +3740,8 @@ class Bot(TelegramObject):
"""Alias for :attr:`promote_chat_member`"""
setChatPermissions = set_chat_permissions
"""Alias for :attr:`set_chat_permissions`"""
setChatAdministratorCustomTitle = set_chat_administrator_custom_title
"""Alias for :attr:`set_chat_administrator_custom_title`"""
exportChatInviteLink = export_chat_invite_link
"""Alias for :attr:`export_chat_invite_link`"""
setChatPhoto = set_chat_photo
+9 -8
View File
@@ -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
+17
View File
@@ -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
@@ -240,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::
+5 -1
View File
@@ -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
+5
View File
@@ -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.
+4 -1
View File
@@ -41,6 +41,8 @@ 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',
@@ -49,4 +51,5 @@ __all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue',
'DispatcherHandlerStop', 'run_async', 'CallbackContext', 'BasePersistence',
'PicklePersistence', 'DictPersistence', 'PrefixHandler', 'Defaults')
'PicklePersistence', 'DictPersistence', 'PrefixHandler', 'PollAnswerHandler',
'PollHandler', 'Defaults')
+4 -4
View File
@@ -93,10 +93,10 @@ 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
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`.
name (:obj:`str`): Optional. The name for this conversationhandler. Required for
persistence
+8 -8
View File
@@ -81,12 +81,12 @@ class PicklePersistence(BasePersistence):
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'])
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 = all.get('bot_data', {})
self.conversations = all['conversations']
self.bot_data = data.get('bot_data', {})
self.conversations = data['conversations']
except IOError:
self.conversations = {}
self.user_data = defaultdict(dict)
@@ -110,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, 'bot_data': self.bot_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:
+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
+3 -1
View File
@@ -91,7 +91,9 @@ class Updater(object):
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.
+11 -3
View File
@@ -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):
+10 -2
View File
@@ -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):
+31 -7
View File
@@ -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):
+10 -2
View File
@@ -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):
+16 -5
View File
@@ -38,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.
@@ -54,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):
+20 -10
View File
@@ -47,7 +47,8 @@ class InputMediaAnimation(InputMedia):
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 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.
@@ -64,7 +65,8 @@ class InputMediaAnimation(InputMedia):
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 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.
@@ -124,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.
@@ -133,7 +136,8 @@ 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.
@@ -164,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.
@@ -182,7 +187,8 @@ class InputMediaVideo(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.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.
@@ -244,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.
@@ -261,7 +268,8 @@ class InputMediaAudio(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.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.
@@ -320,7 +328,8 @@ 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.
@@ -333,7 +342,8 @@ class InputMediaDocument(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.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.
+17 -3
View File
@@ -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):
+10 -2
View File
@@ -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):
+10 -2
View File
@@ -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):
+18 -3
View File
@@ -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):
+17 -3
View File
@@ -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):
@@ -35,7 +35,7 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
video_file_id (:obj:`str`): A valid file identifier for the video file.
title (:obj:`str`): 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, 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.
@@ -49,7 +49,7 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
video_file_id (:obj:`str`): A valid file identifier for the video file.
title (:obj:`str`): 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, 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.
@@ -33,7 +33,7 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
voice_file_id (:obj:`str`): A valid file identifier for the voice message.
title (:obj:`str`): Voice message title.
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.
@@ -46,7 +46,7 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
voice_file_id (:obj:`str`): A valid file identifier for the voice message.
title (:obj:`str`): Voice message title.
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.
+1 -1
View File
@@ -55,7 +55,7 @@ class InlineQueryResultVideo(InlineQueryResult):
mime_type (:obj:`str`): Mime type of the content of video url, "text/html" or "video/mp4".
thumb_url (:obj:`str`): URL of the thumbnail (jpeg only) for the video.
title (:obj:`str`): Title for the result.
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.
+2 -2
View File
@@ -34,7 +34,7 @@ class InlineQueryResultVoice(InlineQueryResult):
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
voice_url (:obj:`str`): A valid URL for the voice recording.
title (:obj:`str`): Voice message title.
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.
@@ -48,7 +48,7 @@ class InlineQueryResultVoice(InlineQueryResult):
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
voice_url (:obj:`str`): A valid URL for the voice recording.
title (:obj:`str`): Voice message title.
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.
+4 -3
View File
@@ -27,15 +27,16 @@ class InputTextMessageContent(InputMessageContent):
Represents the content of a text message to be sent as the result of an inline query.
Attributes:
message_text (:obj:`str`): Text of the message to be sent, 1-4096 characters.
message_text (:obj:`str`): Text of the message to be sent, 1-4096 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 your bot's message.
disable_web_page_preview (:obj:`bool`): Optional. Disables link previews for links in the
sent message.
Args:
message_text (:obj:`str`): Text of the message to be sent, 1-4096 characters. Also found
as :attr:`telegram.constants.MAX_MESSAGE_LENGTH`.
message_text (:obj:`str`): Text of the message to be sent, 1-4096 characters after entities
parsing. Also found as :attr:`telegram.constants.MAX_MESSAGE_LENGTH`.
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 your bot's message.
disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in the
+10 -1
View File
@@ -33,6 +33,7 @@ class KeyboardButton(TelegramObject):
text (:obj:`str`): Text of the button.
request_contact (:obj:`bool`): Optional. If the user's phone number will be sent.
request_location (:obj:`bool`): Optional. If the user's current location will be sent.
request_poll (:class:`KeyboardButtonPollType`): Optional. If the user should create a poll.
Args:
text (:obj:`str`): Text of the button. If none of the optional fields are used, it will be
@@ -41,16 +42,24 @@ class KeyboardButton(TelegramObject):
a contact when the button is pressed. Available in private chats only.
request_location (:obj:`bool`, optional): If True, the user's current location will be sent
when the button is pressed. Available in private chats only.
request_poll (:class:`KeyboardButtonPollType`, optional): If specified, the user will be
asked to create a poll and send it to the bot when the button is pressed. Available in
private chats only.
Note:
:attr:`request_contact` and :attr:`request_location` options will only work in Telegram
versions released after 9 April, 2016. Older clients will ignore them.
:attr:`request_poll` option will only work in Telegram versions released after 23 January,
2020. Older clients will receive unsupported message.
"""
def __init__(self, text, request_contact=None, request_location=None, **kwargs):
def __init__(self, text, request_contact=None, request_location=None, request_poll=None,
**kwargs):
# Required
self.text = text
# Optionals
self.request_contact = request_contact
self.request_location = request_location
self.request_poll = request_poll
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env python
# pylint: disable=R0903
#
# 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 an object that represents a type of a Telegram Poll."""
from telegram import TelegramObject
class KeyboardButtonPollType(TelegramObject):
"""This object represents type of a poll, which is allowed to be created
and sent when the corresponding button is pressed.
Attributes:
type (:obj:`str`): Optional. If :attr:`telegram.Poll.QUIZ` is passed, the user will be
allowed to create only polls in the quiz mode. If :attr:`telegram.Poll.REGULAR` is
passed, only regular polls will be allowed. Otherwise, the user will be allowed to
create a poll of any type.
"""
def __init__(self, type=None):
self.type = type
self._id_attrs = (self.type,)
+277 -61
View File
@@ -510,7 +510,7 @@ class Message(TelegramObject):
bot.send_message(update.message.chat_id, parse_mode=ParseMode.MARKDOWN, *args,
**kwargs)
Sends a message with markdown formatting.
Sends a message with markdown version 1 formatting.
Keyword Args:
quote (:obj:`bool`, optional): If set to ``True``, the message is sent as an actual
@@ -528,6 +528,30 @@ class Message(TelegramObject):
return self.bot.send_message(self.chat_id, *args, **kwargs)
def reply_markdown_v2(self, *args, **kwargs):
"""Shortcut for::
bot.send_message(update.message.chat_id, parse_mode=ParseMode.MARKDOWN_V2, *args,
**kwargs)
Sends a message with markdown version 2 formatting.
Keyword Args:
quote (:obj:`bool`, optional): If set to ``True``, the message is sent as an actual
reply to this 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.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
"""
kwargs['parse_mode'] = ParseMode.MARKDOWN_V2
self._quote(kwargs)
return self.bot.send_message(self.chat_id, *args, **kwargs)
def reply_html(self, *args, **kwargs):
"""Shortcut for::
@@ -899,6 +923,22 @@ class Message(TelegramObject):
return self.bot.delete_message(
chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs)
def stop_poll(self, *args, **kwargs):
"""Shortcut for::
bot.stop_poll(chat_id=message.chat_id,
message_id=message.message_id,
*args,
**kwargs)
Returns:
:class:`telegram.Poll`: On success, the stopped Poll with the
final results is returned.
"""
return self.bot.stop_poll(
chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs)
def parse_entity(self, entity):
"""Returns the text from a given :class:`telegram.MessageEntity`.
@@ -1012,7 +1052,7 @@ class Message(TelegramObject):
}
@staticmethod
def _parse_html(message_text, entities, urled=False):
def _parse_html(message_text, entities, urled=False, offset=0):
if message_text is None:
return None
@@ -1022,38 +1062,78 @@ class Message(TelegramObject):
html_text = ''
last_offset = 0
for entity, text in sorted(entities.items(), key=(lambda item: item[0].offset)):
text = escape(text)
sorted_entities = sorted(entities.items(), key=(lambda item: item[0].offset))
parsed_entities = []
if entity.type == MessageEntity.TEXT_LINK:
insert = '<a href="{}">{}</a>'.format(entity.url, text)
elif entity.type == MessageEntity.TEXT_MENTION and entity.user:
insert = '<a href="tg://user?id={}">{}</a>'.format(entity.user.id, text)
elif entity.type == MessageEntity.URL and urled:
insert = '<a href="{0}">{0}</a>'.format(text)
elif entity.type == MessageEntity.BOLD:
insert = '<b>' + text + '</b>'
elif entity.type == MessageEntity.ITALIC:
insert = '<i>' + text + '</i>'
elif entity.type == MessageEntity.CODE:
insert = '<code>' + text + '</code>'
elif entity.type == MessageEntity.PRE:
insert = '<pre>' + text + '</pre>'
else:
insert = text
for (entity, text) in sorted_entities:
if entity not in parsed_entities:
nested_entities = {
e: t
for (e, t) in sorted_entities if e.offset >= entity.offset
and e.offset + e.length <= entity.offset + entity.length
and e != entity
}
parsed_entities.extend([e for e in nested_entities.keys()])
text = escape(text)
if nested_entities:
text = Message._parse_html(text, nested_entities,
urled=urled, offset=entity.offset)
if entity.type == MessageEntity.TEXT_LINK:
insert = '<a href="{}">{}</a>'.format(entity.url, text)
elif entity.type == MessageEntity.TEXT_MENTION and entity.user:
insert = '<a href="tg://user?id={}">{}</a>'.format(entity.user.id, text)
elif entity.type == MessageEntity.URL and urled:
insert = '<a href="{0}">{0}</a>'.format(text)
elif entity.type == MessageEntity.BOLD:
insert = '<b>' + text + '</b>'
elif entity.type == MessageEntity.ITALIC:
insert = '<i>' + text + '</i>'
elif entity.type == MessageEntity.CODE:
insert = '<code>' + text + '</code>'
elif entity.type == MessageEntity.PRE:
if entity.language:
insert = '<pre><code class="{}">{}</code></pre>'.format(entity.language,
text)
else:
insert = '<pre>' + text + '</pre>'
elif entity.type == MessageEntity.UNDERLINE:
insert = '<u>' + text + '</u>'
elif entity.type == MessageEntity.STRIKETHROUGH:
insert = '<s>' + text + '</s>'
else:
insert = text
if offset == 0:
if sys.maxunicode == 0xffff:
html_text += escape(message_text[last_offset:entity.offset
- offset]) + insert
else:
html_text += escape(message_text[last_offset * 2:(entity.offset
- offset) * 2]
.decode('utf-16-le')) + insert
else:
if sys.maxunicode == 0xffff:
html_text += message_text[last_offset:entity.offset - offset] + insert
else:
html_text += message_text[last_offset * 2:(entity.offset
- offset) * 2].decode('utf-16-le') + insert
last_offset = entity.offset - offset + entity.length
if offset == 0:
if sys.maxunicode == 0xffff:
html_text += escape(message_text[last_offset:entity.offset]) + insert
html_text += escape(message_text[last_offset:])
else:
html_text += escape(message_text[last_offset * 2:entity.offset * 2]
.decode('utf-16-le')) + insert
last_offset = entity.offset + entity.length
if sys.maxunicode == 0xffff:
html_text += escape(message_text[last_offset:])
html_text += escape(message_text[last_offset * 2:].decode('utf-16-le'))
else:
html_text += escape(message_text[last_offset * 2:].decode('utf-16-le'))
if sys.maxunicode == 0xffff:
html_text += message_text[last_offset:]
else:
html_text += message_text[last_offset * 2:].decode('utf-16-le')
return html_text
@property
@@ -1111,7 +1191,9 @@ class Message(TelegramObject):
return self._parse_html(self.caption, self.parse_caption_entities(), urled=True)
@staticmethod
def _parse_markdown(message_text, entities, urled=False):
def _parse_markdown(message_text, entities, urled=False, version=1, offset=0):
version = int(version)
if message_text is None:
return None
@@ -1121,42 +1203,117 @@ class Message(TelegramObject):
markdown_text = ''
last_offset = 0
for entity, text in sorted(entities.items(), key=(lambda item: item[0].offset)):
text = escape_markdown(text)
sorted_entities = sorted(entities.items(), key=(lambda item: item[0].offset))
parsed_entities = []
if entity.type == MessageEntity.TEXT_LINK:
insert = '[{}]({})'.format(text, entity.url)
elif entity.type == MessageEntity.TEXT_MENTION and entity.user:
insert = '[{}](tg://user?id={})'.format(text, entity.user.id)
elif entity.type == MessageEntity.URL and urled:
insert = '[{0}]({0})'.format(text)
elif entity.type == MessageEntity.BOLD:
insert = '*' + text + '*'
elif entity.type == MessageEntity.ITALIC:
insert = '_' + text + '_'
elif entity.type == MessageEntity.CODE:
insert = '`' + text + '`'
elif entity.type == MessageEntity.PRE:
insert = '```' + text + '```'
else:
insert = text
for (entity, text) in sorted_entities:
if entity not in parsed_entities:
nested_entities = {
e: t
for (e, t) in sorted_entities if e.offset >= entity.offset
and e.offset + e.length <= entity.offset + entity.length
and e != entity
}
parsed_entities.extend([e for e in nested_entities.keys()])
orig_text = text
text = escape_markdown(text, version=version)
if nested_entities:
if version < 2:
raise ValueError('Nested entities are not supported for Markdown '
'version 1')
text = Message._parse_markdown(text, nested_entities,
urled=urled, offset=entity.offset,
version=version)
if entity.type == MessageEntity.TEXT_LINK:
if version == 1:
url = entity.url
else:
# Links need special escaping. Also can't have entities nested within
url = escape_markdown(entity.url, version=version,
entity_type=MessageEntity.TEXT_LINK)
insert = '[{}]({})'.format(text, url)
elif entity.type == MessageEntity.TEXT_MENTION and entity.user:
insert = '[{}](tg://user?id={})'.format(text, entity.user.id)
elif entity.type == MessageEntity.URL and urled:
if version == 1:
link = orig_text
else:
link = text
insert = '[{}]({})'.format(link, orig_text)
elif entity.type == MessageEntity.BOLD:
insert = '*' + text + '*'
elif entity.type == MessageEntity.ITALIC:
insert = '_' + text + '_'
elif entity.type == MessageEntity.CODE:
# Monospace needs special escaping. Also can't have entities nested within
insert = '`' + escape_markdown(orig_text, version=version,
entity_type=MessageEntity.CODE) + '`'
elif entity.type == MessageEntity.PRE:
# Monospace needs special escaping. Also can't have entities nested within
code = escape_markdown(orig_text, version=version,
entity_type=MessageEntity.PRE)
if entity.language:
prefix = '```' + entity.language + '\n'
else:
if code.startswith('\\'):
prefix = '```'
else:
prefix = '```\n'
insert = prefix + code + '```'
elif entity.type == MessageEntity.UNDERLINE:
if version == 1:
raise ValueError('Underline entities are not supported for Markdown '
'version 1')
insert = '__' + text + '__'
elif entity.type == MessageEntity.STRIKETHROUGH:
if version == 1:
raise ValueError('Strikethrough entities are not supported for Markdown '
'version 1')
insert = '~' + text + '~'
else:
insert = text
if offset == 0:
if sys.maxunicode == 0xffff:
markdown_text += escape_markdown(message_text[last_offset:entity.offset
- offset],
version=version) + insert
else:
markdown_text += escape_markdown(message_text[last_offset * 2:
(entity.offset - offset) * 2]
.decode('utf-16-le'),
version=version) + insert
else:
if sys.maxunicode == 0xffff:
markdown_text += message_text[last_offset:entity.offset - offset] + insert
else:
markdown_text += message_text[last_offset * 2:(entity.offset
- offset) * 2].decode('utf-16-le') + insert
last_offset = entity.offset - offset + entity.length
if offset == 0:
if sys.maxunicode == 0xffff:
markdown_text += escape_markdown(message_text[last_offset:entity.offset]) + insert
markdown_text += escape_markdown(message_text[last_offset:], version=version)
else:
markdown_text += escape_markdown(message_text[last_offset * 2:entity.offset * 2]
.decode('utf-16-le')) + insert
last_offset = entity.offset + entity.length
if sys.maxunicode == 0xffff:
markdown_text += escape_markdown(message_text[last_offset:])
markdown_text += escape_markdown(message_text[last_offset * 2:]
.decode('utf-16-le'), version=version)
else:
markdown_text += escape_markdown(message_text[last_offset * 2:].decode('utf-16-le'))
if sys.maxunicode == 0xffff:
markdown_text += message_text[last_offset:]
else:
markdown_text += message_text[last_offset * 2:].decode('utf-16-le')
return markdown_text
@property
def text_markdown(self):
"""Creates an Markdown-formatted string from the markup entities found in the message.
"""Creates an Markdown-formatted string from the markup entities found in the message
using :class:`telegram.ParseMode.MARKDOWN`.
Use this if you want to retrieve the message text with the entities formatted as Markdown
in the same way the original message was formatted.
@@ -1167,9 +1324,24 @@ class Message(TelegramObject):
"""
return self._parse_markdown(self.text, self.parse_entities(), urled=False)
@property
def text_markdown_v2(self):
"""Creates an Markdown-formatted string from the markup entities found in the message
using :class:`telegram.ParseMode.MARKDOWN_V2`.
Use this if you want to retrieve the message text with the entities formatted as Markdown
in the same way the original message was formatted.
Returns:
:obj:`str`: Message text with entities formatted as Markdown.
"""
return self._parse_markdown(self.text, self.parse_entities(), urled=False, version=2)
@property
def text_markdown_urled(self):
"""Creates an Markdown-formatted string from the markup entities found in the message.
"""Creates an Markdown-formatted string from the markup entities found in the message
using :class:`telegram.ParseMode.MARKDOWN`.
Use this if you want to retrieve the message text with the entities formatted as Markdown.
This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
@@ -1180,10 +1352,24 @@ class Message(TelegramObject):
"""
return self._parse_markdown(self.text, self.parse_entities(), urled=True)
@property
def text_markdown_v2_urled(self):
"""Creates an Markdown-formatted string from the markup entities found in the message
using :class:`telegram.ParseMode.MARKDOWN_V2`.
Use this if you want to retrieve the message text with the entities formatted as Markdown.
This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
Returns:
:obj:`str`: Message text with entities formatted as Markdown.
"""
return self._parse_markdown(self.text, self.parse_entities(), urled=True, version=2)
@property
def caption_markdown(self):
"""Creates an Markdown-formatted string from the markup entities found in the message's
caption.
caption using :class:`telegram.ParseMode.MARKDOWN`.
Use this if you want to retrieve the message caption with the caption entities formatted as
Markdown in the same way the original message was formatted.
@@ -1194,10 +1380,25 @@ class Message(TelegramObject):
"""
return self._parse_markdown(self.caption, self.parse_caption_entities(), urled=False)
@property
def caption_markdown_v2(self):
"""Creates an Markdown-formatted string from the markup entities found in the message's
caption using :class:`telegram.ParseMode.MARKDOWN_V2`.
Use this if you want to retrieve the message caption with the caption entities formatted as
Markdown in the same way the original message was formatted.
Returns:
:obj:`str`: Message caption with caption entities formatted as Markdown.
"""
return self._parse_markdown(self.caption, self.parse_caption_entities(),
urled=False, version=2)
@property
def caption_markdown_urled(self):
"""Creates an Markdown-formatted string from the markup entities found in the message's
caption.
caption using :class:`telegram.ParseMode.MARKDOWN`.
Use this if you want to retrieve the message caption with the caption entities formatted as
Markdown. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
@@ -1207,3 +1408,18 @@ class Message(TelegramObject):
"""
return self._parse_markdown(self.caption, self.parse_caption_entities(), urled=True)
@property
def caption_markdown_v2_urled(self):
"""Creates an Markdown-formatted string from the markup entities found in the message's
caption using :class:`telegram.ParseMode.MARKDOWN_V2`.
Use this if you want to retrieve the message caption with the caption entities formatted as
Markdown. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink.
Returns:
:obj:`str`: Message caption with caption entities formatted as Markdown.
"""
return self._parse_markdown(self.caption, self.parse_caption_entities(),
urled=True, version=2)
+15 -5
View File
@@ -32,6 +32,8 @@ class MessageEntity(TelegramObject):
length (:obj:`int`): Length of the entity in UTF-16 code units.
url (:obj:`str`): Optional. Url that will be opened after user taps on the text.
user (:class:`telegram.User`): Optional. The mentioned user.
language (:obj:`str`): Optional. Programming language of the entity
text
Args:
type (:obj:`str`): Type of the entity. Can be mention (@username), hashtag, bot_command,
@@ -40,13 +42,16 @@ class MessageEntity(TelegramObject):
without usernames).
offset (:obj:`int`): Offset in UTF-16 code units to the start of the entity.
length (:obj:`int`): Length of the entity in UTF-16 code units.
url (:obj:`str`, optional): For "text_link" only, url that will be opened after usertaps on
the text.
user (:class:`telegram.User`, optional): For "text_mention" only, the mentioned user.
url (:obj:`str`, optional): For :attr:`TEXT_LINK` only, url that will be opened after
usertaps on the text.
user (:class:`telegram.User`, optional): For :attr:`TEXT_MENTION` only, the mentioned
user.
language (:obj:`str`, optional): For :attr:`PRE` only, the programming language of
the entity text
"""
def __init__(self, type, offset, length, url=None, user=None, **kwargs):
def __init__(self, type, offset, length, url=None, user=None, language=None, **kwargs):
# Required
self.type = type
self.offset = offset
@@ -54,6 +59,7 @@ class MessageEntity(TelegramObject):
# Optionals
self.url = url
self.user = user
self.language = language
self._id_attrs = (self.type, self.offset, self.length)
@@ -105,8 +111,12 @@ class MessageEntity(TelegramObject):
""":obj:`str`: 'text_link'"""
TEXT_MENTION = 'text_mention'
""":obj:`str`: 'text_mention'"""
UNDERLINE = 'underline'
""":obj:`str`: 'underline'"""
STRIKETHROUGH = 'strikethrough'
""":obj:`str`: 'strikethrough'"""
ALL_TYPES = [
MENTION, HASHTAG, CASHTAG, PHONE_NUMBER, BOT_COMMAND, URL,
EMAIL, BOLD, ITALIC, CODE, PRE, TEXT_LINK, TEXT_MENTION
EMAIL, BOLD, ITALIC, CODE, PRE, TEXT_LINK, TEXT_MENTION, UNDERLINE, STRIKETHROUGH
]
"""List[:obj:`str`]: List of all the types."""
+2
View File
@@ -25,5 +25,7 @@ class ParseMode(object):
MARKDOWN = 'Markdown'
""":obj:`str`: 'Markdown'"""
MARKDOWN_V2 = 'MarkdownV2'
""":obj:`str`: 'MarkdownV2'"""
HTML = 'HTML'
""":obj:`str`: 'HTML'"""
+17 -3
View File
@@ -28,12 +28,18 @@ class PassportFile(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:`int`): File size.
file_date (:obj:`int`): Unix time when the file was uploaded.
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.
file_size (:obj:`int`): File size.
file_date (:obj:`int`): Unix time when the file was uploaded.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
@@ -41,16 +47,24 @@ class PassportFile(TelegramObject):
"""
def __init__(self, file_id, file_date, file_size=None, bot=None, credentials=None, **kwargs):
def __init__(self,
file_id,
file_unique_id,
file_date,
file_size=None,
bot=None,
credentials=None,
**kwargs):
# Required
self.file_id = file_id
self.file_unique_id = file_unique_id
self.file_size = file_size
self.file_date = file_date
# Optionals
self.bot = bot
self._credentials = credentials
self._id_attrs = (self.file_id,)
self._id_attrs = (self.file_unique_id,)
@classmethod
def de_json(cls, data, bot):
+57 -2
View File
@@ -19,7 +19,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Poll."""
from telegram import (TelegramObject)
from telegram import (TelegramObject, User)
class PollOption(TelegramObject):
@@ -48,6 +48,39 @@ class PollOption(TelegramObject):
return cls(**data)
class PollAnswer(TelegramObject):
"""
This object represents an answer of a user in a non-anonymous poll.
Attributes:
poll_id (:obj:`str`): Unique poll identifier.
user (:class:`telegram.User`): The user, who changed the answer to the poll.
option_ids (List[:obj:`int`]): Identifiers of answer options, chosen by the user.
Args:
poll_id (:obj:`str`): Unique poll identifier.
user (:class:`telegram.User`): The user, who changed the answer to the poll.
option_ids (List[:obj:`int`]): 0-based identifiers of answer options, chosen by the user.
May be empty if the user retracted their vote.
"""
def __init__(self, poll_id, user, option_ids, **kwargs):
self.poll_id = poll_id
self.user = user
self.option_ids = option_ids
@classmethod
def de_json(cls, data, bot):
if not data:
return None
data = super(PollAnswer, cls).de_json(data, bot)
data['user'] = User.de_json(data.get('user'), bot)
return cls(**data)
class Poll(TelegramObject):
"""
This object contains information about a poll.
@@ -56,21 +89,38 @@ class Poll(TelegramObject):
id (:obj:`str`): Unique poll identifier.
question (:obj:`str`): Poll question, 1-255 characters.
options (List[:class:`PollOption`]): List of poll options.
total_voter_count (:obj:`int`): Total number of users that voted in the poll.
is_closed (:obj:`bool`): True, if the poll is closed.
is_anonymous (:obj:`bool`): True, if the poll is anonymous.
type (:obj:`str`): Poll type, currently can be :attr:`REGULAR` or :attr:`QUIZ`.
allows_multiple_answers (:obj:`bool`): True, if the poll allows multiple answers.
correct_option_id (:obj:`int`): Optional. Identifier of the correct answer option.
Args:
id (:obj:`str`): Unique poll identifier.
question (:obj:`str`): Poll question, 1-255 characters.
options (List[:class:`PollOption`]): List of poll options.
is_closed (:obj:`bool`): True, if the poll is closed.
is_anonymous (:obj:`bool`): True, if the poll is anonymous.
type (:obj:`str`): Poll type, currently can be :attr:`REGULAR` or :attr:`QUIZ`.
allows_multiple_answers (:obj:`bool`): True, if the poll allows multiple answers.
correct_option_id (:obj:`int`, optional): 0-based identifier of the correct answer option.
Available only for polls in the quiz mode, which are closed, or was sent (not
forwarded) by the bot or to the private chat with the bot.
"""
def __init__(self, id, question, options, is_closed, **kwargs):
def __init__(self, id, question, options, total_voter_count, is_closed, is_anonymous, type,
allows_multiple_answers, correct_option_id=None, **kwargs):
self.id = id
self.question = question
self.options = options
self.total_voter_count = total_voter_count
self.is_closed = is_closed
self.is_anonymous = is_anonymous
self.type = type
self.allows_multiple_answers = allows_multiple_answers
self.correct_option_id = correct_option_id
self._id_attrs = (self.id,)
@@ -91,3 +141,8 @@ class Poll(TelegramObject):
data['options'] = [x.to_dict() for x in self.options]
return data
REGULAR = "regular"
""":obj:`str`: 'regular'"""
QUIZ = "quiz"
""":obj:`str`: 'quiz'"""
+18 -3
View File
@@ -20,6 +20,7 @@
from telegram import (Message, TelegramObject, InlineQuery, ChosenInlineResult,
CallbackQuery, ShippingQuery, PreCheckoutQuery, Poll)
from telegram.poll import PollAnswer
class Update(TelegramObject):
@@ -42,7 +43,10 @@ class Update(TelegramObject):
pre_checkout_query (:class:`telegram.PreCheckoutQuery`): Optional. New incoming
pre-checkout query.
poll (:class:`telegram.Poll`): Optional. New poll state. Bots receive only updates
about polls, which are sent or stopped by the bot
about stopped polls and polls, which are sent by the bot
poll_answer (:class:`telegram.PollAnswer`): Optional. A user changed their answer
in a non-anonymous poll. Bots receive new votes only in polls that were sent
by the bot itself.
Args:
update_id (:obj:`int`): The update's unique identifier. Update identifiers start from a
@@ -67,6 +71,9 @@ class Update(TelegramObject):
pre-checkout query. Contains full information about checkout
poll (:class:`telegram.Poll`, optional): New poll state. Bots receive only updates
about polls, which are sent or stopped by the bot
poll_answer (:class:`telegram.PollAnswer`, optional): A user changed their answer
in a non-anonymous poll. Bots receive new votes only in polls that were sent
by the bot itself.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
"""
@@ -83,6 +90,7 @@ class Update(TelegramObject):
shipping_query=None,
pre_checkout_query=None,
poll=None,
poll_answer=None,
**kwargs):
# Required
self.update_id = int(update_id)
@@ -97,6 +105,7 @@ class Update(TelegramObject):
self.channel_post = channel_post
self.edited_channel_post = edited_channel_post
self.poll = poll
self.poll_answer = poll_answer
self._effective_user = None
self._effective_chat = None
@@ -137,6 +146,9 @@ class Update(TelegramObject):
elif self.pre_checkout_query:
user = self.pre_checkout_query.from_user
elif self.poll_answer:
user = self.poll_answer.user
self._effective_user = user
return user
@@ -146,7 +158,8 @@ class Update(TelegramObject):
:class:`telegram.Chat`: The chat that this update was sent in, no matter what kind of
update this is. Will be ``None`` for :attr:`inline_query`,
:attr:`chosen_inline_result`, :attr:`callback_query` from inline messages,
:attr:`shipping_query`, :attr:`pre_checkout_query` and :attr:`poll`.
:attr:`shipping_query`, :attr:`pre_checkout_query`, :attr:`poll` and
:attr:`poll_answer`.
"""
if self._effective_chat:
@@ -178,7 +191,8 @@ class Update(TelegramObject):
:class:`telegram.Message`: The message included in this update, no matter what kind of
update this is. Will be ``None`` for :attr:`inline_query`,
:attr:`chosen_inline_result`, :attr:`callback_query` from inline messages,
:attr:`shipping_query`, :attr:`pre_checkout_query` and :attr:`poll`.
:attr:`shipping_query`, :attr:`pre_checkout_query`, :attr:`poll` and
:attr:`poll_answer`.
"""
if self._effective_message:
@@ -237,5 +251,6 @@ class Update(TelegramObject):
edited_channel_post['default_quote'] = data.get('default_quote')
data['edited_channel_post'] = Message.de_json(edited_channel_post, bot)
data['poll'] = Poll.de_json(data.get('poll'), bot)
data['poll_answer'] = PollAnswer.de_json(data.get('poll_answer'), bot)
return cls(**data)
+32 -3
View File
@@ -34,6 +34,12 @@ class User(TelegramObject):
last_name (:obj:`str`): Optional. User's or bot's last name.
username (:obj:`str`): Optional. User's or bot's username.
language_code (:obj:`str`): Optional. IETF language tag of the user's language.
can_join_groups (:obj:`str`): Optional. True, if the bot can be invited to groups.
Returned only in :attr:`telegram.Bot.get_me` requests.
can_read_all_group_messages (:obj:`str`): Optional. True, if privacy mode is disabled
for the bot. Returned only in :attr:`telegram.Bot.get_me` requests.
supports_inline_queries (:obj:`str`): Optional. True, if the bot supports inline queries.
Returned only in :attr:`telegram.Bot.get_me` requests.
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
Args:
@@ -43,6 +49,12 @@ class User(TelegramObject):
last_name (:obj:`str`, optional): User's or bot's last name.
username (:obj:`str`, optional): User's or bot's username.
language_code (:obj:`str`, optional): IETF language tag of the user's language.
can_join_groups (:obj:`str`, optional): True, if the bot can be invited to groups.
Returned only in :attr:`telegram.Bot.get_me` requests.
can_read_all_group_messages (:obj:`str`, optional): True, if privacy mode is disabled
for the bot. Returned only in :attr:`telegram.Bot.get_me` requests.
supports_inline_queries (:obj:`str`, optional): True, if the bot supports inline queries.
Returned only in :attr:`telegram.Bot.get_me` requests.
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
"""
@@ -54,6 +66,9 @@ class User(TelegramObject):
last_name=None,
username=None,
language_code=None,
can_join_groups=None,
can_read_all_group_messages=None,
supports_inline_queries=None,
bot=None,
**kwargs):
# Required
@@ -64,7 +79,9 @@ class User(TelegramObject):
self.last_name = last_name
self.username = username
self.language_code = language_code
self.can_join_groups = can_join_groups
self.can_read_all_group_messages = can_read_all_group_messages
self.supports_inline_queries = supports_inline_queries
self.bot = bot
self._id_attrs = (self.id,)
@@ -99,7 +116,6 @@ class User(TelegramObject):
def de_json(cls, data, bot):
if not data:
return None
data = super(User, cls).de_json(data, bot)
return cls(bot=bot, **data)
@@ -131,13 +147,26 @@ class User(TelegramObject):
name (:obj:`str`): The name used as a link for the user. Defaults to :attr:`full_name`.
Returns:
:obj:`str`: The inline mention for the user as markdown.
:obj:`str`: The inline mention for the user as markdown (version 1).
"""
if name:
return util_mention_markdown(self.id, name)
return util_mention_markdown(self.id, self.full_name)
def mention_markdown_v2(self, name=None):
"""
Args:
name (:obj:`str`): The name used as a link for the user. Defaults to :attr:`full_name`.
Returns:
:obj:`str`: The inline mention for the user as markdown (version 2).
"""
if name:
return util_mention_markdown(self.id, name, version=2)
return util_mention_markdown(self.id, self.full_name, version=2)
def mention_html(self, name=None):
"""
Args:
+29 -5
View File
@@ -44,9 +44,31 @@ def get_signal_name(signum):
return _signames[signum]
def escape_markdown(text):
"""Helper function to escape telegram markup symbols."""
escape_chars = '\*_`\['
def escape_markdown(text, version=1, entity_type=None):
"""
Helper function to escape telegram markup symbols.
Args:
text (:obj:`str`): The text.
version (:obj:`int` | :obj:`str`): Use to specify the version of telegrams Markdown.
Either ``1`` or ``2``. Defaults to ``1``.
entity_type (:obj:`str`, optional): For the entity types ``PRE``, ``CODE`` and the link
part of ``TEXT_LINKS``, only certain characters need to be escaped in ``MarkdownV2``.
See the official API documentation for details. Only valid in combination with
``version=2``, will be ignored else.
"""
if int(version) == 1:
escape_chars = '\*_`\['
elif int(version) == 2:
if entity_type == 'pre' or entity_type == 'code':
escape_chars = '`\\\\'
elif entity_type == 'text_link':
escape_chars = ')\\\\'
else:
escape_chars = '_*\[\]()~`>\#\+\-=|{}\.!'
else:
raise ValueError('Markdown version musst be either 1 or 2!')
return re.sub(r'([%s])' % escape_chars, r'\\\1', text)
@@ -207,17 +229,19 @@ def mention_html(user_id, name):
return u'<a href="tg://user?id={}">{}</a>'.format(user_id, escape(name))
def mention_markdown(user_id, name):
def mention_markdown(user_id, name, version=1):
"""
Args:
user_id (:obj:`int`) The user's id which you want to mention.
name (:obj:`str`) The name the mention is showing.
version (:obj:`int` | :obj:`str`): Use to specify the version of telegrams Markdown.
Either ``1`` or ``2``. Defaults to ``1``
Returns:
:obj:`str`: The inline mention for the user as markdown.
"""
if isinstance(user_id, int):
return u'[{}](tg://user?id={})'.format(escape_markdown(name), user_id)
return u'[{}](tg://user?id={})'.format(escape_markdown(name, version=version), user_id)
def effective_message_type(entity):
+30 -1
View File
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import sys
import logging
from telegram import Update
from future.utils import bytes_to_native_str
@@ -27,7 +28,6 @@ except ImportError:
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
import tornado.web
import tornado.iostream
logging.getLogger(__name__).addHandler(logging.NullHandler())
@@ -91,6 +91,35 @@ class WebhookHandler(tornado.web.RequestHandler):
def __init__(self, application, request, **kwargs):
super(WebhookHandler, self).__init__(application, request, **kwargs)
self.logger = logging.getLogger(__name__)
self._init_asyncio_patch()
def _init_asyncio_patch(self):
"""set default asyncio policy to be compatible with tornado
Tornado 6 (at least) is not compatible with the default
asyncio implementation on Windows
Pick the older SelectorEventLoopPolicy on Windows
if the known-incompatible default policy is in use.
do this as early as possible to make it a low priority and overrideable
ref: https://github.com/tornadoweb/tornado/issues/2608
TODO: if/when tornado supports the defaults in asyncio,
remove and bump tornado requirement for py38
Copied from https://github.com/ipython/ipykernel/pull/456/
"""
if sys.platform.startswith("win") and sys.version_info >= (3, 8):
import asyncio
try:
from asyncio import (
WindowsProactorEventLoopPolicy,
WindowsSelectorEventLoopPolicy,
)
except ImportError:
pass
# not affected
else:
if isinstance(asyncio.get_event_loop_policy(), WindowsProactorEventLoopPolicy):
# WindowsProactorEventLoopPolicy is not compatible with tornado 6
# fallback to the pre-3.8 default of Selector
asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())
def initialize(self, bot, update_queue, default_quote=None):
self.bot = bot
+1 -1
View File
@@ -17,4 +17,4 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
__version__ = '12.4.2'
__version__ = '12.5'
-5
View File
@@ -34,11 +34,6 @@ from telegram.ext import Dispatcher, JobQueue, Updater, BaseFilter, Defaults
from telegram.utils.helpers import _UtcOffsetTimezone
from tests.bots import get_bot
TRAVIS = os.getenv('TRAVIS', False)
if TRAVIS:
pytest_plugins = ['tests.travis_fold']
GITHUB_ACTION = os.getenv('GITHUB_ACTION', False)
if GITHUB_ACTION:
+19 -6
View File
@@ -41,6 +41,7 @@ def animation(bot, chat_id):
class TestAnimation(object):
animation_file_id = 'CgADAQADngIAAuyVeEez0xRovKi9VAI'
animation_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
width = 320
height = 180
duration = 1
@@ -55,7 +56,9 @@ class TestAnimation(object):
def test_creation(self, animation):
assert isinstance(animation, Animation)
assert isinstance(animation.file_id, str)
assert isinstance(animation.file_unique_id, str)
assert animation.file_id != ''
assert animation.file_unique_id != ''
def test_expected_values(self, animation):
assert animation.file_size == self.file_size
@@ -73,7 +76,9 @@ class TestAnimation(object):
assert isinstance(message.animation, Animation)
assert isinstance(message.animation.file_id, str)
assert isinstance(message.animation.file_unique_id, str)
assert message.animation.file_id != ''
assert message.animation.file_unique_id != ''
assert message.animation.file_name == animation.file_name
assert message.animation.mime_type == animation.mime_type
assert message.animation.file_size == animation.file_size
@@ -103,8 +108,12 @@ class TestAnimation(object):
assert isinstance(message.animation, Animation)
assert isinstance(message.animation.file_id, str)
assert message.animation.file_id is not None
assert isinstance(message.animation.file_unique_id, str)
assert message.animation.file_id != ''
assert message.animation.file_unique_id != ''
assert message.animation.duration == animation.duration
assert message.animation.file_name == animation.file_name
assert message.animation.mime_type == animation.mime_type
assert message.animation.file_size == animation.file_size
@@ -143,7 +152,6 @@ class TestAnimation(object):
@flaky(3, 1)
@pytest.mark.timeout(10)
@pytest.mark.skip(reason='Doesnt work without API 4.5')
def test_resend(self, bot, chat_id, animation):
message = bot.send_animation(chat_id, animation.file_id)
@@ -160,6 +168,7 @@ class TestAnimation(object):
def test_de_json(self, bot, animation):
json_dict = {
'file_id': self.animation_file_id,
'file_unique_id': self.animation_file_unique_id,
'width': self.width,
'height': self.height,
'duration': self.duration,
@@ -170,6 +179,7 @@ class TestAnimation(object):
}
animation = Animation.de_json(json_dict, bot)
assert animation.file_id == self.animation_file_id
assert animation.file_unique_id == self.animation_file_unique_id
assert animation.thumb == animation.thumb
assert animation.file_name == self.file_name
assert animation.mime_type == self.mime_type
@@ -180,6 +190,7 @@ class TestAnimation(object):
assert isinstance(animation_dict, dict)
assert animation_dict['file_id'] == animation.file_id
assert animation_dict['file_unique_id'] == animation.file_unique_id
assert animation_dict['width'] == animation.width
assert animation_dict['height'] == animation.height
assert animation_dict['duration'] == animation.duration
@@ -214,10 +225,12 @@ class TestAnimation(object):
assert animation.get_file()
def test_equality(self):
a = Animation(self.animation_file_id, self.height, self.width, self.duration)
b = Animation(self.animation_file_id, self.height, self.width, self.duration)
d = Animation('', 0, 0, 0)
e = Voice(self.animation_file_id, 0)
a = Animation(self.animation_file_id, self.animation_file_unique_id,
self.height, self.width, self.duration)
b = Animation('', self.animation_file_unique_id,
self.height, self.width, self.duration)
d = Animation('', '', 0, 0, 0)
e = Voice(self.animation_file_id, self.animation_file_unique_id, 0)
assert a == b
assert hash(a) == hash(b)
+28 -15
View File
@@ -52,12 +52,16 @@ class TestAudio(object):
thumb_file_size = 1427
thumb_width = 50
thumb_height = 50
audio_file_id = '5a3128a4d2a04750b5b58397f3b5e812'
audio_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
def test_creation(self, audio):
# Make sure file has been uploaded.
assert isinstance(audio, Audio)
assert isinstance(audio.file_id, str)
assert isinstance(audio.file_unique_id, str)
assert audio.file_id != ''
assert audio.file_unique_id != ''
def test_expected_values(self, audio):
assert audio.duration == self.duration
@@ -81,6 +85,8 @@ class TestAudio(object):
assert isinstance(message.audio, Audio)
assert isinstance(message.audio.file_id, str)
assert isinstance(message.audio.file_unique_id, str)
assert message.audio.file_unique_id is not None
assert message.audio.file_id is not None
assert message.audio.duration == self.duration
assert message.audio.performer == self.performer
@@ -98,6 +104,7 @@ class TestAudio(object):
assert new_file.file_size == self.file_size
assert new_file.file_id == audio.file_id
assert new_file.file_unique_id == audio.file_unique_id
assert new_file.file_path.startswith('https://')
new_file.download('telegram.mp3')
@@ -113,6 +120,8 @@ class TestAudio(object):
assert isinstance(message.audio, Audio)
assert isinstance(message.audio.file_id, str)
assert isinstance(message.audio.file_unique_id, str)
assert message.audio.file_unique_id is not None
assert message.audio.file_id is not None
assert message.audio.duration == audio.duration
assert message.audio.mime_type == audio.mime_type
@@ -120,7 +129,6 @@ class TestAudio(object):
@flaky(3, 1)
@pytest.mark.timeout(10)
@pytest.mark.skip(reason='Doesnt work without API 4.5')
def test_resend(self, bot, chat_id, audio):
message = bot.send_audio(chat_id=chat_id, audio=audio.file_id)
@@ -168,17 +176,21 @@ class TestAudio(object):
assert message.caption_markdown == escape_markdown(test_markdown_string)
def test_de_json(self, bot, audio):
json_dict = {'file_id': 'not a file id',
'duration': self.duration,
'performer': self.performer,
'title': self.title,
'caption': self.caption,
'mime_type': self.mime_type,
'file_size': self.file_size,
'thumb': audio.thumb.to_dict()}
json_dict = {
'file_id': self.audio_file_id,
'file_unique_id': self.audio_file_unique_id,
'duration': self.duration,
'performer': self.performer,
'title': self.title,
'caption': self.caption,
'mime_type': self.mime_type,
'file_size': self.file_size,
'thumb': audio.thumb.to_dict()
}
json_audio = Audio.de_json(json_dict, bot)
assert json_audio.file_id == 'not a file id'
assert json_audio.file_id == self.audio_file_id
assert json_audio.file_unique_id == self.audio_file_unique_id
assert json_audio.duration == self.duration
assert json_audio.performer == self.performer
assert json_audio.title == self.title
@@ -191,6 +203,7 @@ class TestAudio(object):
assert isinstance(audio_dict, dict)
assert audio_dict['file_id'] == audio.file_id
assert audio_dict['file_unique_id'] == audio.file_unique_id
assert audio_dict['duration'] == audio.duration
assert audio_dict['mime_type'] == audio.mime_type
assert audio_dict['file_size'] == audio.file_size
@@ -221,11 +234,11 @@ class TestAudio(object):
assert audio.get_file()
def test_equality(self, audio):
a = Audio(audio.file_id, audio.duration)
b = Audio(audio.file_id, audio.duration)
c = Audio(audio.file_id, 0)
d = Audio('', audio.duration)
e = Voice(audio.file_id, audio.duration)
a = Audio(audio.file_id, audio.file_unique_id, audio.duration)
b = Audio('', audio.file_unique_id, audio.duration)
c = Audio(audio.file_id, audio.file_unique_id, 0)
d = Audio('', '', audio.duration)
e = Voice(audio.file_id, audio.file_unique_id, audio.duration)
assert a == b
assert hash(a) == hash(b)
+25 -5
View File
@@ -16,8 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import os
import sys
import time
import datetime as dtm
from platform import python_implementation
@@ -89,6 +87,10 @@ class TestBot(object):
assert get_me_bot.first_name == bot.first_name
assert get_me_bot.last_name == bot.last_name
assert get_me_bot.name == bot.name
assert get_me_bot.can_join_groups == bot.can_join_groups
assert get_me_bot.can_read_all_group_messages == bot.can_read_all_group_messages
assert get_me_bot.supports_inline_queries == bot.supports_inline_queries
assert 'https://t.me/{}'.format(get_me_bot.username) == bot.link
@flaky(3, 1)
@pytest.mark.timeout(10)
@@ -176,14 +178,17 @@ class TestBot(object):
question = 'Is this a test?'
answers = ['Yes', 'No', 'Maybe']
message = bot.send_poll(chat_id=super_group_id, question=question, options=answers,
timeout=60)
is_anonymous=False, allows_multiple_answers=True, timeout=60)
assert message.poll
assert message.poll.question == question
assert message.poll.options[0].text == answers[0]
assert message.poll.options[1].text == answers[1]
assert message.poll.options[2].text == answers[2]
assert not message.poll.is_anonymous
assert message.poll.allows_multiple_answers
assert not message.poll.is_closed
assert message.poll.type == Poll.REGULAR
poll = bot.stop_poll(chat_id=super_group_id, message_id=message.message_id, timeout=60)
assert isinstance(poll, Poll)
@@ -195,6 +200,13 @@ class TestBot(object):
assert poll.options[2].text == answers[2]
assert poll.options[2].voter_count == 0
assert poll.question == question
assert poll.total_voter_count == 0
message_quiz = bot.send_poll(chat_id=super_group_id, question=question, options=answers,
type=Poll.QUIZ, correct_option_id=2, is_closed=True)
assert message_quiz.poll.correct_option_id == 2
assert message_quiz.poll.type == Poll.QUIZ
assert message_quiz.poll.is_closed
@flaky(3, 1)
@pytest.mark.timeout(10)
@@ -350,6 +362,16 @@ class TestBot(object):
assert bot.set_chat_permissions(2, chat_permissions)
def test_set_chat_administrator_custom_title(self, monkeypatch, bot):
def test(_, url, data, *args, **kwargs):
chat_id = data['chat_id'] == 2
user_id = data['user_id'] == 32
custom_title = data['custom_title'] == 'custom_title'
return chat_id and user_id and custom_title
monkeypatch.setattr('telegram.utils.request.Request.post', test)
assert bot.set_chat_administrator_custom_title(2, 32, 'custom_title')
# TODO: Needs improvement. Need an incoming callbackquery to test
def test_answer_callback_query(self, monkeypatch, bot):
# For now just test that our internals pass the correct data
@@ -493,8 +515,6 @@ class TestBot(object):
@flaky(3, 1)
@pytest.mark.timeout(15)
@pytest.mark.xfail
@pytest.mark.skipif(os.getenv('APPVEYOR') and (sys.version_info < (3, 6)),
reason='only run on 3.6 on appveyor')
def test_set_webhook_get_webhook_info_and_delete_webhook(self, bot):
url = 'https://python-telegram-bot.org/test/webhook'
max_connections = 7
+18 -18
View File
@@ -24,7 +24,7 @@ from telegram import CallbackQuery, User, Message, Chat, Audio
@pytest.fixture(scope='class', params=['message', 'inline'])
def callback_query(bot, request):
cbq = CallbackQuery(TestCallbackQuery.id,
cbq = CallbackQuery(TestCallbackQuery.id_,
TestCallbackQuery.from_user,
TestCallbackQuery.chat_instance,
data=TestCallbackQuery.data,
@@ -38,7 +38,7 @@ def callback_query(bot, request):
class TestCallbackQuery(object):
id = 'id'
id_ = 'id'
from_user = User(1, 'test_user', False)
chat_instance = 'chat_instance'
message = Message(3, User(5, 'bot', False), None, Chat(4, 'private'))
@@ -47,7 +47,7 @@ class TestCallbackQuery(object):
game_short_name = 'the_game'
def test_de_json(self, bot):
json_dict = {'id': self.id,
json_dict = {'id': self.id_,
'from': self.from_user.to_dict(),
'chat_instance': self.chat_instance,
'message': self.message.to_dict(),
@@ -57,7 +57,7 @@ class TestCallbackQuery(object):
'default_quote': True}
callback_query = CallbackQuery.de_json(json_dict, bot)
assert callback_query.id == self.id
assert callback_query.id == self.id_
assert callback_query.from_user == self.from_user
assert callback_query.chat_instance == self.chat_instance
assert callback_query.message == self.message
@@ -92,8 +92,8 @@ class TestCallbackQuery(object):
def test(*args, **kwargs):
text = args[0] == 'test'
try:
id = kwargs['inline_message_id'] == callback_query.inline_message_id
return id and text
id_ = kwargs['inline_message_id'] == callback_query.inline_message_id
return id_ and text
except KeyError:
chat_id = kwargs['chat_id'] == callback_query.message.chat_id
message_id = kwargs['message_id'] == callback_query.message.message_id
@@ -107,12 +107,12 @@ class TestCallbackQuery(object):
def test(*args, **kwargs):
caption = kwargs['caption'] == 'new caption'
try:
id = kwargs['inline_message_id'] == callback_query.inline_message_id
return id and caption
id_ = kwargs['inline_message_id'] == callback_query.inline_message_id
return id_ and caption
except KeyError:
id = kwargs['chat_id'] == callback_query.message.chat_id
id_ = kwargs['chat_id'] == callback_query.message.chat_id
message = kwargs['message_id'] == callback_query.message.message_id
return id and message and caption
return id_ and message and caption
monkeypatch.setattr(callback_query.bot, 'edit_message_caption', test)
assert callback_query.edit_message_caption(caption='new caption')
@@ -122,23 +122,23 @@ class TestCallbackQuery(object):
def test(*args, **kwargs):
reply_markup = kwargs['reply_markup'] == [['1', '2']]
try:
id = kwargs['inline_message_id'] == callback_query.inline_message_id
return id and reply_markup
id_ = kwargs['inline_message_id'] == callback_query.inline_message_id
return id_ and reply_markup
except KeyError:
id = kwargs['chat_id'] == callback_query.message.chat_id
id_ = kwargs['chat_id'] == callback_query.message.chat_id
message = kwargs['message_id'] == callback_query.message.message_id
return id and message and reply_markup
return id_ and message and reply_markup
monkeypatch.setattr(callback_query.bot, 'edit_message_reply_markup', test)
assert callback_query.edit_message_reply_markup(reply_markup=[['1', '2']])
assert callback_query.edit_message_reply_markup([['1', '2']])
def test_equality(self):
a = CallbackQuery(self.id, self.from_user, 'chat')
b = CallbackQuery(self.id, self.from_user, 'chat')
c = CallbackQuery(self.id, None, '')
a = CallbackQuery(self.id_, self.from_user, 'chat')
b = CallbackQuery(self.id_, self.from_user, 'chat')
c = CallbackQuery(self.id_, None, '')
d = CallbackQuery('', None, 'chat')
e = Audio(self.id, 1)
e = Audio(self.id_, 'unique_id', 1)
assert a == b
assert hash(a) == hash(b)
+33 -18
View File
@@ -25,17 +25,18 @@ from telegram import User, Message
@pytest.fixture(scope='class')
def chat(bot):
return Chat(TestChat.id, TestChat.title, TestChat.type, username=TestChat.username,
return Chat(TestChat.id_, TestChat.title, TestChat.type_, username=TestChat.username,
all_members_are_administrators=TestChat.all_members_are_administrators,
bot=bot, sticker_set_name=TestChat.sticker_set_name,
can_set_sticker_set=TestChat.can_set_sticker_set,
permissions=TestChat.permissions)
permissions=TestChat.permissions,
slow_mode_delay=TestChat.slow_mode_delay)
class TestChat(object):
id = -28767330
id_ = -28767330
title = 'ToledosPalaceBot - Group'
type = 'group'
type_ = 'group'
username = 'username'
all_members_are_administrators = False
sticker_set_name = 'stickers'
@@ -45,33 +46,36 @@ class TestChat(object):
can_change_info=False,
can_invite_users=True,
)
slow_mode_delay = 30
def test_de_json(self, bot):
json_dict = {
'id': self.id,
'id': self.id_,
'title': self.title,
'type': self.type,
'type': self.type_,
'username': self.username,
'all_members_are_administrators': self.all_members_are_administrators,
'sticker_set_name': self.sticker_set_name,
'can_set_sticker_set': self.can_set_sticker_set,
'permissions': self.permissions.to_dict()
'permissions': self.permissions.to_dict(),
'slow_mode_delay': self.slow_mode_delay
}
chat = Chat.de_json(json_dict, bot)
assert chat.id == self.id
assert chat.id == self.id_
assert chat.title == self.title
assert chat.type == self.type
assert chat.type == self.type_
assert chat.username == self.username
assert chat.all_members_are_administrators == self.all_members_are_administrators
assert chat.sticker_set_name == self.sticker_set_name
assert chat.can_set_sticker_set == self.can_set_sticker_set
assert chat.permissions == self.permissions
assert chat.slow_mode_delay == self.slow_mode_delay
def test_de_json_default_quote(self, bot):
json_dict = {
'id': self.id,
'type': self.type,
'id': self.id_,
'type': self.type_,
'pinned_message': Message(
message_id=123,
from_user=None,
@@ -94,6 +98,7 @@ class TestChat(object):
assert chat_dict['username'] == chat.username
assert chat_dict['all_members_are_administrators'] == chat.all_members_are_administrators
assert chat_dict['permissions'] == chat.permissions.to_dict()
assert chat_dict['slow_mode_delay'] == chat.slow_mode_delay
def test_link(self, chat):
assert chat.link == 'https://t.me/{}'.format(chat.username)
@@ -102,9 +107,9 @@ class TestChat(object):
def test_send_action(self, monkeypatch, chat):
def test(*args, **kwargs):
id = args[0] == chat.id
id_ = args[0] == chat.id
action = kwargs['action'] == ChatAction.TYPING
return id and action
return id_ and action
monkeypatch.setattr(chat.bot, 'send_chat_action', test)
assert chat.send_action(action=ChatAction.TYPING)
@@ -167,6 +172,16 @@ class TestChat(object):
monkeypatch.setattr(chat.bot, 'set_chat_permissions', test)
assert chat.set_permissions(self.permissions)
def test_set_administrator_custom_title(self, monkeypatch, chat):
def test(*args, **kwargs):
chat_id = args[1] == chat.id
user_id = args[2] == 42
custom_title = args[3] == 'custom_title'
return chat_id and user_id and custom_title
monkeypatch.setattr('telegram.Bot.set_chat_administrator_custom_title', test)
assert chat.set_administrator_custom_title(42, 'custom_title')
def test_instance_method_send_message(self, monkeypatch, chat):
def test(*args, **kwargs):
return args[0] == chat.id and args[1] == 'test'
@@ -238,11 +253,11 @@ class TestChat(object):
assert chat.send_poll('test_poll')
def test_equality(self):
a = Chat(self.id, self.title, self.type)
b = Chat(self.id, self.title, self.type)
c = Chat(self.id, '', '')
d = Chat(0, self.title, self.type)
e = User(self.id, '', False)
a = Chat(self.id_, self.title, self.type_)
b = Chat(self.id_, self.title, self.type_)
c = Chat(self.id_, '', '')
d = Chat(0, self.title, self.type_)
e = User(self.id_, '', False)
assert a == b
assert hash(a) == hash(b)
+4
View File
@@ -47,8 +47,11 @@ class TestChatMember(object):
def test_de_json_all_args(self, bot, user):
time = datetime.datetime.utcnow()
custom_title = 'custom_title'
json_dict = {'user': user.to_dict(),
'status': self.status,
'custom_title': custom_title,
'until_date': to_timestamp(time),
'can_be_edited': False,
'can_change_info': True,
@@ -69,6 +72,7 @@ class TestChatMember(object):
assert chat_member.user == user
assert chat_member.status == self.status
assert chat_member.custom_title == custom_title
assert chat_member.can_be_edited is False
assert chat_member.can_change_info is True
assert chat_member.can_post_messages is False
+155
View File
@@ -0,0 +1,155 @@
#!/usr/bin/env python
#
# 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/].
import os
import pytest
from flaky import flaky
from telegram import ChatPhoto, Voice, TelegramError
@pytest.fixture(scope='function')
def chatphoto_file():
f = open('tests/data/telegram.jpg', 'rb')
yield f
f.close()
@pytest.fixture(scope='function')
def chat_photo(bot, super_group_id):
return bot.get_chat(super_group_id, timeout=50).photo
class TestChatPhoto(object):
chatphoto_small_file_id = 'smallCgADAQADngIAAuyVeEez0xRovKi9VAI'
chatphoto_big_file_id = 'bigCgADAQADngIAAuyVeEez0xRovKi9VAI'
chatphoto_small_file_unique_id = 'smalladc3145fd2e84d95b64d68eaa22aa33e'
chatphoto_big_file_unique_id = 'bigadc3145fd2e84d95b64d68eaa22aa33e'
chatphoto_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.jpg'
@flaky(3, 1)
@pytest.mark.timeout(10)
def test_send_all_args(self, bot, super_group_id, chatphoto_file, chat_photo, thumb_file):
assert bot.set_chat_photo(super_group_id, chatphoto_file)
@flaky(3, 1)
@pytest.mark.timeout(10)
def test_get_and_download(self, bot, chat_photo):
new_file = bot.get_file(chat_photo.small_file_id)
assert new_file.file_id == chat_photo.small_file_id
assert new_file.file_path.startswith('https://')
new_file.download('telegram.jpg')
assert os.path.isfile('telegram.jpg')
new_file = bot.get_file(chat_photo.big_file_id)
assert new_file.file_id == chat_photo.big_file_id
assert new_file.file_path.startswith('https://')
new_file.download('telegram.jpg')
assert os.path.isfile('telegram.jpg')
def test_send_with_chat_photo(self, monkeypatch, bot, super_group_id, chat_photo):
def test(_, url, data, **kwargs):
return data['photo'] == chat_photo
monkeypatch.setattr('telegram.utils.request.Request.post', test)
message = bot.set_chat_photo(photo=chat_photo, chat_id=super_group_id)
assert message
def test_de_json(self, bot, chat_photo):
json_dict = {
'small_file_id': self.chatphoto_small_file_id,
'big_file_id': self.chatphoto_big_file_id,
'small_file_unique_id': self.chatphoto_small_file_unique_id,
'big_file_unique_id': self.chatphoto_big_file_unique_id,
}
chat_photo = ChatPhoto.de_json(json_dict, bot)
assert chat_photo.small_file_id == self.chatphoto_small_file_id
assert chat_photo.big_file_id == self.chatphoto_big_file_id
assert chat_photo.small_file_unique_id == self.chatphoto_small_file_unique_id
assert chat_photo.big_file_unique_id == self.chatphoto_big_file_unique_id
def test_to_dict(self, chat_photo):
chat_photo_dict = chat_photo.to_dict()
assert isinstance(chat_photo_dict, dict)
assert chat_photo_dict['small_file_id'] == chat_photo.small_file_id
assert chat_photo_dict['big_file_id'] == chat_photo.big_file_id
assert chat_photo_dict['small_file_unique_id'] == chat_photo.small_file_unique_id
assert chat_photo_dict['big_file_unique_id'] == chat_photo.big_file_unique_id
@flaky(3, 1)
@pytest.mark.timeout(10)
def test_error_send_empty_file(self, bot, super_group_id):
chatphoto_file = open(os.devnull, 'rb')
with pytest.raises(TelegramError):
bot.set_chat_photo(chat_id=super_group_id, photo=chatphoto_file)
@flaky(3, 1)
@pytest.mark.timeout(10)
def test_error_send_empty_file_id(self, bot, super_group_id):
with pytest.raises(TelegramError):
bot.set_chat_photo(chat_id=super_group_id, photo='')
def test_error_send_without_required_args(self, bot, super_group_id):
with pytest.raises(TypeError):
bot.set_chat_photo(chat_id=super_group_id)
def test_get_small_file_instance_method(self, monkeypatch, chat_photo):
def test(*args, **kwargs):
return args[1] == chat_photo.small_file_id
monkeypatch.setattr('telegram.Bot.get_file', test)
assert chat_photo.get_small_file()
def test_get_big_file_instance_method(self, monkeypatch, chat_photo):
def test(*args, **kwargs):
return args[1] == chat_photo.big_file_id
monkeypatch.setattr('telegram.Bot.get_file', test)
assert chat_photo.get_big_file()
def test_equality(self):
a = ChatPhoto(self.chatphoto_small_file_id, self.chatphoto_big_file_id,
self.chatphoto_small_file_unique_id, self.chatphoto_big_file_unique_id)
b = ChatPhoto(self.chatphoto_small_file_id, self.chatphoto_big_file_id,
self.chatphoto_small_file_unique_id, self.chatphoto_big_file_unique_id)
c = ChatPhoto('', '', self.chatphoto_small_file_unique_id,
self.chatphoto_big_file_unique_id)
d = ChatPhoto('', '', 0, 0)
e = Voice(self.chatphoto_small_file_id, self.chatphoto_small_file_unique_id, 0)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a != c
assert hash(a) != hash(c)
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)
+1 -1
View File
@@ -74,7 +74,7 @@ class TestChosenInlineResult(object):
b = ChosenInlineResult(self.result_id, user, 'Query', '')
c = ChosenInlineResult(self.result_id, user, '', '')
d = ChosenInlineResult('', user, 'Query', '')
e = Voice(self.result_id, 0)
e = Voice(self.result_id, 'unique_id', 0)
assert a == b
assert hash(a) == hash(b)
+1 -1
View File
@@ -80,7 +80,7 @@ class TestContact(object):
b = Contact(self.phone_number, self.first_name)
c = Contact(self.phone_number, '')
d = Contact('', self.first_name)
e = Voice('', 0)
e = Voice('', 'unique_id', 0)
assert a == b
assert hash(a) == hash(b)
+24 -12
View File
@@ -47,11 +47,15 @@ class TestDocument(object):
thumb_file_size = 8090
thumb_width = 300
thumb_height = 300
document_file_id = '5a3128a4d2a04750b5b58397f3b5e812'
document_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
def test_creation(self, document):
assert isinstance(document, Document)
assert isinstance(document.file_id, str)
assert isinstance(document.file_unique_id, str)
assert document.file_id != ''
assert document.file_unique_id != ''
def test_expected_values(self, document):
assert document.file_size == self.file_size
@@ -71,6 +75,8 @@ class TestDocument(object):
assert isinstance(message.document, Document)
assert isinstance(message.document.file_id, str)
assert message.document.file_id != ''
assert isinstance(message.document.file_unique_id, str)
assert message.document.file_unique_id != ''
assert isinstance(message.document.thumb, PhotoSize)
assert message.document.file_name == 'telegram_custom.png'
assert message.document.mime_type == document.mime_type
@@ -86,6 +92,7 @@ class TestDocument(object):
assert new_file.file_size == document.file_size
assert new_file.file_id == document.file_id
assert new_file.file_unique_id == document.file_unique_id
assert new_file.file_path.startswith('https://')
new_file.download('telegram.png')
@@ -102,6 +109,8 @@ class TestDocument(object):
assert isinstance(document, Document)
assert isinstance(document.file_id, str)
assert document.file_id != ''
assert isinstance(message.document.file_unique_id, str)
assert message.document.file_unique_id != ''
assert isinstance(document.thumb, PhotoSize)
assert document.file_name == 'telegram.gif'
assert document.mime_type == 'image/gif'
@@ -109,7 +118,6 @@ class TestDocument(object):
@flaky(3, 1)
@pytest.mark.timeout(10)
@pytest.mark.skip(reason='Doesnt work without API 4.5')
def test_send_resend(self, bot, chat_id, document):
message = bot.send_document(chat_id=chat_id, document=document.file_id)
@@ -159,15 +167,18 @@ class TestDocument(object):
assert message.caption_markdown == escape_markdown(test_markdown_string)
def test_de_json(self, bot, document):
json_dict = {'file_id': 'not a file id',
'thumb': document.thumb.to_dict(),
'file_name': self.file_name,
'mime_type': self.mime_type,
'file_size': self.file_size
}
json_dict = {
'file_id': self.document_file_id,
'file_unique_id': self.document_file_unique_id,
'thumb': document.thumb.to_dict(),
'file_name': self.file_name,
'mime_type': self.mime_type,
'file_size': self.file_size
}
test_document = Document.de_json(json_dict, bot)
assert test_document.file_id == 'not a file id'
assert test_document.file_id == self.document_file_id
assert test_document.file_unique_id == self.document_file_unique_id
assert test_document.thumb == document.thumb
assert test_document.file_name == self.file_name
assert test_document.mime_type == self.mime_type
@@ -178,6 +189,7 @@ class TestDocument(object):
assert isinstance(document_dict, dict)
assert document_dict['file_id'] == document.file_id
assert document_dict['file_unique_id'] == document.file_unique_id
assert document_dict['file_name'] == document.file_name
assert document_dict['mime_type'] == document.mime_type
assert document_dict['file_size'] == document.file_size
@@ -207,10 +219,10 @@ class TestDocument(object):
assert document.get_file()
def test_equality(self, document):
a = Document(document.file_id)
b = Document(document.file_id)
d = Document('')
e = Voice(document.file_id, 0)
a = Document(document.file_id, document.file_unique_id)
b = Document('', document.file_unique_id)
d = Document('', '')
e = Voice(document.file_id, document.file_unique_id, 0)
assert a == b
assert hash(a) == hash(b)
+5 -5
View File
@@ -24,7 +24,7 @@ from telegram import EncryptedPassportElement, PassportFile, PassportElementErro
@pytest.fixture(scope='class')
def encrypted_passport_element():
return EncryptedPassportElement(TestEncryptedPassportElement.type,
return EncryptedPassportElement(TestEncryptedPassportElement.type_,
data=TestEncryptedPassportElement.data,
phone_number=TestEncryptedPassportElement.phone_number,
email=TestEncryptedPassportElement.email,
@@ -35,7 +35,7 @@ def encrypted_passport_element():
class TestEncryptedPassportElement(object):
type = 'type'
type_ = 'type'
data = 'data'
phone_number = 'phone_number'
email = 'email'
@@ -45,7 +45,7 @@ class TestEncryptedPassportElement(object):
selfie = PassportFile('file_id', 50, 0)
def test_expected_values(self, encrypted_passport_element):
assert encrypted_passport_element.type == self.type
assert encrypted_passport_element.type == self.type_
assert encrypted_passport_element.data == self.data
assert encrypted_passport_element.phone_number == self.phone_number
assert encrypted_passport_element.email == self.email
@@ -75,8 +75,8 @@ class TestEncryptedPassportElement(object):
== encrypted_passport_element.selfie.to_dict())
def test_equality(self):
a = EncryptedPassportElement(self.type, data=self.data)
b = EncryptedPassportElement(self.type, data=self.data)
a = EncryptedPassportElement(self.type_, data=self.data)
b = EncryptedPassportElement(self.type_, data=self.data)
c = EncryptedPassportElement(self.data, '')
d = PassportElementError('source', 'type', 'message')
+10 -5
View File
@@ -29,6 +29,7 @@ from telegram import File, TelegramError, Voice
@pytest.fixture(scope='class')
def file(bot):
return File(TestFile.file_id,
TestFile.file_unique_id,
file_path=TestFile.file_path,
file_size=TestFile.file_size,
bot=bot)
@@ -36,6 +37,7 @@ def file(bot):
class TestFile(object):
file_id = 'NOTVALIDDOESNOTMATTER'
file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
file_path = (
u'https://api.org/file/bot133505823:AAHZFMHno3mzVLErU5b5jJvaeG--qUyLyG0/document/file_3')
file_size = 28232
@@ -44,12 +46,14 @@ class TestFile(object):
def test_de_json(self, bot):
json_dict = {
'file_id': self.file_id,
'file_unique_id': self.file_unique_id,
'file_path': self.file_path,
'file_size': self.file_size
}
new_file = File.de_json(json_dict, bot)
assert new_file.file_id == self.file_id
assert new_file.file_unique_id == self.file_unique_id
assert new_file.file_path == self.file_path
assert new_file.file_size == self.file_size
@@ -58,6 +62,7 @@ class TestFile(object):
assert isinstance(file_dict, dict)
assert file_dict['file_id'] == file.file_id
assert file_dict['file_unique_id'] == file.file_unique_id
assert file_dict['file_path'] == file.file_path
assert file_dict['file_size'] == file.file_size
@@ -142,11 +147,11 @@ class TestFile(object):
assert buf2[:len(buf)] == buf
def test_equality(self, bot):
a = File(self.file_id, bot)
b = File(self.file_id, bot)
c = File(self.file_id, None)
d = File('', bot)
e = Voice(self.file_id, 0)
a = File(self.file_id, self.file_unique_id, bot)
b = File('', self.file_unique_id, bot)
c = File(self.file_id, self.file_unique_id, None)
d = File('', '', bot)
e = Voice(self.file_id, self.file_unique_id, 0)
assert a == b
assert hash(a) == hash(b)
+1 -1
View File
@@ -300,7 +300,7 @@ class TestFilters(object):
assert Filters.document(update)
def test_filters_document_type(self, update):
update.message.document = Document("file_id",
update.message.document = Document("file_id", 'unique_id',
mime_type="application/vnd.android.package-archive")
assert Filters.document.apk(update)
assert Filters.document.application(update)
+2 -2
View File
@@ -35,11 +35,11 @@ def game():
class TestGame(object):
title = 'Python-telegram-bot Test Game'
description = 'description'
photo = [PhotoSize('Blah', 640, 360, file_size=0)]
photo = [PhotoSize('Blah', 'ElseBlah', 640, 360, file_size=0)]
text = (b'\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467'
b'\\u200d\\U0001f467\\U0001f431http://google.com').decode('unicode-escape')
text_entities = [MessageEntity(13, 17, MessageEntity.URL)]
animation = Animation('blah', 320, 180, 1)
animation = Animation('blah', 'unique_id', 320, 180, 1)
def test_de_json_required(self, bot):
json_dict = {
+36 -1
View File
@@ -24,6 +24,7 @@ import pytest
from telegram import Sticker
from telegram import Update
from telegram import User
from telegram import MessageEntity
from telegram.message import Message
from telegram.utils import helpers
from telegram.utils.helpers import _UtcOffsetTimezone, _datetime_to_float_timestamp
@@ -46,6 +47,34 @@ class TestHelpers(object):
assert expected_str == helpers.escape_markdown(test_str)
def test_escape_markdown_v2(self):
test_str = 'a_b*c[d]e (fg) h~I`>JK#L+MN -O=|p{qr}s.t! u'
expected_str = 'a\_b\*c\[d\]e \(fg\) h\~I\`\>JK\#L\+MN \-O\=\|p\{qr\}s\.t\! u'
assert expected_str == helpers.escape_markdown(test_str, version=2)
def test_escape_markdown_v2_monospaced(self):
test_str = 'mono/pre: `abc` \int (`\some \`stuff)'
expected_str = 'mono/pre: \`abc\` \\\\int (\`\\\\some \\\\\`stuff)'
assert expected_str == helpers.escape_markdown(test_str, version=2,
entity_type=MessageEntity.PRE)
assert expected_str == helpers.escape_markdown(test_str, version=2,
entity_type=MessageEntity.CODE)
def test_escape_markdown_v2_text_link(self):
test_str = 'https://url.containing/funny)cha)\\ra\)cter\s'
expected_str = 'https://url.containing/funny\)cha\)\\\\ra\\\\\)cter\\\\s'
assert expected_str == helpers.escape_markdown(test_str, version=2,
entity_type=MessageEntity.TEXT_LINK)
def test_markdown_invalid_version(self):
with pytest.raises(ValueError):
helpers.escape_markdown('abc', version=-1)
def test_to_float_timestamp_absolute_naive(self):
"""Conversion from timezone-naive datetime to timestamp.
Naive datetimes should be assumed to be in UTC.
@@ -161,7 +190,8 @@ class TestHelpers(object):
assert helpers.effective_message_type(test_message) == 'text'
test_message.text = None
test_message = build_test_message(sticker=Sticker('sticker_id', 50, 50, False))
test_message = build_test_message(sticker=Sticker('sticker_id', 'unique_id',
50, 50, False))
assert helpers.effective_message_type(test_message) == 'sticker'
test_message.sticker = None
@@ -188,3 +218,8 @@ class TestHelpers(object):
expected = '[the name](tg://user?id=1)'
assert expected == helpers.mention_markdown(1, 'the name')
def test_mention_markdown_2(self):
expected = r'[the\_name](tg://user?id=1)'
assert expected == helpers.mention_markdown(1, 'the_name')
+8 -8
View File
@@ -24,12 +24,12 @@ from telegram import User, Location, InlineQuery, Update
@pytest.fixture(scope='class')
def inline_query(bot):
return InlineQuery(TestInlineQuery.id, TestInlineQuery.from_user, TestInlineQuery.query,
return InlineQuery(TestInlineQuery.id_, TestInlineQuery.from_user, TestInlineQuery.query,
TestInlineQuery.offset, location=TestInlineQuery.location, bot=bot)
class TestInlineQuery(object):
id = 1234
id_ = 1234
from_user = User(1, 'First name', False)
query = 'query text'
offset = 'offset'
@@ -37,7 +37,7 @@ class TestInlineQuery(object):
def test_de_json(self, bot):
json_dict = {
'id': self.id,
'id': self.id_,
'from': self.from_user.to_dict(),
'query': self.query,
'offset': self.offset,
@@ -45,7 +45,7 @@ class TestInlineQuery(object):
}
inline_query_json = InlineQuery.de_json(json_dict, bot)
assert inline_query_json.id == self.id
assert inline_query_json.id == self.id_
assert inline_query_json.from_user == self.from_user
assert inline_query_json.location == self.location
assert inline_query_json.query == self.query
@@ -69,11 +69,11 @@ class TestInlineQuery(object):
assert inline_query.answer()
def test_equality(self):
a = InlineQuery(self.id, User(1, '', False), '', '')
b = InlineQuery(self.id, User(1, '', False), '', '')
c = InlineQuery(self.id, User(0, '', False), '', '')
a = InlineQuery(self.id_, User(1, '', False), '', '')
b = InlineQuery(self.id_, User(1, '', False), '', '')
c = InlineQuery(self.id_, User(0, '', False), '', '')
d = InlineQuery(0, User(1, '', False), '', '')
e = Update(self.id)
e = Update(self.id_)
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InlineKeyboardMarkup, InlineQueryResultAudio, InlineQueryR
@pytest.fixture(scope='class')
def inline_query_result_article():
return InlineQueryResultArticle(
TestInlineQueryResultArticle.id,
TestInlineQueryResultArticle.id_,
TestInlineQueryResultArticle.title,
input_message_content=TestInlineQueryResultArticle.input_message_content,
reply_markup=TestInlineQueryResultArticle.reply_markup,
@@ -39,8 +39,8 @@ def inline_query_result_article():
class TestInlineQueryResultArticle(object):
id = 'id'
type = 'article'
id_ = 'id'
type_ = 'article'
title = 'title'
input_message_content = InputTextMessageContent('input_message_content')
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
@@ -52,8 +52,8 @@ class TestInlineQueryResultArticle(object):
thumb_width = 15
def test_expected_values(self, inline_query_result_article):
assert inline_query_result_article.type == self.type
assert inline_query_result_article.id == self.id
assert inline_query_result_article.type == self.type_
assert inline_query_result_article.id == self.id_
assert inline_query_result_article.title == self.title
assert (inline_query_result_article.input_message_content.to_dict()
== self.input_message_content.to_dict())
@@ -88,11 +88,11 @@ class TestInlineQueryResultArticle(object):
== inline_query_result_article.thumb_width)
def test_equality(self):
a = InlineQueryResultArticle(self.id, self.title, self.input_message_content)
b = InlineQueryResultArticle(self.id, self.title, self.input_message_content)
c = InlineQueryResultArticle(self.id, '', self.input_message_content)
a = InlineQueryResultArticle(self.id_, self.title, self.input_message_content)
b = InlineQueryResultArticle(self.id_, self.title, self.input_message_content)
c = InlineQueryResultArticle(self.id_, '', self.input_message_content)
d = InlineQueryResultArticle('', self.title, self.input_message_content)
e = InlineQueryResultAudio(self.id, '', '')
e = InlineQueryResultAudio(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InlineKeyboardMarkup, InlineKeyboardButton, InlineQueryRes
@pytest.fixture(scope='class')
def inline_query_result_audio():
return InlineQueryResultAudio(
TestInlineQueryResultAudio.id,
TestInlineQueryResultAudio.id_,
TestInlineQueryResultAudio.audio_url,
TestInlineQueryResultAudio.title,
performer=TestInlineQueryResultAudio.performer,
@@ -38,8 +38,8 @@ def inline_query_result_audio():
class TestInlineQueryResultAudio(object):
id = 'id'
type = 'audio'
id_ = 'id'
type_ = 'audio'
audio_url = 'audio url'
title = 'title'
performer = 'performer'
@@ -50,8 +50,8 @@ class TestInlineQueryResultAudio(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_audio):
assert inline_query_result_audio.type == self.type
assert inline_query_result_audio.id == self.id
assert inline_query_result_audio.type == self.type_
assert inline_query_result_audio.id == self.id_
assert inline_query_result_audio.audio_url == self.audio_url
assert inline_query_result_audio.title == self.title
assert inline_query_result_audio.performer == self.performer
@@ -81,11 +81,11 @@ class TestInlineQueryResultAudio(object):
== inline_query_result_audio.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultAudio(self.id, self.audio_url, self.title)
b = InlineQueryResultAudio(self.id, self.title, self.title)
c = InlineQueryResultAudio(self.id, '', self.title)
a = InlineQueryResultAudio(self.id_, self.audio_url, self.title)
b = InlineQueryResultAudio(self.id_, self.title, self.title)
c = InlineQueryResultAudio(self.id_, '', self.title)
d = InlineQueryResultAudio('', self.audio_url, self.title)
e = InlineQueryResultVoice(self.id, '', '')
e = InlineQueryResultVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InputTextMessageContent, InlineQueryResultCachedAudio, Inl
@pytest.fixture(scope='class')
def inline_query_result_cached_audio():
return InlineQueryResultCachedAudio(
TestInlineQueryResultCachedAudio.id,
TestInlineQueryResultCachedAudio.id_,
TestInlineQueryResultCachedAudio.audio_file_id,
caption=TestInlineQueryResultCachedAudio.caption,
parse_mode=TestInlineQueryResultCachedAudio.parse_mode,
@@ -35,8 +35,8 @@ def inline_query_result_cached_audio():
class TestInlineQueryResultCachedAudio(object):
id = 'id'
type = 'audio'
id_ = 'id'
type_ = 'audio'
audio_file_id = 'audio file id'
caption = 'caption'
parse_mode = 'HTML'
@@ -44,8 +44,8 @@ class TestInlineQueryResultCachedAudio(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_cached_audio):
assert inline_query_result_cached_audio.type == self.type
assert inline_query_result_cached_audio.id == self.id
assert inline_query_result_cached_audio.type == self.type_
assert inline_query_result_cached_audio.id == self.id_
assert inline_query_result_cached_audio.audio_file_id == self.audio_file_id
assert inline_query_result_cached_audio.caption == self.caption
assert inline_query_result_cached_audio.parse_mode == self.parse_mode
@@ -73,11 +73,11 @@ class TestInlineQueryResultCachedAudio(object):
== inline_query_result_cached_audio.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultCachedAudio(self.id, self.audio_file_id)
b = InlineQueryResultCachedAudio(self.id, self.audio_file_id)
c = InlineQueryResultCachedAudio(self.id, '')
a = InlineQueryResultCachedAudio(self.id_, self.audio_file_id)
b = InlineQueryResultCachedAudio(self.id_, self.audio_file_id)
c = InlineQueryResultCachedAudio(self.id_, '')
d = InlineQueryResultCachedAudio('', self.audio_file_id)
e = InlineQueryResultCachedVoice(self.id, '', '')
e = InlineQueryResultCachedVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
@@ -26,7 +26,7 @@ from telegram import (InlineQueryResultCachedDocument, InlineKeyboardButton, Inl
@pytest.fixture(scope='class')
def inline_query_result_cached_document():
return InlineQueryResultCachedDocument(
TestInlineQueryResultCachedDocument.id,
TestInlineQueryResultCachedDocument.id_,
TestInlineQueryResultCachedDocument.title,
TestInlineQueryResultCachedDocument.document_file_id,
caption=TestInlineQueryResultCachedDocument.caption,
@@ -37,8 +37,8 @@ def inline_query_result_cached_document():
class TestInlineQueryResultCachedDocument(object):
id = 'id'
type = 'document'
id_ = 'id'
type_ = 'document'
document_file_id = 'document file id'
title = 'title'
caption = 'caption'
@@ -48,8 +48,8 @@ class TestInlineQueryResultCachedDocument(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_cached_document):
assert inline_query_result_cached_document.id == self.id
assert inline_query_result_cached_document.type == self.type
assert inline_query_result_cached_document.id == self.id_
assert inline_query_result_cached_document.type == self.type_
assert inline_query_result_cached_document.document_file_id == self.document_file_id
assert inline_query_result_cached_document.title == self.title
assert inline_query_result_cached_document.caption == self.caption
@@ -84,11 +84,11 @@ class TestInlineQueryResultCachedDocument(object):
== inline_query_result_cached_document.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultCachedDocument(self.id, self.title, self.document_file_id)
b = InlineQueryResultCachedDocument(self.id, self.title, self.document_file_id)
c = InlineQueryResultCachedDocument(self.id, self.title, '')
a = InlineQueryResultCachedDocument(self.id_, self.title, self.document_file_id)
b = InlineQueryResultCachedDocument(self.id_, self.title, self.document_file_id)
c = InlineQueryResultCachedDocument(self.id_, self.title, '')
d = InlineQueryResultCachedDocument('', self.title, self.document_file_id)
e = InlineQueryResultCachedVoice(self.id, '', '')
e = InlineQueryResultCachedVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InlineKeyboardButton, InputTextMessageContent, InlineQuery
@pytest.fixture(scope='class')
def inline_query_result_cached_gif():
return InlineQueryResultCachedGif(
TestInlineQueryResultCachedGif.id,
TestInlineQueryResultCachedGif.id_,
TestInlineQueryResultCachedGif.gif_file_id,
title=TestInlineQueryResultCachedGif.title,
caption=TestInlineQueryResultCachedGif.caption,
@@ -36,8 +36,8 @@ def inline_query_result_cached_gif():
class TestInlineQueryResultCachedGif(object):
id = 'id'
type = 'gif'
id_ = 'id'
type_ = 'gif'
gif_file_id = 'gif file id'
title = 'title'
caption = 'caption'
@@ -46,8 +46,8 @@ class TestInlineQueryResultCachedGif(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_cached_gif):
assert inline_query_result_cached_gif.type == self.type
assert inline_query_result_cached_gif.id == self.id
assert inline_query_result_cached_gif.type == self.type_
assert inline_query_result_cached_gif.id == self.id_
assert inline_query_result_cached_gif.gif_file_id == self.gif_file_id
assert inline_query_result_cached_gif.title == self.title
assert inline_query_result_cached_gif.caption == self.caption
@@ -75,11 +75,11 @@ class TestInlineQueryResultCachedGif(object):
== inline_query_result_cached_gif.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultCachedGif(self.id, self.gif_file_id)
b = InlineQueryResultCachedGif(self.id, self.gif_file_id)
c = InlineQueryResultCachedGif(self.id, '')
a = InlineQueryResultCachedGif(self.id_, self.gif_file_id)
b = InlineQueryResultCachedGif(self.id_, self.gif_file_id)
c = InlineQueryResultCachedGif(self.id_, '')
d = InlineQueryResultCachedGif('', self.gif_file_id)
e = InlineQueryResultCachedVoice(self.id, '', '')
e = InlineQueryResultCachedVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
@@ -26,7 +26,7 @@ from telegram import (InlineQueryResultCachedMpeg4Gif, InlineKeyboardButton,
@pytest.fixture(scope='class')
def inline_query_result_cached_mpeg4_gif():
return InlineQueryResultCachedMpeg4Gif(
TestInlineQueryResultCachedMpeg4Gif.id,
TestInlineQueryResultCachedMpeg4Gif.id_,
TestInlineQueryResultCachedMpeg4Gif.mpeg4_file_id,
title=TestInlineQueryResultCachedMpeg4Gif.title,
caption=TestInlineQueryResultCachedMpeg4Gif.caption,
@@ -36,8 +36,8 @@ def inline_query_result_cached_mpeg4_gif():
class TestInlineQueryResultCachedMpeg4Gif(object):
id = 'id'
type = 'mpeg4_gif'
id_ = 'id'
type_ = 'mpeg4_gif'
mpeg4_file_id = 'mpeg4 file id'
title = 'title'
caption = 'caption'
@@ -46,8 +46,8 @@ class TestInlineQueryResultCachedMpeg4Gif(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_cached_mpeg4_gif):
assert inline_query_result_cached_mpeg4_gif.type == self.type
assert inline_query_result_cached_mpeg4_gif.id == self.id
assert inline_query_result_cached_mpeg4_gif.type == self.type_
assert inline_query_result_cached_mpeg4_gif.id == self.id_
assert inline_query_result_cached_mpeg4_gif.mpeg4_file_id == self.mpeg4_file_id
assert inline_query_result_cached_mpeg4_gif.title == self.title
assert inline_query_result_cached_mpeg4_gif.caption == self.caption
@@ -79,11 +79,11 @@ class TestInlineQueryResultCachedMpeg4Gif(object):
== inline_query_result_cached_mpeg4_gif.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultCachedMpeg4Gif(self.id, self.mpeg4_file_id)
b = InlineQueryResultCachedMpeg4Gif(self.id, self.mpeg4_file_id)
c = InlineQueryResultCachedMpeg4Gif(self.id, '')
a = InlineQueryResultCachedMpeg4Gif(self.id_, self.mpeg4_file_id)
b = InlineQueryResultCachedMpeg4Gif(self.id_, self.mpeg4_file_id)
c = InlineQueryResultCachedMpeg4Gif(self.id_, '')
d = InlineQueryResultCachedMpeg4Gif('', self.mpeg4_file_id)
e = InlineQueryResultCachedVoice(self.id, '', '')
e = InlineQueryResultCachedVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InputTextMessageContent, InlineQueryResultCachedPhoto, Inl
@pytest.fixture(scope='class')
def inline_query_result_cached_photo():
return InlineQueryResultCachedPhoto(
TestInlineQueryResultCachedPhoto.id,
TestInlineQueryResultCachedPhoto.id_,
TestInlineQueryResultCachedPhoto.photo_file_id,
title=TestInlineQueryResultCachedPhoto.title,
description=TestInlineQueryResultCachedPhoto.description,
@@ -37,8 +37,8 @@ def inline_query_result_cached_photo():
class TestInlineQueryResultCachedPhoto(object):
id = 'id'
type = 'photo'
id_ = 'id'
type_ = 'photo'
photo_file_id = 'photo file id'
title = 'title'
description = 'description'
@@ -48,8 +48,8 @@ class TestInlineQueryResultCachedPhoto(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_cached_photo):
assert inline_query_result_cached_photo.type == self.type
assert inline_query_result_cached_photo.id == self.id
assert inline_query_result_cached_photo.type == self.type_
assert inline_query_result_cached_photo.id == self.id_
assert inline_query_result_cached_photo.photo_file_id == self.photo_file_id
assert inline_query_result_cached_photo.title == self.title
assert inline_query_result_cached_photo.description == self.description
@@ -83,11 +83,11 @@ class TestInlineQueryResultCachedPhoto(object):
== inline_query_result_cached_photo.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultCachedPhoto(self.id, self.photo_file_id)
b = InlineQueryResultCachedPhoto(self.id, self.photo_file_id)
c = InlineQueryResultCachedPhoto(self.id, '')
a = InlineQueryResultCachedPhoto(self.id_, self.photo_file_id)
b = InlineQueryResultCachedPhoto(self.id_, self.photo_file_id)
c = InlineQueryResultCachedPhoto(self.id_, '')
d = InlineQueryResultCachedPhoto('', self.photo_file_id)
e = InlineQueryResultCachedVoice(self.id, '', '')
e = InlineQueryResultCachedVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -27,22 +27,22 @@ from telegram import (InputTextMessageContent, InlineKeyboardButton,
@pytest.fixture(scope='class')
def inline_query_result_cached_sticker():
return InlineQueryResultCachedSticker(
TestInlineQueryResultCachedSticker.id,
TestInlineQueryResultCachedSticker.id_,
TestInlineQueryResultCachedSticker.sticker_file_id,
input_message_content=TestInlineQueryResultCachedSticker.input_message_content,
reply_markup=TestInlineQueryResultCachedSticker.reply_markup)
class TestInlineQueryResultCachedSticker(object):
id = 'id'
type = 'sticker'
id_ = 'id'
type_ = 'sticker'
sticker_file_id = 'sticker file id'
input_message_content = InputTextMessageContent('input_message_content')
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_cached_sticker):
assert inline_query_result_cached_sticker.type == self.type
assert inline_query_result_cached_sticker.id == self.id
assert inline_query_result_cached_sticker.type == self.type_
assert inline_query_result_cached_sticker.id == self.id_
assert inline_query_result_cached_sticker.sticker_file_id == self.sticker_file_id
assert (inline_query_result_cached_sticker.input_message_content.to_dict()
== self.input_message_content.to_dict())
@@ -65,11 +65,11 @@ class TestInlineQueryResultCachedSticker(object):
== inline_query_result_cached_sticker.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultCachedSticker(self.id, self.sticker_file_id)
b = InlineQueryResultCachedSticker(self.id, self.sticker_file_id)
c = InlineQueryResultCachedSticker(self.id, '')
a = InlineQueryResultCachedSticker(self.id_, self.sticker_file_id)
b = InlineQueryResultCachedSticker(self.id_, self.sticker_file_id)
c = InlineQueryResultCachedSticker(self.id_, '')
d = InlineQueryResultCachedSticker('', self.sticker_file_id)
e = InlineQueryResultCachedVoice(self.id, '', '')
e = InlineQueryResultCachedVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InlineKeyboardMarkup, InlineKeyboardButton, InputTextMessa
@pytest.fixture(scope='class')
def inline_query_result_cached_video():
return InlineQueryResultCachedVideo(
TestInlineQueryResultCachedVideo.id,
TestInlineQueryResultCachedVideo.id_,
TestInlineQueryResultCachedVideo.video_file_id,
TestInlineQueryResultCachedVideo.title,
caption=TestInlineQueryResultCachedVideo.caption,
@@ -37,8 +37,8 @@ def inline_query_result_cached_video():
class TestInlineQueryResultCachedVideo(object):
id = 'id'
type = 'video'
id_ = 'id'
type_ = 'video'
video_file_id = 'video file id'
title = 'title'
caption = 'caption'
@@ -48,8 +48,8 @@ class TestInlineQueryResultCachedVideo(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_cached_video):
assert inline_query_result_cached_video.type == self.type
assert inline_query_result_cached_video.id == self.id
assert inline_query_result_cached_video.type == self.type_
assert inline_query_result_cached_video.id == self.id_
assert inline_query_result_cached_video.video_file_id == self.video_file_id
assert inline_query_result_cached_video.title == self.title
assert inline_query_result_cached_video.description == self.description
@@ -83,11 +83,11 @@ class TestInlineQueryResultCachedVideo(object):
== inline_query_result_cached_video.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultCachedVideo(self.id, self.video_file_id, self.title)
b = InlineQueryResultCachedVideo(self.id, self.video_file_id, self.title)
c = InlineQueryResultCachedVideo(self.id, '', self.title)
a = InlineQueryResultCachedVideo(self.id_, self.video_file_id, self.title)
b = InlineQueryResultCachedVideo(self.id_, self.video_file_id, self.title)
c = InlineQueryResultCachedVideo(self.id_, '', self.title)
d = InlineQueryResultCachedVideo('', self.video_file_id, self.title)
e = InlineQueryResultCachedVoice(self.id, '', '')
e = InlineQueryResultCachedVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InlineQueryResultCachedVoice, InlineKeyboardButton, Inline
@pytest.fixture(scope='class')
def inline_query_result_cached_voice():
return InlineQueryResultCachedVoice(
TestInlineQueryResultCachedVoice.id,
TestInlineQueryResultCachedVoice.id_,
TestInlineQueryResultCachedVoice.voice_file_id,
TestInlineQueryResultCachedVoice.title,
caption=TestInlineQueryResultCachedVoice.caption,
@@ -36,8 +36,8 @@ def inline_query_result_cached_voice():
class TestInlineQueryResultCachedVoice(object):
id = 'id'
type = 'voice'
id_ = 'id'
type_ = 'voice'
voice_file_id = 'voice file id'
title = 'title'
caption = 'caption'
@@ -46,8 +46,8 @@ class TestInlineQueryResultCachedVoice(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_cached_voice):
assert inline_query_result_cached_voice.type == self.type
assert inline_query_result_cached_voice.id == self.id
assert inline_query_result_cached_voice.type == self.type_
assert inline_query_result_cached_voice.id == self.id_
assert inline_query_result_cached_voice.voice_file_id == self.voice_file_id
assert inline_query_result_cached_voice.title == self.title
assert inline_query_result_cached_voice.caption == self.caption
@@ -78,11 +78,11 @@ class TestInlineQueryResultCachedVoice(object):
== inline_query_result_cached_voice.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultCachedVoice(self.id, self.voice_file_id, self.title)
b = InlineQueryResultCachedVoice(self.id, self.voice_file_id, self.title)
c = InlineQueryResultCachedVoice(self.id, '', self.title)
a = InlineQueryResultCachedVoice(self.id_, self.voice_file_id, self.title)
b = InlineQueryResultCachedVoice(self.id_, self.voice_file_id, self.title)
c = InlineQueryResultCachedVoice(self.id_, '', self.title)
d = InlineQueryResultCachedVoice('', self.voice_file_id, self.title)
e = InlineQueryResultCachedAudio(self.id, '', '')
e = InlineQueryResultCachedAudio(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InlineQueryResultVoice, InputTextMessageContent, InlineKey
@pytest.fixture(scope='class')
def inline_query_result_contact():
return InlineQueryResultContact(
TestInlineQueryResultContact.id,
TestInlineQueryResultContact.id_,
TestInlineQueryResultContact.phone_number,
TestInlineQueryResultContact.first_name,
last_name=TestInlineQueryResultContact.last_name,
@@ -38,8 +38,8 @@ def inline_query_result_contact():
class TestInlineQueryResultContact(object):
id = 'id'
type = 'contact'
id_ = 'id'
type_ = 'contact'
phone_number = 'phone_number'
first_name = 'first_name'
last_name = 'last_name'
@@ -50,8 +50,8 @@ class TestInlineQueryResultContact(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_contact):
assert inline_query_result_contact.id == self.id
assert inline_query_result_contact.type == self.type
assert inline_query_result_contact.id == self.id_
assert inline_query_result_contact.type == self.type_
assert inline_query_result_contact.phone_number == self.phone_number
assert inline_query_result_contact.first_name == self.first_name
assert inline_query_result_contact.last_name == self.last_name
@@ -86,11 +86,11 @@ class TestInlineQueryResultContact(object):
== inline_query_result_contact.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultContact(self.id, self.phone_number, self.first_name)
b = InlineQueryResultContact(self.id, self.phone_number, self.first_name)
c = InlineQueryResultContact(self.id, '', self.first_name)
a = InlineQueryResultContact(self.id_, self.phone_number, self.first_name)
b = InlineQueryResultContact(self.id_, self.phone_number, self.first_name)
c = InlineQueryResultContact(self.id_, '', self.first_name)
d = InlineQueryResultContact('', self.phone_number, self.first_name)
e = InlineQueryResultVoice(self.id, '', '')
e = InlineQueryResultVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InlineKeyboardButton, InputTextMessageContent, InlineQuery
@pytest.fixture(scope='class')
def inline_query_result_document():
return InlineQueryResultDocument(
TestInlineQueryResultDocument.id,
TestInlineQueryResultDocument.id_,
TestInlineQueryResultDocument.document_url,
TestInlineQueryResultDocument.title,
TestInlineQueryResultDocument.mime_type,
@@ -41,8 +41,8 @@ def inline_query_result_document():
class TestInlineQueryResultDocument(object):
id = 'id'
type = 'document'
id_ = 'id'
type_ = 'document'
document_url = 'document url'
title = 'title'
caption = 'caption'
@@ -56,8 +56,8 @@ class TestInlineQueryResultDocument(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_document):
assert inline_query_result_document.id == self.id
assert inline_query_result_document.type == self.type
assert inline_query_result_document.id == self.id_
assert inline_query_result_document.type == self.type_
assert inline_query_result_document.document_url == self.document_url
assert inline_query_result_document.title == self.title
assert inline_query_result_document.caption == self.caption
@@ -99,13 +99,13 @@ class TestInlineQueryResultDocument(object):
== inline_query_result_document.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultDocument(self.id, self.document_url, self.title,
a = InlineQueryResultDocument(self.id_, self.document_url, self.title,
self.mime_type)
b = InlineQueryResultDocument(self.id, self.document_url, self.title,
b = InlineQueryResultDocument(self.id_, self.document_url, self.title,
self.mime_type)
c = InlineQueryResultDocument(self.id, '', self.title, self.mime_type)
c = InlineQueryResultDocument(self.id_, '', self.title, self.mime_type)
d = InlineQueryResultDocument('', self.document_url, self.title, self.mime_type)
e = InlineQueryResultVoice(self.id, '', '')
e = InlineQueryResultVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -25,20 +25,20 @@ from telegram import (InlineKeyboardButton, InlineQueryResultGame,
@pytest.fixture(scope='class')
def inline_query_result_game():
return InlineQueryResultGame(TestInlineQueryResultGame.id,
return InlineQueryResultGame(TestInlineQueryResultGame.id_,
TestInlineQueryResultGame.game_short_name,
reply_markup=TestInlineQueryResultGame.reply_markup)
class TestInlineQueryResultGame(object):
id = 'id'
type = 'game'
id_ = 'id'
type_ = 'game'
game_short_name = 'game short name'
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_game):
assert inline_query_result_game.type == self.type
assert inline_query_result_game.id == self.id
assert inline_query_result_game.type == self.type_
assert inline_query_result_game.id == self.id_
assert inline_query_result_game.game_short_name == self.game_short_name
assert (inline_query_result_game.reply_markup.to_dict()
== self.reply_markup.to_dict())
@@ -55,11 +55,11 @@ class TestInlineQueryResultGame(object):
== inline_query_result_game.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultGame(self.id, self.game_short_name)
b = InlineQueryResultGame(self.id, self.game_short_name)
c = InlineQueryResultGame(self.id, '')
a = InlineQueryResultGame(self.id_, self.game_short_name)
b = InlineQueryResultGame(self.id_, self.game_short_name)
c = InlineQueryResultGame(self.id_, '')
d = InlineQueryResultGame('', self.game_short_name)
e = InlineQueryResultVoice(self.id, '', '')
e = InlineQueryResultVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InlineKeyboardButton, InputTextMessageContent, InlineQuery
@pytest.fixture(scope='class')
def inline_query_result_gif():
return InlineQueryResultGif(
TestInlineQueryResultGif.id,
TestInlineQueryResultGif.id_,
TestInlineQueryResultGif.gif_url,
TestInlineQueryResultGif.thumb_url,
gif_width=TestInlineQueryResultGif.gif_width,
@@ -40,8 +40,8 @@ def inline_query_result_gif():
class TestInlineQueryResultGif(object):
id = 'id'
type = 'gif'
id_ = 'id'
type_ = 'gif'
gif_url = 'gif url'
gif_width = 10
gif_height = 15
@@ -54,8 +54,8 @@ class TestInlineQueryResultGif(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_gif):
assert inline_query_result_gif.type == self.type
assert inline_query_result_gif.id == self.id
assert inline_query_result_gif.type == self.type_
assert inline_query_result_gif.id == self.id_
assert inline_query_result_gif.gif_url == self.gif_url
assert inline_query_result_gif.gif_width == self.gif_width
assert inline_query_result_gif.gif_height == self.gif_height
@@ -88,11 +88,11 @@ class TestInlineQueryResultGif(object):
== inline_query_result_gif.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultGif(self.id, self.gif_url, self.thumb_url)
b = InlineQueryResultGif(self.id, self.gif_url, self.thumb_url)
c = InlineQueryResultGif(self.id, '', self.thumb_url)
a = InlineQueryResultGif(self.id_, self.gif_url, self.thumb_url)
b = InlineQueryResultGif(self.id_, self.gif_url, self.thumb_url)
c = InlineQueryResultGif(self.id_, '', self.thumb_url)
d = InlineQueryResultGif('', self.gif_url, self.thumb_url)
e = InlineQueryResultVoice(self.id, '', '')
e = InlineQueryResultVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InputTextMessageContent, InlineQueryResultLocation, Inline
@pytest.fixture(scope='class')
def inline_query_result_location():
return InlineQueryResultLocation(
TestInlineQueryResultLocation.id,
TestInlineQueryResultLocation.id_,
TestInlineQueryResultLocation.latitude,
TestInlineQueryResultLocation.longitude,
TestInlineQueryResultLocation.title,
@@ -39,8 +39,8 @@ def inline_query_result_location():
class TestInlineQueryResultLocation(object):
id = 'id'
type = 'location'
id_ = 'id'
type_ = 'location'
latitude = 0.0
longitude = 1.0
title = 'title'
@@ -52,8 +52,8 @@ class TestInlineQueryResultLocation(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_location):
assert inline_query_result_location.id == self.id
assert inline_query_result_location.type == self.type
assert inline_query_result_location.id == self.id_
assert inline_query_result_location.type == self.type_
assert inline_query_result_location.latitude == self.latitude
assert inline_query_result_location.longitude == self.longitude
assert inline_query_result_location.title == self.title
@@ -90,11 +90,11 @@ class TestInlineQueryResultLocation(object):
== inline_query_result_location.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultLocation(self.id, self.longitude, self.latitude, self.title)
b = InlineQueryResultLocation(self.id, self.longitude, self.latitude, self.title)
c = InlineQueryResultLocation(self.id, 0, self.latitude, self.title)
a = InlineQueryResultLocation(self.id_, self.longitude, self.latitude, self.title)
b = InlineQueryResultLocation(self.id_, self.longitude, self.latitude, self.title)
c = InlineQueryResultLocation(self.id_, 0, self.latitude, self.title)
d = InlineQueryResultLocation('', self.longitude, self.latitude, self.title)
e = InlineQueryResultVoice(self.id, '', '')
e = InlineQueryResultVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InlineQueryResultMpeg4Gif, InlineKeyboardButton, InlineQue
@pytest.fixture(scope='class')
def inline_query_result_mpeg4_gif():
return InlineQueryResultMpeg4Gif(
TestInlineQueryResultMpeg4Gif.id,
TestInlineQueryResultMpeg4Gif.id_,
TestInlineQueryResultMpeg4Gif.mpeg4_url,
TestInlineQueryResultMpeg4Gif.thumb_url,
mpeg4_width=TestInlineQueryResultMpeg4Gif.mpeg4_width,
@@ -40,8 +40,8 @@ def inline_query_result_mpeg4_gif():
class TestInlineQueryResultMpeg4Gif(object):
id = 'id'
type = 'mpeg4_gif'
id_ = 'id'
type_ = 'mpeg4_gif'
mpeg4_url = 'mpeg4 url'
mpeg4_width = 10
mpeg4_height = 15
@@ -54,8 +54,8 @@ class TestInlineQueryResultMpeg4Gif(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_mpeg4_gif):
assert inline_query_result_mpeg4_gif.type == self.type
assert inline_query_result_mpeg4_gif.id == self.id
assert inline_query_result_mpeg4_gif.type == self.type_
assert inline_query_result_mpeg4_gif.id == self.id_
assert inline_query_result_mpeg4_gif.mpeg4_url == self.mpeg4_url
assert inline_query_result_mpeg4_gif.mpeg4_width == self.mpeg4_width
assert inline_query_result_mpeg4_gif.mpeg4_height == self.mpeg4_height
@@ -95,11 +95,11 @@ class TestInlineQueryResultMpeg4Gif(object):
== inline_query_result_mpeg4_gif.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultMpeg4Gif(self.id, self.mpeg4_url, self.thumb_url)
b = InlineQueryResultMpeg4Gif(self.id, self.mpeg4_url, self.thumb_url)
c = InlineQueryResultMpeg4Gif(self.id, '', self.thumb_url)
a = InlineQueryResultMpeg4Gif(self.id_, self.mpeg4_url, self.thumb_url)
b = InlineQueryResultMpeg4Gif(self.id_, self.mpeg4_url, self.thumb_url)
c = InlineQueryResultMpeg4Gif(self.id_, '', self.thumb_url)
d = InlineQueryResultMpeg4Gif('', self.mpeg4_url, self.thumb_url)
e = InlineQueryResultVoice(self.id, '', '')
e = InlineQueryResultVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InputTextMessageContent, InlineKeyboardButton, InlineKeybo
@pytest.fixture(scope='class')
def inline_query_result_photo():
return InlineQueryResultPhoto(
TestInlineQueryResultPhoto.id,
TestInlineQueryResultPhoto.id_,
TestInlineQueryResultPhoto.photo_url,
TestInlineQueryResultPhoto.thumb_url,
photo_width=TestInlineQueryResultPhoto.photo_width,
@@ -40,8 +40,8 @@ def inline_query_result_photo():
class TestInlineQueryResultPhoto(object):
id = 'id'
type = 'photo'
id_ = 'id'
type_ = 'photo'
photo_url = 'photo url'
photo_width = 10
photo_height = 15
@@ -54,8 +54,8 @@ class TestInlineQueryResultPhoto(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_photo):
assert inline_query_result_photo.type == self.type
assert inline_query_result_photo.id == self.id
assert inline_query_result_photo.type == self.type_
assert inline_query_result_photo.id == self.id_
assert inline_query_result_photo.photo_url == self.photo_url
assert inline_query_result_photo.photo_width == self.photo_width
assert inline_query_result_photo.photo_height == self.photo_height
@@ -91,11 +91,11 @@ class TestInlineQueryResultPhoto(object):
== inline_query_result_photo.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultPhoto(self.id, self.photo_url, self.thumb_url)
b = InlineQueryResultPhoto(self.id, self.photo_url, self.thumb_url)
c = InlineQueryResultPhoto(self.id, '', self.thumb_url)
a = InlineQueryResultPhoto(self.id_, self.photo_url, self.thumb_url)
b = InlineQueryResultPhoto(self.id_, self.photo_url, self.thumb_url)
c = InlineQueryResultPhoto(self.id_, '', self.thumb_url)
d = InlineQueryResultPhoto('', self.photo_url, self.thumb_url)
e = InlineQueryResultVoice(self.id, '', '')
e = InlineQueryResultVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InlineQueryResultVoice, InputTextMessageContent, InlineKey
@pytest.fixture(scope='class')
def inline_query_result_venue():
return InlineQueryResultVenue(
TestInlineQueryResultVenue.id,
TestInlineQueryResultVenue.id_,
TestInlineQueryResultVenue.latitude,
TestInlineQueryResultVenue.longitude,
TestInlineQueryResultVenue.title,
@@ -41,8 +41,8 @@ def inline_query_result_venue():
class TestInlineQueryResultVenue(object):
id = 'id'
type = 'venue'
id_ = 'id'
type_ = 'venue'
latitude = 'latitude'
longitude = 'longitude'
title = 'title'
@@ -56,8 +56,8 @@ class TestInlineQueryResultVenue(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_venue):
assert inline_query_result_venue.id == self.id
assert inline_query_result_venue.type == self.type
assert inline_query_result_venue.id == self.id_
assert inline_query_result_venue.type == self.type_
assert inline_query_result_venue.latitude == self.latitude
assert inline_query_result_venue.longitude == self.longitude
assert inline_query_result_venue.title == self.title
@@ -96,14 +96,14 @@ class TestInlineQueryResultVenue(object):
== inline_query_result_venue.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultVenue(self.id, self.longitude, self.latitude, self.title,
a = InlineQueryResultVenue(self.id_, self.longitude, self.latitude, self.title,
self.address)
b = InlineQueryResultVenue(self.id, self.longitude, self.latitude, self.title,
b = InlineQueryResultVenue(self.id_, self.longitude, self.latitude, self.title,
self.address)
c = InlineQueryResultVenue(self.id, '', self.latitude, self.title, self.address)
c = InlineQueryResultVenue(self.id_, '', self.latitude, self.title, self.address)
d = InlineQueryResultVenue('', self.longitude, self.latitude, self.title,
self.address)
e = InlineQueryResultVoice(self.id, '', '')
e = InlineQueryResultVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+9 -9
View File
@@ -26,7 +26,7 @@ from telegram import (InlineKeyboardButton, InputTextMessageContent, InlineQuery
@pytest.fixture(scope='class')
def inline_query_result_video():
return InlineQueryResultVideo(
TestInlineQueryResultVideo.id,
TestInlineQueryResultVideo.id_,
TestInlineQueryResultVideo.video_url,
TestInlineQueryResultVideo.mime_type,
TestInlineQueryResultVideo.thumb_url,
@@ -42,8 +42,8 @@ def inline_query_result_video():
class TestInlineQueryResultVideo(object):
id = 'id'
type = 'video'
id_ = 'id'
type_ = 'video'
video_url = 'video url'
mime_type = 'mime type'
video_width = 10
@@ -58,8 +58,8 @@ class TestInlineQueryResultVideo(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_video):
assert inline_query_result_video.type == self.type
assert inline_query_result_video.id == self.id
assert inline_query_result_video.type == self.type_
assert inline_query_result_video.id == self.id_
assert inline_query_result_video.video_url == self.video_url
assert inline_query_result_video.mime_type == self.mime_type
assert inline_query_result_video.video_width == self.video_width
@@ -100,15 +100,15 @@ class TestInlineQueryResultVideo(object):
== inline_query_result_video.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultVideo(self.id, self.video_url, self.mime_type,
a = InlineQueryResultVideo(self.id_, self.video_url, self.mime_type,
self.thumb_url, self.title)
b = InlineQueryResultVideo(self.id, self.video_url, self.mime_type,
b = InlineQueryResultVideo(self.id_, self.video_url, self.mime_type,
self.thumb_url, self.title)
c = InlineQueryResultVideo(self.id, '', self.mime_type, self.thumb_url,
c = InlineQueryResultVideo(self.id_, '', self.mime_type, self.thumb_url,
self.title)
d = InlineQueryResultVideo('', self.video_url, self.mime_type, self.thumb_url,
self.title)
e = InlineQueryResultVoice(self.id, '', '')
e = InlineQueryResultVoice(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+10 -10
View File
@@ -26,8 +26,8 @@ from telegram import (InlineKeyboardButton, InputTextMessageContent, InlineQuery
@pytest.fixture(scope='class')
def inline_query_result_voice():
return InlineQueryResultVoice(
type=TestInlineQueryResultVoice.type,
id=TestInlineQueryResultVoice.id,
type=TestInlineQueryResultVoice.type_,
id=TestInlineQueryResultVoice.id_,
voice_url=TestInlineQueryResultVoice.voice_url,
title=TestInlineQueryResultVoice.title,
voice_duration=TestInlineQueryResultVoice.voice_duration,
@@ -38,8 +38,8 @@ def inline_query_result_voice():
class TestInlineQueryResultVoice(object):
id = 'id'
type = 'voice'
id_ = 'id'
type_ = 'voice'
voice_url = 'voice url'
title = 'title'
voice_duration = 'voice_duration'
@@ -49,8 +49,8 @@ class TestInlineQueryResultVoice(object):
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]])
def test_expected_values(self, inline_query_result_voice):
assert inline_query_result_voice.type == self.type
assert inline_query_result_voice.id == self.id
assert inline_query_result_voice.type == self.type_
assert inline_query_result_voice.id == self.id_
assert inline_query_result_voice.voice_url == self.voice_url
assert inline_query_result_voice.title == self.title
assert inline_query_result_voice.voice_duration == self.voice_duration
@@ -78,11 +78,11 @@ class TestInlineQueryResultVoice(object):
== inline_query_result_voice.reply_markup.to_dict())
def test_equality(self):
a = InlineQueryResultVoice(self.id, self.voice_url, self.title)
b = InlineQueryResultVoice(self.id, self.voice_url, self.title)
c = InlineQueryResultVoice(self.id, '', self.title)
a = InlineQueryResultVoice(self.id_, self.voice_url, self.title)
b = InlineQueryResultVoice(self.id_, self.voice_url, self.title)
c = InlineQueryResultVoice(self.id_, '', self.title)
d = InlineQueryResultVoice('', self.voice_url, self.title)
e = InlineQueryResultAudio(self.id, '', '')
e = InlineQueryResultAudio(self.id_, '', '')
assert a == b
assert hash(a) == hash(b)
+20 -20
View File
@@ -83,7 +83,7 @@ def input_media_document(class_thumb_file):
class TestInputMediaVideo(object):
type = "video"
type_ = "video"
media = "NOTAREALFILEID"
caption = "My Caption"
width = 3
@@ -93,7 +93,7 @@ class TestInputMediaVideo(object):
supports_streaming = True
def test_expected_values(self, input_media_video):
assert input_media_video.type == self.type
assert input_media_video.type == self.type_
assert input_media_video.media == self.media
assert input_media_video.caption == self.caption
assert input_media_video.width == self.width
@@ -117,7 +117,7 @@ class TestInputMediaVideo(object):
def test_with_video(self, video): # noqa: F811
# fixture found in test_video
input_media_video = InputMediaVideo(video, caption="test 3")
assert input_media_video.type == self.type
assert input_media_video.type == self.type_
assert input_media_video.media == video.file_id
assert input_media_video.width == video.width
assert input_media_video.height == video.height
@@ -127,19 +127,19 @@ class TestInputMediaVideo(object):
def test_with_video_file(self, video_file): # noqa: F811
# fixture found in test_video
input_media_video = InputMediaVideo(video_file, caption="test 3")
assert input_media_video.type == self.type
assert input_media_video.type == self.type_
assert isinstance(input_media_video.media, InputFile)
assert input_media_video.caption == "test 3"
class TestInputMediaPhoto(object):
type = "photo"
type_ = "photo"
media = "NOTAREALFILEID"
caption = "My Caption"
parse_mode = 'Markdown'
def test_expected_values(self, input_media_photo):
assert input_media_photo.type == self.type
assert input_media_photo.type == self.type_
assert input_media_photo.media == self.media
assert input_media_photo.caption == self.caption
assert input_media_photo.parse_mode == self.parse_mode
@@ -154,20 +154,20 @@ class TestInputMediaPhoto(object):
def test_with_photo(self, photo): # noqa: F811
# fixture found in test_photo
input_media_photo = InputMediaPhoto(photo, caption="test 2")
assert input_media_photo.type == self.type
assert input_media_photo.type == self.type_
assert input_media_photo.media == photo.file_id
assert input_media_photo.caption == "test 2"
def test_with_photo_file(self, photo_file): # noqa: F811
# fixture found in test_photo
input_media_photo = InputMediaPhoto(photo_file, caption="test 2")
assert input_media_photo.type == self.type
assert input_media_photo.type == self.type_
assert isinstance(input_media_photo.media, InputFile)
assert input_media_photo.caption == "test 2"
class TestInputMediaAnimation(object):
type = "animation"
type_ = "animation"
media = "NOTAREALFILEID"
caption = "My Caption"
parse_mode = 'Markdown'
@@ -176,7 +176,7 @@ class TestInputMediaAnimation(object):
duration = 1
def test_expected_values(self, input_media_animation):
assert input_media_animation.type == self.type
assert input_media_animation.type == self.type_
assert input_media_animation.media == self.media
assert input_media_animation.caption == self.caption
assert input_media_animation.parse_mode == self.parse_mode
@@ -195,20 +195,20 @@ class TestInputMediaAnimation(object):
def test_with_animation(self, animation): # noqa: F811
# fixture found in test_animation
input_media_animation = InputMediaAnimation(animation, caption="test 2")
assert input_media_animation.type == self.type
assert input_media_animation.type == self.type_
assert input_media_animation.media == animation.file_id
assert input_media_animation.caption == "test 2"
def test_with_animation_file(self, animation_file): # noqa: F811
# fixture found in test_animation
input_media_animation = InputMediaAnimation(animation_file, caption="test 2")
assert input_media_animation.type == self.type
assert input_media_animation.type == self.type_
assert isinstance(input_media_animation.media, InputFile)
assert input_media_animation.caption == "test 2"
class TestInputMediaAudio(object):
type = "audio"
type_ = "audio"
media = "NOTAREALFILEID"
caption = "My Caption"
duration = 3
@@ -217,7 +217,7 @@ class TestInputMediaAudio(object):
parse_mode = 'HTML'
def test_expected_values(self, input_media_audio):
assert input_media_audio.type == self.type
assert input_media_audio.type == self.type_
assert input_media_audio.media == self.media
assert input_media_audio.caption == self.caption
assert input_media_audio.duration == self.duration
@@ -239,7 +239,7 @@ class TestInputMediaAudio(object):
def test_with_audio(self, audio): # noqa: F811
# fixture found in test_audio
input_media_audio = InputMediaAudio(audio, caption="test 3")
assert input_media_audio.type == self.type
assert input_media_audio.type == self.type_
assert input_media_audio.media == audio.file_id
assert input_media_audio.duration == audio.duration
assert input_media_audio.performer == audio.performer
@@ -249,19 +249,19 @@ class TestInputMediaAudio(object):
def test_with_audio_file(self, audio_file): # noqa: F811
# fixture found in test_audio
input_media_audio = InputMediaAudio(audio_file, caption="test 3")
assert input_media_audio.type == self.type
assert input_media_audio.type == self.type_
assert isinstance(input_media_audio.media, InputFile)
assert input_media_audio.caption == "test 3"
class TestInputMediaDocument(object):
type = "document"
type_ = "document"
media = "NOTAREALFILEID"
caption = "My Caption"
parse_mode = 'HTML'
def test_expected_values(self, input_media_document):
assert input_media_document.type == self.type
assert input_media_document.type == self.type_
assert input_media_document.media == self.media
assert input_media_document.caption == self.caption
assert input_media_document.parse_mode == self.parse_mode
@@ -277,14 +277,14 @@ class TestInputMediaDocument(object):
def test_with_document(self, document): # noqa: F811
# fixture found in test_document
input_media_document = InputMediaDocument(document, caption="test 3")
assert input_media_document.type == self.type
assert input_media_document.type == self.type_
assert input_media_document.media == document.file_id
assert input_media_document.caption == "test 3"
def test_with_document_file(self, document_file): # noqa: F811
# fixture found in test_document
input_media_document = InputMediaDocument(document_file, caption="test 3")
assert input_media_document.type == self.type
assert input_media_document.type == self.type_
assert isinstance(input_media_document.media, InputFile)
assert input_media_document.caption == "test 3"
-1
View File
@@ -40,7 +40,6 @@ def job_queue(bot, _dp):
jq.stop()
@pytest.mark.skipif(os.getenv('APPVEYOR'), reason="On Appveyor precise timings are not accurate.")
@pytest.mark.skipif(os.getenv('GITHUB_ACTIONS', False) and os.name == 'nt',
reason="On windows precise timings are not accurate.")
@flaky(10, 1) # Timings aren't quite perfect
+6 -1
View File
@@ -20,24 +20,28 @@
import pytest
from telegram import KeyboardButton
from telegram.keyboardbuttonpolltype import KeyboardButtonPollType
@pytest.fixture(scope='class')
def keyboard_button():
return KeyboardButton(TestKeyboardButton.text,
request_location=TestKeyboardButton.request_location,
request_contact=TestKeyboardButton.request_contact)
request_contact=TestKeyboardButton.request_contact,
request_poll=TestKeyboardButton.request_poll)
class TestKeyboardButton(object):
text = 'text'
request_location = True
request_contact = True
request_poll = KeyboardButtonPollType("quiz")
def test_expected_values(self, keyboard_button):
assert keyboard_button.text == self.text
assert keyboard_button.request_location == self.request_location
assert keyboard_button.request_contact == self.request_contact
assert keyboard_button.request_poll == self.request_poll
def test_to_dict(self, keyboard_button):
keyboard_button_dict = keyboard_button.to_dict()
@@ -46,3 +50,4 @@ class TestKeyboardButton(object):
assert keyboard_button_dict['text'] == keyboard_button.text
assert keyboard_button_dict['request_location'] == keyboard_button.request_location
assert keyboard_button_dict['request_contact'] == keyboard_button.request_contact
assert keyboard_button_dict['request_poll'] == keyboard_button.request_poll.to_dict()

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