mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-22 00:55:24 +00:00
Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38a33581b1 | |||
| fe821c08e6 | |||
| 0a9f4bfbdd | |||
| c4364c7166 | |||
| d63e710784 | |||
| f379f54d5a | |||
| bdf0cb91f3 | |||
| 3101ea8432 | |||
| beb8ba3db0 | |||
| f0b1aeb6fd | |||
| d65558888e | |||
| 61a66a32c8 | |||
| 392d4e1a9c | |||
| 9cb34af65a | |||
| e9cb6675ca | |||
| 982f6707e1 | |||
| d55d981e22 | |||
| f20953f7a9 | |||
| e18220be10 | |||
| 90729c21d7 | |||
| 55e3ecf9f8 | |||
| 8d2c7af1f3 | |||
| e86ae25a62 | |||
| 2d3357bfeb | |||
| b6f4783fd3 | |||
| f94ea9acbb | |||
| 157652cfdf | |||
| 104d0127aa | |||
| e0b22e60b4 | |||
| 16613d7ce0 | |||
| eac7f02211 | |||
| 28ded6718e | |||
| 13a641b3d7 | |||
| 27cccc7734 | |||
| f7ec7a7c4c | |||
| 8d6970ab02 | |||
| 1dc67dcbda | |||
| 14f712b3c4 | |||
| 0fb0fbb93f |
@@ -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.
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Telegram Group
|
||||
url: https://telegram.me/pythontelegrambotgroup
|
||||
about: Questions asked on the group usually get answered faster.
|
||||
- name: IRC Channel
|
||||
url: https://webchat.freenode.net/?channels=##python-telegram-bot
|
||||
about: In case you are unable to join our group due to Telegram restrictions, you can use our IRC channel
|
||||
@@ -2,7 +2,7 @@
|
||||
name: Question
|
||||
about: Get help with errors or general questions
|
||||
title: "[QUESTION]"
|
||||
labels: 'question :question:'
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
test-build: True
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Initialize vendored libs
|
||||
run:
|
||||
git submodule update --init --recursive
|
||||
@@ -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}
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Initialize vendored libs
|
||||
run:
|
||||
git submodule update --init --recursive
|
||||
@@ -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}
|
||||
@@ -103,7 +102,7 @@ jobs:
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Initialize vendored libs
|
||||
run:
|
||||
git submodule update --init --recursive
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -36,6 +36,8 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||
- `Evan Haberecht <https://github.com/habereet>`_
|
||||
- `evgfilim1 <https://github.com/evgfilim1>`_
|
||||
- `franciscod <https://github.com/franciscod>`_
|
||||
- `gamgi <https://github.com/gamgi>`_
|
||||
- `Harshil <https://github.com/harshil21>`_
|
||||
- `Hugo Damer <https://github.com/HakimusGIT>`_
|
||||
- `ihoru <https://github.com/ihoru>`_
|
||||
- `Jasmin Bom <https://github.com/jsmnbom>`_
|
||||
|
||||
+94
@@ -2,6 +2,100 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Version 12.6
|
||||
============
|
||||
*Released 2020-04-10*
|
||||
|
||||
**Major Changes:**
|
||||
|
||||
- Bot API 4.7 support. **Note:** In ``Bot.create_new_sticker_set`` and ``Bot.add_sticker_to_set``, the order of the parameters had be changed, as the ``png_sticker`` parameter is now optional. (`#1858`_)
|
||||
|
||||
**Minor changes, CI improvements or bug fixes:**
|
||||
|
||||
- Add tests for ``swtich_inline_query(_current_chat)`` with empty string (`#1635`_)
|
||||
- Doc fixes (`#1854`_, `#1874`_, `#1884`_)
|
||||
- Update issue templates (`#1880`_)
|
||||
- Favor concrete types over "Iterable" (`#1882`_)
|
||||
- Pass last valid ``CallbackContext`` to ``TIMEOUT`` handlers of ``ConversationHandler`` (`#1826`_)
|
||||
- Tweak handling of persistence and update persistence after job calls (`#1827`_)
|
||||
- Use checkout@v2 for GitHub actions (`#1887`_)
|
||||
|
||||
.. _`#1858`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1858
|
||||
.. _`#1635`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1635
|
||||
.. _`#1854`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1854
|
||||
.. _`#1874`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1874
|
||||
.. _`#1884`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1884
|
||||
.. _`#1880`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1880
|
||||
.. _`#1882`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1882
|
||||
.. _`#1826`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1826
|
||||
.. _`#1827`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1827
|
||||
.. _`#1887`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1887
|
||||
|
||||
Version 12.5.1
|
||||
==============
|
||||
*Released 2020-03-30*
|
||||
|
||||
**Minor changes, doc fixes or bug fixes:**
|
||||
|
||||
- Add missing docs for `PollHandler` and `PollAnswerHandler` (`#1853`_)
|
||||
- Fix wording in `Filters` docs (`#1855`_)
|
||||
- Reorder tests to make them more stable (`#1835`_)
|
||||
- Make `ConversationHandler` attributes immutable (`#1756`_)
|
||||
- Make `PrefixHandler` attributes `command` and `prefix` editable (`#1636`_)
|
||||
- Fix UTC as default `tzinfo` for `Job` (`#1696`_)
|
||||
|
||||
.. _`#1853`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1853
|
||||
.. _`#1855`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1855
|
||||
.. _`#1835`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1835
|
||||
.. _`#1756`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1756
|
||||
.. _`#1636`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1636
|
||||
.. _`#1696`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1696
|
||||
|
||||
Version 12.5
|
||||
============
|
||||
*Released 2020-03-29*
|
||||
|
||||
**New Features:**
|
||||
|
||||
- `Bot.link` gives the `t.me` link of the bot (`#1770`_)
|
||||
|
||||
**Major Changes:**
|
||||
|
||||
- Bot API 4.5 and 4.6 support. (`#1508`_, `#1723`_)
|
||||
|
||||
**Minor changes, CI improvements or bug fixes:**
|
||||
|
||||
- Remove legacy CI files (`#1783`_, `#1791`_)
|
||||
- Update pre-commit config file (`#1787`_)
|
||||
- Remove builtin names (`#1792`_)
|
||||
- CI improvements (`#1808`_, `#1848`_)
|
||||
- Support Python 3.8 (`#1614`_, `#1824`_)
|
||||
- Use stale bot for auto closing stale issues (`#1820`_, `#1829`_, `#1840`_)
|
||||
- Doc fixes (`#1778`_, `#1818`_)
|
||||
- Fix typo in `edit_message_media` (`#1779`_)
|
||||
- In examples, answer CallbackQueries and use `edit_message_text` shortcut (`#1721`_)
|
||||
- Revert accidental change in vendored urllib3 (`#1775`_)
|
||||
|
||||
.. _`#1783`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1783
|
||||
.. _`#1787`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1787
|
||||
.. _`#1792`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1792
|
||||
.. _`#1791`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1791
|
||||
.. _`#1808`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1808
|
||||
.. _`#1614`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1614
|
||||
.. _`#1770`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1770
|
||||
.. _`#1824`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1824
|
||||
.. _`#1820`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1820
|
||||
.. _`#1829`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1829
|
||||
.. _`#1840`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1840
|
||||
.. _`#1778`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1778
|
||||
.. _`#1779`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1779
|
||||
.. _`#1721`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1721
|
||||
.. _`#1775`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1775
|
||||
.. _`#1848`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1848
|
||||
.. _`#1818`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1818
|
||||
.. _`#1508`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1508
|
||||
.. _`#1723`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1723
|
||||
|
||||
Version 12.4.2
|
||||
==============
|
||||
*Released 2020-02-10*
|
||||
|
||||
+1
-1
@@ -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.7** are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
|
||||
@@ -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
@@ -58,9 +58,9 @@ author = u'Leandro Toledo'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '12.4' # telegram.__version__[:3]
|
||||
version = '12.6' # telegram.__version__[:3]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '12.4.2' # telegram.__version__
|
||||
release = '12.6' # telegram.__version__
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.BotCommand
|
||||
===================
|
||||
|
||||
.. autoclass:: telegram.BotCommand
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.Dice
|
||||
=============
|
||||
|
||||
.. autoclass:: telegram.Dice
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.ext.PollAnswerHandler
|
||||
==============================
|
||||
|
||||
.. autoclass:: telegram.ext.PollAnswerHandler
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.ext.PollHandler
|
||||
========================
|
||||
|
||||
.. autoclass:: telegram.ext.PollHandler
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -26,6 +26,8 @@ Handlers
|
||||
telegram.ext.commandhandler
|
||||
telegram.ext.inlinequeryhandler
|
||||
telegram.ext.messagehandler
|
||||
telegram.ext.pollanswerhandler
|
||||
telegram.ext.pollhandler
|
||||
telegram.ext.precheckoutqueryhandler
|
||||
telegram.ext.prefixhandler
|
||||
telegram.ext.regexhandler
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.KeyboardButtonPollType
|
||||
===============================
|
||||
|
||||
.. autoclass:: telegram.KeyboardButtonPollType
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.PollAnswer
|
||||
===================
|
||||
|
||||
.. autoclass:: telegram.PollAnswer
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -9,6 +9,7 @@ telegram package
|
||||
telegram.animation
|
||||
telegram.audio
|
||||
telegram.bot
|
||||
telegram.botcommand
|
||||
telegram.callbackquery
|
||||
telegram.chat
|
||||
telegram.chataction
|
||||
@@ -17,6 +18,7 @@ telegram package
|
||||
telegram.chatphoto
|
||||
telegram.constants
|
||||
telegram.contact
|
||||
telegram.dice
|
||||
telegram.document
|
||||
telegram.error
|
||||
telegram.file
|
||||
@@ -31,6 +33,7 @@ telegram package
|
||||
telegram.inputmediaphoto
|
||||
telegram.inputmediavideo
|
||||
telegram.keyboardbutton
|
||||
telegram.keyboardbuttonpolltype
|
||||
telegram.location
|
||||
telegram.loginurl
|
||||
telegram.message
|
||||
@@ -38,6 +41,7 @@ telegram package
|
||||
telegram.parsemode
|
||||
telegram.photosize
|
||||
telegram.poll
|
||||
telegram.pollanswer
|
||||
telegram.polloption
|
||||
telegram.replykeyboardremove
|
||||
telegram.replykeyboardmarkup
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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',
|
||||
],)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"""A library that provides a Python interface to the Telegram Bot API"""
|
||||
|
||||
from .base import TelegramObject
|
||||
from .botcommand import BotCommand
|
||||
from .user import User
|
||||
from .files.chatphoto import ChatPhoto
|
||||
from .chat import Chat
|
||||
@@ -36,8 +37,10 @@ from .files.location import Location
|
||||
from .files.venue import Venue
|
||||
from .files.videonote import VideoNote
|
||||
from .chataction import ChatAction
|
||||
from .dice import Dice
|
||||
from .userprofilephotos import UserProfilePhotos
|
||||
from .keyboardbutton import KeyboardButton
|
||||
from .keyboardbuttonpolltype import KeyboardButtonPollType
|
||||
from .replymarkup import ReplyMarkup
|
||||
from .replykeyboardmarkup import ReplyKeyboardMarkup
|
||||
from .replykeyboardremove import ReplyKeyboardRemove
|
||||
@@ -48,7 +51,7 @@ from .files.file import File
|
||||
from .parsemode import ParseMode
|
||||
from .messageentity import MessageEntity
|
||||
from .games.game import Game
|
||||
from .poll import Poll, PollOption
|
||||
from .poll import Poll, PollOption, PollAnswer
|
||||
from .loginurl import LoginUrl
|
||||
from .games.callbackgame import CallbackGame
|
||||
from .payment.shippingaddress import ShippingAddress
|
||||
@@ -138,7 +141,7 @@ __all__ = [
|
||||
'InlineQueryResultPhoto', 'InlineQueryResultVenue', 'InlineQueryResultVideo',
|
||||
'InlineQueryResultVoice', 'InlineQueryResultGame', 'InputContactMessageContent', 'InputFile',
|
||||
'InputLocationMessageContent', 'InputMessageContent', 'InputTextMessageContent',
|
||||
'InputVenueMessageContent', 'KeyboardButton', 'Location', 'EncryptedCredentials',
|
||||
'InputVenueMessageContent', 'Location', 'EncryptedCredentials',
|
||||
'PassportFile', 'EncryptedPassportElement', 'PassportData', 'Message', 'MessageEntity',
|
||||
'ParseMode', 'PhotoSize', 'ReplyKeyboardRemove', 'ReplyKeyboardMarkup', 'ReplyMarkup',
|
||||
'Sticker', 'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue',
|
||||
@@ -156,5 +159,6 @@ __all__ = [
|
||||
'InputMediaAudio', 'InputMediaDocument', 'TelegramDecryptionError',
|
||||
'PassportElementErrorSelfie', 'PassportElementErrorTranslationFile',
|
||||
'PassportElementErrorTranslationFiles', 'PassportElementErrorUnspecified', 'Poll',
|
||||
'PollOption', 'LoginUrl'
|
||||
'PollOption', 'PollAnswer', 'LoginUrl', 'KeyboardButton', 'KeyboardButtonPollType', 'Dice',
|
||||
'BotCommand'
|
||||
]
|
||||
|
||||
+493
-179
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=R0903
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2020
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram Bot Command."""
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
class BotCommand(TelegramObject):
|
||||
"""
|
||||
This object represents a bot command.
|
||||
|
||||
Attributes:
|
||||
command (:obj:`str`): Text of the command.
|
||||
description (:obj:`str`): Description of the command.
|
||||
|
||||
Args:
|
||||
command (:obj:`str`): Text of the command, 1-32 characters. Can contain only lowercase
|
||||
English letters, digits and underscores.
|
||||
description (:obj:`str`): Description of the command, 3-256 characters.
|
||||
"""
|
||||
def __init__(self, command, description, **kwargs):
|
||||
self.command = command
|
||||
self.description = description
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, bot):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return cls(**data)
|
||||
@@ -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
|
||||
|
||||
@@ -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::
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=R0903
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2020
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram Dice."""
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
class Dice(TelegramObject):
|
||||
"""
|
||||
This object represents a dice with random value from 1 to 6. (The singular form of "dice" is
|
||||
"die". However, PTB mimics the Telegram API, which uses the term "dice".)
|
||||
|
||||
Attributes:
|
||||
value (:obj:`int`): Value of the dice.
|
||||
|
||||
Args:
|
||||
value (:obj:`int`): Value of the dice, 1-6.
|
||||
"""
|
||||
def __init__(self, value, **kwargs):
|
||||
self.value = value
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, bot):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return cls(**data)
|
||||
@@ -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')
|
||||
|
||||
@@ -302,6 +302,10 @@ class PrefixHandler(CommandHandler):
|
||||
pass_user_data=False,
|
||||
pass_chat_data=False):
|
||||
|
||||
self._prefix = list()
|
||||
self._command = list()
|
||||
self._commands = list()
|
||||
|
||||
super(PrefixHandler, self).__init__(
|
||||
'nocommand', callback, filters=filters, allow_edited=None, pass_args=pass_args,
|
||||
pass_update_queue=pass_update_queue,
|
||||
@@ -309,15 +313,36 @@ class PrefixHandler(CommandHandler):
|
||||
pass_user_data=pass_user_data,
|
||||
pass_chat_data=pass_chat_data)
|
||||
|
||||
self.prefix = prefix
|
||||
self.command = command
|
||||
self._build_commands()
|
||||
|
||||
@property
|
||||
def prefix(self):
|
||||
return self._prefix
|
||||
|
||||
@prefix.setter
|
||||
def prefix(self, prefix):
|
||||
if isinstance(prefix, string_types):
|
||||
self.prefix = [prefix.lower()]
|
||||
self._prefix = [prefix.lower()]
|
||||
else:
|
||||
self.prefix = prefix
|
||||
self._prefix = prefix
|
||||
self._build_commands()
|
||||
|
||||
@property
|
||||
def command(self):
|
||||
return self._command
|
||||
|
||||
@command.setter
|
||||
def command(self, command):
|
||||
if isinstance(command, string_types):
|
||||
self.command = [command.lower()]
|
||||
self._command = [command.lower()]
|
||||
else:
|
||||
self.command = command
|
||||
self.command = [x.lower() + y.lower() for x in self.prefix for y in self.command]
|
||||
self._command = command
|
||||
self._build_commands()
|
||||
|
||||
def _build_commands(self):
|
||||
self._commands = [x.lower() + y.lower() for x in self.prefix for y in self.command]
|
||||
|
||||
def check_update(self, update):
|
||||
"""Determines whether an update should be passed to this handlers :attr:`callback`.
|
||||
@@ -334,7 +359,7 @@ class PrefixHandler(CommandHandler):
|
||||
|
||||
if message.text:
|
||||
text_list = message.text.split()
|
||||
if text_list[0].lower() not in self.command:
|
||||
if text_list[0].lower() not in self._commands:
|
||||
return None
|
||||
filter_result = self.filters(update)
|
||||
if filter_result:
|
||||
|
||||
@@ -29,10 +29,11 @@ from telegram.utils.promise import Promise
|
||||
|
||||
|
||||
class _ConversationTimeoutContext(object):
|
||||
def __init__(self, conversation_key, update, dispatcher):
|
||||
def __init__(self, conversation_key, update, dispatcher, callback_context):
|
||||
self.conversation_key = conversation_key
|
||||
self.update = update
|
||||
self.dispatcher = dispatcher
|
||||
self.callback_context = callback_context
|
||||
|
||||
|
||||
class ConversationHandler(Handler):
|
||||
@@ -93,11 +94,12 @@ class ConversationHandler(Handler):
|
||||
per_user (:obj:`bool`): If the conversationkey should contain the User's ID.
|
||||
per_message (:obj:`bool`): If the conversationkey should contain the Message's
|
||||
ID.
|
||||
conversation_timeout (:obj:`float`|:obj:`datetime.timedelta`): Optional. When this handler
|
||||
is inactive more than this timeout (in seconds), it will be automatically ended. If
|
||||
this value is 0 (default), there will be no timeout. When it's triggered, the last
|
||||
received update will be handled by ALL the handler's who's `check_update` method
|
||||
returns True that are in the state :attr:`ConversationHandler.TIMEOUT`.
|
||||
conversation_timeout (:obj:`float` | :obj:`datetime.timedelta`): Optional. When this
|
||||
handler is inactive more than this timeout (in seconds), it will be automatically
|
||||
ended. If this value is 0 (default), there will be no timeout. When it's triggered, the
|
||||
last received update and the corresponding ``context`` will be handled by ALL the
|
||||
handler's who's `check_update` method returns True that are in the state
|
||||
:attr:`ConversationHandler.TIMEOUT`.
|
||||
name (:obj:`str`): Optional. The name for this conversationhandler. Required for
|
||||
persistence
|
||||
persistent (:obj:`bool`): Optional. If the conversations dict for this handler should be
|
||||
@@ -130,8 +132,9 @@ class ConversationHandler(Handler):
|
||||
conversation_timeout (:obj:`float` | :obj:`datetime.timedelta`, optional): When this
|
||||
handler is inactive more than this timeout (in seconds), it will be automatically
|
||||
ended. If this value is 0 or None (default), there will be no timeout. The last
|
||||
received update will be handled by ALL the handler's who's `check_update` method
|
||||
returns True that are in the state :attr:`ConversationHandler.TIMEOUT`.
|
||||
received update and the corresponding ``context`` will be handled by ALL the handler's
|
||||
who's `check_update` method returns True that are in the state
|
||||
:attr:`ConversationHandler.TIMEOUT`.
|
||||
name (:obj:`str`, optional): The name for this conversationhandler. Required for
|
||||
persistence
|
||||
persistent (:obj:`bool`, optional): If the conversations dict for this handler should be
|
||||
@@ -165,23 +168,23 @@ class ConversationHandler(Handler):
|
||||
persistent=False,
|
||||
map_to_parent=None):
|
||||
|
||||
self.entry_points = entry_points
|
||||
self.states = states
|
||||
self.fallbacks = fallbacks
|
||||
self._entry_points = entry_points
|
||||
self._states = states
|
||||
self._fallbacks = fallbacks
|
||||
|
||||
self.allow_reentry = allow_reentry
|
||||
self.per_user = per_user
|
||||
self.per_chat = per_chat
|
||||
self.per_message = per_message
|
||||
self.conversation_timeout = conversation_timeout
|
||||
self.name = name
|
||||
self._allow_reentry = allow_reentry
|
||||
self._per_user = per_user
|
||||
self._per_chat = per_chat
|
||||
self._per_message = per_message
|
||||
self._conversation_timeout = conversation_timeout
|
||||
self._name = name
|
||||
if persistent and not self.name:
|
||||
raise ValueError("Conversations can't be persistent when handler is unnamed.")
|
||||
self.persistent = persistent
|
||||
self._persistence = None
|
||||
""":obj:`telegram.ext.BasePersistance`: The persistence used to store conversations.
|
||||
Set by dispatcher"""
|
||||
self.map_to_parent = map_to_parent
|
||||
self._map_to_parent = map_to_parent
|
||||
|
||||
self.timeout_jobs = dict()
|
||||
self._timeout_jobs_lock = Lock()
|
||||
@@ -225,6 +228,87 @@ class ConversationHandler(Handler):
|
||||
"since inline queries have no chat context.")
|
||||
break
|
||||
|
||||
@property
|
||||
def entry_points(self):
|
||||
return self._entry_points
|
||||
|
||||
@entry_points.setter
|
||||
def entry_points(self, value):
|
||||
raise ValueError('You can not assign a new value to entry_points after initialization.')
|
||||
|
||||
@property
|
||||
def states(self):
|
||||
return self._states
|
||||
|
||||
@states.setter
|
||||
def states(self, value):
|
||||
raise ValueError('You can not assign a new value to states after initialization.')
|
||||
|
||||
@property
|
||||
def fallbacks(self):
|
||||
return self._fallbacks
|
||||
|
||||
@fallbacks.setter
|
||||
def fallbacks(self, value):
|
||||
raise ValueError('You can not assign a new value to fallbacks after initialization.')
|
||||
|
||||
@property
|
||||
def allow_reentry(self):
|
||||
return self._allow_reentry
|
||||
|
||||
@allow_reentry.setter
|
||||
def allow_reentry(self, value):
|
||||
raise ValueError('You can not assign a new value to allow_reentry after initialization.')
|
||||
|
||||
@property
|
||||
def per_user(self):
|
||||
return self._per_user
|
||||
|
||||
@per_user.setter
|
||||
def per_user(self, value):
|
||||
raise ValueError('You can not assign a new value to per_user after initialization.')
|
||||
|
||||
@property
|
||||
def per_chat(self):
|
||||
return self._per_chat
|
||||
|
||||
@per_chat.setter
|
||||
def per_chat(self, value):
|
||||
raise ValueError('You can not assign a new value to per_chat after initialization.')
|
||||
|
||||
@property
|
||||
def per_message(self):
|
||||
return self._per_message
|
||||
|
||||
@per_message.setter
|
||||
def per_message(self, value):
|
||||
raise ValueError('You can not assign a new value to per_message after initialization.')
|
||||
|
||||
@property
|
||||
def conversation_timeout(self):
|
||||
return self._conversation_timeout
|
||||
|
||||
@conversation_timeout.setter
|
||||
def conversation_timeout(self, value):
|
||||
raise ValueError('You can not assign a new value to conversation_timeout after '
|
||||
'initialization.')
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
raise ValueError('You can not assign a new value to name after initialization.')
|
||||
|
||||
@property
|
||||
def map_to_parent(self):
|
||||
return self._map_to_parent
|
||||
|
||||
@map_to_parent.setter
|
||||
def map_to_parent(self, value):
|
||||
raise ValueError('You can not assign a new value to map_to_parent after initialization.')
|
||||
|
||||
@property
|
||||
def persistence(self):
|
||||
return self._persistence
|
||||
@@ -385,7 +469,8 @@ class ConversationHandler(Handler):
|
||||
# Add the new timeout job
|
||||
self.timeout_jobs[conversation_key] = dispatcher.job_queue.run_once(
|
||||
self._trigger_timeout, self.conversation_timeout,
|
||||
context=_ConversationTimeoutContext(conversation_key, update, dispatcher))
|
||||
context=_ConversationTimeoutContext(conversation_key, update,
|
||||
dispatcher, context))
|
||||
|
||||
if isinstance(self.map_to_parent, dict) and new_state in self.map_to_parent:
|
||||
self.update_state(self.END, conversation_key)
|
||||
@@ -422,9 +507,9 @@ class ConversationHandler(Handler):
|
||||
callback_context = None
|
||||
if isinstance(context, CallbackContext):
|
||||
job = context.job
|
||||
callback_context = context
|
||||
|
||||
context = job.context
|
||||
callback_context = context.callback_context
|
||||
|
||||
with self._timeout_jobs_lock:
|
||||
found_job = self.timeout_jobs[context.conversation_key]
|
||||
|
||||
@@ -226,6 +226,8 @@ class DictPersistence(BasePersistence):
|
||||
user_id (:obj:`int`): The user the data might have been changed for.
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.user_data` [user_id].
|
||||
"""
|
||||
if self._user_data is None:
|
||||
self._user_data = defaultdict(dict)
|
||||
if self._user_data.get(user_id) == data:
|
||||
return
|
||||
self._user_data[user_id] = data
|
||||
@@ -238,6 +240,8 @@ class DictPersistence(BasePersistence):
|
||||
chat_id (:obj:`int`): The chat the data might have been changed for.
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.chat_data` [chat_id].
|
||||
"""
|
||||
if self._chat_data is None:
|
||||
self._chat_data = defaultdict(dict)
|
||||
if self._chat_data.get(chat_id) == data:
|
||||
return
|
||||
self._chat_data[chat_id] = data
|
||||
|
||||
+52
-55
@@ -323,53 +323,6 @@ class Dispatcher(object):
|
||||
|
||||
"""
|
||||
|
||||
def persist_update(update):
|
||||
"""Persist a single update.
|
||||
|
||||
Args:
|
||||
update (:class:`telegram.Update`):
|
||||
The update to process.
|
||||
|
||||
"""
|
||||
if self.persistence and isinstance(update, Update):
|
||||
if self.persistence.store_bot_data:
|
||||
try:
|
||||
self.persistence.update_bot_data(self.bot_data)
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving bot data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
if self.persistence.store_chat_data and update.effective_chat:
|
||||
chat_id = update.effective_chat.id
|
||||
try:
|
||||
self.persistence.update_chat_data(chat_id,
|
||||
self.chat_data[chat_id])
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving chat data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
if self.persistence.store_user_data and update.effective_user:
|
||||
user_id = update.effective_user.id
|
||||
try:
|
||||
self.persistence.update_user_data(user_id,
|
||||
self.user_data[user_id])
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving user data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
|
||||
# An error happened while polling
|
||||
if isinstance(update, TelegramError):
|
||||
try:
|
||||
@@ -388,13 +341,13 @@ class Dispatcher(object):
|
||||
if not context and self.use_context:
|
||||
context = CallbackContext.from_update(update, self)
|
||||
handler.handle_update(update, self, check, context)
|
||||
persist_update(update)
|
||||
self.update_persistence(update=update)
|
||||
break
|
||||
|
||||
# Stop processing with any other handler.
|
||||
except DispatcherHandlerStop:
|
||||
self.logger.debug('Stopping further handlers due to DispatcherHandlerStop')
|
||||
persist_update(update)
|
||||
self.update_persistence(update=update)
|
||||
break
|
||||
|
||||
# Dispatch any error.
|
||||
@@ -471,18 +424,62 @@ class Dispatcher(object):
|
||||
del self.handlers[group]
|
||||
self.groups.remove(group)
|
||||
|
||||
def update_persistence(self):
|
||||
def update_persistence(self, update=None):
|
||||
"""Update :attr:`user_data`, :attr:`chat_data` and :attr:`bot_data` in :attr:`persistence`.
|
||||
|
||||
Args:
|
||||
update (:class:`telegram.Update`, optional): The update to process. If passed, only the
|
||||
corresponding ``user_data`` and ``chat_data`` will be updated.
|
||||
"""
|
||||
if self.persistence:
|
||||
chat_ids = self.chat_data.keys()
|
||||
user_ids = self.user_data.keys()
|
||||
|
||||
if isinstance(update, Update):
|
||||
if update.effective_chat:
|
||||
chat_ids = [update.effective_chat.id]
|
||||
else:
|
||||
chat_ids = []
|
||||
if update.effective_user:
|
||||
user_ids = [update.effective_user.id]
|
||||
else:
|
||||
user_ids = []
|
||||
|
||||
if self.persistence.store_bot_data:
|
||||
self.persistence.update_bot_data(self.bot_data)
|
||||
try:
|
||||
self.persistence.update_bot_data(self.bot_data)
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving bot data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
if self.persistence.store_chat_data:
|
||||
for chat_id in self.chat_data:
|
||||
self.persistence.update_chat_data(chat_id, self.chat_data[chat_id])
|
||||
for chat_id in chat_ids:
|
||||
try:
|
||||
self.persistence.update_chat_data(chat_id, self.chat_data[chat_id])
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving chat data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
if self.persistence.store_user_data:
|
||||
for user_id in self.user_data:
|
||||
self.persistence.update_user_data(user_id, self.user_data[user_id])
|
||||
for user_id in user_ids:
|
||||
try:
|
||||
self.persistence.update_user_data(user_id, self.user_data[user_id])
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving user data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
|
||||
def add_error_handler(self, callback):
|
||||
"""Registers an error handler in the Dispatcher. This handler will receive every error
|
||||
|
||||
+70
-24
@@ -50,7 +50,7 @@ class BaseFilter(object):
|
||||
>>> Filters.text & (~ Filters.forwarded)
|
||||
|
||||
Note:
|
||||
Filters use the same short circuiting logic that pythons `and`, `or` and `not`.
|
||||
Filters use the same short circuiting logic as python's `and`, `or` and `not`.
|
||||
This means that for example:
|
||||
|
||||
>>> Filters.regex(r'(a?x)') | Filters.regex(r'(b?x)')
|
||||
@@ -236,35 +236,35 @@ class Filters(object):
|
||||
class _Text(BaseFilter):
|
||||
name = 'Filters.text'
|
||||
|
||||
class _TextIterable(BaseFilter):
|
||||
class _TextStrings(BaseFilter):
|
||||
|
||||
def __init__(self, iterable):
|
||||
self.iterable = iterable
|
||||
self.name = 'Filters.text({})'.format(iterable)
|
||||
def __init__(self, strings):
|
||||
self.strings = strings
|
||||
self.name = 'Filters.text({})'.format(strings)
|
||||
|
||||
def filter(self, message):
|
||||
if message.text:
|
||||
return message.text in self.iterable
|
||||
return message.text in self.strings
|
||||
return False
|
||||
|
||||
def __call__(self, update):
|
||||
if isinstance(update, Update):
|
||||
return self.filter(update.effective_message)
|
||||
else:
|
||||
return self._TextIterable(update)
|
||||
return self._TextStrings(update)
|
||||
|
||||
def filter(self, message):
|
||||
return bool(message.text)
|
||||
|
||||
text = _Text()
|
||||
"""Text Messages. If an iterable of strings is passed, it filters messages to only allow those
|
||||
whose text is appearing in the given iterable.
|
||||
"""Text Messages. If a list of strings is passed, it filters messages to only allow those
|
||||
whose text is appearing in the given list.
|
||||
|
||||
Examples:
|
||||
To allow any text message, simply use
|
||||
``MessageHandler(Filters.text, callback_method)``.
|
||||
|
||||
A simple usecase for passing an iterable is to allow only messages that were send by a
|
||||
A simple usecase for passing a list is to allow only messages that were send by a
|
||||
custom :class:`telegram.ReplyKeyboardMarkup`::
|
||||
|
||||
buttons = ['Start', 'Settings', 'Back']
|
||||
@@ -272,44 +272,48 @@ class Filters(object):
|
||||
...
|
||||
MessageHandler(Filters.text(buttons), callback_method)
|
||||
|
||||
Note:
|
||||
Dice messages don't have text. If you want to filter either text or dice messages, use
|
||||
``Filters.text | Filters.dice``.
|
||||
|
||||
Args:
|
||||
update (Iterable[:obj:`str`], optional): Which messages to allow. Only exact matches
|
||||
are allowed. If not specified, will allow any text message.
|
||||
update (List[:obj:`str`] | Tuple[:obj:`str`], optional): Which messages to allow. Only
|
||||
exact matches are allowed. If not specified, will allow any text message.
|
||||
"""
|
||||
|
||||
class _Caption(BaseFilter):
|
||||
name = 'Filters.caption'
|
||||
|
||||
class _CaptionIterable(BaseFilter):
|
||||
class _CaptionStrings(BaseFilter):
|
||||
|
||||
def __init__(self, iterable):
|
||||
self.iterable = iterable
|
||||
self.name = 'Filters.caption({})'.format(iterable)
|
||||
def __init__(self, strings):
|
||||
self.strings = strings
|
||||
self.name = 'Filters.caption({})'.format(strings)
|
||||
|
||||
def filter(self, message):
|
||||
if message.caption:
|
||||
return message.caption in self.iterable
|
||||
return message.caption in self.strings
|
||||
return False
|
||||
|
||||
def __call__(self, update):
|
||||
if isinstance(update, Update):
|
||||
return self.filter(update.effective_message)
|
||||
else:
|
||||
return self._CaptionIterable(update)
|
||||
return self._CaptionStrings(update)
|
||||
|
||||
def filter(self, message):
|
||||
return bool(message.caption)
|
||||
|
||||
caption = _Caption()
|
||||
"""Messages with a caption. If an iterable of strings is passed, it filters messages to only
|
||||
allow those whose caption is appearing in the given iterable.
|
||||
"""Messages with a caption. If a list of strings is passed, it filters messages to only
|
||||
allow those whose caption is appearing in the given list.
|
||||
|
||||
Examples:
|
||||
``MessageHandler(Filters.caption, callback_method)``
|
||||
|
||||
Args:
|
||||
update (Iterable[:obj:`str`], optional): Which captions to allow. Only exact matches
|
||||
are allowed. If not specified, will allow any message with a caption.
|
||||
update (List[:obj:`str`] | Tuple[:obj:`str`], optional): Which captions to allow. Only
|
||||
exact matches are allowed. If not specified, will allow any message with a caption.
|
||||
"""
|
||||
|
||||
class _Command(BaseFilter):
|
||||
@@ -368,7 +372,7 @@ class Filters(object):
|
||||
if you need to specify flags on your pattern.
|
||||
|
||||
Note:
|
||||
Filters use the same short circuiting logic that pythons `and`, `or` and `not`.
|
||||
Filters use the same short circuiting logic as python's `and`, `or` and `not`.
|
||||
This means that for example:
|
||||
|
||||
>>> Filters.regex(r'(a?x)') | Filters.regex(r'(b?x)')
|
||||
@@ -427,7 +431,7 @@ class Filters(object):
|
||||
send media with wrong types that don't fit to this handler.
|
||||
|
||||
Example:
|
||||
Filters.documents.category('audio/') returnes `True` for all types
|
||||
Filters.documents.category('audio/') returns `True` for all types
|
||||
of audio sent as file, for example 'audio/mpeg' or 'audio/x-wav'
|
||||
"""
|
||||
|
||||
@@ -957,6 +961,48 @@ officedocument.wordprocessingml.document")``-
|
||||
poll = _Poll()
|
||||
"""Messages that contain a :class:`telegram.Poll`."""
|
||||
|
||||
class _Dice(BaseFilter):
|
||||
name = 'Filters.dice'
|
||||
|
||||
class _DiceValues(BaseFilter):
|
||||
|
||||
def __init__(self, values):
|
||||
self.values = [values] if isinstance(values, int) else values
|
||||
self.name = 'Filters.dice({})'.format(values)
|
||||
|
||||
def filter(self, message):
|
||||
return bool(message.dice and message.dice.value in self.values)
|
||||
|
||||
def __call__(self, update):
|
||||
if isinstance(update, Update):
|
||||
return self.filter(update.effective_message)
|
||||
else:
|
||||
return self._DiceValues(update)
|
||||
|
||||
def filter(self, message):
|
||||
return bool(message.dice)
|
||||
|
||||
dice = _Dice()
|
||||
"""Dice Messages. If an integer or a list of integers is passed, it filters messages to only
|
||||
allow those whose dice value is appearing in the given list.
|
||||
|
||||
Examples:
|
||||
To allow any dice message, simply use
|
||||
``MessageHandler(Filters.dice, callback_method)``.
|
||||
To allow only dice with value 6, use
|
||||
``MessageHandler(Filters.dice(6), callback_method)``.
|
||||
To allow only dice with value 5 `or` 6, use
|
||||
``MessageHandler(Filters.dice([5, 6]), callback_method)``.
|
||||
|
||||
Args:
|
||||
update (:obj:`int` | List[:obj:`int`], optional): Which values to allow. If not
|
||||
specified, will allow any dice message.
|
||||
|
||||
Note:
|
||||
Dice messages don't have text. If you want to filter either text or dice messages, use
|
||||
``Filters.text | Filters.dice``.
|
||||
"""
|
||||
|
||||
class language(BaseFilter):
|
||||
"""Filters messages to only allow those which are from users with a certain language code.
|
||||
|
||||
|
||||
@@ -129,10 +129,11 @@ class JobQueue(object):
|
||||
* :obj:`datetime.timedelta` will be interpreted as "time from now" in which the
|
||||
job should run.
|
||||
* :obj:`datetime.datetime` will be interpreted as a specific date and time at
|
||||
which the job should run.
|
||||
which the job should run. If the timezone (``datetime.tzinfo``) is ``None``, UTC
|
||||
will be assumed.
|
||||
* :obj:`datetime.time` will be interpreted as a specific time of day at which the
|
||||
job should run. This could be either today or, if the time has already passed,
|
||||
tomorrow.
|
||||
tomorrow. If the timezone (``time.tzinfo``) is ``None``, UTC will be assumed.
|
||||
|
||||
context (:obj:`object`, optional): Additional data needed for the callback function.
|
||||
Can be accessed through ``job.context`` in the callback. Defaults to ``None``.
|
||||
@@ -172,10 +173,11 @@ class JobQueue(object):
|
||||
* :obj:`datetime.timedelta` will be interpreted as "time from now" in which the
|
||||
job should run.
|
||||
* :obj:`datetime.datetime` will be interpreted as a specific date and time at
|
||||
which the job should run.
|
||||
which the job should run. If the timezone (``datetime.tzinfo``) is ``None``, UTC
|
||||
will be assumed.
|
||||
* :obj:`datetime.time` will be interpreted as a specific time of day at which the
|
||||
job should run. This could be either today or, if the time has already passed,
|
||||
tomorrow.
|
||||
tomorrow. If the timezone (``time.tzinfo``) is ``None``, UTC will be assumed.
|
||||
|
||||
Defaults to ``interval``
|
||||
context (:obj:`object`, optional): Additional data needed for the callback function.
|
||||
@@ -285,9 +287,10 @@ class JobQueue(object):
|
||||
if job.enabled:
|
||||
try:
|
||||
current_week_day = datetime.datetime.now(job.tzinfo).date().weekday()
|
||||
if any(day == current_week_day for day in job.days):
|
||||
if current_week_day in job.days:
|
||||
self.logger.debug('Running job %s', job.name)
|
||||
job.run(self._dispatcher)
|
||||
self._dispatcher.update_persistence()
|
||||
|
||||
except Exception:
|
||||
self.logger.exception('An uncaught error was raised while executing job %s',
|
||||
@@ -400,7 +403,7 @@ class Job(object):
|
||||
days=Days.EVERY_DAY,
|
||||
name=None,
|
||||
job_queue=None,
|
||||
tzinfo=_UTC):
|
||||
tzinfo=None):
|
||||
|
||||
self.callback = callback
|
||||
self.context = context
|
||||
@@ -413,7 +416,7 @@ class Job(object):
|
||||
|
||||
self._days = None
|
||||
self.days = days
|
||||
self.tzinfo = tzinfo
|
||||
self.tzinfo = tzinfo or _UTC
|
||||
|
||||
self._job_queue = weakref.proxy(job_queue) if job_queue is not None else None
|
||||
|
||||
|
||||
@@ -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:
|
||||
@@ -224,6 +224,8 @@ class PicklePersistence(BasePersistence):
|
||||
user_id (:obj:`int`): The user the data might have been changed for.
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.user_data` [user_id].
|
||||
"""
|
||||
if self.user_data is None:
|
||||
self.user_data = defaultdict(dict)
|
||||
if self.user_data.get(user_id) == data:
|
||||
return
|
||||
self.user_data[user_id] = data
|
||||
@@ -242,6 +244,8 @@ class PicklePersistence(BasePersistence):
|
||||
chat_id (:obj:`int`): The chat the data might have been changed for.
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.chat_data` [chat_id].
|
||||
"""
|
||||
if self.chat_data is None:
|
||||
self.chat_data = defaultdict(dict)
|
||||
if self.chat_data.get(chat_id) == data:
|
||||
return
|
||||
self.chat_data[chat_id] = data
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -568,9 +570,9 @@ class Updater(object):
|
||||
"""Blocks until one of the signals are received and stops the updater.
|
||||
|
||||
Args:
|
||||
stop_signals (:obj:`iterable`): Iterable containing signals from the signal module that
|
||||
should be subscribed to. Updater.stop() will be called on receiving one of those
|
||||
signals. Defaults to (``SIGINT``, ``SIGTERM``, ``SIGABRT``).
|
||||
stop_signals (:obj:`list` | :obj:`tuple`): List containing signals from the signal
|
||||
module that should be subscribed to. Updater.stop() will be called on receiving one
|
||||
of those signals. Defaults to (``SIGINT``, ``SIGTERM``, ``SIGABRT``).
|
||||
|
||||
"""
|
||||
for sig in stop_signals:
|
||||
|
||||
@@ -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
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
@@ -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):
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -26,6 +26,9 @@ class Sticker(TelegramObject):
|
||||
|
||||
Attributes:
|
||||
file_id (:obj:`str`): Unique identifier for this file.
|
||||
file_unique_id (:obj:`str`): Unique identifier for this file, which
|
||||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
width (:obj:`int`): Sticker width.
|
||||
height (:obj:`int`): Sticker height.
|
||||
is_animated (:obj:`bool`): True, if the sticker is animated.
|
||||
@@ -39,7 +42,10 @@ class Sticker(TelegramObject):
|
||||
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
|
||||
|
||||
Args:
|
||||
file_id (:obj:`str`): Unique identifier for this file.
|
||||
file_id (:obj:`str`): Identifier for this file, which can be used to download
|
||||
or reuse the file.
|
||||
file_unique_id (:obj:`str`): Unique and the same over time and
|
||||
for different bots file identifier.
|
||||
width (:obj:`int`): Sticker width.
|
||||
height (:obj:`int`): Sticker height.
|
||||
is_animated (:obj:`bool`): True, if the sticker is animated.
|
||||
@@ -58,6 +64,7 @@ class Sticker(TelegramObject):
|
||||
|
||||
def __init__(self,
|
||||
file_id,
|
||||
file_unique_id,
|
||||
width,
|
||||
height,
|
||||
is_animated,
|
||||
@@ -70,6 +77,7 @@ class Sticker(TelegramObject):
|
||||
**kwargs):
|
||||
# Required
|
||||
self.file_id = str(file_id)
|
||||
self.file_unique_id = str(file_unique_id)
|
||||
self.width = int(width)
|
||||
self.height = int(height)
|
||||
self.is_animated = is_animated
|
||||
@@ -81,7 +89,7 @@ class Sticker(TelegramObject):
|
||||
self.mask_position = mask_position
|
||||
self.bot = bot
|
||||
|
||||
self._id_attrs = (self.file_id,)
|
||||
self._id_attrs = (self.file_unique_id,)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, bot):
|
||||
@@ -130,6 +138,8 @@ class StickerSet(TelegramObject):
|
||||
is_animated (:obj:`bool`): True, if the sticker set contains animated stickers.
|
||||
contains_masks (:obj:`bool`): True, if the sticker set contains masks.
|
||||
stickers (List[:class:`telegram.Sticker`]): List of all set stickers.
|
||||
thumb (:class:`telegram.PhotoSize`): Optional. Sticker set thumbnail in the .WEBP or .TGS
|
||||
format
|
||||
|
||||
Args:
|
||||
name (:obj:`str`): Sticker set name.
|
||||
@@ -137,15 +147,20 @@ class StickerSet(TelegramObject):
|
||||
is_animated (:obj:`bool`): True, if the sticker set contains animated stickers.
|
||||
contains_masks (:obj:`bool`): True, if the sticker set contains masks.
|
||||
stickers (List[:class:`telegram.Sticker`]): List of all set stickers.
|
||||
thumb (:class:`telegram.PhotoSize`, optional): Sticker set thumbnail in the .WEBP or .TGS
|
||||
format
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name, title, is_animated, contains_masks, stickers, bot=None, **kwargs):
|
||||
def __init__(self, name, title, is_animated, contains_masks, stickers, bot=None, thumb=None,
|
||||
**kwargs):
|
||||
self.name = name
|
||||
self.title = title
|
||||
self.is_animated = is_animated
|
||||
self.contains_masks = contains_masks
|
||||
self.stickers = stickers
|
||||
# Optionals
|
||||
self.thumb = thumb
|
||||
|
||||
self._id_attrs = (self.name,)
|
||||
|
||||
@@ -156,6 +171,7 @@ class StickerSet(TelegramObject):
|
||||
|
||||
data = super(StickerSet, StickerSet).de_json(data, bot)
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
data['stickers'] = Sticker.de_list(data.get('stickers'), bot)
|
||||
|
||||
return StickerSet(bot=bot, **data)
|
||||
|
||||
+10
-2
@@ -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):
|
||||
|
||||
@@ -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
@@ -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):
|
||||
|
||||
@@ -30,27 +30,28 @@ class InlineKeyboardButton(TelegramObject):
|
||||
|
||||
Attributes:
|
||||
text (:obj:`str`): Label text on the button.
|
||||
url (:obj:`str`): Optional. HTTP url to be opened when button is pressed.
|
||||
url (:obj:`str`): Optional. HTTP or tg:// url to be opened when button is pressed.
|
||||
login_url (:class:`telegram.LoginUrl`) Optional. An HTTP URL used to automatically
|
||||
authorize the user.
|
||||
authorize the user. Can be used as a replacement for the Telegram Login Widget.
|
||||
callback_data (:obj:`str`): Optional. Data to be sent in a callback query to the bot when
|
||||
button is pressed, UTF-8 1-64 bytes.
|
||||
switch_inline_query (:obj:`str`): Optional. Will prompt the user to select one of their
|
||||
chats, open that chat and insert the bot's username and the specified inline query in
|
||||
the input field.
|
||||
the input field. Can be empty, in which case just the bot’s username will be inserted.
|
||||
switch_inline_query_current_chat (:obj:`str`): Optional. Will insert the bot's username and
|
||||
the specified inline query in the current chat's input field.
|
||||
the specified inline query in the current chat's input field. Can be empty, in which
|
||||
case just the bot’s username will be inserted.
|
||||
callback_game (:class:`telegram.CallbackGame`): Optional. Description of the game that will
|
||||
be launched when the user presses the button.
|
||||
pay (:obj:`bool`): Optional. Specify True, to send a Pay button.
|
||||
|
||||
Args:
|
||||
text (:obj:`str`): Label text on the button.
|
||||
url (:obj:`str`): HTTP url to be opened when button is pressed.
|
||||
url (:obj:`str`): HTTP or tg:// url to be opened when button is pressed.
|
||||
login_url (:class:`telegram.LoginUrl`, optional) An HTTP URL used to automatically
|
||||
authorize the user.
|
||||
authorize the user. Can be used as a replacement for the Telegram Login Widget.
|
||||
callback_data (:obj:`str`, optional): Data to be sent in a callback query to the bot when
|
||||
button is pressed, 1-64 UTF-8 bytes.
|
||||
button is pressed, UTF-8 1-64 bytes.
|
||||
switch_inline_query (:obj:`str`, optional): If set, pressing the button will prompt the
|
||||
user to select one of their chats, open that chat and insert the bot's username and the
|
||||
specified inline query in the input field. Can be empty, in which case just the bot's
|
||||
|
||||
@@ -33,9 +33,9 @@ class InlineQueryResultAudio(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
audio_url (:obj:`str`): A valid URL for the audio file.
|
||||
title (:obj:`str`): Title.
|
||||
performer (:obj:`str`): Optional. Caption, 0-200 characters.
|
||||
audio_duration (:obj:`str`): Optional. Performer.
|
||||
caption (:obj:`str`): Optional. Audio duration in seconds.
|
||||
performer (:obj:`str`): Optional. Performer.
|
||||
audio_duration (:obj:`str`): Optional. Audio duration in seconds.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -48,9 +48,9 @@ class InlineQueryResultAudio(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
audio_url (:obj:`str`): A valid URL for the audio file.
|
||||
title (:obj:`str`): Title.
|
||||
performer (:obj:`str`, optional): Caption, 0-200 characters.
|
||||
audio_duration (:obj:`str`, optional): Performer.
|
||||
caption (:obj:`str`, optional): Audio duration in seconds.
|
||||
performer (:obj:`str`, optional): Performer.
|
||||
audio_duration (:obj:`str`, optional): Audio duration in seconds.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -26,13 +26,13 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to an mp3 audio file stored on the Telegram servers. By default, this audio
|
||||
file will be sent by the user. Alternatively, you can use :attr:`input_message_content` to
|
||||
send amessage with the specified content instead of the audio.
|
||||
send a message with the specified content instead of the audio.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): 'audio'.
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
audio_file_id (:obj:`str`): A valid file identifier for the audio file.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -44,7 +44,7 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
audio_file_id (:obj:`str`): A valid file identifier for the audio file.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -34,7 +34,8 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
|
||||
title (:obj:`str`): Title for the result.
|
||||
document_file_id (:obj:`str`): A valid file identifier for the file.
|
||||
description (:obj:`str`): Optional. Short description of the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the document to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -48,7 +49,8 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
|
||||
title (:obj:`str`): Title for the result.
|
||||
document_file_id (:obj:`str`): A valid file identifier for the file.
|
||||
description (:obj:`str`, optional): Short description of the result.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the document to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -34,7 +34,8 @@ class InlineQueryResultCachedGif(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
gif_file_id (:obj:`str`): A valid file identifier for the GIF file.
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the GIF file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -47,7 +48,8 @@ class InlineQueryResultCachedGif(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
gif_file_id (:obj:`str`): A valid file identifier for the GIF file.
|
||||
title (:obj:`str`, optional): Title for the result.caption (:obj:`str`, optional):
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the GIF file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -34,7 +34,8 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
mpeg4_file_id (:obj:`str`): A valid file identifier for the MP4 file.
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -47,7 +48,8 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
mpeg4_file_id (:obj:`str`): A valid file identifier for the MP4 file.
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the MPEG-4 file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -35,7 +35,8 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
|
||||
photo_file_id (:obj:`str`): A valid file identifier of the photo.
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
description (:obj:`str`): Optional. Short description of the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the photo to be sent, 0-1024 characters after
|
||||
entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -49,7 +50,8 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
|
||||
photo_file_id (:obj:`str`): A valid file identifier of the photo.
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
description (:obj:`str`, optional): Short description of the result.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the photo to be sent, 0-1024 characters after
|
||||
entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -37,8 +37,8 @@ class InlineQueryResultCachedSticker(InlineQueryResult):
|
||||
message to be sent instead of the sticker.
|
||||
|
||||
Args:
|
||||
id (:obj:`str`):
|
||||
sticker_file_id (:obj:`str`):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
sticker_file_id (:obj:`str`): A valid file identifier of the sticker.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
|
||||
@@ -35,7 +35,8 @@ 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 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.
|
||||
@@ -49,7 +50,8 @@ 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 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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -33,7 +33,8 @@ class InlineQueryResultDocument(InlineQueryResult):
|
||||
type (:obj:`str`): 'document'.
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
title (:obj:`str`): Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the document to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -52,7 +53,8 @@ class InlineQueryResultDocument(InlineQueryResult):
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
title (:obj:`str`): Title for the result.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the document to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -60,9 +62,9 @@ class InlineQueryResultDocument(InlineQueryResult):
|
||||
mime_type (:obj:`str`): Mime type of the content of the file, either "application/pdf"
|
||||
or "application/zip".
|
||||
description (:obj:`str`, optional): Short description of the result.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the file.
|
||||
thumb_url (:obj:`str`, optional): URL of the thumbnail (jpeg only) for the file.
|
||||
thumb_width (:obj:`int`, optional): Thumbnail width.
|
||||
|
||||
@@ -37,14 +37,15 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
gif_duration (:obj:`int`): Optional. Duration of the GIF.
|
||||
thumb_url (:obj:`str`): URL of the static thumbnail for the result (jpeg or gif).
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the GIF file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
message to be sent instead of the gif.
|
||||
message to be sent instead of the GIF animation.
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
@@ -53,15 +54,16 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
gif_height (:obj:`int`, optional): Height of the GIF.
|
||||
gif_duration (:obj:`int`, optional): Duration of the GIF
|
||||
thumb_url (:obj:`str`): URL of the static thumbnail for the result (jpeg or gif).
|
||||
title (:obj:`str`, optional): Title for the result.caption (:obj:`str`, optional):
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
caption (:obj:`str`, optional): Caption of the GIF file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the gif.
|
||||
message to be sent instead of the GIF animation.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
|
||||
@@ -38,14 +38,15 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
mpeg4_duration (:obj:`int`): Optional. Video duration.
|
||||
thumb_url (:obj:`str`): URL of the static thumbnail (jpeg or gif) for the result.
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
message to be sent instead of the MPEG-4 file.
|
||||
message to be sent instead of the video animation.
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
@@ -55,14 +56,15 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
mpeg4_duration (:obj:`int`, optional): Video duration.
|
||||
thumb_url (:obj:`str`): URL of the static thumbnail (jpeg or gif) for the result.
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the MPEG-4 file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the MPEG-4 file.
|
||||
message to be sent instead of the video animation.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
|
||||
@@ -38,7 +38,8 @@ class InlineQueryResultPhoto(InlineQueryResult):
|
||||
photo_height (:obj:`int`): Optional. Height of the photo.
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
description (:obj:`str`): Optional. Short description of the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the photo to be sent, 0-1024 characters after
|
||||
entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -56,7 +57,8 @@ class InlineQueryResultPhoto(InlineQueryResult):
|
||||
photo_height (:obj:`int`, optional): Height of the photo.
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
description (:obj:`str`, optional): Short description of the result.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the photo to be sent, 0-1024 characters after
|
||||
entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -29,6 +29,10 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
:attr:`input_message_content` to send a message with the specified content instead of
|
||||
the video.
|
||||
|
||||
Note:
|
||||
If an InlineQueryResultVideo message contains an embedded video (e.g., YouTube), you must
|
||||
replace its content using :attr:`input_message_content`.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): 'video'.
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
@@ -36,7 +40,8 @@ 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 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.
|
||||
@@ -47,7 +52,9 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
message to be sent instead of the video.
|
||||
message to be sent instead of the video. This field is required if
|
||||
InlineQueryResultVideo is used to send an HTML-page as a result
|
||||
(e.g., a YouTube video).
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
@@ -55,7 +62,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.
|
||||
@@ -66,7 +73,9 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the video.
|
||||
message to be sent instead of the video. This field is required if
|
||||
InlineQueryResultVideo is used to send an HTML-page as a result
|
||||
(e.g., a YouTube video).
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
|
||||
@@ -33,30 +33,30 @@ class InlineQueryResultVoice(InlineQueryResult):
|
||||
type (:obj:`str`): 'voice'.
|
||||
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.
|
||||
title (:obj:`str`): Recording title.
|
||||
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
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
voice_duration (:obj:`int`): Optional. Recording duration in seconds.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
message to be sent instead of the voice.
|
||||
message to be sent instead of the voice recording.
|
||||
|
||||
Args:
|
||||
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.
|
||||
title (:obj:`str`): Recording title.
|
||||
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
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
voice_duration (:obj:`int`, optional): Recording duration in seconds.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the voice.
|
||||
message to be sent instead of the voice recording.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,)
|
||||
+302
-64
@@ -23,7 +23,7 @@ from html import escape
|
||||
|
||||
from telegram import (Animation, Audio, Contact, Document, Chat, Location, PhotoSize, Sticker,
|
||||
TelegramObject, User, Video, Voice, Venue, MessageEntity, Game, Invoice,
|
||||
SuccessfulPayment, VideoNote, PassportData, Poll, InlineKeyboardMarkup)
|
||||
SuccessfulPayment, VideoNote, PassportData, Poll, InlineKeyboardMarkup, Dice)
|
||||
from telegram import ParseMode
|
||||
from telegram.utils.helpers import escape_markdown, to_timestamp, from_timestamp
|
||||
|
||||
@@ -106,6 +106,7 @@ class Message(TelegramObject):
|
||||
passport_data (:class:`telegram.PassportData`): Optional. Telegram Passport data.
|
||||
poll (:class:`telegram.Poll`): Optional. Message is a native poll,
|
||||
information about the poll.
|
||||
dice (:class:`telegram.Dice`): Optional. Message is a dice.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
|
||||
@@ -199,7 +200,7 @@ class Message(TelegramObject):
|
||||
smaller than 52 bits, so a signed 64 bit integer or double-precision float type are
|
||||
safe for storing this identifier.
|
||||
pinned_message (:class:`telegram.message`, optional): Specified message was pinned. Note
|
||||
that the Message object in this field will not contain further attr:`reply_to_message`
|
||||
that the Message object in this field will not contain further :attr:`reply_to_message`
|
||||
fields even if it is itself a reply.
|
||||
invoice (:class:`telegram.Invoice`, optional): Message is an invoice for a payment,
|
||||
information about the invoice.
|
||||
@@ -214,6 +215,7 @@ class Message(TelegramObject):
|
||||
passport_data (:class:`telegram.PassportData`, optional): Telegram Passport data.
|
||||
poll (:class:`telegram.Poll`, optional): Message is a native poll,
|
||||
information about the poll.
|
||||
dice (:class:`telegram.Dice`, optional): Message is a dice with random value from 1 to 6.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message. login_url buttons are represented as ordinary url buttons.
|
||||
default_quote (:obj:`bool`, optional): Default setting for the `quote` parameter of the
|
||||
@@ -229,7 +231,7 @@ class Message(TelegramObject):
|
||||
MESSAGE_TYPES = ['text', 'new_chat_members', 'left_chat_member', 'new_chat_title',
|
||||
'new_chat_photo', 'delete_chat_photo', 'group_chat_created',
|
||||
'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id',
|
||||
'migrate_from_chat_id', 'pinned_message',
|
||||
'migrate_from_chat_id', 'pinned_message', 'poll', 'dice',
|
||||
'passport_data'] + ATTACHMENT_TYPES
|
||||
|
||||
def __init__(self,
|
||||
@@ -282,6 +284,7 @@ class Message(TelegramObject):
|
||||
reply_markup=None,
|
||||
bot=None,
|
||||
default_quote=None,
|
||||
dice=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
self.message_id = int(message_id)
|
||||
@@ -331,6 +334,7 @@ class Message(TelegramObject):
|
||||
self.animation = animation
|
||||
self.passport_data = passport_data
|
||||
self.poll = poll
|
||||
self.dice = dice
|
||||
self.reply_markup = reply_markup
|
||||
self.bot = bot
|
||||
self.default_quote = default_quote
|
||||
@@ -404,6 +408,7 @@ class Message(TelegramObject):
|
||||
data['successful_payment'] = SuccessfulPayment.de_json(data.get('successful_payment'), bot)
|
||||
data['passport_data'] = PassportData.de_json(data.get('passport_data'), bot)
|
||||
data['poll'] = Poll.de_json(data.get('poll'), bot)
|
||||
data['dice'] = Dice.de_json(data.get('dice'), bot)
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
|
||||
return cls(bot=bot, **data)
|
||||
@@ -510,7 +515,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 +533,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::
|
||||
|
||||
@@ -784,6 +813,23 @@ class Message(TelegramObject):
|
||||
self._quote(kwargs)
|
||||
return self.bot.send_poll(self.chat_id, *args, **kwargs)
|
||||
|
||||
def reply_dice(self, *args, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
bot.send_dice(update.message.chat_id, *args, **kwargs)
|
||||
|
||||
Keyword Args:
|
||||
quote (:obj:`bool`, optional): If set to ``True``, the dice 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.
|
||||
|
||||
"""
|
||||
self._quote(kwargs)
|
||||
return self.bot.send_dice(self.chat_id, *args, **kwargs)
|
||||
|
||||
def forward(self, chat_id, *args, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
@@ -899,6 +945,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 +1074,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 +1084,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 +1213,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 +1225,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 +1346,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 +1374,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 +1402,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 +1430,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)
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -25,5 +25,7 @@ class ParseMode(object):
|
||||
|
||||
MARKDOWN = 'Markdown'
|
||||
""":obj:`str`: 'Markdown'"""
|
||||
MARKDOWN_V2 = 'MarkdownV2'
|
||||
""":obj:`str`: 'MarkdownV2'"""
|
||||
HTML = 'HTML'
|
||||
""":obj:`str`: 'HTML'"""
|
||||
|
||||
@@ -32,10 +32,12 @@ class PersonalDetails(TelegramObject):
|
||||
country_code (:obj:`str`): Citizenship (ISO 3166-1 alpha-2 country code).
|
||||
residence_country_code (:obj:`str`): Country of residence (ISO 3166-1 alpha-2 country
|
||||
code).
|
||||
first_name (:obj:`str`): First Name in the language of the user's country of residence.
|
||||
middle_name (:obj:`str`): Optional. Middle Name in the language of the user's country of
|
||||
first_name_native (:obj:`str`): First Name in the language of the user's country of
|
||||
residence.
|
||||
middle_name_native (:obj:`str`): Optional. Middle Name in the language of the user's
|
||||
country of residence.
|
||||
last_name_native (:obj:`str`): Last Name in the language of the user's country of
|
||||
residence.
|
||||
last_name (:obj:`str`): Last Name in the language of the user's country of residence.
|
||||
"""
|
||||
|
||||
def __init__(self, first_name, last_name, birth_date, gender, country_code,
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
Vendored
+1
-1
Submodule telegram/vendor/ptb_urllib3 updated: d2403a79fc...1954df0395
+1
-1
@@ -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.6'
|
||||
|
||||
@@ -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:
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
+19
-6
@@ -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
@@ -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)
|
||||
|
||||
+97
-21
@@ -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
|
||||
@@ -28,7 +26,7 @@ from future.utils import string_types
|
||||
|
||||
from telegram import (Bot, Update, ChatAction, TelegramError, User, InlineKeyboardMarkup,
|
||||
InlineKeyboardButton, InlineQueryResultArticle, InputTextMessageContent,
|
||||
ShippingOption, LabeledPrice, ChatPermissions, Poll,
|
||||
ShippingOption, LabeledPrice, ChatPermissions, Poll, BotCommand,
|
||||
InlineQueryResultDocument)
|
||||
from telegram.error import BadRequest, InvalidToken, NetworkError, RetryAfter
|
||||
from telegram.utils.helpers import from_timestamp, escape_markdown
|
||||
@@ -82,6 +80,7 @@ class TestBot(object):
|
||||
@pytest.mark.timeout(10)
|
||||
def test_get_me_and_properties(self, bot):
|
||||
get_me_bot = bot.get_me()
|
||||
commands = bot.get_my_commands()
|
||||
|
||||
assert isinstance(get_me_bot, User)
|
||||
assert get_me_bot.id == bot.id
|
||||
@@ -89,6 +88,11 @@ 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
|
||||
assert commands == bot.commands
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
@@ -172,20 +176,32 @@ class TestBot(object):
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_send_and_stop_poll(self, bot, super_group_id):
|
||||
@pytest.mark.parametrize('reply_markup', [
|
||||
None,
|
||||
InlineKeyboardMarkup.from_button(InlineKeyboardButton(text='text', callback_data='data')),
|
||||
InlineKeyboardMarkup.from_button(
|
||||
InlineKeyboardButton(text='text', callback_data='data')).to_dict()
|
||||
])
|
||||
def test_send_and_stop_poll(self, bot, super_group_id, reply_markup):
|
||||
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)
|
||||
# Since only the poll and not the complete message is returned, we can't check that the
|
||||
# reply_markup is correct. So we just test that sending doesn't give an error.
|
||||
poll = bot.stop_poll(chat_id=super_group_id, message_id=message.message_id,
|
||||
reply_markup=reply_markup, timeout=60)
|
||||
assert isinstance(poll, Poll)
|
||||
assert poll.is_closed
|
||||
assert poll.options[0].text == answers[0]
|
||||
@@ -195,18 +211,20 @@ 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)
|
||||
def test_send_game(self, bot, chat_id):
|
||||
game_short_name = 'test_game'
|
||||
message = bot.send_game(chat_id, game_short_name)
|
||||
def test_send_dice(self, bot, chat_id):
|
||||
message = bot.send_dice(chat_id)
|
||||
|
||||
assert message.game
|
||||
assert message.game.description == ('A no-op test game, for python-telegram-bot '
|
||||
'bot framework testing.')
|
||||
assert message.game.animation.file_id != ''
|
||||
assert message.game.photo[0].file_size == 851
|
||||
assert message.dice
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
@@ -350,6 +368,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 +521,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
|
||||
@@ -576,6 +602,18 @@ class TestBot(object):
|
||||
def test_delete_chat_sticker_set(self):
|
||||
pass
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_send_game(self, bot, chat_id):
|
||||
game_short_name = 'test_game'
|
||||
message = bot.send_game(chat_id, game_short_name)
|
||||
|
||||
assert message.game
|
||||
assert message.game.description == ('A no-op test game, for python-telegram-bot '
|
||||
'bot framework testing.')
|
||||
assert message.game.animation.file_id != ''
|
||||
assert message.game.photo[0].file_size == 851
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_set_game_score_1(self, bot, chat_id):
|
||||
@@ -775,17 +813,17 @@ class TestBot(object):
|
||||
assert isinstance(invite_link, string_types)
|
||||
assert invite_link != ''
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_delete_chat_photo(self, bot, channel_id):
|
||||
assert bot.delete_chat_photo(channel_id)
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_set_chat_photo(self, bot, channel_id):
|
||||
with open('tests/data/telegram_test_channel.jpg', 'rb') as f:
|
||||
assert bot.set_chat_photo(channel_id, f)
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_delete_chat_photo(self, bot, channel_id):
|
||||
assert bot.delete_chat_photo(channel_id)
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_set_chat_title(self, bot, channel_id):
|
||||
@@ -884,3 +922,41 @@ class TestBot(object):
|
||||
def test_send_message_default_quote(self, default_bot, chat_id):
|
||||
message = default_bot.send_message(chat_id, 'test')
|
||||
assert message.default_quote is True
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_set_and_get_my_commands(self, bot):
|
||||
commands = [
|
||||
BotCommand('cmd1', 'descr1'),
|
||||
BotCommand('cmd2', 'descr2'),
|
||||
]
|
||||
bot.set_my_commands([])
|
||||
assert bot.get_my_commands() == []
|
||||
assert bot.commands == []
|
||||
assert bot.set_my_commands(commands)
|
||||
|
||||
for bc in [bot.get_my_commands(), bot.commands]:
|
||||
assert len(bc) == 2
|
||||
assert bc[0].command == 'cmd1'
|
||||
assert bc[0].description == 'descr1'
|
||||
assert bc[1].command == 'cmd2'
|
||||
assert bc[1].description == 'descr2'
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_set_and_get_my_commands_strings(self, bot):
|
||||
commands = [
|
||||
['cmd1', 'descr1'],
|
||||
['cmd2', 'descr2'],
|
||||
]
|
||||
bot.set_my_commands([])
|
||||
assert bot.get_my_commands() == []
|
||||
assert bot.commands == []
|
||||
assert bot.set_my_commands(commands)
|
||||
|
||||
for bc in [bot.get_my_commands(), bot.commands]:
|
||||
assert len(bc) == 2
|
||||
assert bc[0].command == 'cmd1'
|
||||
assert bc[0].description == 'descr1'
|
||||
assert bc[1].command == 'cmd2'
|
||||
assert bc[1].description == 'descr2'
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/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 pytest
|
||||
|
||||
from telegram import BotCommand
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def bot_command():
|
||||
return BotCommand(command='start', description='A command')
|
||||
|
||||
|
||||
class TestBotCommand(object):
|
||||
command = 'start'
|
||||
description = 'A command'
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {'command': self.command, 'description': self.description}
|
||||
bot_command = BotCommand.de_json(json_dict, bot)
|
||||
|
||||
assert bot_command.command == self.command
|
||||
assert bot_command.description == self.description
|
||||
|
||||
assert BotCommand.de_json(None, bot) is None
|
||||
|
||||
def test_to_dict(self, bot_command):
|
||||
bot_command_dict = bot_command.to_dict()
|
||||
|
||||
assert isinstance(bot_command_dict, dict)
|
||||
assert bot_command_dict['command'] == bot_command.command
|
||||
assert bot_command_dict['description'] == bot_command.description
|
||||
+18
-18
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -379,6 +379,29 @@ class TestPrefixHandler(BaseTest):
|
||||
assert not is_match(handler, make_message_update('/test'))
|
||||
assert not mock_filter.tested
|
||||
|
||||
def test_edit_prefix(self):
|
||||
handler = self.make_default_handler()
|
||||
handler.prefix = ['?', '§']
|
||||
assert handler._commands == list(combinations(['?', '§'], self.COMMANDS))
|
||||
handler.prefix = '+'
|
||||
assert handler._commands == list(combinations(['+'], self.COMMANDS))
|
||||
|
||||
def test_edit_command(self):
|
||||
handler = self.make_default_handler()
|
||||
handler.command = 'foo'
|
||||
assert handler._commands == list(combinations(self.PREFIXES, ['foo']))
|
||||
|
||||
def test_basic_after_editing(self, dp, prefix, command):
|
||||
"""Test the basic expected response from a prefix handler"""
|
||||
handler = self.make_default_handler()
|
||||
dp.add_handler(handler)
|
||||
text = prefix + command
|
||||
|
||||
assert self.response(dp, make_message_update(text))
|
||||
handler.command = 'foo'
|
||||
text = prefix + 'foo'
|
||||
assert self.response(dp, make_message_update(text))
|
||||
|
||||
def test_context(self, cdp, prefix_message_update):
|
||||
handler = self.make_default_handler(self.callback_context)
|
||||
cdp.add_handler(handler)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user