mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-28 12:14:42 +00:00
Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1e7f4fae6f | |||
| dae5ab47a0 | |||
| a9d9b1d750 | |||
| 90496f70a5 | |||
| 940b42e048 | |||
| a582515766 | |||
| 3d42df3366 | |||
| 2c67a9833b | |||
| 5e8a961669 | |||
| a5ba64becb | |||
| 2a3169a22f | |||
| 894d8281ab | |||
| 2fdf48023b | |||
| 4e717a172b | |||
| 10c9ec2313 | |||
| 096a7c3593 | |||
| e9d9f01bd4 | |||
| 8b4b22cc89 | |||
| b294c92bad | |||
| 264de2b7c1 | |||
| ac64027580 | |||
| 34bdbc632a | |||
| bbcff96804 | |||
| 93449443b2 | |||
| 8cdb20a85a | |||
| 6fddb49af5 | |||
| b0aef0c718 | |||
| 88eccc6608 | |||
| 3d8771bbdf | |||
| 7152b5aaf9 | |||
| 98147fce32 | |||
| e54e9f2347 | |||
| 3545139dd7 | |||
| d0c27e2d46 | |||
| 3318239cf6 | |||
| aadb6df271 | |||
| 2cc9aac7dc | |||
| 1d007b1b60 | |||
| 3257148d13 | |||
| 805a798b50 | |||
| e60a42010b | |||
| ae88129f0f | |||
| 3812251dac | |||
| e1193425ca | |||
| ccf5e6c692 | |||
| 32dd415fb8 | |||
| f13aeaa2a1 | |||
| 4cd07361d1 | |||
| b38a1840b2 | |||
| fba3cc90d9 | |||
| 965ad17af8 | |||
| d5399de99b | |||
| 280306d1e9 | |||
| c84e21d8eb | |||
| 738e5a0784 | |||
| ec6dc7fa10 | |||
| b71196dad3 | |||
| 425912da4a | |||
| 2c92c356b8 | |||
| f379a34ccd | |||
| 4328eaefb2 | |||
| 79dc6edf25 | |||
| c7e9281068 | |||
| edad6e8b53 | |||
| 7eb7c30741 | |||
| 3ae14dda80 | |||
| ac60d057a5 | |||
| e492d5b97b | |||
| 3afb0ae6c3 | |||
| 179cf14bd8 | |||
| a1eabc0cae | |||
| 5e90231f4e | |||
| 2cb6377dab | |||
| 316d046628 | |||
| 5e0e4c01ff | |||
| 24546bda67 | |||
| d70577b9cf | |||
| 3fc57479f3 | |||
| e11efa2e5b | |||
| d84551134b | |||
| cebd2d6a86 | |||
| 725c21b88d | |||
| 9d005d5124 | |||
| 2cde878d1e | |||
| 984bea16d1 | |||
| 474ff8ae41 | |||
| 2ed4cbd26d | |||
| 7944805627 | |||
| b5891a6a61 | |||
| df813c46e1 | |||
| 36a74da4f4 | |||
| 0c10c537f7 | |||
| 26ce9bb343 |
@@ -25,7 +25,7 @@ Setting things up
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo pip install -r requirements.txt -r requirements-dev.txt
|
||||
$ pip install -r requirements.txt -r requirements-dev.txt
|
||||
|
||||
|
||||
5. Install pre-commit hooks:
|
||||
@@ -66,6 +66,26 @@ Here's how to make a one-off code change.
|
||||
- You can refer to relevant issues in the commit message by writing, e.g., "#105".
|
||||
|
||||
- Your code should adhere to the `PEP 8 Style Guide`_, with the exception that we have a maximum line length of 99.
|
||||
|
||||
- Document your code. This project uses `sphinx`_ to generate static HTML docs. To build them, first make sure you have the required dependencies:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install -r docs/requirements-docs.txt
|
||||
|
||||
then run the following from the PTB root directory:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ make -C docs html
|
||||
|
||||
or, if you don't have ``make`` available (e.g. on Windows):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sphinx-build docs/source docs/build/html
|
||||
|
||||
Once the process terminates, you can view the built documentation by opening ``docs/build/html/index.html`` with a browser.
|
||||
|
||||
- For consistency, please conform to `Google Python Style Guide`_ and `Google Python Style Docstrings`_. In addition, code should be formatted consistently with other code around it.
|
||||
|
||||
@@ -217,6 +237,7 @@ break the API classes. For example:
|
||||
.. _`issue tracker`: https://github.com/python-telegram-bot/python-telegram-bot/issues
|
||||
.. _`developers' mailing list`: mailto:devs@python-telegram-bot.org
|
||||
.. _`PEP 8 Style Guide`: https://www.python.org/dev/peps/pep-0008/
|
||||
.. _`sphinx`: http://sphinx-doc.org
|
||||
.. _`Google Python Style Guide`: https://google-styleguide.googlecode.com/svn/trunk/pyguide.html
|
||||
.. _`Google Python Style Docstrings`: http://sphinx-doc.org/latest/ext/example_google.html
|
||||
.. _AUTHORS.rst: ../AUTHORS.rst
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]"
|
||||
labels: 'bug :bug:'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Thanks for reporting issues of python-telegram-bot!
|
||||
|
||||
Use this template to notify us if you found a bug, or if you want to request a new feature.
|
||||
If you're looking for help with programming your bot using our library, feel free to ask your
|
||||
questions in out telegram group at: https://t.me/pythontelegrambotgroup
|
||||
Use this template to notify us if you found a bug.
|
||||
|
||||
To make it easier for us to help you please enter detailed information below.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[FEATURE]"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### Is your feature request related to a problem? Please describe.
|
||||
A clear and concise description of what the problem is.
|
||||
Ex. *I want to do X, but there is no way to do it.*
|
||||
|
||||
#### Describe the solution you'd like
|
||||
A clear and concise description of what you want to happen.
|
||||
Ex. *I think it would be nice if you would add feature Y so it will make it easier.*
|
||||
|
||||
#### Describe alternatives you've considered
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
Ex. *I considered Z, but that didn't work because...*
|
||||
|
||||
#### Additional context
|
||||
Add any other context or screenshots about the feature request here.
|
||||
Ex. *Here's a photo of my cat!*
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: Question
|
||||
about: Get help with errors or general questions
|
||||
title: "[QUESTION]"
|
||||
labels: 'question :question:'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Hey there, you have a question? We are happy to answer. Please make sure no similar question was opened already.
|
||||
|
||||
The following template is a suggestion how you can report an issue you run into whilst using our library. If you just want to ask a question, feel free to delete everything; just make sure you have a describing title :)
|
||||
|
||||
Please mind that there is also a users' Telegram group at https://t.me/pythontelegrambotgroup for questions about the library. Questions asked there might be answered quicker than here. In case you are unable to join our group due to Telegram restrictions, you can use our IRC channel at https://webchat.freenode.net/?channels=##python-telegram-bot to participate in the group.
|
||||
-->
|
||||
|
||||
### Issue I am facing
|
||||
Please describe the issue here in as much detail as possible
|
||||
|
||||
### Traceback to the issue
|
||||
```
|
||||
put it here
|
||||
```
|
||||
|
||||
### Related part of your code
|
||||
```python
|
||||
put it here
|
||||
```
|
||||
@@ -0,0 +1,14 @@
|
||||
name: Warning maintainers
|
||||
on:
|
||||
pull_request:
|
||||
paths: examples/**
|
||||
jobs:
|
||||
job:
|
||||
runs-on: ubuntu-latest
|
||||
name: about example change
|
||||
steps:
|
||||
- name: running the check
|
||||
uses: Poolitzer/notifier-action@master
|
||||
with:
|
||||
notify-message: Hey there. Relax, I am just a little warning for the maintainers to release directly after merging your PR, otherwise we have broken examples and people might get confused :)
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,116 @@
|
||||
name: Testing your PR
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
schedule:
|
||||
- cron: 7 3 * * *
|
||||
|
||||
jobs:
|
||||
pytest:
|
||||
name: pytest
|
||||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [2.7, 3.5, 3.6, 3.7]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
python-version: 3.7
|
||||
test-build: True
|
||||
- os: windows-latest
|
||||
python-version: 3.7
|
||||
test-build: True
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Initialize vendored libs
|
||||
run:
|
||||
git submodule update --init --recursive
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -W ignore -m pip install --upgrade pip
|
||||
python -W ignore -m pip install -U codecov pytest-cov
|
||||
python -W ignore -m pip install -r requirements.txt
|
||||
python -W ignore -m pip install -r requirements-dev.txt
|
||||
|
||||
- name: Test with pytest
|
||||
run: |
|
||||
pytest -v -m nocoverage
|
||||
nocov_exit=$?
|
||||
pytest -v -m "not nocoverage" --cov
|
||||
cov_exit=$?
|
||||
global_exit=$(( nocov_exit > cov_exit ? nocov_exit : cov_exit ))
|
||||
exit ${global_exit}
|
||||
env:
|
||||
JOB_INDEX: ${{ strategy.job-index }}
|
||||
BOTS: W3sidG9rZW4iOiAiNjk2MTg4NzMyOkFBR1Z3RUtmSEhsTmpzY3hFRE5LQXdraEdzdFpfa28xbUMwIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WldGaU1UUmxNbVF5TnpNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMi43IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzI3X2JvdCJ9LCB7InRva2VuIjogIjY3MTQ2ODg4NjpBQUdQR2ZjaVJJQlVORmU4MjR1SVZkcTdKZTNfWW5BVE5HdyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpHWXdPVGxrTXpNeE4yWTIiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNCIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zNF9ib3QifSwgeyJ0b2tlbiI6ICI2MjkzMjY1Mzg6QUFGUnJaSnJCN29CM211ekdzR0pYVXZHRTVDUXpNNUNVNG8iLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpNbU01WVdKaFl6a3hNMlUxIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgQ1B5dGhvbiAzLjUiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX2NweXRob25fMzVfYm90In0sIHsidG9rZW4iOiAiNjQwMjA4OTQzOkFBRmhCalFwOXFtM1JUeFN6VXBZekJRakNsZS1Kano1aGNrIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WXpoa1pUZzFOamMxWXpWbCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMy42IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzM2X2JvdCJ9LCB7InRva2VuIjogIjY5NTEwNDA4ODpBQUhmenlsSU9qU0lJUy1lT25JMjB5MkUyMEhvZEhzZnotMCIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOk9HUTFNRGd3WmpJd1pqRmwiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNyIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zN19ib3QifSwgeyJ0b2tlbiI6ICI2OTE0MjM1NTQ6QUFGOFdrakNaYm5IcVBfaTZHaFRZaXJGRWxackdhWU9oWDAiLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpZamM1TlRoaU1tUXlNV1ZoIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgUHlQeSAyLjciLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX3B5cHlfMjdfYm90In0sIHsidG9rZW4iOiAiNjg0MzM5OTg0OkFBRk1nRUVqcDAxcjVyQjAwN3lDZFZOc2c4QWxOc2FVLWNjIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TVRBek1UWTNNR1V5TmpnMCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIFB5UHkgMy41IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19weXB5XzM1X2JvdCJ9LCB7InRva2VuIjogIjY5MDA5MTM0NzpBQUZMbVI1cEFCNVljcGVfbU9oN3pNNEpGQk9oMHozVDBUbyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpEaGxOekU1TURrd1lXSmkiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIEFwcFZleW9yIHVzaW5nIENQeXRob24gMy40IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX2FwcHZleW9yX2NweXRob25fMzRfYm90In0sIHsidG9rZW4iOiAiNjk0MzA4MDUyOkFBRUIyX3NvbkNrNTVMWTlCRzlBTy1IOGp4aVBTNTVvb0JBIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WW1aaVlXWm1NakpoWkdNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gQXBwVmV5b3IgdXNpbmcgQ1B5dGhvbiAyLjciLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfYXBwdmV5b3JfY3B5dGhvbl8yN19ib3QifV0=
|
||||
TEST_BUILD: ${{ matrix.test-build }}
|
||||
TEST_PRE_COMMIT: ${{ matrix.test-pre-commit }}
|
||||
shell: bash --noprofile --norc {0}
|
||||
|
||||
- name: Submit coverage
|
||||
run: |
|
||||
if [ "$CODECOV_TOKEN" != "" ]; then
|
||||
codecov -F github -t $CODECOV_TOKEN --name "${{ matrix.os }}-${{ matrix.python-version }}"
|
||||
fi
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
shell: bash
|
||||
test_official:
|
||||
name: test-official
|
||||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.7]
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Initialize vendored libs
|
||||
run:
|
||||
git submodule update --init --recursive
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -W ignore -m pip install --upgrade pip
|
||||
python -W ignore -m pip install -r requirements.txt
|
||||
python -W ignore -m pip install -r requirements-dev.txt
|
||||
- name: Compare to official api
|
||||
run: |
|
||||
pytest -v tests/test_official.py
|
||||
exit $?
|
||||
env:
|
||||
TEST_OFFICIAL: "true"
|
||||
shell: bash --noprofile --norc {0}
|
||||
test_pre_commit:
|
||||
name: test-pre-commit
|
||||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.7]
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Initialize vendored libs
|
||||
run:
|
||||
git submodule update --init --recursive
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -W ignore -m pip install --upgrade pip
|
||||
python -W ignore -m pip install -r requirements.txt
|
||||
python -W ignore -m pip install -r requirements-dev.txt
|
||||
- name: Run pre-commit tests
|
||||
run: pre-commit run --all-files
|
||||
@@ -23,6 +23,11 @@ var/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
.env
|
||||
.pybuild
|
||||
debian/tmp
|
||||
debian/python3-telegram
|
||||
debian/python3-telegram-doc
|
||||
debian/.debhelper
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
@@ -64,6 +69,7 @@ target/
|
||||
*.sublime*
|
||||
|
||||
# unitests files
|
||||
game.gif
|
||||
telegram.mp3
|
||||
telegram.mp4
|
||||
telegram2.mp4
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
repos:
|
||||
- repo: git://github.com/python-telegram-bot/mirrors-yapf
|
||||
sha: master
|
||||
sha: 5769e088ef6e0a0d1eb63bd6d0c1fe9f3606d6c8
|
||||
hooks:
|
||||
- id: yapf
|
||||
files: ^(telegram|tests)/.*\.py$
|
||||
args:
|
||||
- --diff
|
||||
- repo: git://github.com/pre-commit/pre-commit-hooks
|
||||
sha: v2.0.0
|
||||
sha: 0b70e285e369bcb24b57b74929490ea7be9c4b19
|
||||
hooks:
|
||||
- id: flake8
|
||||
exclude: ^(setup.py|docs/source/conf.py)$
|
||||
args:
|
||||
- --ignore=W605,W503
|
||||
- repo: git://github.com/pre-commit/mirrors-pylint
|
||||
sha: v2.2.2
|
||||
sha: 9d8dcbc2b86c796275680f239c1e90dcd50bd398
|
||||
hooks:
|
||||
- id: pylint
|
||||
files: ^telegram/.*\.py$
|
||||
|
||||
+11
-5
@@ -2,16 +2,20 @@ language: python
|
||||
matrix:
|
||||
include:
|
||||
- python: 2.7
|
||||
- python: 3.4
|
||||
- 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
|
||||
@@ -33,16 +37,18 @@ before_cache:
|
||||
- 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; fi
|
||||
- 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; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != 'pypy'* ]]; then pip install ujson; else true; fi
|
||||
|
||||
script:
|
||||
- pytest -v -m nocoverage
|
||||
- pytest -v -m "not nocoverage" --cov
|
||||
- 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
|
||||
|
||||
+7
-1
@@ -4,7 +4,7 @@ Credits
|
||||
``python-telegram-bot`` was originally created by
|
||||
`Leandro Toledo <https://github.com/leandrotoledo>`_ and is now maintained by
|
||||
`Jannes Höke <https://github.com/jh0ker>`_ (`@jh0ker <https://t.me/jh0ker>`_ on Telegram),
|
||||
`Noam Meltzer <https://github.com/tsnoam>`_, `Pieter Schutz <https://github.com/eldinnie>`_ and `Jasmin Bom <https://github.com/jsmnbom>`_.
|
||||
`Noam Meltzer <https://github.com/tsnoam>`_, `Pieter Schutz <https://github.com/eldinnie>`_, `Jasmin Bom <https://github.com/jsmnbom>`_ and `Hinrich Mahler <https://github.com/Bibo-Joshi>`_.
|
||||
|
||||
We're vendoring urllib3 as part of ``python-telegram-bot`` which is distributed under the MIT
|
||||
license. For more info, full credits & license terms, the sources can be found here:
|
||||
@@ -16,6 +16,7 @@ Contributors
|
||||
The following wonderful people contributed directly or indirectly to this project:
|
||||
|
||||
- `Alateas <https://github.com/alateas>`_
|
||||
- `Ales Dokshanin <https://github.com/alesdokshanin>`_
|
||||
- `Ambro17 <https://github.com/Ambro17>`_
|
||||
- `Anton Tagunov <https://github.com/anton-tagunov>`_
|
||||
- `Avanatiker <https://github.com/Avanatiker>`_
|
||||
@@ -49,6 +50,7 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||
- `Kirill Vasin <https://github.com/vasinkd>`_
|
||||
- `Kjwon15 <https://github.com/kjwon15>`_
|
||||
- `Li-aung Yip <https://github.com/LiaungYip>`_
|
||||
- `Loo Zheng Yuan <https://github.com/loozhengyuan>`_
|
||||
- `macrojames <https://github.com/macrojames>`_
|
||||
- `Michael Elovskikh <https://github.com/wronglink>`_
|
||||
- `Mischa Krüger <https://github.com/Makman2>`_
|
||||
@@ -63,11 +65,14 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||
- `Patrick Hofmann <https://github.com/PH89>`_
|
||||
- `Paul Larsen <https://github.com/PaulSonOfLars>`_
|
||||
- `Pieter Schutz <https://github.com/eldinnie>`_
|
||||
- `Poolitzer <https://github.com/Poolitzer>`_
|
||||
- `Rahiel Kasim <https://github.com/rahiel>`_
|
||||
- `Sahil Sharma <https://github.com/sahilsharma811>`_
|
||||
- `Sascha <https://github.com/saschalalala>`_
|
||||
- `Shelomentsev D <https://github.com/shelomentsevd>`_
|
||||
- `Simon Schürrle <https://github.com/SitiSchu>`_
|
||||
- `sooyhwang <https://github.com/sooyhwang>`_
|
||||
- `syntx <https://github.com/syntx>`_
|
||||
- `thodnev <https://github.com/thodnev>`_
|
||||
- `Trainer Jono <https://github.com/Tr-Jono>`_
|
||||
- `Valentijn <https://github.com/Faalentijn>`_
|
||||
@@ -75,5 +80,6 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||
- `Vorobjev Simon <https://github.com/simonvorobjev>`_
|
||||
- `Wagner Macedo <https://github.com/wagnerluis1982>`_
|
||||
- `wjt <https://github.com/wjt>`_
|
||||
- `zeshuaro <https://github.com/zeshuaro>`_
|
||||
|
||||
Please add yourself here alphabetically when you submit your first pull request.
|
||||
|
||||
+197
-36
@@ -1,23 +1,138 @@
|
||||
=======
|
||||
Changes
|
||||
=======
|
||||
=========
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Version 12.0.0b1
|
||||
================
|
||||
*Released 2019-02-13*
|
||||
Version 12.3.0
|
||||
==============
|
||||
*Released 2020-01-11*
|
||||
|
||||
First beta release ever.
|
||||
It has been so long since last release that we would like to test the impact before a final release.
|
||||
**New features:**
|
||||
|
||||
*We do NOT recommend using this beta release in production.*
|
||||
- `Filters.caption` allows only messages with caption (`#1631`_).
|
||||
- Filter for exact messages/captions with new capability of `Filters.text` and `Filters.caption`. Especially useful in combination with ReplyKeyboardMarkup. (`#1631`_).
|
||||
|
||||
**Major changes:**
|
||||
|
||||
- Fix inconsistent handling of naive datetimes (`#1506`_).
|
||||
|
||||
**Minor changes, CI improvments or bug fixes:**
|
||||
|
||||
- Documentation fixes (`#1558`_, `#1569`_, `#1579`_, `#1572`_, `#1566`_, `#1577`_, `#1656`_).
|
||||
- Add mutex protection on `ConversationHandler` (`#1533`_).
|
||||
- Add `MAX_PHOTOSIZE_UPLOAD` constant (`#1560`_).
|
||||
- Add args and kwargs to `Message.forward()` (`#1574`_).
|
||||
- Transfer to GitHub Actions CI (`#1555`_, `#1556`_, `#1605`_, `#1606`_, `#1607`_, `#1612`_, `#1615`_, `#1645`_).
|
||||
- Fix deprecation warning with Py3.8 by vendored urllib3 (`#1618`_).
|
||||
- Simplify assignements for optional arguments (`#1600`_)
|
||||
- Allow private groups for `Message.link` (`#1619`_).
|
||||
- Fix wrong signature call for `ConversationHandler.TIMEOUT` handlers (`#1653`_).
|
||||
|
||||
.. _`#1631`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1558
|
||||
.. _`#1506`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1506
|
||||
.. _`#1558`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1558
|
||||
.. _`#1569`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1569
|
||||
.. _`#1579`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1579
|
||||
.. _`#1572`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1572
|
||||
.. _`#1566`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1566
|
||||
.. _`#1577`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1577
|
||||
.. _`#1533`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1533
|
||||
.. _`#1560`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1560
|
||||
.. _`#1574`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1574
|
||||
.. _`#1555`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1555
|
||||
.. _`#1556`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1556
|
||||
.. _`#1605`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1605
|
||||
.. _`#1606`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1606
|
||||
.. _`#1607`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1607
|
||||
.. _`#1612`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1612
|
||||
.. _`#1615`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1615
|
||||
.. _`#1618`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1618
|
||||
.. _`#1600`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1600
|
||||
.. _`#1619`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1619
|
||||
.. _`#1653`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1653
|
||||
.. _`#1656`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1656
|
||||
.. _`#1645`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1645
|
||||
|
||||
Version 12.2.0
|
||||
==============
|
||||
*Released 2019-10-14*
|
||||
|
||||
**New features:**
|
||||
|
||||
- Nested ConversationHandlers (`#1512`_).
|
||||
|
||||
**Minor changes, CI improvments or bug fixes:**
|
||||
|
||||
- Fix CI failures due to non-backward compat attrs depndency (`#1540`_).
|
||||
- travis.yaml: TEST_OFFICIAL removed from allowed_failures.
|
||||
- Fix typos in examples (`#1537`_).
|
||||
- Fix Bot.to_dict to use proper first_name (`#1525`_).
|
||||
- Refactor ``test_commandhandler.py`` (`#1408`_).
|
||||
- Add Python 3.8 (RC version) to Travis testing matrix (`#1543`_).
|
||||
- test_bot.py: Add to_dict test (`#1544`_).
|
||||
- Flake config moved into setup.cfg (`#1546`_).
|
||||
|
||||
.. _`#1512`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1512
|
||||
.. _`#1540`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1540
|
||||
.. _`#1537`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1537
|
||||
.. _`#1525`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1525
|
||||
.. _`#1408`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1408
|
||||
.. _`#1543`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1543
|
||||
.. _`#1544`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1544
|
||||
.. _`#1546`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1546
|
||||
|
||||
Version 12.1.1
|
||||
==============
|
||||
*Released 2019-09-18*
|
||||
|
||||
**Hot fix release**
|
||||
|
||||
Fixed regression in the vendored urllib3 (`#1517`_).
|
||||
|
||||
.. _`#1517`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1517
|
||||
|
||||
Version 12.1.0
|
||||
================
|
||||
*Released 2019-09-13*
|
||||
|
||||
**Major changes:**
|
||||
|
||||
- Bot API 4.4 support (`#1464`_, `#1510`_)
|
||||
- Add `get_file` method to `Animation` & `ChatPhoto`. Add, `get_small_file` & `get_big_file`
|
||||
methods to `ChatPhoto` (`#1489`_)
|
||||
- Tools for deep linking (`#1049`_)
|
||||
|
||||
**Minor changes and/or bug fixes:**
|
||||
|
||||
- Documentation fixes (`#1500`_, `#1499`_)
|
||||
- Improved examples (`#1502`_)
|
||||
|
||||
.. _`#1464`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1464
|
||||
.. _`#1502`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1502
|
||||
.. _`#1499`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1499
|
||||
.. _`#1500`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1500
|
||||
.. _`#1049`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1049
|
||||
.. _`#1489`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1489
|
||||
.. _`#1510`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1510
|
||||
|
||||
Version 12.0.0
|
||||
================
|
||||
*Released 2019-08-29*
|
||||
|
||||
Well... This felt like decades. But here we are with a new release.
|
||||
|
||||
Expect minor releases soon (mainly complete Bot API 4.4 support)
|
||||
|
||||
**Major and/or breaking changes:**
|
||||
|
||||
- Context based callbacks
|
||||
- Persistence
|
||||
- PrefixHandler added (Handler overhaul)
|
||||
- Deprecation of RegexHandler and edited_messages, channel_post, etc. arguments (Filter overhaul)
|
||||
- Various ConversationHandler changes and fixes
|
||||
- Bot API 4.1, 4.2, 4.3 support
|
||||
- Python 3.4 is no longer supported
|
||||
- Error Handler now handles all types of exceptions (`#1485`_)
|
||||
- Return UTC from from_timestamp() (`#1485`_)
|
||||
|
||||
**See the wiki page at https://git.io/fxJuV for a detailed guide on how to migrate from version 11 to version 12.**
|
||||
|
||||
@@ -64,6 +179,7 @@ ConversationHandler
|
||||
- Use warnings.warn for ConversationHandler warnings (`#1343`_)
|
||||
- Fix unresolvable promises (`#1270`_)
|
||||
|
||||
|
||||
Bug fixes & improvements
|
||||
------------------------
|
||||
|
||||
@@ -82,6 +198,22 @@ Bug fixes & improvements
|
||||
- Allow SOCKSConnection to parse username and password from URL (`#1211`_)
|
||||
- Fix for arguments in passport/data.py (`#1213`_)
|
||||
- Improve message entity parsing by adding text_mention (`#1206`_)
|
||||
- Documentation fixes (`#1348`_, `#1397`_, `#1436`_)
|
||||
- Merged filters short-circuit (`#1350`_)
|
||||
- Fix webhook listen with tornado (`#1383`_)
|
||||
- Call task_done() on update queue after update processing finished (`#1428`_)
|
||||
- Fix send_location() - latitude may be 0 (`#1437`_)
|
||||
- Make MessageEntity objects comparable (`#1465`_)
|
||||
- Add prefix to thread names (`#1358`_)
|
||||
|
||||
Buf fixes since v12.0.0b1
|
||||
-------------------------
|
||||
|
||||
- Fix setting bot on ShippingQuery (`#1355`_)
|
||||
- Fix _trigger_timeout() missing 1 required positional argument: 'job' (`#1367`_)
|
||||
- Add missing message.text check in PrefixHandler check_update (`#1375`_)
|
||||
- Make updates persist even on DispatcherHandlerStop (`#1463`_)
|
||||
- Dispatcher force updating persistence object's chat data attribute(`#1462`_)
|
||||
|
||||
.. _`#1100`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1100
|
||||
.. _`#1283`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1283
|
||||
@@ -110,6 +242,22 @@ Bug fixes & improvements
|
||||
.. _`#1319`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1319
|
||||
.. _`#1343`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1343
|
||||
.. _`#1270`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1270
|
||||
.. _`#1348`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1348
|
||||
.. _`#1350`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1350
|
||||
.. _`#1383`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1383
|
||||
.. _`#1397`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1397
|
||||
.. _`#1428`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1428
|
||||
.. _`#1436`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1436
|
||||
.. _`#1437`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1437
|
||||
.. _`#1465`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1465
|
||||
.. _`#1358`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1358
|
||||
.. _`#1355`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1355
|
||||
.. _`#1367`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1367
|
||||
.. _`#1375`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1375
|
||||
.. _`#1463`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1463
|
||||
.. _`#1462`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1462
|
||||
.. _`#1483`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1483
|
||||
.. _`#1485`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1485
|
||||
|
||||
Internal improvements
|
||||
---------------------
|
||||
@@ -118,12 +266,11 @@ Internal improvements
|
||||
- Use multiple bots for CI to improve testing times significantly.
|
||||
- Allow pypy to fail in CI.
|
||||
- Remove the last CamelCase CheckUpdate methods from the handlers we missed earlier.
|
||||
- test_official is now executed in a different job
|
||||
|
||||
Pre-2019 (up and including to version 11.1.0)
|
||||
=============================================
|
||||
|
||||
**2018-09-01**
|
||||
*Released 11.1.0*
|
||||
Version 11.1.0
|
||||
==============
|
||||
*Released 2018-09-01*
|
||||
|
||||
Fixes and updates for Telegram Passport: (`#1198`_)
|
||||
|
||||
@@ -137,8 +284,9 @@ Fixes and updates for Telegram Passport: (`#1198`_)
|
||||
|
||||
.. _`#1198`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1198
|
||||
|
||||
**2018-08-29**
|
||||
*Released 11.0.0*
|
||||
Version 11.0.0
|
||||
==============
|
||||
*Released 2018-08-29*
|
||||
|
||||
Fully support Bot API version 4.0!
|
||||
(also some bugfixes :))
|
||||
@@ -193,8 +341,9 @@ Non Bot API 4.0 changes:
|
||||
.. _`#1184`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1184
|
||||
.. _`our telegram passport wiki page`: https://git.io/fAvYd
|
||||
|
||||
**2018-05-02**
|
||||
*Released 10.1.0*
|
||||
Version 10.1.0
|
||||
==============
|
||||
*Released 2018-05-02*
|
||||
|
||||
Fixes changing previous behaviour:
|
||||
|
||||
@@ -220,8 +369,9 @@ Fixes:
|
||||
.. _`#1096`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1096
|
||||
.. _`#1099`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1099
|
||||
|
||||
**2018-04-17**
|
||||
*Released 10.0.2*
|
||||
Version 10.0.2
|
||||
==============
|
||||
*Released 2018-04-17*
|
||||
|
||||
Important fix:
|
||||
|
||||
@@ -252,8 +402,9 @@ Fixes:
|
||||
.. _`#1076`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1076
|
||||
.. _`#1071`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1071
|
||||
|
||||
**2018-03-05**
|
||||
*Released 10.0.1*
|
||||
Version 10.0.1
|
||||
==============
|
||||
*Released 2018-03-05*
|
||||
|
||||
Fixes:
|
||||
|
||||
@@ -263,8 +414,9 @@ Fixes:
|
||||
.. _`#1032`: https://github.com/python-telegram-bot/python-telegram-bot/pull/826
|
||||
.. _`#912`: https://github.com/python-telegram-bot/python-telegram-bot/pull/826
|
||||
|
||||
**2018-03-02**
|
||||
*Released 10.0.0*
|
||||
Version 10.0.0
|
||||
==============
|
||||
*Released 2018-03-02*
|
||||
|
||||
Non backward compatabile changes and changed defaults
|
||||
|
||||
@@ -329,8 +481,9 @@ Changes
|
||||
.. _`#1019`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1019
|
||||
.. _`#1020`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1020
|
||||
|
||||
**2017-12-08**
|
||||
*Released 9.0.0*
|
||||
Version 9.0.0
|
||||
=============
|
||||
*Released 2017-12-08*
|
||||
|
||||
Breaking changes (possibly)
|
||||
|
||||
@@ -355,15 +508,17 @@ Changes
|
||||
.. _`#694`: https://github.com/python-telegram-bot/python-telegram-bot/pull/694
|
||||
.. _`#870`: https://github.com/python-telegram-bot/python-telegram-bot/pull/870
|
||||
|
||||
**2017-10-15**
|
||||
*Released 8.1.1*
|
||||
Version 8.1.1
|
||||
=============
|
||||
*Released 2017-10-15*
|
||||
|
||||
- Fix Commandhandler crashing on single character messages (PR `#873`_).
|
||||
|
||||
.. _`#873`: https://github.com/python-telegram-bot/python-telegram-bot/pull/871
|
||||
|
||||
**2017-10-14**
|
||||
*Released 8.1.0*
|
||||
Version 8.1.0
|
||||
=============
|
||||
*Released 2017-10-14*
|
||||
|
||||
New features
|
||||
- Support Bot API 3.4 (PR `#865`_).
|
||||
@@ -380,8 +535,9 @@ Changes
|
||||
.. _`#865`: https://github.com/python-telegram-bot/python-telegram-bot/pull/865
|
||||
.. _`#869`: https://github.com/python-telegram-bot/python-telegram-bot/pull/869
|
||||
|
||||
**2017-09-01**
|
||||
*Released 8.0.0*
|
||||
Version 8.0.0
|
||||
=============
|
||||
*Released 2017-09-01*
|
||||
|
||||
New features
|
||||
|
||||
@@ -422,14 +578,16 @@ Changes
|
||||
.. _`#793`: https://github.com/python-telegram-bot/python-telegram-bot/pull/793
|
||||
.. _`#810`: https://github.com/python-telegram-bot/python-telegram-bot/pull/810
|
||||
|
||||
**2017-07-28**
|
||||
*Released 7.0.1*
|
||||
Version 7.0.1
|
||||
===============
|
||||
*Released 2017-07-28*
|
||||
|
||||
- Fix TypeError exception in RegexHandler (PR #751).
|
||||
- Small documentation fix (PR #749).
|
||||
|
||||
**2017-07-25**
|
||||
*Released 7.0.0*
|
||||
Version 7.0.0
|
||||
=============
|
||||
*Released 2017-07-25*
|
||||
|
||||
- Fully support Bot API 3.2.
|
||||
- New filters for handling messages from specific chat/user id (PR #677).
|
||||
@@ -447,6 +605,9 @@ Changes
|
||||
- Improved documentation.
|
||||
- Improved unitests.
|
||||
|
||||
Pre-version 7.0
|
||||
===============
|
||||
|
||||
**2017-06-18**
|
||||
|
||||
*Released 6.1.0*
|
||||
|
||||
+8
-26
@@ -29,14 +29,9 @@ We have a vibrant community of developers helping each other in our `Telegram gr
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0.html
|
||||
:alt: LGPLv3 License
|
||||
|
||||
.. image:: https://travis-ci.org/python-telegram-bot/python-telegram-bot.svg?branch=master
|
||||
:target: https://travis-ci.org/python-telegram-bot/python-telegram-bot
|
||||
:alt: Travis CI Status
|
||||
|
||||
.. image:: https://img.shields.io/appveyor/ci/python-telegram-bot/python-telegram-bot/master.svg?logo=appveyor
|
||||
:target: https://ci.appveyor.com/project/python-telegram-bot/python-telegram-bot
|
||||
:alt: AppVeyor CI Status
|
||||
|
||||
.. image:: https://github.com/python-telegram-bot/python-telegram-bot/workflows/Testing%20your%20PR/badge.svg?event=schedule
|
||||
:target: https://github.com/python-telegram-bot/python-telegram-bot/
|
||||
:alt: Github Actions workflow
|
||||
|
||||
.. image:: https://codecov.io/gh/python-telegram-bot/python-telegram-bot/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gh/python-telegram-bot/python-telegram-bot
|
||||
@@ -104,21 +99,7 @@ All types and methods of the Telegram Bot API **4.1** are supported.
|
||||
Installing
|
||||
==========
|
||||
|
||||
**Beta note**
|
||||
|
||||
The newest stable release is currently version 11.1.0.
|
||||
|
||||
The newest release is a beta release for version 12.
|
||||
Install or upgrade with:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
$ pip install python-telegram-bot=12.0.0b1 --upgrade
|
||||
|
||||
|
||||
See CHANGES.rst for the changelog and make sure to report any bugs you find!
|
||||
|
||||
You can install or upgrade the stable python-telegram-bot with:
|
||||
You can install or upgrade python-telegram-bot with:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
@@ -206,11 +187,12 @@ You can get help in several ways:
|
||||
|
||||
1. We have a vibrant community of developers helping each other in our `Telegram group <https://telegram.me/pythontelegrambotgroup>`_. Join us!
|
||||
|
||||
2. Our `Wiki pages <https://github.com/python-telegram-bot/python-telegram-bot/wiki/>`_ offer a growing amount of resources.
|
||||
2. Report bugs, request new features or ask questions by `creating an issue <https://github.com/python-telegram-bot/python-telegram-bot/issues/new/choose>`_.
|
||||
|
||||
3. You can ask for help on Stack Overflow using the `python-telegram-bot tag <https://stackoverflow.com/questions/tagged/python-telegram-bot>`_.
|
||||
3. Our `Wiki pages <https://github.com/python-telegram-bot/python-telegram-bot/wiki/>`_ offer a growing amount of resources.
|
||||
|
||||
4. You can even ask for help on Stack Overflow using the `python-telegram-bot tag <https://stackoverflow.com/questions/tagged/python-telegram-bot>`_.
|
||||
|
||||
4. As last resort, the developers are ready to help you with `serious issues <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
|
||||
|
||||
|
||||
============
|
||||
|
||||
+5
-2
@@ -2,15 +2,15 @@ environment:
|
||||
|
||||
matrix:
|
||||
# For Python versions available on Appveyor, see
|
||||
# http://www.appveyor.com/docs/installed-software#python
|
||||
# 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:\\Python34"
|
||||
- PYTHON: "C:\\Python35"
|
||||
- PYTHON: "C:\\Python36"
|
||||
- PYTHON: "C:\\Python37"
|
||||
# - PYTHON: "C:\\Python38"
|
||||
|
||||
branches:
|
||||
only:
|
||||
@@ -27,6 +27,8 @@ install:
|
||||
# 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"
|
||||
@@ -34,6 +36,7 @@ install:
|
||||
build: off
|
||||
|
||||
test_script:
|
||||
- "pytest --version"
|
||||
- "pytest -m \"not nocoverage\" --cov --cov-report xml:coverage.xml"
|
||||
|
||||
after_test:
|
||||
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
cp -R contrib/debian .
|
||||
debuild -us -uc
|
||||
debian/rules clean
|
||||
rm -rf debian
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram (12.0.0b1) unstable; urgency=medium
|
||||
|
||||
* Debian packaging;
|
||||
* Initial Release.
|
||||
|
||||
-- Marco Marinello <me@marcomarinello.it> Thu, 22 Aug 2019 20:36:47 +0200
|
||||
@@ -0,0 +1 @@
|
||||
11
|
||||
@@ -0,0 +1,25 @@
|
||||
Source: telegram
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Maintainer: Marco Marinello <me@marcomarinello.it>
|
||||
Build-Depends: debhelper (>= 11), dh-python, python3-all, python3-setuptools
|
||||
Standards-Version: 4.1.3
|
||||
Homepage: https://python-telegram-bot.org
|
||||
X-Python-Version: >= 3.2
|
||||
Vcs-Browser: https://github.com/python-telegram-bot/python-telegram-bot
|
||||
Vcs-Git: https://github.com/python-telegram-bot/python-telegram-bot.git
|
||||
|
||||
Package: python3-telegram-bot
|
||||
Architecture: any
|
||||
Depends: ${python3:Depends}, ${misc:Depends}
|
||||
Description: We have made you a wrapper you can't refuse!
|
||||
The Python Telegram bot (Python 3)
|
||||
This library provides a pure Python interface for the Telegram Bot API.
|
||||
It's compatible with Python versions 2.7, 3.3+ and PyPy.
|
||||
.
|
||||
In addition to the pure API implementation, this library features
|
||||
a number of high-level
|
||||
classes to make the development of bots easy and straightforward.
|
||||
These classes are contained in the telegram.ext submodule.
|
||||
.
|
||||
This package installs the library for Python 3.
|
||||
@@ -0,0 +1,30 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: telegram
|
||||
Source: https://github.com/python-telegram-bot/python-telegram-bot
|
||||
|
||||
Files: *
|
||||
Copyright: 2019 Leandro Toledo
|
||||
2019 see AUTHORS file
|
||||
License: LGPLv3
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2019 Marco Marinello <me@marcomarinello.it>
|
||||
License: GPL-3.0+
|
||||
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This package 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 General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
AUTHORS.rst /usr/share/doc/python3-telegram-bot
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/make -f
|
||||
# See debhelper(7) (uncomment to enable)
|
||||
# output every command that modifies files on the build system.
|
||||
#export DH_VERBOSE = 1
|
||||
|
||||
export PYBUILD_NAME=telegram
|
||||
|
||||
%:
|
||||
DEB_BUILD_OPTIONS=nocheck dh $@ --with python2,python3 --buildsystem=pybuild
|
||||
|
||||
|
||||
# If you need to rebuild the Sphinx documentation
|
||||
# Add spinxdoc to the dh --with line
|
||||
#override_dh_auto_build:
|
||||
# dh_auto_build
|
||||
# PYTHONPATH=. http_proxy='127.0.0.1:9' sphinx-build -N -bhtml docs/ build/html # HTML generator
|
||||
# PYTHONPATH=. http_proxy='127.0.0.1:9' sphinx-build -N -bman docs/ build/man # Manpage generator
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
3.0 (native)
|
||||
@@ -0,0 +1 @@
|
||||
extend-diff-ignore = "^[^/]*[.]egg-info/"
|
||||
@@ -1,3 +1,3 @@
|
||||
sphinx>=1.5.4
|
||||
sphinx>=1.7.9
|
||||
sphinx_rtd_theme
|
||||
sphinx-pypi-upload
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
.. include:: ../../CHANGES.rst
|
||||
+7
-3
@@ -24,7 +24,7 @@ sys.path.insert(0, os.path.abspath('../..'))
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
needs_sphinx = '1.5.4' # fixes issues with autodoc-skip-member and napoleon
|
||||
needs_sphinx = '1.7.9'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
@@ -58,9 +58,9 @@ author = u'Leandro Toledo'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '12.0' # telegram.__version__[:3]
|
||||
version = '12.3' # telegram.__version__[:3]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '12.0.0b1' # telegram.__version__
|
||||
release = '12.3.0' # telegram.__version__
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
@@ -289,6 +289,10 @@ texinfo_documents = [
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#texinfo_no_detailmenu = False
|
||||
|
||||
# Napoleon stuff
|
||||
|
||||
napoleon_use_admonition_for_examples = True
|
||||
|
||||
# -- script stuff --------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
+20
-9
@@ -6,8 +6,23 @@
|
||||
Welcome to Python Telegram Bot's documentation!
|
||||
===============================================
|
||||
|
||||
Below you can find the documentation for the python-telegram-bot library. except for the .ext package most of the
|
||||
objects in the package reflect the types as defined by the `telegram bot api <https://core.telegram.org/bots/api>`_.
|
||||
Guides and tutorials
|
||||
====================
|
||||
|
||||
If you're just starting out with the library, we recommend following our `"Your first Bot" <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-Your-first-Bot>`_ tutorial that you can find on our `wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki>`_.
|
||||
On our wiki you will also find guides like how to use handlers, webhooks, emoji, proxies and much more.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
A great way to learn is by looking at examples. Ours can be found at our `github in the examples folder <https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples>`_.
|
||||
|
||||
|
||||
Reference
|
||||
=========
|
||||
|
||||
Below you can find a reference of all the classes and methods in python-telegram-bot.
|
||||
Apart from the `telegram.ext` package the objects should reflect the types defined in the `official telegram bot api documentation <https://core.telegram.org/bots/api>`_.
|
||||
|
||||
.. toctree::
|
||||
telegram
|
||||
@@ -15,13 +30,9 @@ objects in the package reflect the types as defined by the `telegram bot api <ht
|
||||
Changelog
|
||||
---------
|
||||
|
||||
.. include:: ../../CHANGES.rst
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
changelog
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.ChatPermissions
|
||||
========================
|
||||
|
||||
.. autoclass:: telegram.ChatPermissions
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.LoginUrl
|
||||
=================
|
||||
|
||||
.. autoclass:: telegram.LoginUrl
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.Poll
|
||||
=============
|
||||
|
||||
.. autoclass:: telegram.Poll
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.PollOption
|
||||
===================
|
||||
|
||||
.. autoclass:: telegram.PollOption
|
||||
:members:
|
||||
:show-inheritance:
|
||||
+91
-94
@@ -1,117 +1,122 @@
|
||||
.. include:: telegram.ext.rst
|
||||
|
||||
|
||||
telegram package
|
||||
================
|
||||
|
||||
.. toctree::
|
||||
|
||||
telegram.ext
|
||||
telegram.utils
|
||||
telegram.animation
|
||||
telegram.audio
|
||||
telegram.bot
|
||||
telegram.callbackquery
|
||||
telegram.chat
|
||||
telegram.chataction
|
||||
telegram.chatmember
|
||||
telegram.chatphoto
|
||||
telegram.constants
|
||||
telegram.contact
|
||||
telegram.document
|
||||
telegram.error
|
||||
telegram.file
|
||||
telegram.forcereply
|
||||
telegram.inlinekeyboardbutton
|
||||
telegram.inlinekeyboardmarkup
|
||||
telegram.inputfile
|
||||
telegram.inputmedia
|
||||
telegram.inputmediaanimation
|
||||
telegram.inputmediaaudio
|
||||
telegram.inputmediadocument
|
||||
telegram.inputmediaphoto
|
||||
telegram.inputmediavideo
|
||||
telegram.keyboardbutton
|
||||
telegram.location
|
||||
telegram.message
|
||||
telegram.messageentity
|
||||
telegram.parsemode
|
||||
telegram.photosize
|
||||
telegram.replykeyboardremove
|
||||
telegram.replykeyboardmarkup
|
||||
telegram.replymarkup
|
||||
telegram.telegramobject
|
||||
telegram.update
|
||||
telegram.user
|
||||
telegram.userprofilephotos
|
||||
telegram.venue
|
||||
telegram.video
|
||||
telegram.videonote
|
||||
telegram.voice
|
||||
telegram.webhookinfo
|
||||
telegram.animation
|
||||
telegram.audio
|
||||
telegram.bot
|
||||
telegram.callbackquery
|
||||
telegram.chat
|
||||
telegram.chataction
|
||||
telegram.chatmember
|
||||
telegram.chatpermissions
|
||||
telegram.chatphoto
|
||||
telegram.constants
|
||||
telegram.contact
|
||||
telegram.document
|
||||
telegram.error
|
||||
telegram.file
|
||||
telegram.forcereply
|
||||
telegram.inlinekeyboardbutton
|
||||
telegram.inlinekeyboardmarkup
|
||||
telegram.inputfile
|
||||
telegram.inputmedia
|
||||
telegram.inputmediaanimation
|
||||
telegram.inputmediaaudio
|
||||
telegram.inputmediadocument
|
||||
telegram.inputmediaphoto
|
||||
telegram.inputmediavideo
|
||||
telegram.keyboardbutton
|
||||
telegram.location
|
||||
telegram.loginurl
|
||||
telegram.message
|
||||
telegram.messageentity
|
||||
telegram.parsemode
|
||||
telegram.photosize
|
||||
telegram.poll
|
||||
telegram.polloption
|
||||
telegram.replykeyboardremove
|
||||
telegram.replykeyboardmarkup
|
||||
telegram.replymarkup
|
||||
telegram.telegramobject
|
||||
telegram.update
|
||||
telegram.user
|
||||
telegram.userprofilephotos
|
||||
telegram.venue
|
||||
telegram.video
|
||||
telegram.videonote
|
||||
telegram.voice
|
||||
telegram.webhookinfo
|
||||
|
||||
Stickers
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
|
||||
telegram.sticker
|
||||
telegram.stickerset
|
||||
telegram.maskposition
|
||||
telegram.sticker
|
||||
telegram.stickerset
|
||||
telegram.maskposition
|
||||
|
||||
Inline Mode
|
||||
-----------
|
||||
|
||||
.. toctree::
|
||||
|
||||
telegram.inlinequery
|
||||
telegram.inlinequeryresult
|
||||
telegram.inlinequeryresultarticle
|
||||
telegram.inlinequeryresultaudio
|
||||
telegram.inlinequeryresultcachedaudio
|
||||
telegram.inlinequeryresultcacheddocument
|
||||
telegram.inlinequeryresultcachedgif
|
||||
telegram.inlinequeryresultcachedmpeg4gif
|
||||
telegram.inlinequeryresultcachedphoto
|
||||
telegram.inlinequeryresultcachedsticker
|
||||
telegram.inlinequeryresultcachedvideo
|
||||
telegram.inlinequeryresultcachedvoice
|
||||
telegram.inlinequeryresultcontact
|
||||
telegram.inlinequeryresultdocument
|
||||
telegram.inlinequeryresultgame
|
||||
telegram.inlinequeryresultgif
|
||||
telegram.inlinequeryresultlocation
|
||||
telegram.inlinequeryresultmpeg4gif
|
||||
telegram.inlinequeryresultphoto
|
||||
telegram.inlinequeryresultvenue
|
||||
telegram.inlinequeryresultvideo
|
||||
telegram.inlinequeryresultvoice
|
||||
telegram.inputmessagecontent
|
||||
telegram.inputtextmessagecontent
|
||||
telegram.inputlocationmessagecontent
|
||||
telegram.inputvenuemessagecontent
|
||||
telegram.inputcontactmessagecontent
|
||||
telegram.choseninlineresult
|
||||
telegram.inlinequery
|
||||
telegram.inlinequeryresult
|
||||
telegram.inlinequeryresultarticle
|
||||
telegram.inlinequeryresultaudio
|
||||
telegram.inlinequeryresultcachedaudio
|
||||
telegram.inlinequeryresultcacheddocument
|
||||
telegram.inlinequeryresultcachedgif
|
||||
telegram.inlinequeryresultcachedmpeg4gif
|
||||
telegram.inlinequeryresultcachedphoto
|
||||
telegram.inlinequeryresultcachedsticker
|
||||
telegram.inlinequeryresultcachedvideo
|
||||
telegram.inlinequeryresultcachedvoice
|
||||
telegram.inlinequeryresultcontact
|
||||
telegram.inlinequeryresultdocument
|
||||
telegram.inlinequeryresultgame
|
||||
telegram.inlinequeryresultgif
|
||||
telegram.inlinequeryresultlocation
|
||||
telegram.inlinequeryresultmpeg4gif
|
||||
telegram.inlinequeryresultphoto
|
||||
telegram.inlinequeryresultvenue
|
||||
telegram.inlinequeryresultvideo
|
||||
telegram.inlinequeryresultvoice
|
||||
telegram.inputmessagecontent
|
||||
telegram.inputtextmessagecontent
|
||||
telegram.inputlocationmessagecontent
|
||||
telegram.inputvenuemessagecontent
|
||||
telegram.inputcontactmessagecontent
|
||||
telegram.choseninlineresult
|
||||
|
||||
Payments
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
|
||||
telegram.labeledprice
|
||||
telegram.invoice
|
||||
telegram.shippingaddress
|
||||
telegram.orderinfo
|
||||
telegram.shippingoption
|
||||
telegram.successfulpayment
|
||||
telegram.shippingquery
|
||||
telegram.precheckoutquery
|
||||
telegram.labeledprice
|
||||
telegram.invoice
|
||||
telegram.shippingaddress
|
||||
telegram.orderinfo
|
||||
telegram.shippingoption
|
||||
telegram.successfulpayment
|
||||
telegram.shippingquery
|
||||
telegram.precheckoutquery
|
||||
|
||||
Games
|
||||
-----
|
||||
|
||||
.. toctree::
|
||||
|
||||
telegram.game
|
||||
telegram.callbackgame
|
||||
telegram.gamehighscore
|
||||
telegram.game
|
||||
telegram.callbackgame
|
||||
telegram.gamehighscore
|
||||
|
||||
Passport
|
||||
--------
|
||||
@@ -137,12 +142,4 @@ Passport
|
||||
telegram.encryptedpassportelement
|
||||
telegram.encryptedcredentials
|
||||
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
.. automodule:: telegram
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:noindex:
|
||||
.. include:: telegram.utils.rst
|
||||
|
||||
+8
-2
@@ -1,6 +1,6 @@
|
||||
# Examples
|
||||
|
||||
In this folder there are small examples to show what a bot written with `python-telegram-bot` looks like. Some bots focus on one specific aspect of the Telegram Bot API while others focus on one of the mechanics of this library. Except for the [`echobot.py`](#pure-api) example, they all use the high-level framework this library provides with the [`telegram.ext`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.html) submodule.
|
||||
In this folder are small examples to show what a bot written with `python-telegram-bot` looks like. Some bots focus on one specific aspect of the Telegram Bot API while others focus on one of the mechanics of this library. Except for the [`echobot.py`](#pure-api) example, they all use the high-level framework this library provides with the [`telegram.ext`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.html) submodule.
|
||||
|
||||
All examples are licensed under the [CC0 License](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/LICENSE.txt) and are therefore fully dedicated to the public domain. You can use them as the base for your own bots without worrying about copyrights.
|
||||
|
||||
@@ -16,9 +16,15 @@ A common task for a bot is to ask information from the user. In v5.0 of this lib
|
||||
### [`conversationbot2.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot2.py)
|
||||
A more complex example of a bot that uses the `ConversationHandler`. It is also more confusing. Good thing there is a [fancy state diagram](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot2.png) for this one, too!
|
||||
|
||||
### [`nestedconversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/nestedconversationbot.py)
|
||||
A even more complex example of a bot that uses the nested `ConversationHandler`s. While it's certainly not that complex that you couldn't built it without nested `ConversationHanldler`s, it gives a good impression on how to work with them. Of course, there is a [fancy state diagram](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/nestedconversationbot.png) for this example, too!
|
||||
|
||||
### [`inlinekeyboard.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinekeyboard.py)
|
||||
This example sheds some light on inline keyboards, callback queries and message editing.
|
||||
|
||||
### [`inlinekeyboard2.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinekeyboard2.py)
|
||||
A more complex example about inline keyboards, callback queries and message editing. This example showcases how an interactive menu could be build using inline keyboards.
|
||||
|
||||
### [`inlinebot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinebot.py)
|
||||
A basic example of an [inline bot](https://core.telegram.org/bots/inline). Don't forget to enable inline mode with [@BotFather](https://telegram.me/BotFather).
|
||||
|
||||
@@ -29,4 +35,4 @@ A basic example of a bot that can accept payments. Don't forget to enable and co
|
||||
A basic example of a bot store conversation state and user_data over multiple restarts.
|
||||
|
||||
## Pure API
|
||||
The [`echobot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot.py) example uses only the pure, "bare-metal" API wrapper.
|
||||
The [`echobot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot.py) example uses only the pure, "bare-metal" API wrapper.
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
#
|
||||
# THIS EXAMPLE HAS BEEN UPDATED TO WORK WITH THE BETA VERSION 12 OF PYTHON-TELEGRAM-BOT.
|
||||
# If you're still using version 11.1.0, please see the examples at
|
||||
# https://github.com/python-telegram-bot/python-telegram-bot/tree/v11.1.0/examples
|
||||
|
||||
"""
|
||||
First, a few callback functions are defined. Then, those functions are passed to
|
||||
@@ -21,7 +17,7 @@ bot.
|
||||
import logging
|
||||
|
||||
from telegram import (ReplyKeyboardMarkup, ReplyKeyboardRemove)
|
||||
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
|
||||
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters,
|
||||
ConversationHandler)
|
||||
|
||||
# Enable logging
|
||||
@@ -131,7 +127,7 @@ def main():
|
||||
entry_points=[CommandHandler('start', start)],
|
||||
|
||||
states={
|
||||
GENDER: [RegexHandler('^(Boy|Girl|Other)$', gender)],
|
||||
GENDER: [MessageHandler(Filters.regex('^(Boy|Girl|Other)$'), gender)],
|
||||
|
||||
PHOTO: [MessageHandler(Filters.photo, photo),
|
||||
CommandHandler('skip', skip_photo)],
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
#
|
||||
# THIS EXAMPLE HAS BEEN UPDATED TO WORK WITH THE BETA VERSION 12 OF PYTHON-TELEGRAM-BOT.
|
||||
# If you're still using version 11.1.0, please see the examples at
|
||||
# https://github.com/python-telegram-bot/python-telegram-bot/tree/v11.1.0/examples
|
||||
|
||||
"""
|
||||
First, a few callback functions are defined. Then, those functions are passed to
|
||||
@@ -21,7 +17,7 @@ bot.
|
||||
import logging
|
||||
|
||||
from telegram import ReplyKeyboardMarkup
|
||||
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
|
||||
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters,
|
||||
ConversationHandler)
|
||||
|
||||
# Enable logging
|
||||
@@ -80,9 +76,9 @@ def received_information(update, context):
|
||||
del user_data['choice']
|
||||
|
||||
update.message.reply_text("Neat! Just so you know, this is what you already told me:"
|
||||
"{}"
|
||||
"You can tell me more, or change your opinion on something.".format(
|
||||
facts_to_str(user_data)), reply_markup=markup)
|
||||
"{} You can tell me more, or change your opinion"
|
||||
" on something.".format(facts_to_str(user_data)),
|
||||
reply_markup=markup)
|
||||
|
||||
return CHOOSING
|
||||
|
||||
@@ -102,7 +98,7 @@ def done(update, context):
|
||||
|
||||
def error(update, context):
|
||||
"""Log Errors caused by Updates."""
|
||||
logger.warning('Update "%s" caused error "%s"', update, error)
|
||||
logger.warning('Update "%s" caused error "%s"', update, context.error)
|
||||
|
||||
|
||||
def main():
|
||||
@@ -119,25 +115,22 @@ def main():
|
||||
entry_points=[CommandHandler('start', start)],
|
||||
|
||||
states={
|
||||
CHOOSING: [RegexHandler('^(Age|Favourite colour|Number of siblings)$',
|
||||
regular_choice,
|
||||
pass_user_data=True),
|
||||
RegexHandler('^Something else...$',
|
||||
custom_choice),
|
||||
CHOOSING: [MessageHandler(Filters.regex('^(Age|Favourite colour|Number of siblings)$'),
|
||||
regular_choice),
|
||||
MessageHandler(Filters.regex('^Something else...$'),
|
||||
custom_choice)
|
||||
],
|
||||
|
||||
TYPING_CHOICE: [MessageHandler(Filters.text,
|
||||
regular_choice,
|
||||
pass_user_data=True),
|
||||
regular_choice)
|
||||
],
|
||||
|
||||
TYPING_REPLY: [MessageHandler(Filters.text,
|
||||
received_information,
|
||||
pass_user_data=True),
|
||||
received_information),
|
||||
],
|
||||
},
|
||||
|
||||
fallbacks=[RegexHandler('^Done$', done, pass_user_data=True)]
|
||||
fallbacks=[MessageHandler(Filters.regex('^Done$'), done)]
|
||||
)
|
||||
|
||||
dp.add_handler(conv_handler)
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Bot that explains Telegram's "Deep Linking Parameters" functionality.
|
||||
|
||||
This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
This Bot uses the Updater class to handle the bot.
|
||||
|
||||
First, a few handler functions are defined. Then, those functions are passed to
|
||||
the Dispatcher and registered at their respective places.
|
||||
Then, the bot is started and runs until we press Ctrl-C on the command line.
|
||||
|
||||
Usage:
|
||||
Deep Linking example. Send /start to get the link.
|
||||
Press Ctrl-C on the command line or send a signal to the process to stop the
|
||||
bot.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from telegram import ParseMode, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from telegram.ext import Updater, CommandHandler, Filters
|
||||
|
||||
# Enable logging
|
||||
from telegram.utils import helpers
|
||||
|
||||
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
level=logging.INFO)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Define constants that will allow us to reuse the deep-linking parameters.
|
||||
CHECK_THIS_OUT = 'check-this-out'
|
||||
USING_ENTITIES = 'using-entities-here'
|
||||
SO_COOL = 'so-cool'
|
||||
|
||||
|
||||
def start(update, context):
|
||||
"""Send a deep-linked URL when the command /start is issued."""
|
||||
bot = context.bot
|
||||
url = helpers.create_deep_linked_url(bot.get_me().username, CHECK_THIS_OUT, group=True)
|
||||
text = "Feel free to tell your friends about it:\n\n" + url
|
||||
update.message.reply_text(text)
|
||||
|
||||
|
||||
def deep_linked_level_1(update, context):
|
||||
"""Reached through the CHECK_THIS_OUT payload"""
|
||||
bot = context.bot
|
||||
url = helpers.create_deep_linked_url(bot.get_me().username, SO_COOL)
|
||||
text = "Awesome, you just accessed hidden functionality! " \
|
||||
" Now let's get back to the private chat."
|
||||
keyboard = InlineKeyboardMarkup.from_button(
|
||||
InlineKeyboardButton(text='Continue here!', url=url)
|
||||
)
|
||||
update.message.reply_text(text, reply_markup=keyboard)
|
||||
|
||||
|
||||
def deep_linked_level_2(update, context):
|
||||
"""Reached through the SO_COOL payload"""
|
||||
bot = context.bot
|
||||
url = helpers.create_deep_linked_url(bot.get_me().username, USING_ENTITIES)
|
||||
text = "You can also mask the deep-linked URLs as links: " \
|
||||
"[▶️ CLICK HERE]({0}).".format(url)
|
||||
update.message.reply_text(text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True)
|
||||
|
||||
|
||||
def deep_linked_level_3(update, context):
|
||||
"""Reached through the USING_ENTITIES payload"""
|
||||
payload = context.args
|
||||
update.message.reply_text("Congratulations! This is as deep as it gets 👏🏻\n\n"
|
||||
"The payload was: {0}".format(payload))
|
||||
|
||||
|
||||
def error(update, context):
|
||||
"""Log Errors caused by Updates."""
|
||||
logger.warning('Update "%s" caused error "%s"', update, context.error)
|
||||
|
||||
|
||||
def main():
|
||||
"""Start the bot."""
|
||||
# Create the Updater and pass it your bot's token.
|
||||
updater = Updater("TOKEN", use_context=True)
|
||||
|
||||
# Get the dispatcher to register handlers
|
||||
dp = updater.dispatcher
|
||||
|
||||
# More info on what deep linking actually is (read this first if it's unclear to you):
|
||||
# https://core.telegram.org/bots#deep-linking
|
||||
|
||||
# Register a deep-linking handler
|
||||
dp.add_handler(CommandHandler("start", deep_linked_level_1, Filters.regex(CHECK_THIS_OUT)))
|
||||
|
||||
# This one works with a textual link instead of an URL
|
||||
dp.add_handler(CommandHandler("start", deep_linked_level_2, Filters.regex(SO_COOL)))
|
||||
|
||||
# We can also pass on the deep-linking payload
|
||||
dp.add_handler(CommandHandler("start",
|
||||
deep_linked_level_3,
|
||||
Filters.regex(USING_ENTITIES),
|
||||
pass_args=True))
|
||||
|
||||
# Make sure the deep-linking handlers occur *before* the normal /start handler.
|
||||
dp.add_handler(CommandHandler("start", start))
|
||||
|
||||
# log all errors
|
||||
dp.add_error_handler(error)
|
||||
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
|
||||
# Run the bot until you press Ctrl-C or the process receives SIGINT,
|
||||
# SIGTERM or SIGABRT. This should be used most of the time, since
|
||||
# start_polling() is non-blocking and will stop the bot gracefully.
|
||||
updater.idle()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,10 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
#
|
||||
# THIS EXAMPLE HAS BEEN UPDATED TO WORK WITH THE BETA VERSION 12 OF PYTHON-TELEGRAM-BOT.
|
||||
# If you're still using version 11.1.0, please see the examples at
|
||||
# https://github.com/python-telegram-bot/python-telegram-bot/tree/v11.1.0/examples
|
||||
|
||||
"""
|
||||
Simple Bot to reply to Telegram messages.
|
||||
@@ -30,8 +26,8 @@ logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Define a few command handlers. These usually take the two arguments bot and
|
||||
# update. Error handlers also receive the raised TelegramError object in error.
|
||||
# Define a few command handlers. These usually take the two arguments update and
|
||||
# context. Error handlers also receive the raised TelegramError object in error.
|
||||
def start(update, context):
|
||||
"""Send a message when the command /start is issued."""
|
||||
update.message.reply_text('Hi!')
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
#
|
||||
# THIS EXAMPLE HAS BEEN UPDATED TO WORK WITH THE BETA VERSION 12 OF PYTHON-TELEGRAM-BOT.
|
||||
# If you're still using version 11.1.0, please see the examples at
|
||||
# https://github.com/python-telegram-bot/python-telegram-bot/tree/v11.1.0/examples
|
||||
|
||||
"""
|
||||
First, a few handler functions are defined. Then, those functions are passed to
|
||||
@@ -31,8 +27,8 @@ logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Define a few command handlers. These usually take the two arguments bot and
|
||||
# update. Error handlers also receive the raised TelegramError object in error.
|
||||
# Define a few command handlers. These usually take the two arguments update and
|
||||
# context. Error handlers also receive the raised TelegramError object in error.
|
||||
def start(update, context):
|
||||
"""Send a message when the command /start is issued."""
|
||||
update.message.reply_text('Hi!')
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
#
|
||||
# THIS EXAMPLE HAS BEEN UPDATED TO WORK WITH THE BETA VERSION 12 OF PYTHON-TELEGRAM-BOT.
|
||||
# If you're still using version 11.1.0, please see the examples at
|
||||
# https://github.com/python-telegram-bot/python-telegram-bot/tree/v11.1.0/examples
|
||||
|
||||
"""
|
||||
Basic example for a bot that uses inline keyboards.
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Simple inline keyboard bot with multiple CallbackQueryHandlers.
|
||||
|
||||
This Bot uses the Updater class to handle the bot.
|
||||
First, a few callback functions are defined as callback query handler. Then, those functions are
|
||||
passed to the Dispatcher and registered at their respective places.
|
||||
Then, the bot is started and runs until we press Ctrl-C on the command line.
|
||||
Usage:
|
||||
Example of a bot that uses inline keyboard that has multiple CallbackQueryHandlers arranged in a
|
||||
ConversationHandler.
|
||||
Send /start to initiate the conversation.
|
||||
Press Ctrl-C on the command line to stop the bot.
|
||||
"""
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, ConversationHandler
|
||||
import logging
|
||||
|
||||
# Enable logging
|
||||
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
level=logging.INFO)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Stages
|
||||
FIRST, SECOND = range(2)
|
||||
# Callback data
|
||||
ONE, TWO, THREE, FOUR = range(4)
|
||||
|
||||
|
||||
def start(update, context):
|
||||
"""Send message on `/start`."""
|
||||
# Get user that sent /start and log his name
|
||||
user = update.message.from_user
|
||||
logger.info("User %s started the conversation.", user.first_name)
|
||||
# Build InlineKeyboard where each button has a displayed text
|
||||
# and a string as callback_data
|
||||
# The keyboard is a list of button rows, where each row is in turn
|
||||
# a list (hence `[[...]]`).
|
||||
keyboard = [
|
||||
[InlineKeyboardButton("1", callback_data=str(ONE)),
|
||||
InlineKeyboardButton("2", callback_data=str(TWO))]
|
||||
]
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
# Send message with text and appended InlineKeyboard
|
||||
update.message.reply_text(
|
||||
"Start handler, Choose a route",
|
||||
reply_markup=reply_markup
|
||||
)
|
||||
# Tell ConversationHandler that we're in state `FIRST` now
|
||||
return FIRST
|
||||
|
||||
|
||||
def start_over(update, context):
|
||||
"""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
|
||||
keyboard = [
|
||||
[InlineKeyboardButton("1", callback_data=str(ONE)),
|
||||
InlineKeyboardButton("2", callback_data=str(TWO))]
|
||||
]
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
# Instead of sending a new message, edit the message that
|
||||
# originated the CallbackQuery. This gives the feeling of an
|
||||
# interactive menu.
|
||||
bot.edit_message_text(
|
||||
chat_id=query.message.chat_id,
|
||||
message_id=query.message.message_id,
|
||||
text="Start handler, Choose a route",
|
||||
reply_markup=reply_markup
|
||||
)
|
||||
return FIRST
|
||||
|
||||
|
||||
def one(update, context):
|
||||
"""Show new choice of buttons"""
|
||||
query = update.callback_query
|
||||
bot = context.bot
|
||||
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,
|
||||
text="First CallbackQueryHandler, Choose a route",
|
||||
reply_markup=reply_markup
|
||||
)
|
||||
return FIRST
|
||||
|
||||
|
||||
def two(update, context):
|
||||
"""Show new choice of buttons"""
|
||||
query = update.callback_query
|
||||
bot = context.bot
|
||||
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,
|
||||
text="Second CallbackQueryHandler, Choose a route",
|
||||
reply_markup=reply_markup
|
||||
)
|
||||
return FIRST
|
||||
|
||||
|
||||
def three(update, context):
|
||||
"""Show new choice of buttons"""
|
||||
query = update.callback_query
|
||||
bot = context.bot
|
||||
keyboard = [
|
||||
[InlineKeyboardButton("Yes, let's do it again!", callback_data=str(ONE)),
|
||||
InlineKeyboardButton("Nah, I've had enough ...", callback_data=str(TWO))]
|
||||
]
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
bot.edit_message_text(
|
||||
chat_id=query.message.chat_id,
|
||||
message_id=query.message.message_id,
|
||||
text="Third CallbackQueryHandler. Do want to start over?",
|
||||
reply_markup=reply_markup
|
||||
)
|
||||
# Transfer to conversation state `SECOND`
|
||||
return SECOND
|
||||
|
||||
|
||||
def four(update, context):
|
||||
"""Show new choice of buttons"""
|
||||
query = update.callback_query
|
||||
bot = context.bot
|
||||
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,
|
||||
text="Fourth CallbackQueryHandler, Choose a route",
|
||||
reply_markup=reply_markup
|
||||
)
|
||||
return FIRST
|
||||
|
||||
|
||||
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,
|
||||
text="See you next time!"
|
||||
)
|
||||
return ConversationHandler.END
|
||||
|
||||
|
||||
def error(update, context):
|
||||
"""Log Errors caused by Updates."""
|
||||
logger.warning('Update "%s" caused error "%s"', update, context.error)
|
||||
|
||||
|
||||
def main():
|
||||
# Create the Updater and pass it your bot's token.
|
||||
updater = Updater("TOKEN", use_context=True)
|
||||
|
||||
# Get the dispatcher to register handlers
|
||||
dp = updater.dispatcher
|
||||
|
||||
# Setup conversation handler with the states FIRST and SECOND
|
||||
# Use the pattern parameter to pass CallbackQueries with specific
|
||||
# data pattern to the corresponding handlers.
|
||||
# ^ means "start of line/string"
|
||||
# $ means "end of line/string"
|
||||
# So ^ABC$ will only allow 'ABC'
|
||||
conv_handler = ConversationHandler(
|
||||
entry_points=[CommandHandler('start', start)],
|
||||
states={
|
||||
FIRST: [CallbackQueryHandler(one, pattern='^' + str(ONE) + '$'),
|
||||
CallbackQueryHandler(two, pattern='^' + str(TWO) + '$'),
|
||||
CallbackQueryHandler(three, pattern='^' + str(THREE) + '$'),
|
||||
CallbackQueryHandler(four, pattern='^' + str(FOUR) + '$')],
|
||||
SECOND: [CallbackQueryHandler(start_over, pattern='^' + str(ONE) + '$'),
|
||||
CallbackQueryHandler(end, pattern='^' + str(TWO) + '$')]
|
||||
},
|
||||
fallbacks=[CommandHandler('start', start)]
|
||||
)
|
||||
|
||||
# Add ConversationHandler to dispatcher that will be used for handling
|
||||
# updates
|
||||
dp.add_handler(conv_handler)
|
||||
|
||||
# log all errors
|
||||
dp.add_error_handler(error)
|
||||
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
|
||||
# Run the bot until you press Ctrl-C or the process receives SIGINT,
|
||||
# SIGTERM or SIGABRT. This should be used most of the time, since
|
||||
# start_polling() is non-blocking and will stop the bot gracefully.
|
||||
updater.idle()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 492 KiB |
@@ -0,0 +1,362 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
First, a few callback functions are defined. Then, those functions are passed to
|
||||
the Dispatcher and registered at their respective places.
|
||||
Then, the bot is started and runs until we press Ctrl-C on the command line.
|
||||
|
||||
Usage:
|
||||
Example of a bot-user conversation using nested ConversationHandlers.
|
||||
Send /start to initiate the conversation.
|
||||
Press Ctrl-C on the command line or send a signal to the process to stop the
|
||||
bot.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from telegram import (InlineKeyboardMarkup, InlineKeyboardButton)
|
||||
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters,
|
||||
ConversationHandler, CallbackQueryHandler)
|
||||
|
||||
# Enable logging
|
||||
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
level=logging.INFO)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# State definitions for top level conversation
|
||||
SELECTING_ACTION, ADDING_MEMBER, ADDING_SELF, DESCRIBING_SELF = map(chr, range(4))
|
||||
# State definitions for second level conversation
|
||||
SELECTING_LEVEL, SELECTING_GENDER = map(chr, range(4, 6))
|
||||
# State definitions for descriptions conversation
|
||||
SELECTING_FEATURE, TYPING = map(chr, range(6, 8))
|
||||
# Meta states
|
||||
STOPPING, SHOWING = map(chr, range(8, 10))
|
||||
# Shortcut for ConversationHandler.END
|
||||
END = ConversationHandler.END
|
||||
|
||||
# Different constants for this example
|
||||
(PARENTS, CHILDREN, SELF, GENDER, MALE, FEMALE, AGE, NAME, START_OVER, FEATURES,
|
||||
CURRENT_FEATURE, CURRENT_LEVEL) = map(chr, range(10, 22))
|
||||
|
||||
|
||||
# Helper
|
||||
def _name_switcher(level):
|
||||
if level == PARENTS:
|
||||
return ('Father', 'Mother')
|
||||
elif level == CHILDREN:
|
||||
return ('Brother', 'Sister')
|
||||
|
||||
|
||||
# Top level conversation callbacks
|
||||
def start(update, context):
|
||||
"""Select an action: Adding parent/child or show data."""
|
||||
text = 'You may add a familiy member, yourself show the gathered data or end the ' \
|
||||
'conversation. To abort, simply type /stop.'
|
||||
buttons = [[
|
||||
InlineKeyboardButton(text='Add family member', callback_data=str(ADDING_MEMBER)),
|
||||
InlineKeyboardButton(text='Add yourself', callback_data=str(ADDING_SELF))
|
||||
], [
|
||||
InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)),
|
||||
InlineKeyboardButton(text='Done', callback_data=str(END))
|
||||
]]
|
||||
keyboard = InlineKeyboardMarkup(buttons)
|
||||
|
||||
# If we're starting over we don't need do send a new message
|
||||
if context.user_data.get(START_OVER):
|
||||
update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
|
||||
else:
|
||||
update.message.reply_text('Hi, I\'m FamiliyBot and here to help you gather information'
|
||||
'about your family.')
|
||||
update.message.reply_text(text=text, reply_markup=keyboard)
|
||||
|
||||
context.user_data[START_OVER] = False
|
||||
return SELECTING_ACTION
|
||||
|
||||
|
||||
def adding_self(update, context):
|
||||
"""Add information about youself."""
|
||||
context.user_data[CURRENT_LEVEL] = SELF
|
||||
text = 'Okay, please tell me about yourself.'
|
||||
button = InlineKeyboardButton(text='Add info', callback_data=str(MALE))
|
||||
keyboard = InlineKeyboardMarkup.from_button(button)
|
||||
|
||||
update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
|
||||
|
||||
return DESCRIBING_SELF
|
||||
|
||||
|
||||
def show_data(update, context):
|
||||
"""Pretty print gathered data."""
|
||||
def prettyprint(user_data, level):
|
||||
people = user_data.get(level)
|
||||
if not people:
|
||||
return '\nNo information yet.'
|
||||
|
||||
text = ''
|
||||
if level == SELF:
|
||||
for person in user_data[level]:
|
||||
text += '\nName: {0}, Age: {1}'.format(person.get(NAME, '-'), person.get(AGE, '-'))
|
||||
else:
|
||||
male, female = _name_switcher(level)
|
||||
|
||||
for person in user_data[level]:
|
||||
gender = female if person[GENDER] == FEMALE else male
|
||||
text += '\n{0}: Name: {1}, Age: {2}'.format(gender, person.get(NAME, '-'),
|
||||
person.get(AGE, '-'))
|
||||
return text
|
||||
|
||||
ud = context.user_data
|
||||
text = 'Yourself:' + prettyprint(ud, SELF)
|
||||
text += '\n\nParents:' + prettyprint(ud, PARENTS)
|
||||
text += '\n\nChildren:' + prettyprint(ud, CHILDREN)
|
||||
|
||||
buttons = [[
|
||||
InlineKeyboardButton(text='Back', callback_data=str(END))
|
||||
]]
|
||||
keyboard = InlineKeyboardMarkup(buttons)
|
||||
|
||||
update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
|
||||
ud[START_OVER] = True
|
||||
|
||||
return SHOWING
|
||||
|
||||
|
||||
def stop(update, context):
|
||||
"""End Conversation by command."""
|
||||
update.message.reply_text('Okay, bye.')
|
||||
|
||||
return END
|
||||
|
||||
|
||||
def end(update, context):
|
||||
"""End conversation from InlineKeyboardButton."""
|
||||
text = 'See you around!'
|
||||
update.callback_query.edit_message_text(text=text)
|
||||
|
||||
return END
|
||||
|
||||
|
||||
# Second level conversation callbacks
|
||||
def select_level(update, context):
|
||||
"""Choose to add a parent or a child."""
|
||||
text = 'You may add a parent or a child. Also you can show the gathered data or go back.'
|
||||
buttons = [[
|
||||
InlineKeyboardButton(text='Add parent', callback_data=str(PARENTS)),
|
||||
InlineKeyboardButton(text='Add child', callback_data=str(CHILDREN))
|
||||
], [
|
||||
InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)),
|
||||
InlineKeyboardButton(text='Back', callback_data=str(END))
|
||||
]]
|
||||
keyboard = InlineKeyboardMarkup(buttons)
|
||||
update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
|
||||
|
||||
return SELECTING_LEVEL
|
||||
|
||||
|
||||
def select_gender(update, context):
|
||||
"""Choose to add mother or father."""
|
||||
level = update.callback_query.data
|
||||
context.user_data[CURRENT_LEVEL] = level
|
||||
|
||||
text = 'Please choose, whom to add.'
|
||||
|
||||
male, female = _name_switcher(level)
|
||||
|
||||
buttons = [[
|
||||
InlineKeyboardButton(text='Add ' + male, callback_data=str(MALE)),
|
||||
InlineKeyboardButton(text='Add ' + female, callback_data=str(FEMALE))
|
||||
], [
|
||||
InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)),
|
||||
InlineKeyboardButton(text='Back', callback_data=str(END))
|
||||
]]
|
||||
|
||||
keyboard = InlineKeyboardMarkup(buttons)
|
||||
update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
|
||||
|
||||
return SELECTING_GENDER
|
||||
|
||||
|
||||
def end_second_level(update, context):
|
||||
"""Return to top level conversation."""
|
||||
context.user_data[START_OVER] = True
|
||||
start(update, context)
|
||||
|
||||
return END
|
||||
|
||||
|
||||
# Third level callbacks
|
||||
def select_feature(update, context):
|
||||
"""Select a feature to update for the person."""
|
||||
buttons = [[
|
||||
InlineKeyboardButton(text='Name', callback_data=str(NAME)),
|
||||
InlineKeyboardButton(text='Age', callback_data=str(AGE)),
|
||||
InlineKeyboardButton(text='Done', callback_data=str(END)),
|
||||
]]
|
||||
keyboard = InlineKeyboardMarkup(buttons)
|
||||
|
||||
# If we collect features for a new person, clear the cache and save the gender
|
||||
if not context.user_data.get(START_OVER):
|
||||
context.user_data[FEATURES] = {GENDER: update.callback_query.data}
|
||||
text = 'Please select a feature to update.'
|
||||
update.callback_query.edit_message_text(text=text, reply_markup=keyboard)
|
||||
# But after we do that, we need to send a new message
|
||||
else:
|
||||
text = 'Got it! Please select a feature to update.'
|
||||
update.message.reply_text(text=text, reply_markup=keyboard)
|
||||
|
||||
context.user_data[START_OVER] = False
|
||||
return SELECTING_FEATURE
|
||||
|
||||
|
||||
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.edit_message_text(text=text)
|
||||
|
||||
return TYPING
|
||||
|
||||
|
||||
def save_input(update, context):
|
||||
"""Save input for feature and return to feature selection."""
|
||||
ud = context.user_data
|
||||
ud[FEATURES][ud[CURRENT_FEATURE]] = update.message.text
|
||||
|
||||
ud[START_OVER] = True
|
||||
|
||||
return select_feature(update, context)
|
||||
|
||||
|
||||
def end_describing(update, context):
|
||||
"""End gathering of features and return to parent conversation."""
|
||||
ud = context.user_data
|
||||
level = ud[CURRENT_LEVEL]
|
||||
if not ud.get(level):
|
||||
ud[level] = []
|
||||
ud[level].append(ud[FEATURES])
|
||||
|
||||
# Print upper level menu
|
||||
if level == SELF:
|
||||
ud[START_OVER] = True
|
||||
start(update, context)
|
||||
else:
|
||||
select_level(update, context)
|
||||
|
||||
return END
|
||||
|
||||
|
||||
def stop_nested(update, context):
|
||||
"""Completely end conversation from within nested conversation."""
|
||||
update.message.reply_text('Okay, bye.')
|
||||
|
||||
return STOPPING
|
||||
|
||||
|
||||
# Error handler
|
||||
def error(update, context):
|
||||
"""Log Errors caused by Updates."""
|
||||
logger.warning('Update "%s" caused error "%s"', update, context.error)
|
||||
|
||||
|
||||
def main():
|
||||
# Create the Updater and pass it your bot's token.
|
||||
# Make sure to set use_context=True to use the new context based callbacks
|
||||
# Post version 12 this will no longer be necessary
|
||||
updater = Updater("TOKEN", use_context=True)
|
||||
|
||||
# Get the dispatcher to register handlers
|
||||
dp = updater.dispatcher
|
||||
|
||||
# Set up third level ConversationHandler (collecting features)
|
||||
description_conv = ConversationHandler(
|
||||
entry_points=[CallbackQueryHandler(select_feature,
|
||||
pattern='^' + str(MALE) + '$|^' + str(FEMALE) + '$')],
|
||||
|
||||
states={
|
||||
SELECTING_FEATURE: [CallbackQueryHandler(ask_for_input,
|
||||
pattern='^(?!' + str(END) + ').*$')],
|
||||
TYPING: [MessageHandler(Filters.text, save_input)],
|
||||
},
|
||||
|
||||
fallbacks=[
|
||||
CallbackQueryHandler(end_describing, pattern='^' + str(END) + '$'),
|
||||
CommandHandler('stop', stop_nested)
|
||||
],
|
||||
|
||||
map_to_parent={
|
||||
# Return to second level menu
|
||||
END: SELECTING_LEVEL,
|
||||
# End conversation alltogether
|
||||
STOPPING: STOPPING,
|
||||
}
|
||||
)
|
||||
|
||||
# Set up second level ConversationHandler (adding a person)
|
||||
add_member_conv = ConversationHandler(
|
||||
entry_points=[CallbackQueryHandler(select_level,
|
||||
pattern='^' + str(ADDING_MEMBER) + '$')],
|
||||
|
||||
states={
|
||||
SELECTING_LEVEL: [CallbackQueryHandler(select_gender,
|
||||
pattern='^{0}$|^{1}$'.format(str(PARENTS),
|
||||
str(CHILDREN)))],
|
||||
SELECTING_GENDER: [description_conv]
|
||||
},
|
||||
|
||||
fallbacks=[
|
||||
CallbackQueryHandler(show_data, pattern='^' + str(SHOWING) + '$'),
|
||||
CallbackQueryHandler(end_second_level, pattern='^' + str(END) + '$'),
|
||||
CommandHandler('stop', stop_nested)
|
||||
],
|
||||
|
||||
map_to_parent={
|
||||
# After showing data return to top level menu
|
||||
SHOWING: SHOWING,
|
||||
# Return to top level menu
|
||||
END: SELECTING_ACTION,
|
||||
# End conversation alltogether
|
||||
STOPPING: END,
|
||||
}
|
||||
)
|
||||
|
||||
# Set up top level ConversationHandler (selecting action)
|
||||
conv_handler = ConversationHandler(
|
||||
entry_points=[CommandHandler('start', start)],
|
||||
|
||||
states={
|
||||
SHOWING: [CallbackQueryHandler(start, pattern='^' + str(END) + '$')],
|
||||
SELECTING_ACTION: [
|
||||
add_member_conv,
|
||||
CallbackQueryHandler(show_data, pattern='^' + str(SHOWING) + '$'),
|
||||
CallbackQueryHandler(adding_self, pattern='^' + str(ADDING_SELF) + '$'),
|
||||
CallbackQueryHandler(end, pattern='^' + str(END) + '$'),
|
||||
],
|
||||
DESCRIBING_SELF: [description_conv],
|
||||
},
|
||||
|
||||
fallbacks=[CommandHandler('stop', stop)],
|
||||
)
|
||||
# Because the states of the third level conversation map to the ones of the
|
||||
# second level conversation, we need to be a bit hacky about that:
|
||||
conv_handler.states[SELECTING_LEVEL] = conv_handler.states[SELECTING_ACTION]
|
||||
conv_handler.states[STOPPING] = conv_handler.entry_points
|
||||
|
||||
dp.add_handler(conv_handler)
|
||||
|
||||
# log all errors
|
||||
dp.add_error_handler(error)
|
||||
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
|
||||
# Run the bot until you press Ctrl-C or the process receives SIGINT,
|
||||
# SIGTERM or SIGABRT. This should be used most of the time, since
|
||||
# start_polling() is non-blocking and will stop the bot gracefully.
|
||||
updater.idle()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,10 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
#
|
||||
# THIS EXAMPLE HAS BEEN UPDATED TO WORK WITH THE BETA VERSION 12 OF PYTHON-TELEGRAM-BOT.
|
||||
# If you're still using version 11.1.0, please see the examples at
|
||||
# https://github.com/python-telegram-bot/python-telegram-bot/tree/v11.1.0/examples
|
||||
|
||||
"""
|
||||
Simple Bot to print/download all incoming passport data
|
||||
@@ -25,7 +21,7 @@ logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def msg(bot, update):
|
||||
def msg(update, context):
|
||||
# If we received any passport data
|
||||
passport_data = update.message.passport_data
|
||||
if passport_data:
|
||||
@@ -81,9 +77,9 @@ def msg(bot, update):
|
||||
actual_file.download()
|
||||
|
||||
|
||||
def error(bot, update, error):
|
||||
def error(update, context):
|
||||
"""Log Errors caused by Updates."""
|
||||
logger.warning('Update "%s" caused error "%s"', update, error)
|
||||
logger.warning('Update "%s" caused error "%s"', update, context.error)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
#
|
||||
# THIS EXAMPLE HAS BEEN UPDATED TO WORK WITH THE BETA VERSION 12 OF PYTHON-TELEGRAM-BOT.
|
||||
# If you're still using version 11.1.0, please see the examples at
|
||||
# https://github.com/python-telegram-bot/python-telegram-bot/tree/v11.1.0/examples
|
||||
|
||||
"""
|
||||
Basic example for a bot that can receive payment from user.
|
||||
@@ -46,7 +42,7 @@ def start_with_shipping_callback(update, context):
|
||||
currency = "USD"
|
||||
# price in dollars
|
||||
price = 1
|
||||
# price * 100 so as to include 2 d.p.
|
||||
# price * 100 so as to include 2 decimal points
|
||||
# check https://core.telegram.org/bots/payments#supported-currencies for more details
|
||||
prices = [LabeledPrice("Test", price * 100)]
|
||||
|
||||
@@ -70,7 +66,7 @@ def start_without_shipping_callback(update, context):
|
||||
currency = "USD"
|
||||
# price in dollars
|
||||
price = 1
|
||||
# price * 100 so as to include 2 d.p.
|
||||
# price * 100 so as to include 2 decimal points
|
||||
prices = [LabeledPrice("Test", price * 100)]
|
||||
|
||||
# optionally pass need_name=True, need_phone_number=True,
|
||||
@@ -107,9 +103,9 @@ def precheckout_callback(update, context):
|
||||
query.answer(ok=True)
|
||||
|
||||
|
||||
# finally, after contacting to the payment provider...
|
||||
# finally, after contacting the payment provider...
|
||||
def successful_payment_callback(update, context):
|
||||
# do something after successful receive of payment?
|
||||
# do something after successfully receiving payment?
|
||||
update.message.reply_text("Thank you for your payment!")
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
#
|
||||
# THIS EXAMPLE HAS BEEN UPDATED TO WORK WITH THE BETA VERSION 12 OF PYTHON-TELEGRAM-BOT.
|
||||
# If you're still using version 11.1.0, please see the examples at
|
||||
# https://github.com/python-telegram-bot/python-telegram-bot/tree/v11.1.0/examples
|
||||
|
||||
"""
|
||||
First, a few callback functions are defined. Then, those functions are passed to
|
||||
@@ -19,7 +15,7 @@ bot.
|
||||
"""
|
||||
|
||||
from telegram import ReplyKeyboardMarkup
|
||||
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
|
||||
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters,
|
||||
ConversationHandler, PicklePersistence)
|
||||
|
||||
import logging
|
||||
@@ -62,13 +58,13 @@ def start(update, context):
|
||||
|
||||
|
||||
def regular_choice(update, context):
|
||||
text = update.message.text
|
||||
text = update.message.text.lower()
|
||||
context.user_data['choice'] = text
|
||||
if context.user_data.get(text):
|
||||
reply_text = 'Your {}, I already know the following ' \
|
||||
'about that: {}'.format(text.lower(), context.user_data[text.lower()])
|
||||
'about that: {}'.format(text, context.user_data[text])
|
||||
else:
|
||||
reply_text = 'Your {}? Yes, I would love to hear about that!'.format(text.lower())
|
||||
reply_text = 'Your {}? Yes, I would love to hear about that!'.format(text)
|
||||
update.message.reply_text(reply_text)
|
||||
|
||||
return TYPING_REPLY
|
||||
@@ -113,7 +109,7 @@ def done(update, context):
|
||||
|
||||
def error(update, context):
|
||||
"""Log Errors caused by Updates."""
|
||||
logger.warning('Update "%s" caused error "%s"', update, error)
|
||||
logger.warning('Update "%s" caused error "%s"', update, context.error)
|
||||
|
||||
|
||||
def main():
|
||||
@@ -129,10 +125,10 @@ def main():
|
||||
entry_points=[CommandHandler('start', start)],
|
||||
|
||||
states={
|
||||
CHOOSING: [RegexHandler('^(Age|Favourite colour|Number of siblings)$',
|
||||
regular_choice),
|
||||
RegexHandler('^Something else...$',
|
||||
custom_choice),
|
||||
CHOOSING: [MessageHandler(Filters.regex('^(Age|Favourite colour|Number of siblings)$'),
|
||||
regular_choice),
|
||||
MessageHandler(Filters.regex('^Something else...$'),
|
||||
custom_choice),
|
||||
],
|
||||
|
||||
TYPING_CHOICE: [MessageHandler(Filters.text,
|
||||
@@ -144,7 +140,7 @@ def main():
|
||||
],
|
||||
},
|
||||
|
||||
fallbacks=[RegexHandler('^Done$', done)],
|
||||
fallbacks=[MessageHandler(Filters.regex('^Done$'), done)],
|
||||
name="my_conversation",
|
||||
persistent=True
|
||||
)
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
#
|
||||
# THIS EXAMPLE HAS BEEN UPDATED TO WORK WITH THE BETA VERSION 12 OF PYTHON-TELEGRAM-BOT.
|
||||
# If you're still using version 11.1.0, please see the examples at
|
||||
# https://github.com/python-telegram-bot/python-telegram-bot/tree/v11.1.0/examples
|
||||
|
||||
"""
|
||||
Simple Bot to send timed Telegram messages.
|
||||
@@ -33,8 +29,8 @@ logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Define a few command handlers. These usually take the two arguments bot and
|
||||
# update. Error handlers also receive the raised TelegramError object in error.
|
||||
# Define a few command handlers. These usually take the two arguments update and
|
||||
# context. Error handlers also receive the raised TelegramError object in error.
|
||||
def start(update, context):
|
||||
update.message.reply_text('Hi! Use /set <seconds> to set a timer')
|
||||
|
||||
@@ -55,9 +51,12 @@ def set_timer(update, context):
|
||||
update.message.reply_text('Sorry we can not go back to future!')
|
||||
return
|
||||
|
||||
# Add job to queue
|
||||
job = context.job_queue.run_once(alarm, due, context=chat_id)
|
||||
context.chat_data['job'] = job
|
||||
# Add job to queue and stop current one if there is a timer already
|
||||
if 'job' in context.chat_data:
|
||||
old_job = context.chat_data['job']
|
||||
old_job.schedule_removal()
|
||||
new_job = context.job_queue.run_once(alarm, due, context=chat_id)
|
||||
context.chat_data['job'] = new_job
|
||||
|
||||
update.message.reply_text('Timer successfully set!')
|
||||
|
||||
|
||||
@@ -8,3 +8,4 @@ beautifulsoup4
|
||||
pytest==4.2.0
|
||||
pytest-timeout
|
||||
wheel
|
||||
attrs==19.1.0
|
||||
|
||||
@@ -14,7 +14,8 @@ upload-dir = docs/build/html
|
||||
|
||||
[flake8]
|
||||
max-line-length = 99
|
||||
ignore = W503
|
||||
ignore = W503, W605
|
||||
exclude = setup.py, docs/source/conf.py
|
||||
|
||||
[yapf]
|
||||
based_on_style = google
|
||||
|
||||
@@ -23,6 +23,7 @@ from .user import User
|
||||
from .files.chatphoto import ChatPhoto
|
||||
from .chat import Chat
|
||||
from .chatmember import ChatMember
|
||||
from .chatpermissions import ChatPermissions
|
||||
from .files.photosize import PhotoSize
|
||||
from .files.audio import Audio
|
||||
from .files.voice import Voice
|
||||
@@ -47,6 +48,8 @@ from .files.file import File
|
||||
from .parsemode import ParseMode
|
||||
from .messageentity import MessageEntity
|
||||
from .games.game import Game
|
||||
from .poll import Poll, PollOption
|
||||
from .loginurl import LoginUrl
|
||||
from .games.callbackgame import CallbackGame
|
||||
from .payment.shippingaddress import ShippingAddress
|
||||
from .payment.orderinfo import OrderInfo
|
||||
@@ -57,11 +60,11 @@ from .passport.passportfile import PassportFile
|
||||
from .passport.data import IdDocumentData, PersonalDetails, ResidentialAddress
|
||||
from .passport.encryptedpassportelement import EncryptedPassportElement
|
||||
from .passport.passportdata import PassportData
|
||||
from .inline.inlinekeyboardbutton import InlineKeyboardButton
|
||||
from .inline.inlinekeyboardmarkup import InlineKeyboardMarkup
|
||||
from .message import Message
|
||||
from .callbackquery import CallbackQuery
|
||||
from .choseninlineresult import ChosenInlineResult
|
||||
from .inline.inlinekeyboardbutton import InlineKeyboardButton
|
||||
from .inline.inlinekeyboardmarkup import InlineKeyboardMarkup
|
||||
from .inline.inputmessagecontent import InputMessageContent
|
||||
from .inline.inlinequery import InlineQuery
|
||||
from .inline.inlinequeryresult import InlineQueryResult
|
||||
@@ -123,8 +126,8 @@ from .version import __version__ # noqa: F401
|
||||
__author__ = 'devs@python-telegram-bot.org'
|
||||
|
||||
__all__ = [
|
||||
'Audio', 'Bot', 'Chat', 'ChatMember', 'ChatAction', 'ChosenInlineResult', 'CallbackQuery',
|
||||
'Contact', 'Document', 'File', 'ForceReply', 'InlineKeyboardButton',
|
||||
'Audio', 'Bot', 'Chat', 'ChatMember', 'ChatPermissions', 'ChatAction', 'ChosenInlineResult',
|
||||
'CallbackQuery', 'Contact', 'Document', 'File', 'ForceReply', 'InlineKeyboardButton',
|
||||
'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult', 'InlineQueryResult',
|
||||
'InlineQueryResultArticle', 'InlineQueryResultAudio', 'InlineQueryResultCachedAudio',
|
||||
'InlineQueryResultCachedDocument', 'InlineQueryResultCachedGif',
|
||||
@@ -152,5 +155,6 @@ __all__ = [
|
||||
'PersonalDetails', 'ResidentialAddress', 'InputMediaVideo', 'InputMediaAnimation',
|
||||
'InputMediaAudio', 'InputMediaDocument', 'TelegramDecryptionError',
|
||||
'PassportElementErrorSelfie', 'PassportElementErrorTranslationFile',
|
||||
'PassportElementErrorTranslationFiles', 'PassportElementErrorUnspecified'
|
||||
'PassportElementErrorTranslationFiles', 'PassportElementErrorUnspecified', 'Poll',
|
||||
'PollOption', 'LoginUrl'
|
||||
]
|
||||
|
||||
+14
-1
@@ -17,15 +17,28 @@
|
||||
# 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 subprocess
|
||||
|
||||
import certifi
|
||||
import future
|
||||
|
||||
|
||||
from . import __version__ as telegram_ver
|
||||
|
||||
|
||||
def _git_revision():
|
||||
try:
|
||||
output = subprocess.check_output(["git", "describe", "--long", "--tags"],
|
||||
stderr=subprocess.STDOUT)
|
||||
except (subprocess.SubprocessError, OSError):
|
||||
return None
|
||||
return output.decode().strip()
|
||||
|
||||
|
||||
def print_ver_info():
|
||||
print('python-telegram-bot {0}'.format(telegram_ver))
|
||||
git_revision = _git_revision()
|
||||
print('python-telegram-bot {0}'.format(telegram_ver) + (' ({0})'.format(git_revision)
|
||||
if git_revision else ''))
|
||||
print('certifi {0}'.format(certifi.__version__))
|
||||
print('future {0}'.format(future.__version__))
|
||||
print('Python {0}'.format(sys.version.replace('\n', ' ')))
|
||||
|
||||
+160
-35
@@ -37,7 +37,7 @@ from future.utils import string_types
|
||||
from telegram import (User, Message, Update, Chat, ChatMember, UserProfilePhotos, File,
|
||||
ReplyMarkup, TelegramObject, WebhookInfo, GameHighScore, StickerSet,
|
||||
PhotoSize, Audio, Document, Sticker, Video, Animation, Voice, VideoNote,
|
||||
Location, Venue, Contact, InputFile)
|
||||
Location, Venue, Contact, InputFile, Poll)
|
||||
from telegram.error import InvalidToken, TelegramError
|
||||
from telegram.utils.helpers import to_timestamp
|
||||
from telegram.utils.request import Request
|
||||
@@ -260,13 +260,16 @@ class Bot(TelegramObject):
|
||||
@log
|
||||
def delete_message(self, chat_id, message_id, timeout=None, **kwargs):
|
||||
"""
|
||||
Use this method to delete a message. A message can only be deleted if it was sent less
|
||||
than 48 hours ago. Any such recently sent outgoing message may be deleted. Additionally,
|
||||
if the bot is an administrator in a group chat, it can delete any message. If the bot is
|
||||
an administrator in a supergroup, it can delete messages from any other user and service
|
||||
messages about people joining or leaving the group (other types of service messages may
|
||||
only be removed by the group creator). In channels, bots can only remove their own
|
||||
messages.
|
||||
Use this method to delete a message, including service messages, with the following
|
||||
limitations:
|
||||
|
||||
- A message can only be deleted if it was sent less than 48 hours ago.
|
||||
- Bots can delete outgoing messages in private chats, groups, and supergroups.
|
||||
- Bots can delete incoming messages in private chats.
|
||||
- Bots granted can_post_messages permissions can delete outgoing messages in channels.
|
||||
- If the bot is an administrator of a group, it can delete any message there.
|
||||
- If the bot has can_delete_messages permission in a supergroup or a channel, it can
|
||||
delete any message there.
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
|
||||
@@ -1030,7 +1033,7 @@ class Bot(TelegramObject):
|
||||
"""
|
||||
url = '{0}/sendLocation'.format(self.base_url)
|
||||
|
||||
if not (all([latitude, longitude]) or location):
|
||||
if not ((latitude is not None and longitude is not None) or location):
|
||||
raise ValueError("Either location or latitude and longitude must be passed as"
|
||||
"argument.")
|
||||
|
||||
@@ -1511,7 +1514,8 @@ class Bot(TelegramObject):
|
||||
calling get_file again.
|
||||
|
||||
Args:
|
||||
file_id (:obj:`str` | :class:`telegram.Audio` | :class:`telegram.Document` | \
|
||||
file_id (:obj:`str` | :class:`telegram.Animation` | :class:`telegram.Audio` | \
|
||||
:class:`telegram.ChatPhoto` | :class:`telegram.Document` | \
|
||||
:class:`telegram.PhotoSize` | :class:`telegram.Sticker` | \
|
||||
:class:`telegram.Video` | :class:`telegram.VideoNote` | \
|
||||
:class:`telegram.Voice`):
|
||||
@@ -2682,14 +2686,18 @@ class Bot(TelegramObject):
|
||||
return result
|
||||
|
||||
@log
|
||||
def restrict_chat_member(self, chat_id, user_id, until_date=None, can_send_messages=None,
|
||||
can_send_media_messages=None, can_send_other_messages=None,
|
||||
can_add_web_page_previews=None, timeout=None, **kwargs):
|
||||
def restrict_chat_member(self, chat_id, user_id, permissions, until_date=None,
|
||||
timeout=None, **kwargs):
|
||||
"""
|
||||
Use this method to restrict a user in a supergroup. The bot must be an administrator in
|
||||
the supergroup for this to work and must have the appropriate admin rights. Pass True for
|
||||
all boolean parameters to lift restrictions from a user.
|
||||
|
||||
Note:
|
||||
Since Bot API 4.4, :attr:`restrict_chat_member` takes the new user permissions in a
|
||||
single argument of type :class:`telegram.ChatPermissions`. The old way of passing
|
||||
parameters will not keep working forever.
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
|
||||
of the target supergroup (in the format @supergroupusername).
|
||||
@@ -2698,15 +2706,7 @@ class Bot(TelegramObject):
|
||||
will be lifted for the user, unix time. If user is restricted for more than 366
|
||||
days or less than 30 seconds from the current time, they are considered to be
|
||||
restricted forever.
|
||||
can_send_messages (:obj:`bool`, optional): Pass True, if the user can send text
|
||||
messages, contacts, locations and venues.
|
||||
can_send_media_messages (:obj:`bool`, optional): Pass True, if the user can send
|
||||
audios, documents, photos, videos, video notes and voice notes, implies
|
||||
can_send_messages.
|
||||
can_send_other_messages (:obj:`bool`, optional): Pass True, if the user can send
|
||||
animations, games, stickers and use inline bots, implies can_send_media_messages.
|
||||
can_add_web_page_previews (:obj:`bool`, optional): Pass True, if the user may add
|
||||
web page previews to their messages, implies can_send_media_messages.
|
||||
permissions (:class:`telegram.ChatPermissions`): New user permissions.
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
@@ -2717,24 +2717,15 @@ class Bot(TelegramObject):
|
||||
|
||||
Raises:
|
||||
:class:`telegram.TelegramError`
|
||||
|
||||
"""
|
||||
url = '{0}/restrictChatMember'.format(self.base_url)
|
||||
|
||||
data = {'chat_id': chat_id, 'user_id': user_id}
|
||||
data = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions.to_dict()}
|
||||
|
||||
if until_date is not None:
|
||||
if isinstance(until_date, datetime):
|
||||
until_date = to_timestamp(until_date)
|
||||
data['until_date'] = until_date
|
||||
if can_send_messages is not None:
|
||||
data['can_send_messages'] = can_send_messages
|
||||
if can_send_media_messages is not None:
|
||||
data['can_send_media_messages'] = can_send_media_messages
|
||||
if can_send_other_messages is not None:
|
||||
data['can_send_other_messages'] = can_send_other_messages
|
||||
if can_add_web_page_previews is not None:
|
||||
data['can_add_web_page_previews'] = can_add_web_page_previews
|
||||
data.update(kwargs)
|
||||
|
||||
result = self._request.post(url, data, timeout=timeout)
|
||||
@@ -2812,6 +2803,38 @@ class Bot(TelegramObject):
|
||||
|
||||
return result
|
||||
|
||||
@log
|
||||
def set_chat_permissions(self, chat_id, permissions, timeout=None, **kwargs):
|
||||
"""
|
||||
Use this method to set default chat permissions for all members. The bot must be an
|
||||
administrator in the group or a supergroup for this to work and must have the
|
||||
:attr:`can_restrict_members` admin rights. Returns True on success.
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username of
|
||||
the target supergroup (in the format `@supergroupusername`).
|
||||
permissions (:class:`telegram.ChatPermissions`): New default chat permissions.
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: Returns True on success.
|
||||
|
||||
Raises:
|
||||
:class:`telegram.TelegramError`
|
||||
|
||||
"""
|
||||
url = '{0}/setChatPermissions'.format(self.base_url)
|
||||
|
||||
data = {'chat_id': chat_id, 'permissions': permissions.to_dict()}
|
||||
data.update(kwargs)
|
||||
|
||||
result = self._request.post(url, data, timeout=timeout)
|
||||
|
||||
return result
|
||||
|
||||
@log
|
||||
def export_chat_invite_link(self, chat_id, timeout=None, **kwargs):
|
||||
"""
|
||||
@@ -2955,8 +2978,9 @@ class Bot(TelegramObject):
|
||||
@log
|
||||
def set_chat_description(self, chat_id, description, timeout=None, **kwargs):
|
||||
"""
|
||||
Use this method to change the description of a supergroup or a channel. The bot must be an
|
||||
administrator in the chat for this to work and must have the appropriate admin rights.
|
||||
Use this method to change the description of a group, a supergroup or a channel. The bot
|
||||
must be an administrator in the chat for this to work and must have the appropriate admin
|
||||
rights.
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
|
||||
@@ -3319,8 +3343,103 @@ class Bot(TelegramObject):
|
||||
|
||||
return result
|
||||
|
||||
@log
|
||||
def send_poll(self,
|
||||
chat_id,
|
||||
question,
|
||||
options,
|
||||
disable_notification=None,
|
||||
reply_to_message_id=None,
|
||||
reply_markup=None,
|
||||
timeout=None,
|
||||
**kwargs):
|
||||
"""
|
||||
Use this method to send a native poll. A native poll can't be sent to a private chat.
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target private chat.
|
||||
question (:obj:`str`): Poll question, 1-255 characters.
|
||||
options (List[:obj:`str`]): List of answer options, 2-10 strings 1-100 characters each.
|
||||
disable_notification (:obj:`bool`, optional): Sends the message silently. Users will
|
||||
receive a notification with no sound.
|
||||
reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the
|
||||
original message.
|
||||
reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A
|
||||
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
|
||||
to remove reply keyboard or to force a reply from the user.
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.Message`: On success, the sent Message is returned.
|
||||
|
||||
Raises:
|
||||
:class:`telegram.TelegramError`
|
||||
|
||||
"""
|
||||
url = '{0}/sendPoll'.format(self.base_url)
|
||||
|
||||
data = {
|
||||
'chat_id': chat_id,
|
||||
'question': question,
|
||||
'options': options
|
||||
}
|
||||
|
||||
return self._message(url, data, timeout=timeout, disable_notification=disable_notification,
|
||||
reply_to_message_id=reply_to_message_id, reply_markup=reply_markup,
|
||||
**kwargs)
|
||||
|
||||
@log
|
||||
def stop_poll(self,
|
||||
chat_id,
|
||||
message_id,
|
||||
reply_markup=None,
|
||||
timeout=None,
|
||||
**kwargs):
|
||||
"""
|
||||
Use this method to stop a poll which was sent by the bot.
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
|
||||
of the target channel (in the format @channelusername).
|
||||
message_id (:obj:`int`): Identifier of the original message with the poll.
|
||||
reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A
|
||||
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
|
||||
to remove reply keyboard or to force a reply from the user.
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.Poll`: On success, the stopped Poll with the
|
||||
final results is returned.
|
||||
|
||||
Raises:
|
||||
:class:`telegram.TelegramError`
|
||||
|
||||
"""
|
||||
url = '{0}/stopPoll'.format(self.base_url)
|
||||
|
||||
data = {
|
||||
'chat_id': chat_id,
|
||||
'message_id': message_id
|
||||
}
|
||||
|
||||
if reply_markup:
|
||||
if isinstance(reply_markup, ReplyMarkup):
|
||||
data['reply_markup'] = reply_markup.to_json()
|
||||
else:
|
||||
data['reply_markup'] = reply_markup
|
||||
|
||||
result = self._request.post(url, data, timeout=timeout)
|
||||
|
||||
return Poll.de_json(result, self)
|
||||
|
||||
def to_dict(self):
|
||||
data = {'id': self.id, 'username': self.username, 'first_name': self.username}
|
||||
data = {'id': self.id, 'username': self.username, 'first_name': self.first_name}
|
||||
|
||||
if self.last_name:
|
||||
data['last_name'] = self.last_name
|
||||
@@ -3428,6 +3547,8 @@ class Bot(TelegramObject):
|
||||
"""Alias for :attr:`restrict_chat_member`"""
|
||||
promoteChatMember = promote_chat_member
|
||||
"""Alias for :attr:`promote_chat_member`"""
|
||||
setChatPermissions = set_chat_permissions
|
||||
"""Alias for :attr:`set_chat_permissions`"""
|
||||
exportChatInviteLink = export_chat_invite_link
|
||||
"""Alias for :attr:`export_chat_invite_link`"""
|
||||
setChatPhoto = set_chat_photo
|
||||
@@ -3456,3 +3577,7 @@ class Bot(TelegramObject):
|
||||
"""Alias for :attr:`delete_sticker_from_set`"""
|
||||
setPassportDataErrors = set_passport_data_errors
|
||||
"""Alias for :attr:`set_passport_data_errors`"""
|
||||
sendPoll = send_poll
|
||||
"""Alias for :attr:`send_poll`"""
|
||||
stopPoll = stop_poll
|
||||
"""Alias for :attr:`stop_poll`"""
|
||||
|
||||
+36
-7
@@ -20,6 +20,7 @@
|
||||
"""This module contains an object that represents a Telegram Chat."""
|
||||
|
||||
from telegram import TelegramObject, ChatPhoto
|
||||
from .chatpermissions import ChatPermissions
|
||||
|
||||
|
||||
class Chat(TelegramObject):
|
||||
@@ -32,12 +33,13 @@ class Chat(TelegramObject):
|
||||
username (:obj:`str`): Optional. Username.
|
||||
first_name (:obj:`str`): Optional. First name of the other party in a private chat.
|
||||
last_name (:obj:`str`): Optional. Last name of the other party in a private chat.
|
||||
all_members_are_administrators (:obj:`bool`): Optional.
|
||||
photo (:class:`telegram.ChatPhoto`): Optional. Chat photo.
|
||||
description (:obj:`str`): Optional. Description, for supergroups and channel chats.
|
||||
description (:obj:`str`): Optional. Description, for groups, supergroups and channel chats.
|
||||
invite_link (:obj:`str`): Optional. Chat invite link, for supergroups and channel chats.
|
||||
pinned_message (:class:`telegram.Message`): Optional. Pinned message, for supergroups.
|
||||
Returned only in get_chat.
|
||||
permissions (:class:`telegram.ChatPermission`): Optional. Default chat member permissions,
|
||||
for groups and supergroups. 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.
|
||||
@@ -54,15 +56,15 @@ class Chat(TelegramObject):
|
||||
available.
|
||||
first_name(:obj:`str`, optional): First name of the other party in a private chat.
|
||||
last_name(:obj:`str`, optional): Last name of the other party in a private chat.
|
||||
all_members_are_administrators (:obj:`bool`, optional): True if a group has `All Members
|
||||
Are Admins` enabled.
|
||||
photo (:class:`telegram.ChatPhoto`, optional): Chat photo. Returned only in getChat.
|
||||
description (:obj:`str`, optional): Description, for supergroups and channel chats.
|
||||
description (:obj:`str`, optional): Description, for groups, supergroups and channel chats.
|
||||
Returned only in get_chat.
|
||||
invite_link (:obj:`str`, optional): Chat invite link, for supergroups and channel chats.
|
||||
Returned only in get_chat.
|
||||
pinned_message (:class:`telegram.Message`, optional): Pinned message, for supergroups.
|
||||
Returned only in get_chat.
|
||||
permissions (:class:`telegram.ChatPermission`): Optional. Default chat member permissions,
|
||||
for groups and supergroups. 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.
|
||||
@@ -88,12 +90,12 @@ class Chat(TelegramObject):
|
||||
username=None,
|
||||
first_name=None,
|
||||
last_name=None,
|
||||
all_members_are_administrators=None,
|
||||
bot=None,
|
||||
photo=None,
|
||||
description=None,
|
||||
invite_link=None,
|
||||
pinned_message=None,
|
||||
permissions=None,
|
||||
sticker_set_name=None,
|
||||
can_set_sticker_set=None,
|
||||
**kwargs):
|
||||
@@ -105,11 +107,13 @@ class Chat(TelegramObject):
|
||||
self.username = username
|
||||
self.first_name = first_name
|
||||
self.last_name = last_name
|
||||
self.all_members_are_administrators = all_members_are_administrators
|
||||
# TODO: Remove (also from tests), when Telegram drops this completely
|
||||
self.all_members_are_administrators = kwargs.get('all_members_are_administrators')
|
||||
self.photo = photo
|
||||
self.description = description
|
||||
self.invite_link = invite_link
|
||||
self.pinned_message = pinned_message
|
||||
self.permissions = permissions
|
||||
self.sticker_set_name = sticker_set_name
|
||||
self.can_set_sticker_set = can_set_sticker_set
|
||||
|
||||
@@ -132,6 +136,7 @@ class Chat(TelegramObject):
|
||||
data['photo'] = ChatPhoto.de_json(data.get('photo'), bot)
|
||||
from telegram import Message
|
||||
data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot)
|
||||
data['permissions'] = ChatPermissions.de_json(data.get('permissions'), bot)
|
||||
|
||||
return cls(bot=bot, **data)
|
||||
|
||||
@@ -221,6 +226,17 @@ class Chat(TelegramObject):
|
||||
"""
|
||||
return self.bot.unban_chat_member(self.id, *args, **kwargs)
|
||||
|
||||
def set_permissions(self, *args, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
bot.set_chat_permissions(update.message.chat.id, *args, **kwargs)
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: If the action was sent successfully.
|
||||
|
||||
"""
|
||||
return self.bot.set_chat_permissions(self.id, *args, **kwargs)
|
||||
|
||||
def send_message(self, *args, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
@@ -337,3 +353,16 @@ class Chat(TelegramObject):
|
||||
|
||||
"""
|
||||
return self.bot.send_voice(self.id, *args, **kwargs)
|
||||
|
||||
def send_poll(self, *args, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
bot.send_poll(Chat.id, *args, **kwargs)
|
||||
|
||||
Where Chat is the current instance.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.Message`: On success, instance representing the message posted.
|
||||
|
||||
"""
|
||||
return self.bot.send_poll(self.id, *args, **kwargs)
|
||||
|
||||
+22
-13
@@ -32,24 +32,27 @@ class ChatMember(TelegramObject):
|
||||
for this user.
|
||||
can_be_edited (:obj:`bool`): Optional. If the bot is allowed to edit administrator
|
||||
privileges of that user.
|
||||
can_change_info (:obj:`bool`): Optional. If the administrator can change the chat title,
|
||||
photo and other settings.
|
||||
can_change_info (:obj:`bool`): Optional. If the user can change the chat title, photo and
|
||||
other settings.
|
||||
can_post_messages (:obj:`bool`): Optional. If the administrator can post in the channel.
|
||||
can_edit_messages (:obj:`bool`): Optional. If the administrator can edit messages of other
|
||||
users.
|
||||
can_delete_messages (:obj:`bool`): Optional. If the administrator can delete messages of
|
||||
other users.
|
||||
can_invite_users (:obj:`bool`): Optional. If the administrator can invite new users to the
|
||||
chat.
|
||||
can_invite_users (:obj:`bool`): Optional. If the user can invite new users to the chat.
|
||||
can_restrict_members (:obj:`bool`): Optional. If the administrator can restrict, ban or
|
||||
unban chat members.
|
||||
can_pin_messages (:obj:`bool`): Optional. If the administrator can pin messages.
|
||||
can_pin_messages (:obj:`bool`): Optional. If the user can pin messages.
|
||||
can_promote_members (:obj:`bool`): Optional. If the administrator can add new
|
||||
administrators.
|
||||
is_member (:obj:`bool`): Optional. Restricted only. True, if the user is a member of the
|
||||
chat at the moment of the request.
|
||||
can_send_messages (:obj:`bool`): Optional. If the user can send text messages, contacts,
|
||||
locations and venues.
|
||||
can_send_media_messages (:obj:`bool`): Optional. If the user can send media messages,
|
||||
implies can_send_messages.
|
||||
can_send_polls (:obj:`bool`): Optional. True, if the user is allowed to
|
||||
send polls.
|
||||
can_send_other_messages (:obj:`bool`): Optional. If the user can send animations, games,
|
||||
stickers and use inline bots, implies can_send_media_messages.
|
||||
can_add_web_page_previews (:obj:`bool`): Optional. If user may add web page previews to his
|
||||
@@ -63,29 +66,33 @@ class ChatMember(TelegramObject):
|
||||
restrictions will be lifted for this user.
|
||||
can_be_edited (:obj:`bool`, optional): Administrators only. True, if the bot is allowed to
|
||||
edit administrator privileges of that user.
|
||||
can_change_info (:obj:`bool`, optional): Administrators only. True, if the administrator
|
||||
can change the chat title, photo and other settings.
|
||||
can_change_info (:obj:`bool`, optional): Administrators and restricted only. True, if the
|
||||
user can change the chat title, photo and other settings.
|
||||
can_post_messages (:obj:`bool`, optional): Administrators only. True, if the administrator
|
||||
can post in the channel, channels only.
|
||||
can_edit_messages (:obj:`bool`, optional): Administrators only. True, if the administrator
|
||||
can edit messages of other users, channels only.
|
||||
can_delete_messages (:obj:`bool`, optional): Administrators only. True, if the
|
||||
administrator can delete messages of other user.
|
||||
can_invite_users (:obj:`bool`, optional): Administrators only. True, if the administrator
|
||||
can invite new users to the chat.
|
||||
can_invite_users (:obj:`bool`, optional): Administrators and restricted only. True, if the
|
||||
user can invite new users to the chat.
|
||||
can_restrict_members (:obj:`bool`, optional): Administrators only. True, if the
|
||||
administrator can restrict, ban or unban chat members.
|
||||
can_pin_messages (:obj:`bool`, optional): Administrators only. True, if the administrator
|
||||
can pin messages, supergroups only.
|
||||
can_pin_messages (:obj:`bool`, optional): Administrators and restricted only. True, if the
|
||||
user can pin messages, supergroups only.
|
||||
can_promote_members (:obj:`bool`, optional): Administrators only. True, if the
|
||||
administrator can add new administrators with a subset of his own privileges or demote
|
||||
administrators that he has promoted, directly or indirectly (promoted by administrators
|
||||
that were appointed by the user).
|
||||
is_member (:obj:`bool`, optional): Restricted only. True, if the user is a member of the
|
||||
chat at the moment of the request.
|
||||
can_send_messages (:obj:`bool`, optional): Restricted only. True, if the user can send text
|
||||
messages, contacts, locations and venues.
|
||||
can_send_media_messages (:obj:`bool`, optional): Restricted only. True, if the user can
|
||||
send audios, documents, photos, videos, video notes and voice notes, implies
|
||||
can_send_messages.
|
||||
can_send_polls (:obj:`bool`, optional): Restricted only. True, if the user is allowed to
|
||||
send polls.
|
||||
can_send_other_messages (:obj:`bool`, optional): Restricted only. True, if the user can
|
||||
send animations, games, stickers and use inline bots, implies can_send_media_messages.
|
||||
can_add_web_page_previews (:obj:`bool`, optional): Restricted only. True, if user may add
|
||||
@@ -110,8 +117,8 @@ class ChatMember(TelegramObject):
|
||||
can_delete_messages=None, can_invite_users=None,
|
||||
can_restrict_members=None, can_pin_messages=None,
|
||||
can_promote_members=None, can_send_messages=None,
|
||||
can_send_media_messages=None, can_send_other_messages=None,
|
||||
can_add_web_page_previews=None, **kwargs):
|
||||
can_send_media_messages=None, can_send_polls=None, can_send_other_messages=None,
|
||||
can_add_web_page_previews=None, is_member=None, **kwargs):
|
||||
# Required
|
||||
self.user = user
|
||||
self.status = status
|
||||
@@ -127,8 +134,10 @@ class ChatMember(TelegramObject):
|
||||
self.can_promote_members = can_promote_members
|
||||
self.can_send_messages = can_send_messages
|
||||
self.can_send_media_messages = can_send_media_messages
|
||||
self.can_send_polls = can_send_polls
|
||||
self.can_send_other_messages = can_send_other_messages
|
||||
self.can_add_web_page_previews = can_add_web_page_previews
|
||||
self.is_member = is_member
|
||||
|
||||
self._id_attrs = (self.user, self.status)
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2018
|
||||
# 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 ChatPermission."""
|
||||
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
class ChatPermissions(TelegramObject):
|
||||
"""Describes actions that a non-administrator user is allowed to take in a chat.
|
||||
|
||||
Attributes:
|
||||
can_send_messages (:obj:`bool`): Optional. True, if the user is allowed to send text
|
||||
messages, contacts, locations and venues.
|
||||
can_send_media_messages (:obj:`bool`): Optional. True, if the user is allowed to send
|
||||
audios, documents, photos, videos, video notes and voice notes, implies
|
||||
:attr:`can_send_messages`.
|
||||
can_send_polls (:obj:`bool`): Optional. True, if the user is allowed to send polls, implies
|
||||
:attr:`can_send_messages`.
|
||||
can_send_other_messages (:obj:`bool`): Optional. True, if the user is allowed to send
|
||||
animations, games, stickers and use inline bots, implies
|
||||
:attr:`can_send_media_messages`.
|
||||
can_add_web_page_previews (:obj:`bool`): Optional. True, if the user is allowed to add web
|
||||
page previews to their messages, implies :attr:`can_send_media_messages`.
|
||||
can_change_info (:obj:`bool`): Optional. True, if the user is allowed to change the chat
|
||||
title, photo and other settings. Ignored in public supergroups.
|
||||
can_invite_users (:obj:`bool`): Optional. True, if the user is allowed to invite new users
|
||||
to the chat.
|
||||
can_pin_messages (:obj:`bool`): Optional. True, if the user is allowed to pin messages.
|
||||
Ignored in public supergroups.
|
||||
|
||||
Args:
|
||||
can_send_messages (:obj:`bool`, optional): True, if the user is allowed to send text
|
||||
messages, contacts, locations and venues.
|
||||
can_send_media_messages (:obj:`bool`, optional): True, if the user is allowed to send
|
||||
audios, documents, photos, videos, video notes and voice notes, implies
|
||||
:attr:`can_send_messages`.
|
||||
can_send_polls (:obj:`bool`, optional): True, if the user is allowed to send polls, implies
|
||||
:attr:`can_send_messages`.
|
||||
can_send_other_messages (:obj:`bool`, optional): True, if the user is allowed to send
|
||||
animations, games, stickers and use inline bots, implies
|
||||
:attr:`can_send_media_messages`.
|
||||
can_add_web_page_previews (:obj:`bool`, optional): True, if the user is allowed to add web
|
||||
page previews to their messages, implies :attr:`can_send_media_messages`.
|
||||
can_change_info (:obj:`bool`, optional): True, if the user is allowed to change the chat
|
||||
title, photo and other settings. Ignored in public supergroups.
|
||||
can_invite_users (:obj:`bool`, optional): True, if the user is allowed to invite new users
|
||||
to the chat.
|
||||
can_pin_messages (:obj:`bool`, optional): True, if the user is allowed to pin messages.
|
||||
Ignored in public supergroups.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, can_send_messages=None, can_send_media_messages=None, can_send_polls=None,
|
||||
can_send_other_messages=None, can_add_web_page_previews=None,
|
||||
can_change_info=None, can_invite_users=None, can_pin_messages=None, **kwargs):
|
||||
# Required
|
||||
self.can_send_messages = can_send_messages
|
||||
self.can_send_media_messages = can_send_media_messages
|
||||
self.can_send_polls = can_send_polls
|
||||
self.can_send_other_messages = can_send_other_messages
|
||||
self.can_add_web_page_previews = can_add_web_page_previews
|
||||
self.can_change_info = can_change_info
|
||||
self.can_invite_users = can_invite_users
|
||||
self.can_pin_messages = can_pin_messages
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, bot):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return cls(**data)
|
||||
@@ -17,7 +17,8 @@
|
||||
"""Constants in the Telegram network.
|
||||
|
||||
The following constants were extracted from the
|
||||
`Telegram Bots FAQ <https://core.telegram.org/bots/faq>`_.
|
||||
`Telegram Bots FAQ <https://core.telegram.org/bots/faq>`_ and
|
||||
`Telegram Bots API <https://core.telegram.org/bots/api>`_.
|
||||
|
||||
Attributes:
|
||||
MAX_MESSAGE_LENGTH (:obj:`int`): 4096
|
||||
@@ -25,6 +26,7 @@ Attributes:
|
||||
SUPPORTED_WEBHOOK_PORTS (List[:obj:`int`]): [443, 80, 88, 8443]
|
||||
MAX_FILESIZE_DOWNLOAD (:obj:`int`): In bytes (20MB)
|
||||
MAX_FILESIZE_UPLOAD (:obj:`int`): In bytes (50MB)
|
||||
MAX_PHOTOSIZE_UPLOAD (:obj:`int`): In bytes (10MB)
|
||||
MAX_MESSAGES_PER_SECOND_PER_CHAT (:obj:`int`): `1`. Telegram may allow short bursts that go
|
||||
over this limit, but eventually you'll begin receiving 429 errors.
|
||||
MAX_MESSAGES_PER_SECOND (:obj:`int`): 30
|
||||
@@ -47,6 +49,7 @@ MAX_CAPTION_LENGTH = 1024
|
||||
SUPPORTED_WEBHOOK_PORTS = [443, 80, 88, 8443]
|
||||
MAX_FILESIZE_DOWNLOAD = int(20E6) # (20MB)
|
||||
MAX_FILESIZE_UPLOAD = int(50E6) # (50MB)
|
||||
MAX_PHOTOSIZE_UPLOAD = int(10E6) # (10MB)
|
||||
MAX_MESSAGES_PER_SECOND_PER_CHAT = 1
|
||||
MAX_MESSAGES_PER_SECOND = 30
|
||||
MAX_MESSAGES_PER_MINUTE_PER_GROUP = 20
|
||||
|
||||
@@ -101,7 +101,7 @@ class BasePersistence(object):
|
||||
|
||||
Args:
|
||||
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].
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.user_data` [user_id].
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -111,7 +111,7 @@ class BasePersistence(object):
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int`): The chat the data might have been changed for.
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.chat_data`[user_id].
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.chat_data` [user_id].
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ class CallbackContext(object):
|
||||
matches (List[:obj:`re match object`], optional): If the associated update originated from
|
||||
a regex-supported handler or had a :class:`Filters.regex`, this will contain a list of
|
||||
match objects for every pattern where ``re.search(pattern, string)`` returned a match.
|
||||
Note that filters short circuit, so combined regex filters will not always
|
||||
be evaluated.
|
||||
args (List[:obj:`str`], optional): Arguments passed to a command if the associated update
|
||||
is handled by :class:`telegram.ext.CommandHandler`, :class:`telegram.ext.PrefixHandler`
|
||||
or :class:`telegram.ext.StringCommandHandler`. It contains a list of the words in the
|
||||
@@ -71,13 +73,31 @@ class CallbackContext(object):
|
||||
raise ValueError('CallbackContext should not be used with a non context aware '
|
||||
'dispatcher!')
|
||||
self._dispatcher = dispatcher
|
||||
self.chat_data = None
|
||||
self.user_data = None
|
||||
self._chat_data = None
|
||||
self._user_data = None
|
||||
self.args = None
|
||||
self.matches = None
|
||||
self.error = None
|
||||
self.job = None
|
||||
|
||||
@property
|
||||
def chat_data(self):
|
||||
return self._chat_data
|
||||
|
||||
@chat_data.setter
|
||||
def chat_data(self, value):
|
||||
raise AttributeError("You can not assign a new value to chat_data, see "
|
||||
"https://git.io/fjxKe")
|
||||
|
||||
@property
|
||||
def user_data(self):
|
||||
return self._user_data
|
||||
|
||||
@user_data.setter
|
||||
def user_data(self, value):
|
||||
raise AttributeError("You can not assign a new value to user_data, see "
|
||||
"https://git.io/fjxKe")
|
||||
|
||||
@classmethod
|
||||
def from_error(cls, update, error, dispatcher):
|
||||
self = cls.from_update(update, dispatcher)
|
||||
@@ -92,9 +112,9 @@ class CallbackContext(object):
|
||||
user = update.effective_user
|
||||
|
||||
if chat:
|
||||
self.chat_data = dispatcher.chat_data[chat.id]
|
||||
self._chat_data = dispatcher.chat_data[chat.id]
|
||||
if user:
|
||||
self.user_data = dispatcher.user_data[user.id]
|
||||
self._user_data = dispatcher.user_data[user.id]
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -332,14 +332,15 @@ class PrefixHandler(CommandHandler):
|
||||
if isinstance(update, Update) and update.effective_message:
|
||||
message = update.effective_message
|
||||
|
||||
text_list = message.text.split()
|
||||
if text_list[0].lower() not in self.command:
|
||||
return None
|
||||
filter_result = self.filters(update)
|
||||
if filter_result:
|
||||
return text_list[1:], filter_result
|
||||
else:
|
||||
return False
|
||||
if message.text:
|
||||
text_list = message.text.split()
|
||||
if text_list[0].lower() not in self.command:
|
||||
return None
|
||||
filter_result = self.filters(update)
|
||||
if filter_result:
|
||||
return text_list[1:], filter_result
|
||||
else:
|
||||
return False
|
||||
|
||||
def collect_additional_context(self, context, update, dispatcher, check_result):
|
||||
context.args = check_result[0]
|
||||
|
||||
@@ -20,10 +20,11 @@
|
||||
|
||||
import logging
|
||||
import warnings
|
||||
from threading import Lock
|
||||
|
||||
from telegram import Update
|
||||
from telegram.ext import (Handler, CallbackQueryHandler, InlineQueryHandler,
|
||||
ChosenInlineResultHandler)
|
||||
ChosenInlineResultHandler, CallbackContext)
|
||||
from telegram.utils.promise import Promise
|
||||
|
||||
|
||||
@@ -37,8 +38,7 @@ class _ConversationTimeoutContext(object):
|
||||
class ConversationHandler(Handler):
|
||||
"""
|
||||
A handler to hold a conversation with a single user by managing four collections of other
|
||||
handlers. Note that neither posts in Telegram Channels, nor group interactions with multiple
|
||||
users are managed by instances of this class.
|
||||
handlers.
|
||||
|
||||
The first collection, a ``list`` named :attr:`entry_points`, is used to initiate the
|
||||
conversation, for example with a :class:`telegram.ext.CommandHandler` or
|
||||
@@ -57,11 +57,6 @@ class ConversationHandler(Handler):
|
||||
a regular text message is expected. You could use this for a ``/cancel`` command or to let the
|
||||
user know their message was not recognized.
|
||||
|
||||
The fourth, optional collection of handlers, a ``list`` named :attr:`timed_out_behavior` is
|
||||
used if the wait for ``run_async`` takes longer than defined in :attr:`run_async_timeout`.
|
||||
For example, you can let the user know that they should wait for a bit before they can
|
||||
continue.
|
||||
|
||||
To change the state of conversation, the callback function of a handler must return the new
|
||||
state after responding to the user. If it does not return anything (returning ``None`` by
|
||||
default), the state will not change. If an entry point callback function returns None,
|
||||
@@ -69,6 +64,20 @@ class ConversationHandler(Handler):
|
||||
To end the conversation, the callback function must return :attr:`END` or ``-1``. To
|
||||
handle the conversation timeout, use handler :attr:`TIMEOUT` or ``-2``.
|
||||
|
||||
Note:
|
||||
In each of the described collections of handlers, a handler may in turn be a
|
||||
:class:`ConversationHandler`. In that case, the nested :class:`ConversationHandler` should
|
||||
have the attribute :attr:`map_to_parent` which allows to return to the parent conversation
|
||||
at specified states within the nested conversation.
|
||||
|
||||
Note that the keys in :attr:`map_to_parent` must not appear as keys in :attr:`states`
|
||||
attribute or else the latter will be ignored. You may map :attr:`END` to one of the parents
|
||||
states to continue the parent conversation after this has ended or even map a state to
|
||||
:attr:`END` to end the *parent* conversation from within the nested one. For an example on
|
||||
nested :class:`ConversationHandler` s, see our `examples`_.
|
||||
|
||||
.. _`examples`: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples
|
||||
|
||||
Attributes:
|
||||
entry_points (List[:class:`telegram.ext.Handler`]): A list of ``Handler`` objects that can
|
||||
trigger the start of the conversation.
|
||||
@@ -86,13 +95,16 @@ class ConversationHandler(Handler):
|
||||
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
|
||||
this value is 0 (default), there will be no timeout. When it's triggered, the last
|
||||
received update will be handled by ALL the handler's who's `check_update` method
|
||||
returns True that are in the state :attr:`ConversationHandler.TIMEOUT`.
|
||||
name (:obj:`str`): Optional. The name for this conversationhandler. Required for
|
||||
persistence
|
||||
persistent (:obj:`bool`): Optional. If the conversations dict for this handler should be
|
||||
saved. Name is required and persistence has to be set in :class:`telegram.ext.Updater`
|
||||
map_to_parent (Dict[:obj:`object`, :obj:`object`]): Optional. A :obj:`dict` that can be
|
||||
used to instruct a nested conversationhandler to transition into a mapped state on
|
||||
its parent conversationhandler in place of a specified nested state.
|
||||
|
||||
Args:
|
||||
entry_points (List[:class:`telegram.ext.Handler`]): A list of ``Handler`` objects that can
|
||||
@@ -124,6 +136,9 @@ class ConversationHandler(Handler):
|
||||
persistence
|
||||
persistent (:obj:`bool`, optional): If the conversations dict for this handler should be
|
||||
saved. Name is required and persistence has to be set in :class:`telegram.ext.Updater`
|
||||
map_to_parent (Dict[:obj:`object`, :obj:`object`], optional): A :obj:`dict` that can be
|
||||
used to instruct a nested conversationhandler to transition into a mapped state on
|
||||
its parent conversationhandler in place of a specified nested state.
|
||||
|
||||
Raises:
|
||||
ValueError
|
||||
@@ -147,7 +162,8 @@ class ConversationHandler(Handler):
|
||||
per_message=False,
|
||||
conversation_timeout=None,
|
||||
name=None,
|
||||
persistent=False):
|
||||
persistent=False,
|
||||
map_to_parent=None):
|
||||
|
||||
self.entry_points = entry_points
|
||||
self.states = states
|
||||
@@ -165,9 +181,12 @@ class ConversationHandler(Handler):
|
||||
self.persistence = None
|
||||
""":obj:`telegram.ext.BasePersistance`: The persistence used to store conversations.
|
||||
Set by dispatcher"""
|
||||
self.map_to_parent = map_to_parent
|
||||
|
||||
self.timeout_jobs = dict()
|
||||
self._timeout_jobs_lock = Lock()
|
||||
self.conversations = dict()
|
||||
self._conversations_lock = Lock()
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -245,7 +264,8 @@ class ConversationHandler(Handler):
|
||||
return None
|
||||
|
||||
key = self._get_key(update)
|
||||
state = self.conversations.get(key)
|
||||
with self._conversations_lock:
|
||||
state = self.conversations.get(key)
|
||||
|
||||
# Resolve promises
|
||||
if isinstance(state, tuple) and len(state) == 2 and isinstance(state[1], Promise):
|
||||
@@ -264,7 +284,8 @@ class ConversationHandler(Handler):
|
||||
if res is None and old_state is None:
|
||||
res = self.END
|
||||
self.update_state(res, key)
|
||||
state = self.conversations.get(key)
|
||||
with self._conversations_lock:
|
||||
state = self.conversations.get(key)
|
||||
else:
|
||||
handlers = self.states.get(self.WAITING, [])
|
||||
for handler in handlers:
|
||||
@@ -323,43 +344,72 @@ class ConversationHandler(Handler):
|
||||
|
||||
"""
|
||||
conversation_key, handler, check_result = check_result
|
||||
|
||||
with self._timeout_jobs_lock:
|
||||
# Remove the old timeout job (if present)
|
||||
timeout_job = self.timeout_jobs.pop(conversation_key, None)
|
||||
|
||||
if timeout_job is not None:
|
||||
timeout_job.schedule_removal()
|
||||
|
||||
new_state = handler.handle_update(update, dispatcher, check_result, context)
|
||||
timeout_job = self.timeout_jobs.pop(conversation_key, None)
|
||||
|
||||
if timeout_job is not None:
|
||||
timeout_job.schedule_removal()
|
||||
if self.conversation_timeout and new_state != self.END:
|
||||
self.timeout_jobs[conversation_key] = dispatcher.job_queue.run_once(
|
||||
self._trigger_timeout, self.conversation_timeout,
|
||||
context=_ConversationTimeoutContext(conversation_key, update, dispatcher))
|
||||
with self._timeout_jobs_lock:
|
||||
if self.conversation_timeout and new_state != self.END:
|
||||
# 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))
|
||||
|
||||
self.update_state(new_state, conversation_key)
|
||||
if isinstance(self.map_to_parent, dict) and new_state in self.map_to_parent:
|
||||
self.update_state(self.END, conversation_key)
|
||||
return self.map_to_parent.get(new_state)
|
||||
else:
|
||||
self.update_state(new_state, conversation_key)
|
||||
|
||||
def update_state(self, new_state, key):
|
||||
if new_state == self.END:
|
||||
if key in self.conversations:
|
||||
# If there is no key in conversations, nothing is done.
|
||||
del self.conversations[key]
|
||||
if self.persistent:
|
||||
self.persistence.update_conversation(self.name, key, None)
|
||||
with self._conversations_lock:
|
||||
if key in self.conversations:
|
||||
# If there is no key in conversations, nothing is done.
|
||||
del self.conversations[key]
|
||||
if self.persistent:
|
||||
self.persistence.update_conversation(self.name, key, None)
|
||||
|
||||
elif isinstance(new_state, Promise):
|
||||
self.conversations[key] = (self.conversations.get(key), new_state)
|
||||
if self.persistent:
|
||||
self.persistence.update_conversation(self.name, key,
|
||||
(self.conversations.get(key), new_state))
|
||||
with self._conversations_lock:
|
||||
self.conversations[key] = (self.conversations.get(key), new_state)
|
||||
if self.persistent:
|
||||
self.persistence.update_conversation(self.name, key,
|
||||
(self.conversations.get(key), new_state))
|
||||
|
||||
elif new_state is not None:
|
||||
self.conversations[key] = new_state
|
||||
if self.persistent:
|
||||
self.persistence.update_conversation(self.name, key, new_state)
|
||||
with self._conversations_lock:
|
||||
self.conversations[key] = new_state
|
||||
if self.persistent:
|
||||
self.persistence.update_conversation(self.name, key, new_state)
|
||||
|
||||
def _trigger_timeout(self, bot, job):
|
||||
def _trigger_timeout(self, context, job=None):
|
||||
self.logger.debug('conversation timeout was triggered!')
|
||||
del self.timeout_jobs[job.context.conversation_key]
|
||||
|
||||
# Backward compatibility with bots that do not use CallbackContext
|
||||
callback_context = None
|
||||
if isinstance(context, CallbackContext):
|
||||
job = context.job
|
||||
callback_context = context
|
||||
|
||||
context = job.context
|
||||
|
||||
with self._timeout_jobs_lock:
|
||||
found_job = self.timeout_jobs[context.conversation_key]
|
||||
if found_job is not job:
|
||||
# The timeout has been canceled in handle_update
|
||||
return
|
||||
del self.timeout_jobs[context.conversation_key]
|
||||
|
||||
handlers = self.states.get(self.TIMEOUT, [])
|
||||
for handler in handlers:
|
||||
check = handler.check_update(job.context.update)
|
||||
check = handler.check_update(context.update)
|
||||
if check is not None and check is not False:
|
||||
handler.handle_update(job.context.update, job.context.dispatcher, check)
|
||||
self.update_state(self.END, job.context.conversation_key)
|
||||
handler.handle_update(context.update, context.dispatcher, check, callback_context)
|
||||
self.update_state(self.END, context.conversation_key)
|
||||
|
||||
@@ -176,7 +176,7 @@ class DictPersistence(BasePersistence):
|
||||
|
||||
Args:
|
||||
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].
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.user_data` [user_id].
|
||||
"""
|
||||
if self._user_data.get(user_id) == data:
|
||||
return
|
||||
@@ -188,7 +188,7 @@ class DictPersistence(BasePersistence):
|
||||
|
||||
Args:
|
||||
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].
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.chat_data` [chat_id].
|
||||
"""
|
||||
if self._chat_data.get(chat_id) == data:
|
||||
return
|
||||
|
||||
+63
-34
@@ -52,7 +52,7 @@ def run_async(func):
|
||||
|
||||
Warning:
|
||||
If you're using @run_async you cannot rely on adding custom attributes to
|
||||
:class:`telegram.ext.CallbackContext`s. See its docs for more info.
|
||||
:class:`telegram.ext.CallbackContext`. See its docs for more info.
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
@@ -166,7 +166,8 @@ class Dispatcher(object):
|
||||
base_name = '{}_'.format(base_name) if base_name else ''
|
||||
|
||||
for i in range(workers):
|
||||
thread = Thread(target=self._pooled, name='{}{}'.format(base_name, i))
|
||||
thread = Thread(target=self._pooled, name='Bot:{}:worker:{}{}'.format(self.bot.id,
|
||||
base_name, i))
|
||||
self.__async_threads.add(thread)
|
||||
thread.start()
|
||||
|
||||
@@ -214,7 +215,7 @@ class Dispatcher(object):
|
||||
|
||||
Warning:
|
||||
If you're using @run_async you cannot rely on adding custom attributes to
|
||||
:class:`telegram.ext.CallbackContext`s. See its docs for more info.
|
||||
:class:`telegram.ext.CallbackContext`. See its docs for more info.
|
||||
|
||||
Args:
|
||||
func (:obj:`callable`): The function to run in the thread.
|
||||
@@ -274,6 +275,7 @@ class Dispatcher(object):
|
||||
|
||||
self.logger.debug('Processing Update: %s' % update)
|
||||
self.process_update(update)
|
||||
self.update_queue.task_done()
|
||||
|
||||
self.running = False
|
||||
self.logger.debug('Dispatcher thread stopped')
|
||||
@@ -313,6 +315,43 @@ class Dispatcher(object):
|
||||
The update to process.
|
||||
|
||||
"""
|
||||
|
||||
def persist_update(update):
|
||||
"""Persist a single update.
|
||||
|
||||
Args:
|
||||
update (:class:`telegram.Update`):
|
||||
The update to process.
|
||||
|
||||
"""
|
||||
if self.persistence and isinstance(update, Update):
|
||||
if self.persistence.store_chat_data and update.effective_chat:
|
||||
chat_id = update.effective_chat.id
|
||||
try:
|
||||
self.persistence.update_chat_data(chat_id,
|
||||
self.chat_data[chat_id])
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving chat data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
if self.persistence.store_user_data and update.effective_user:
|
||||
user_id = update.effective_user.id
|
||||
try:
|
||||
self.persistence.update_user_data(user_id,
|
||||
self.user_data[user_id])
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving user data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
|
||||
# An error happened while polling
|
||||
if isinstance(update, TelegramError):
|
||||
try:
|
||||
@@ -331,43 +370,27 @@ class Dispatcher(object):
|
||||
if not context and self.use_context:
|
||||
context = CallbackContext.from_update(update, self)
|
||||
handler.handle_update(update, self, check, context)
|
||||
if self.persistence and isinstance(update, Update):
|
||||
if self.persistence.store_chat_data and update.effective_chat:
|
||||
chat_id = update.effective_chat.id
|
||||
try:
|
||||
self.persistence.update_chat_data(chat_id,
|
||||
self.chat_data[chat_id])
|
||||
except Exception:
|
||||
self.logger.exception('Saving chat data raised an error')
|
||||
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:
|
||||
self.logger.exception('Saving user data raised an error')
|
||||
persist_update(update)
|
||||
break
|
||||
|
||||
# Stop processing with any other handler.
|
||||
except DispatcherHandlerStop:
|
||||
self.logger.debug('Stopping further handlers due to DispatcherHandlerStop')
|
||||
persist_update(update)
|
||||
break
|
||||
|
||||
# Dispatch any error.
|
||||
except TelegramError as te:
|
||||
self.logger.warning('A TelegramError was raised while processing the Update')
|
||||
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, te)
|
||||
self.dispatch_error(update, e)
|
||||
except DispatcherHandlerStop:
|
||||
self.logger.debug('Error handler stopped further handlers')
|
||||
break
|
||||
# Errors should not stop the thread.
|
||||
except Exception:
|
||||
self.logger.exception('An uncaught error was raised while handling the error')
|
||||
|
||||
# Errors should not stop the thread.
|
||||
except Exception:
|
||||
self.logger.exception('An uncaught error was raised while processing the update')
|
||||
self.logger.exception('An error was raised while processing the update and an '
|
||||
'uncaught error was raised while handling the error '
|
||||
'with an error_handler')
|
||||
|
||||
def add_handler(self, handler, group=DEFAULT_GROUP):
|
||||
"""Register a handler.
|
||||
@@ -433,17 +456,23 @@ class Dispatcher(object):
|
||||
"""Update :attr:`user_data` and :attr:`chat_data` in :attr:`persistence`.
|
||||
"""
|
||||
if self.persistence:
|
||||
for chat_id in self.chat_data:
|
||||
self.persistence.update_chat_data(chat_id, self.chat_data[chat_id])
|
||||
for user_id in self.user_data:
|
||||
self.persistence.update_user_data(user_id, self.user_data[user_id])
|
||||
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])
|
||||
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])
|
||||
|
||||
def add_error_handler(self, callback):
|
||||
"""Registers an error handler in the Dispatcher.
|
||||
"""Registers an error handler in the Dispatcher. This handler will receive every error
|
||||
which happens in your bot.
|
||||
|
||||
Warning: The errors handled within these handlers won't show up in the logger, so you
|
||||
need to make sure that you reraise the error.
|
||||
|
||||
Args:
|
||||
callback (:obj:`callable`): The callback function for this error handler. Will be
|
||||
called when an error is raised Callback signature for context based API:
|
||||
called when an error is raised. Callback signature for context based API:
|
||||
|
||||
``def callback(update: Update, context: CallbackContext)``
|
||||
|
||||
@@ -469,7 +498,7 @@ class Dispatcher(object):
|
||||
|
||||
Args:
|
||||
update (:obj:`str` | :class:`telegram.Update` | None): The update that caused the error
|
||||
error (:class:`telegram.TelegramError`): The Telegram error that was raised.
|
||||
error (:obj:`Exception`): The error that was raised.
|
||||
|
||||
"""
|
||||
if self.error_handlers:
|
||||
|
||||
+251
-63
@@ -22,7 +22,9 @@ import re
|
||||
|
||||
from future.utils import string_types
|
||||
|
||||
from telegram import Chat
|
||||
from telegram import Chat, Update
|
||||
|
||||
__all__ = ['Filters', 'BaseFilter', 'InvertedFilter', 'MergedFilter']
|
||||
|
||||
|
||||
class BaseFilter(object):
|
||||
@@ -47,6 +49,16 @@ class BaseFilter(object):
|
||||
>>> (Filters.text & (Filters.entity(URL) | Filters.entity(TEXT_LINK)))
|
||||
>>> Filters.text & (~ Filters.forwarded)
|
||||
|
||||
Note:
|
||||
Filters use the same short circuiting logic that pythons `and`, `or` and `not`.
|
||||
This means that for example:
|
||||
|
||||
>>> Filters.regex(r'(a?x)') | Filters.regex(r'(b?x)')
|
||||
|
||||
With a message.text of `x`, will only ever return the matches for the first filter,
|
||||
since the second one is never evaluated.
|
||||
|
||||
|
||||
If you want to create your own filters create a class inheriting from this class and implement
|
||||
a `filter` method that returns a boolean: `True` if the message should be handled, `False`
|
||||
otherwise. Note that the filters work only as class instances, not actual class objects
|
||||
@@ -175,21 +187,27 @@ class MergedFilter(BaseFilter):
|
||||
# We need to check if the filters are data filters and if so return the merged data.
|
||||
# If it's not a data filter or an or_filter but no matches return bool
|
||||
if self.and_filter:
|
||||
comp_output = self.and_filter(update)
|
||||
if base_output and comp_output:
|
||||
if self.data_filter:
|
||||
merged = self._merge(base_output, comp_output)
|
||||
if merged:
|
||||
return merged
|
||||
return True
|
||||
# And filter needs to short circuit if base is falsey
|
||||
if base_output:
|
||||
comp_output = self.and_filter(update)
|
||||
if comp_output:
|
||||
if self.data_filter:
|
||||
merged = self._merge(base_output, comp_output)
|
||||
if merged:
|
||||
return merged
|
||||
return True
|
||||
elif self.or_filter:
|
||||
comp_output = self.or_filter(update)
|
||||
if base_output or comp_output:
|
||||
# Or filter needs to short circuit if base is truthey
|
||||
if base_output:
|
||||
if self.data_filter:
|
||||
merged = self._merge(base_output, comp_output)
|
||||
if merged:
|
||||
return merged
|
||||
return base_output
|
||||
return True
|
||||
else:
|
||||
comp_output = self.or_filter(update)
|
||||
if comp_output:
|
||||
if self.data_filter:
|
||||
return comp_output
|
||||
return True
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
@@ -213,16 +231,92 @@ class Filters(object):
|
||||
return True
|
||||
|
||||
all = _All()
|
||||
""":obj:`Filter`: All Messages."""
|
||||
"""All Messages."""
|
||||
|
||||
class _Text(BaseFilter):
|
||||
name = 'Filters.text'
|
||||
|
||||
class _TextIterable(BaseFilter):
|
||||
|
||||
def __init__(self, iterable):
|
||||
self.iterable = iterable
|
||||
self.name = 'Filters.text({})'.format(iterable)
|
||||
|
||||
def filter(self, message):
|
||||
if message.text and not message.text.startswith('/'):
|
||||
return message.text in self.iterable
|
||||
return False
|
||||
|
||||
def __call__(self, update):
|
||||
if isinstance(update, Update):
|
||||
if self.update_filter:
|
||||
return self.filter(update)
|
||||
else:
|
||||
return self.filter(update.effective_message)
|
||||
else:
|
||||
return self._TextIterable(update)
|
||||
|
||||
def filter(self, message):
|
||||
return bool(message.text and not message.text.startswith('/'))
|
||||
|
||||
text = _Text()
|
||||
""":obj:`Filter`: Text Messages."""
|
||||
"""Text Messages. If an iterable of strings is passed, it filters messages to only allow those
|
||||
whose text is appearing in the given iterable.
|
||||
|
||||
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
|
||||
custom :class:`telegram.ReplyKeyboardMarkup`::
|
||||
|
||||
buttons = ['Start', 'Settings', 'Back']
|
||||
markup = ReplyKeyboardMarkup.from_column(buttons)
|
||||
...
|
||||
MessageHandler(Filters.text(buttons), callback_method)
|
||||
|
||||
Args:
|
||||
update (Iterable[: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):
|
||||
|
||||
def __init__(self, iterable):
|
||||
self.iterable = iterable
|
||||
self.name = 'Filters.caption({})'.format(iterable)
|
||||
|
||||
def filter(self, message):
|
||||
if message.caption:
|
||||
return message.caption in self.iterable
|
||||
return False
|
||||
|
||||
def __call__(self, update):
|
||||
if isinstance(update, Update):
|
||||
if self.update_filter:
|
||||
return self.filter(update)
|
||||
else:
|
||||
return self.filter(update.effective_message)
|
||||
else:
|
||||
return self._CaptionIterable(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.
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
class _Command(BaseFilter):
|
||||
name = 'Filters.command'
|
||||
@@ -231,7 +325,7 @@ class Filters(object):
|
||||
return bool(message.text and message.text.startswith('/'))
|
||||
|
||||
command = _Command()
|
||||
""":obj:`Filter`: Messages starting with ``/``."""
|
||||
"""Messages starting with ``/``."""
|
||||
|
||||
class regex(BaseFilter):
|
||||
"""
|
||||
@@ -249,6 +343,15 @@ class Filters(object):
|
||||
you want your pattern to be case insensitive. This approach is recommended
|
||||
if you need to specify flags on your pattern.
|
||||
|
||||
Note:
|
||||
Filters use the same short circuiting logic that pythons `and`, `or` and `not`.
|
||||
This means that for example:
|
||||
|
||||
>>> Filters.regex(r'(a?x)') | Filters.regex(r'(b?x)')
|
||||
|
||||
With a message.text of `x`, will only ever return the matches for the first filter,
|
||||
since the second one is never evaluated.
|
||||
|
||||
Args:
|
||||
pattern (:obj:`str` | :obj:`Pattern`): The regex pattern.
|
||||
"""
|
||||
@@ -276,7 +379,7 @@ class Filters(object):
|
||||
return bool(message.reply_to_message)
|
||||
|
||||
reply = _Reply()
|
||||
""":obj:`Filter`: Messages that are a reply to another message."""
|
||||
"""Messages that are a reply to another message."""
|
||||
|
||||
class _Audio(BaseFilter):
|
||||
name = 'Filters.audio'
|
||||
@@ -285,7 +388,7 @@ class Filters(object):
|
||||
return bool(message.audio)
|
||||
|
||||
audio = _Audio()
|
||||
""":obj:`Filter`: Messages that contain :class:`telegram.Audio`."""
|
||||
"""Messages that contain :class:`telegram.Audio`."""
|
||||
|
||||
class _Document(BaseFilter):
|
||||
name = 'Filters.document'
|
||||
@@ -299,7 +402,7 @@ class Filters(object):
|
||||
The user can manipulate the mime-type of a message and
|
||||
send media with wrong types that don't fit to this handler.
|
||||
|
||||
Examples:
|
||||
Example:
|
||||
Filters.documents.category('audio/') returnes `True` for all types
|
||||
of audio sent as file, for example 'audio/mpeg' or 'audio/x-wav'
|
||||
"""
|
||||
@@ -332,8 +435,8 @@ class Filters(object):
|
||||
The user can manipulate the mime-type of a message and
|
||||
send media with wrong types that don't fit to this handler.
|
||||
|
||||
Examples:
|
||||
Filters.documents.mime_type('audio/mpeg') filters all audio in mp3 format.
|
||||
Example:
|
||||
``Filters.documents.mime_type('audio/mpeg')`` filters all audio in mp3 format.
|
||||
"""
|
||||
|
||||
def __init__(self, mimetype):
|
||||
@@ -369,7 +472,91 @@ class Filters(object):
|
||||
return bool(message.document)
|
||||
|
||||
document = _Document()
|
||||
""":obj:`Filter`: Messages that contain :class:`telegram.Document`."""
|
||||
"""
|
||||
Subset for messages containing a document/file.
|
||||
|
||||
Examples:
|
||||
Use these filters like: ``Filters.document.mp3``,
|
||||
``Filters.document.mime_type("text/plain")`` etc. Or use just
|
||||
``Filters.document`` for all document messages.
|
||||
|
||||
Attributes:
|
||||
category: This Filter filters documents by their category in the mime-type attribute.
|
||||
|
||||
Example:
|
||||
``Filters.documents.category('audio/')`` filters all types
|
||||
of audio sent as file, for example 'audio/mpeg' or 'audio/x-wav'. The following
|
||||
attributes can be used as a shortcut like: ``Filters.document.audio``
|
||||
|
||||
application:
|
||||
audio:
|
||||
image:
|
||||
video:
|
||||
text:
|
||||
mime_type: This Filter filters documents by their mime-type attribute.
|
||||
|
||||
Example:
|
||||
``Filters.documents.mime_type('audio/mpeg')`` filters all audio in mp3 format. The
|
||||
following attributes can be used as a shortcut like: ``Filters.document.jpg``
|
||||
apk:
|
||||
doc:
|
||||
docx:
|
||||
exe:
|
||||
gif:
|
||||
jpg:
|
||||
mp3:
|
||||
pdf:
|
||||
py:
|
||||
svg:
|
||||
txt:
|
||||
targz:
|
||||
wav:
|
||||
xml:
|
||||
zip:
|
||||
category: This Filter filters documents by their category in the mime-type attribute
|
||||
|
||||
Note:
|
||||
This Filter only filters by the mime_type of the document,
|
||||
it doesn't check the validity of the document.
|
||||
The user can manipulate the mime-type of a message and
|
||||
send media with wrong types that don't fit to this handler.
|
||||
|
||||
Example:
|
||||
``Filters.documents.category('audio/')`` filters all types
|
||||
of audio sent as file, for example 'audio/mpeg' or 'audio/x-wav'
|
||||
application: Same as ``Filters.document.category("application")``.
|
||||
audio: Same as ``Filters.document.category("audio")``.
|
||||
image: Same as ``Filters.document.category("image")``.
|
||||
video: Same as ``Filters.document.category("video")``.
|
||||
text: Same as ``Filters.document.category("text")``.
|
||||
mime_type: This Filter filters documents by their mime-type attribute
|
||||
|
||||
Note:
|
||||
This Filter only filters by the mime_type of the document,
|
||||
it doesn't check the validity of document.
|
||||
|
||||
The user can manipulate the mime-type of a message and
|
||||
send media with wrong types that don't fit to this handler.
|
||||
|
||||
Example:
|
||||
``Filters.documents.mime_type('audio/mpeg')`` filters all audio in mp3 format.
|
||||
apk: Same as ``Filters.document.mime_type("application/vnd.android.package-archive")``-
|
||||
doc: Same as ``Filters.document.mime_type("application/msword")``-
|
||||
docx: Same as ``Filters.document.mime_type("application/vnd.openxmlformats-\
|
||||
officedocument.wordprocessingml.document")``-
|
||||
exe: Same as ``Filters.document.mime_type("application/x-ms-dos-executable")``-
|
||||
gif: Same as ``Filters.document.mime_type("video/mp4")``-
|
||||
jpg: Same as ``Filters.document.mime_type("image/jpeg")``-
|
||||
mp3: Same as ``Filters.document.mime_type("audio/mpeg")``-
|
||||
pdf: Same as ``Filters.document.mime_type("application/pdf")``-
|
||||
py: Same as ``Filters.document.mime_type("text/x-python")``-
|
||||
svg: Same as ``Filters.document.mime_type("image/svg+xml")``-
|
||||
txt: Same as ``Filters.document.mime_type("text/plain")``-
|
||||
targz: Same as ``Filters.document.mime_type("application/x-compressed-tar")``-
|
||||
wav: Same as ``Filters.document.mime_type("audio/x-wav")``-
|
||||
xml: Same as ``Filters.document.mime_type("application/xml")``-
|
||||
zip: Same as ``Filters.document.mime_type("application/zip")``-
|
||||
"""
|
||||
|
||||
class _Animation(BaseFilter):
|
||||
name = 'Filters.animation'
|
||||
@@ -378,7 +565,7 @@ class Filters(object):
|
||||
return bool(message.animation)
|
||||
|
||||
animation = _Animation()
|
||||
""":obj:`Filter`: Messages that contain :class:`telegram.Animation`."""
|
||||
"""Messages that contain :class:`telegram.Animation`."""
|
||||
|
||||
class _Photo(BaseFilter):
|
||||
name = 'Filters.photo'
|
||||
@@ -387,7 +574,7 @@ class Filters(object):
|
||||
return bool(message.photo)
|
||||
|
||||
photo = _Photo()
|
||||
""":obj:`Filter`: Messages that contain :class:`telegram.PhotoSize`."""
|
||||
"""Messages that contain :class:`telegram.PhotoSize`."""
|
||||
|
||||
class _Sticker(BaseFilter):
|
||||
name = 'Filters.sticker'
|
||||
@@ -396,7 +583,7 @@ class Filters(object):
|
||||
return bool(message.sticker)
|
||||
|
||||
sticker = _Sticker()
|
||||
""":obj:`Filter`: Messages that contain :class:`telegram.Sticker`."""
|
||||
"""Messages that contain :class:`telegram.Sticker`."""
|
||||
|
||||
class _Video(BaseFilter):
|
||||
name = 'Filters.video'
|
||||
@@ -405,7 +592,7 @@ class Filters(object):
|
||||
return bool(message.video)
|
||||
|
||||
video = _Video()
|
||||
""":obj:`Filter`: Messages that contain :class:`telegram.Video`."""
|
||||
"""Messages that contain :class:`telegram.Video`."""
|
||||
|
||||
class _Voice(BaseFilter):
|
||||
name = 'Filters.voice'
|
||||
@@ -414,7 +601,7 @@ class Filters(object):
|
||||
return bool(message.voice)
|
||||
|
||||
voice = _Voice()
|
||||
""":obj:`Filter`: Messages that contain :class:`telegram.Voice`."""
|
||||
"""Messages that contain :class:`telegram.Voice`."""
|
||||
|
||||
class _VideoNote(BaseFilter):
|
||||
name = 'Filters.video_note'
|
||||
@@ -423,7 +610,7 @@ class Filters(object):
|
||||
return bool(message.video_note)
|
||||
|
||||
video_note = _VideoNote()
|
||||
""":obj:`Filter`: Messages that contain :class:`telegram.VideoNote`."""
|
||||
"""Messages that contain :class:`telegram.VideoNote`."""
|
||||
|
||||
class _Contact(BaseFilter):
|
||||
name = 'Filters.contact'
|
||||
@@ -432,7 +619,7 @@ class Filters(object):
|
||||
return bool(message.contact)
|
||||
|
||||
contact = _Contact()
|
||||
""":obj:`Filter`: Messages that contain :class:`telegram.Contact`."""
|
||||
"""Messages that contain :class:`telegram.Contact`."""
|
||||
|
||||
class _Location(BaseFilter):
|
||||
name = 'Filters.location'
|
||||
@@ -441,7 +628,7 @@ class Filters(object):
|
||||
return bool(message.location)
|
||||
|
||||
location = _Location()
|
||||
""":obj:`Filter`: Messages that contain :class:`telegram.Location`."""
|
||||
"""Messages that contain :class:`telegram.Location`."""
|
||||
|
||||
class _Venue(BaseFilter):
|
||||
name = 'Filters.venue'
|
||||
@@ -450,7 +637,7 @@ class Filters(object):
|
||||
return bool(message.venue)
|
||||
|
||||
venue = _Venue()
|
||||
""":obj:`Filter`: Messages that contain :class:`telegram.Venue`."""
|
||||
"""Messages that contain :class:`telegram.Venue`."""
|
||||
|
||||
class _StatusUpdate(BaseFilter):
|
||||
"""Subset for messages containing a status update.
|
||||
@@ -469,7 +656,7 @@ class Filters(object):
|
||||
return bool(message.new_chat_members)
|
||||
|
||||
new_chat_members = _NewChatMembers()
|
||||
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.new_chat_members`."""
|
||||
"""Messages that contain :attr:`telegram.Message.new_chat_members`."""
|
||||
|
||||
class _LeftChatMember(BaseFilter):
|
||||
name = 'Filters.status_update.left_chat_member'
|
||||
@@ -478,7 +665,7 @@ class Filters(object):
|
||||
return bool(message.left_chat_member)
|
||||
|
||||
left_chat_member = _LeftChatMember()
|
||||
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.left_chat_member`."""
|
||||
"""Messages that contain :attr:`telegram.Message.left_chat_member`."""
|
||||
|
||||
class _NewChatTitle(BaseFilter):
|
||||
name = 'Filters.status_update.new_chat_title'
|
||||
@@ -487,7 +674,7 @@ class Filters(object):
|
||||
return bool(message.new_chat_title)
|
||||
|
||||
new_chat_title = _NewChatTitle()
|
||||
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.new_chat_title`."""
|
||||
"""Messages that contain :attr:`telegram.Message.new_chat_title`."""
|
||||
|
||||
class _NewChatPhoto(BaseFilter):
|
||||
name = 'Filters.status_update.new_chat_photo'
|
||||
@@ -496,7 +683,7 @@ class Filters(object):
|
||||
return bool(message.new_chat_photo)
|
||||
|
||||
new_chat_photo = _NewChatPhoto()
|
||||
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.new_chat_photo`."""
|
||||
"""Messages that contain :attr:`telegram.Message.new_chat_photo`."""
|
||||
|
||||
class _DeleteChatPhoto(BaseFilter):
|
||||
name = 'Filters.status_update.delete_chat_photo'
|
||||
@@ -505,7 +692,7 @@ class Filters(object):
|
||||
return bool(message.delete_chat_photo)
|
||||
|
||||
delete_chat_photo = _DeleteChatPhoto()
|
||||
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.delete_chat_photo`."""
|
||||
"""Messages that contain :attr:`telegram.Message.delete_chat_photo`."""
|
||||
|
||||
class _ChatCreated(BaseFilter):
|
||||
name = 'Filters.status_update.chat_created'
|
||||
@@ -515,7 +702,7 @@ class Filters(object):
|
||||
or message.channel_chat_created)
|
||||
|
||||
chat_created = _ChatCreated()
|
||||
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.group_chat_created`,
|
||||
"""Messages that contain :attr:`telegram.Message.group_chat_created`,
|
||||
:attr: `telegram.Message.supergroup_chat_created` or
|
||||
:attr: `telegram.Message.channel_chat_created`."""
|
||||
|
||||
@@ -526,7 +713,7 @@ class Filters(object):
|
||||
return bool(message.migrate_from_chat_id or message.migrate_to_chat_id)
|
||||
|
||||
migrate = _Migrate()
|
||||
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.migrate_from_chat_id` or
|
||||
"""Messages that contain :attr:`telegram.Message.migrate_from_chat_id` or
|
||||
:attr: `telegram.Message.migrate_to_chat_id`."""
|
||||
|
||||
class _PinnedMessage(BaseFilter):
|
||||
@@ -536,7 +723,7 @@ class Filters(object):
|
||||
return bool(message.pinned_message)
|
||||
|
||||
pinned_message = _PinnedMessage()
|
||||
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.pinned_message`."""
|
||||
"""Messages that contain :attr:`telegram.Message.pinned_message`."""
|
||||
|
||||
class _ConnectedWebsite(BaseFilter):
|
||||
name = 'Filters.status_update.connected_website'
|
||||
@@ -545,7 +732,7 @@ class Filters(object):
|
||||
return bool(message.connected_website)
|
||||
|
||||
connected_website = _ConnectedWebsite()
|
||||
""":obj:`Filter`: Messages that contain :attr:`telegram.Message.connected_website`."""
|
||||
"""Messages that contain :attr:`telegram.Message.connected_website`."""
|
||||
|
||||
name = 'Filters.status_update'
|
||||
|
||||
@@ -564,24 +751,24 @@ class Filters(object):
|
||||
``Filters.status_update`` for all status update messages.
|
||||
|
||||
Attributes:
|
||||
chat_created (:obj:`Filter`): Messages that contain
|
||||
chat_created: Messages that contain
|
||||
:attr:`telegram.Message.group_chat_created`,
|
||||
:attr:`telegram.Message.supergroup_chat_created` or
|
||||
:attr:`telegram.Message.channel_chat_created`.
|
||||
delete_chat_photo (:obj:`Filter`): Messages that contain
|
||||
delete_chat_photo: Messages that contain
|
||||
:attr:`telegram.Message.delete_chat_photo`.
|
||||
left_chat_member (:obj:`Filter`): Messages that contain
|
||||
left_chat_member: Messages that contain
|
||||
:attr:`telegram.Message.left_chat_member`.
|
||||
migrate (:obj:`Filter`): Messages that contain
|
||||
migrate: Messages that contain
|
||||
:attr:`telegram.Message.migrate_from_chat_id` or
|
||||
:attr: `telegram.Message.migrate_from_chat_id`.
|
||||
new_chat_members (:obj:`Filter`): Messages that contain
|
||||
new_chat_members: Messages that contain
|
||||
:attr:`telegram.Message.new_chat_members`.
|
||||
new_chat_photo (:obj:`Filter`): Messages that contain
|
||||
new_chat_photo: Messages that contain
|
||||
:attr:`telegram.Message.new_chat_photo`.
|
||||
new_chat_title (:obj:`Filter`): Messages that contain
|
||||
new_chat_title: Messages that contain
|
||||
:attr:`telegram.Message.new_chat_title`.
|
||||
pinned_message (:obj:`Filter`): Messages that contain
|
||||
pinned_message: Messages that contain
|
||||
:attr:`telegram.Message.pinned_message`.
|
||||
"""
|
||||
|
||||
@@ -592,7 +779,7 @@ class Filters(object):
|
||||
return bool(message.forward_date)
|
||||
|
||||
forwarded = _Forwarded()
|
||||
""":obj:`Filter`: Messages that are forwarded."""
|
||||
"""Messages that are forwarded."""
|
||||
|
||||
class _Game(BaseFilter):
|
||||
name = 'Filters.game'
|
||||
@@ -601,7 +788,7 @@ class Filters(object):
|
||||
return bool(message.game)
|
||||
|
||||
game = _Game()
|
||||
""":obj:`Filter`: Messages that contain :class:`telegram.Game`."""
|
||||
"""Messages that contain :class:`telegram.Game`."""
|
||||
|
||||
class entity(BaseFilter):
|
||||
"""
|
||||
@@ -654,7 +841,7 @@ class Filters(object):
|
||||
return message.chat.type == Chat.PRIVATE
|
||||
|
||||
private = _Private()
|
||||
""":obj:`Filter`: Messages sent in a private chat."""
|
||||
"""Messages sent in a private chat."""
|
||||
|
||||
class _Group(BaseFilter):
|
||||
name = 'Filters.group'
|
||||
@@ -663,7 +850,7 @@ class Filters(object):
|
||||
return message.chat.type in [Chat.GROUP, Chat.SUPERGROUP]
|
||||
|
||||
group = _Group()
|
||||
""":obj:`Filter`: Messages sent in a group chat."""
|
||||
"""Messages sent in a group chat."""
|
||||
|
||||
class user(BaseFilter):
|
||||
"""Filters messages to allow only those which are from specified user ID.
|
||||
@@ -749,7 +936,7 @@ class Filters(object):
|
||||
return bool(message.invoice)
|
||||
|
||||
invoice = _Invoice()
|
||||
""":obj:`Filter`: Messages that contain :class:`telegram.Invoice`."""
|
||||
"""Messages that contain :class:`telegram.Invoice`."""
|
||||
|
||||
class _SuccessfulPayment(BaseFilter):
|
||||
name = 'Filters.successful_payment'
|
||||
@@ -758,7 +945,7 @@ class Filters(object):
|
||||
return bool(message.successful_payment)
|
||||
|
||||
successful_payment = _SuccessfulPayment()
|
||||
""":obj:`Filter`: Messages that confirm a :class:`telegram.SuccessfulPayment`."""
|
||||
"""Messages that confirm a :class:`telegram.SuccessfulPayment`."""
|
||||
|
||||
class _PassportData(BaseFilter):
|
||||
name = 'Filters.passport_data'
|
||||
@@ -767,13 +954,14 @@ class Filters(object):
|
||||
return bool(message.passport_data)
|
||||
|
||||
passport_data = _PassportData()
|
||||
""":obj:`Filter`: Messages that contain a :class:`telegram.PassportData`"""
|
||||
"""Messages that contain a :class:`telegram.PassportData`"""
|
||||
|
||||
class language(BaseFilter):
|
||||
"""Filters messages to only allow those which are from users with a certain language code.
|
||||
|
||||
Note: According to telegrams documentation, every single user does not have the
|
||||
`language_code` attribute.
|
||||
Note:
|
||||
According to official telegram api documentation, not every single user has the
|
||||
`language_code` attribute. Do not count on this filter working on all users.
|
||||
|
||||
Examples:
|
||||
``MessageHandler(Filters.language("en"), callback_method)``
|
||||
@@ -860,13 +1048,13 @@ class Filters(object):
|
||||
types.
|
||||
|
||||
Attributes:
|
||||
message (:obj:`Filter`): Updates with :attr:`telegram.Update.message`
|
||||
edited_message (:obj:`Filter`): Updates with :attr:`telegram.Update.edited_message`
|
||||
messages (:obj:`Filter`): Updates with either :attr:`telegram.Update.message` or
|
||||
message: Updates with :attr:`telegram.Update.message`
|
||||
edited_message: Updates with :attr:`telegram.Update.edited_message`
|
||||
messages: Updates with either :attr:`telegram.Update.message` or
|
||||
:attr:`telegram.Update.edited_message`
|
||||
channel_post (:obj:`Filter`): Updates with :attr:`telegram.Update.channel_post`
|
||||
edited_channel_post (:obj:`Filter`): Updates with
|
||||
channel_post: Updates with :attr:`telegram.Update.channel_post`
|
||||
edited_channel_post: Updates with
|
||||
:attr:`telegram.Update.edited_channel_post`
|
||||
channel_posts (:obj:`Filter`): Updates with either :attr:`telegram.Update.channel_post` or
|
||||
channel_posts: Updates with either :attr:`telegram.Update.channel_post` or
|
||||
:attr:`telegram.Update.edited_channel_post`
|
||||
"""
|
||||
|
||||
@@ -151,14 +151,11 @@ class Handler(object):
|
||||
optional_args['update_queue'] = dispatcher.update_queue
|
||||
if self.pass_job_queue:
|
||||
optional_args['job_queue'] = dispatcher.job_queue
|
||||
if self.pass_user_data or self.pass_chat_data:
|
||||
chat = update.effective_chat
|
||||
if self.pass_user_data:
|
||||
user = update.effective_user
|
||||
|
||||
if self.pass_user_data:
|
||||
optional_args['user_data'] = dispatcher.user_data[user.id if user else None]
|
||||
|
||||
if self.pass_chat_data:
|
||||
optional_args['chat_data'] = dispatcher.chat_data[chat.id if chat else None]
|
||||
optional_args['user_data'] = dispatcher.user_data[user.id if user else None]
|
||||
if self.pass_chat_data:
|
||||
chat = update.effective_chat
|
||||
optional_args['chat_data'] = dispatcher.chat_data[chat.id if chat else None]
|
||||
|
||||
return optional_args
|
||||
|
||||
+62
-36
@@ -29,6 +29,7 @@ from threading import Thread, Lock, Event
|
||||
|
||||
from telegram.ext.callbackcontext import CallbackContext
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
from telegram.utils.helpers import to_float_timestamp, _UTC
|
||||
|
||||
|
||||
class Days(object):
|
||||
@@ -42,7 +43,8 @@ class JobQueue(object):
|
||||
Attributes:
|
||||
_queue (:obj:`PriorityQueue`): The queue that holds the Jobs.
|
||||
bot (:class:`telegram.Bot`): The bot instance that should be passed to the jobs.
|
||||
DEPRECATED: Use set_dispatcher instead.
|
||||
DEPRECATED: Use :attr:`set_dispatcher` instead.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, bot=None):
|
||||
@@ -68,32 +70,38 @@ class JobQueue(object):
|
||||
self._running = False
|
||||
|
||||
def set_dispatcher(self, dispatcher):
|
||||
"""Set the dispatcher to be used by this JobQueue. Use this instead of passing a
|
||||
:class:`telegram.Bot` to the JobQueue, which is deprecated.
|
||||
|
||||
Args:
|
||||
dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher.
|
||||
|
||||
"""
|
||||
self._dispatcher = dispatcher
|
||||
|
||||
def _put(self, job, next_t=None, last_t=None):
|
||||
if next_t is None:
|
||||
next_t = job.interval
|
||||
if next_t is None:
|
||||
raise ValueError('next_t is None')
|
||||
def _put(self, job, time_spec=None, previous_t=None):
|
||||
"""
|
||||
Enqueues the job, scheduling its next run at the correct time.
|
||||
|
||||
if isinstance(next_t, datetime.datetime):
|
||||
next_t = (next_t - datetime.datetime.now()).total_seconds()
|
||||
Args:
|
||||
job (telegram.ext.Job): job to enqueue
|
||||
time_spec (optional):
|
||||
Specification of the time for which the job should be scheduled. The precise
|
||||
semantics of this parameter depend on its type (see
|
||||
:func:`telegram.ext.JobQueue.run_repeating` for details).
|
||||
Defaults to now + ``job.interval``.
|
||||
previous_t (optional):
|
||||
Time at which the job last ran (``None`` if it hasn't run yet).
|
||||
|
||||
elif isinstance(next_t, datetime.time):
|
||||
next_datetime = datetime.datetime.combine(datetime.date.today(), next_t)
|
||||
|
||||
if datetime.datetime.now().time() > next_t:
|
||||
next_datetime += datetime.timedelta(days=1)
|
||||
|
||||
next_t = (next_datetime - datetime.datetime.now()).total_seconds()
|
||||
|
||||
elif isinstance(next_t, datetime.timedelta):
|
||||
next_t = next_t.total_seconds()
|
||||
|
||||
next_t += last_t or time.time()
|
||||
|
||||
self.logger.debug('Putting job %s with t=%f', job.name, next_t)
|
||||
"""
|
||||
# get time at which to run:
|
||||
time_spec = time_spec or job.interval
|
||||
if time_spec is None:
|
||||
raise ValueError("no time specification given for scheduling non-repeating job")
|
||||
next_t = to_float_timestamp(time_spec, reference_timestamp=previous_t)
|
||||
|
||||
# enqueue:
|
||||
self.logger.debug('Putting job %s with t=%f', job.name, time_spec)
|
||||
self._queue.put((next_t, job))
|
||||
|
||||
# Wake up the loop if this job should be executed next
|
||||
@@ -133,7 +141,7 @@ class JobQueue(object):
|
||||
|
||||
"""
|
||||
job = Job(callback, repeat=False, context=context, name=name, job_queue=self)
|
||||
self._put(job, next_t=when)
|
||||
self._put(job, time_spec=when)
|
||||
return job
|
||||
|
||||
def run_repeating(self, callback, interval, first=None, context=None, name=None):
|
||||
@@ -172,6 +180,11 @@ class JobQueue(object):
|
||||
:class:`telegram.ext.Job`: The new ``Job`` instance that has been added to the job
|
||||
queue.
|
||||
|
||||
Notes:
|
||||
`interval` is always respected "as-is". That means that if DST changes during that
|
||||
interval, the job might not run at the time one would expect. It is always recommended
|
||||
to pin servers to UTC time, then time related behaviour can always be expected.
|
||||
|
||||
"""
|
||||
job = Job(callback,
|
||||
interval=interval,
|
||||
@@ -179,7 +192,7 @@ class JobQueue(object):
|
||||
context=context,
|
||||
name=name,
|
||||
job_queue=self)
|
||||
self._put(job, next_t=first)
|
||||
self._put(job, time_spec=first)
|
||||
return job
|
||||
|
||||
def run_daily(self, callback, time, days=Days.EVERY_DAY, context=None, name=None):
|
||||
@@ -190,7 +203,8 @@ class JobQueue(object):
|
||||
job. It should take ``bot, job`` as parameters, where ``job`` is the
|
||||
:class:`telegram.ext.Job` instance. It can be used to access its ``Job.context``
|
||||
or change it to a repeating job.
|
||||
time (:obj:`datetime.time`): Time of day at which the job should run.
|
||||
time (:obj:`datetime.time`): Time of day at which the job should run. If the timezone
|
||||
(``time.tzinfo``) is ``None``, UTC will be assumed.
|
||||
days (Tuple[:obj:`int`], optional): Defines on which days of the week the job should
|
||||
run. Defaults to ``EVERY_DAY``
|
||||
context (:obj:`object`, optional): Additional data needed for the callback function.
|
||||
@@ -202,15 +216,21 @@ class JobQueue(object):
|
||||
:class:`telegram.ext.Job`: The new ``Job`` instance that has been added to the job
|
||||
queue.
|
||||
|
||||
Notes:
|
||||
Daily is just an alias for "24 Hours". That means that if DST changes during that
|
||||
interval, the job might not run at the time one would expect. It is always recommended
|
||||
to pin servers to UTC time, then time related behaviour can always be expected.
|
||||
|
||||
"""
|
||||
job = Job(callback,
|
||||
interval=datetime.timedelta(days=1),
|
||||
repeat=True,
|
||||
days=days,
|
||||
tzinfo=time.tzinfo,
|
||||
context=context,
|
||||
name=name,
|
||||
job_queue=self)
|
||||
self._put(job, next_t=time)
|
||||
self._put(job, time_spec=time)
|
||||
return job
|
||||
|
||||
def _set_next_peek(self, t):
|
||||
@@ -254,7 +274,7 @@ class JobQueue(object):
|
||||
|
||||
if job.enabled:
|
||||
try:
|
||||
current_week_day = datetime.datetime.now().weekday()
|
||||
current_week_day = datetime.datetime.now(job.tzinfo).date().weekday()
|
||||
if any(day == current_week_day for day in job.days):
|
||||
self.logger.debug('Running job %s', job.name)
|
||||
job.run(self._dispatcher)
|
||||
@@ -266,7 +286,7 @@ class JobQueue(object):
|
||||
self.logger.debug('Skipping disabled job %s', job.name)
|
||||
|
||||
if job.repeat and not job.removed:
|
||||
self._put(job, last_t=t)
|
||||
self._put(job, previous_t=t)
|
||||
else:
|
||||
self.logger.debug('Dropping non-repeating or removed job %s', job.name)
|
||||
|
||||
@@ -277,7 +297,8 @@ class JobQueue(object):
|
||||
if not self._running:
|
||||
self._running = True
|
||||
self.__start_lock.release()
|
||||
self.__thread = Thread(target=self._main_loop, name="job_queue")
|
||||
self.__thread = Thread(target=self._main_loop,
|
||||
name="Bot:{}:job_queue".format(self._dispatcher.bot.id))
|
||||
self.__thread.start()
|
||||
self.logger.debug('%s thread started', self.__class__.__name__)
|
||||
else:
|
||||
@@ -339,10 +360,11 @@ class Job(object):
|
||||
It should take ``bot, job`` as parameters, where ``job`` is the
|
||||
:class:`telegram.ext.Job` instance. It can be used to access it's :attr:`context`
|
||||
or change it to a repeating job.
|
||||
interval (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta`, optional): The interval in
|
||||
which the job will run. If it is an :obj:`int` or a :obj:`float`, it will be
|
||||
interpreted as seconds. If you don't set this value, you must set :attr:`repeat` to
|
||||
``False`` and specify :attr:`next_t` when you put the job into the job queue.
|
||||
interval (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta`, optional): The time
|
||||
interval between executions of the job. If it is an :obj:`int` or a :obj:`float`,
|
||||
it will be interpreted as seconds. If you don't set this value, you must set
|
||||
:attr:`repeat` to ``False`` and specify :attr:`time_spec` when you put the job into
|
||||
the job queue.
|
||||
repeat (:obj:`bool`, optional): If this job should be periodically execute its callback
|
||||
function (``True``) or only once (``False``). Defaults to ``True``.
|
||||
context (:obj:`object`, optional): Additional data needed for the callback function. Can be
|
||||
@@ -352,7 +374,9 @@ class Job(object):
|
||||
Defaults to ``Days.EVERY_DAY``
|
||||
job_queue (:class:`telegram.ext.JobQueue`, optional): The ``JobQueue`` this job belongs to.
|
||||
Only optional for backward compatibility with ``JobQueue.put()``.
|
||||
|
||||
tzinfo (:obj:`datetime.tzinfo`, optional): timezone associated to this job. Used when
|
||||
checking the day of the week to determine whether a job should run (only relevant when
|
||||
``days is not Days.EVERY_DAY``). Defaults to UTC.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
@@ -362,19 +386,21 @@ class Job(object):
|
||||
context=None,
|
||||
days=Days.EVERY_DAY,
|
||||
name=None,
|
||||
job_queue=None):
|
||||
job_queue=None,
|
||||
tzinfo=_UTC):
|
||||
|
||||
self.callback = callback
|
||||
self.context = context
|
||||
self.name = name or callback.__name__
|
||||
|
||||
self._repeat = repeat
|
||||
self._repeat = None
|
||||
self._interval = None
|
||||
self.interval = interval
|
||||
self.repeat = repeat
|
||||
|
||||
self._days = None
|
||||
self.days = days
|
||||
self.tzinfo = tzinfo
|
||||
|
||||
self._job_queue = weakref.proxy(job_queue) if job_queue is not None else None
|
||||
|
||||
|
||||
@@ -56,12 +56,12 @@ class PicklePersistence(BasePersistence):
|
||||
on any transaction *and* on call fo :meth:`flush`. Default is ``False``.
|
||||
"""
|
||||
|
||||
def __init__(self, filename, store_user_data=True, store_chat_data=True, singe_file=True,
|
||||
def __init__(self, filename, store_user_data=True, store_chat_data=True, single_file=True,
|
||||
on_flush=False):
|
||||
self.filename = filename
|
||||
self.store_user_data = store_user_data
|
||||
self.store_chat_data = store_chat_data
|
||||
self.single_file = singe_file
|
||||
self.single_file = single_file
|
||||
self.on_flush = on_flush
|
||||
self.user_data = None
|
||||
self.chat_data = None
|
||||
@@ -191,7 +191,7 @@ class PicklePersistence(BasePersistence):
|
||||
|
||||
Args:
|
||||
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].
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.user_data` [user_id].
|
||||
"""
|
||||
if self.user_data.get(user_id) == data:
|
||||
return
|
||||
@@ -209,7 +209,7 @@ class PicklePersistence(BasePersistence):
|
||||
|
||||
Args:
|
||||
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].
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.chat_data` [chat_id].
|
||||
"""
|
||||
if self.chat_data.get(chat_id) == data:
|
||||
return
|
||||
|
||||
@@ -157,7 +157,8 @@ class Updater(object):
|
||||
self.__threads = []
|
||||
|
||||
def _init_thread(self, target, name, *args, **kwargs):
|
||||
thr = Thread(target=self._thread_wrapper, name=name, args=(target,) + args, kwargs=kwargs)
|
||||
thr = Thread(target=self._thread_wrapper, name="Bot:{}:{}".format(self.bot.id, name),
|
||||
args=(target,) + args, kwargs=kwargs)
|
||||
thr.start()
|
||||
self.__threads.append(thr)
|
||||
|
||||
@@ -383,7 +384,7 @@ class Updater(object):
|
||||
ssl_ctx = None
|
||||
|
||||
# Create and start server
|
||||
self.httpd = WebhookServer(port, app, ssl_ctx)
|
||||
self.httpd = WebhookServer(listen, port, app, ssl_ctx)
|
||||
|
||||
if use_ssl:
|
||||
# DO NOT CHANGE: Only set webhook if SSL is handled by library
|
||||
|
||||
@@ -34,6 +34,7 @@ class Animation(TelegramObject):
|
||||
file_name (:obj:`str`): Optional. Original animation filename 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 file identifier.
|
||||
@@ -44,6 +45,8 @@ class Animation(TelegramObject):
|
||||
file_name (:obj:`str`, optional): Original animation filename 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.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
|
||||
@@ -56,16 +59,19 @@ class Animation(TelegramObject):
|
||||
file_name=None,
|
||||
mime_type=None,
|
||||
file_size=None,
|
||||
bot=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
self.file_id = str(file_id)
|
||||
self.width = int(width)
|
||||
self.height = int(height)
|
||||
self.duration = duration
|
||||
# Optionals
|
||||
self.thumb = thumb
|
||||
self.file_name = file_name
|
||||
self.mime_type = mime_type
|
||||
self.file_size = file_size
|
||||
self.bot = bot
|
||||
|
||||
self._id_attrs = (self.file_id,)
|
||||
|
||||
@@ -78,4 +84,22 @@ class Animation(TelegramObject):
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
|
||||
return cls(**data)
|
||||
return cls(bot=bot, **data)
|
||||
|
||||
def get_file(self, timeout=None, **kwargs):
|
||||
"""Convenience wrapper over :attr:`telegram.Bot.get_file`
|
||||
|
||||
Args:
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.File`
|
||||
|
||||
Raises:
|
||||
:class:`telegram.TelegramError`
|
||||
|
||||
"""
|
||||
return self.bot.get_file(self.file_id, timeout=timeout, **kwargs)
|
||||
|
||||
@@ -17,9 +17,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/].
|
||||
"""This module contains an object that represents a Telegram ChatPhoto."""
|
||||
|
||||
# TODO: add direct download shortcuts.
|
||||
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
@@ -27,14 +24,14 @@ class ChatPhoto(TelegramObject):
|
||||
"""This object represents a chat photo.
|
||||
|
||||
Attributes:
|
||||
small_file_id (:obj:`str`): Unique file identifier of small (160x160) chat photo.
|
||||
big_file_id (:obj:`str`): Unique file identifier of big (640x640) chat photo.
|
||||
small_file_id (:obj:`str`): File identifier of small (160x160) chat photo.
|
||||
big_file_id (:obj:`str`): File identifier of big (640x640) chat photo.
|
||||
|
||||
Args:
|
||||
small_file_id (:obj:`str`): Unique file identifier of small (160x160) chat photo. This
|
||||
file_id can be used only for photo download.
|
||||
big_file_id (:obj:`str`): Unique file identifier of big (640x640) chat photo. This file_id
|
||||
can be used only for photo download.
|
||||
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.
|
||||
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
@@ -43,6 +40,9 @@ class ChatPhoto(TelegramObject):
|
||||
def __init__(self, small_file_id, big_file_id, bot=None, **kwargs):
|
||||
self.small_file_id = small_file_id
|
||||
self.big_file_id = big_file_id
|
||||
self.bot = bot
|
||||
|
||||
self._id_attrs = (self.small_file_id, self.big_file_id)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, bot):
|
||||
@@ -50,3 +50,41 @@ class ChatPhoto(TelegramObject):
|
||||
return None
|
||||
|
||||
return cls(bot=bot, **data)
|
||||
|
||||
def get_small_file(self, timeout=None, **kwargs):
|
||||
"""Convenience wrapper over :attr:`telegram.Bot.get_file` for getting the
|
||||
small (160x160) chat photo
|
||||
|
||||
Args:
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.File`
|
||||
|
||||
Raises:
|
||||
:class:`telegram.TelegramError`
|
||||
|
||||
"""
|
||||
return self.bot.get_file(self.small_file_id, timeout=timeout, **kwargs)
|
||||
|
||||
def get_big_file(self, timeout=None, **kwargs):
|
||||
"""Convenience wrapper over :attr:`telegram.Bot.get_file` for getting the
|
||||
big (640x640) chat photo
|
||||
|
||||
Args:
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.File`
|
||||
|
||||
Raises:
|
||||
:class:`telegram.TelegramError`
|
||||
|
||||
"""
|
||||
return self.bot.get_file(self.big_file_id, timeout=timeout, **kwargs)
|
||||
|
||||
@@ -37,8 +37,10 @@ class InputMediaAnimation(InputMedia):
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): ``animation``.
|
||||
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.
|
||||
media (:obj:`str` | `filelike object` | :class:`telegram.Animation`): Animation to
|
||||
send. Pass a file_id as String to send an animation that exists on the Telegram
|
||||
servers (recommended), pass an HTTP URL as a String for Telegram to get an
|
||||
animation from the Internet, or upload a new animation using multipart/form-data.
|
||||
Lastly you can pass an existing :class:`telegram.Animation` object to send.
|
||||
thumb (`filelike object`): Optional. Thumbnail of the
|
||||
file sent. The thumbnail should be in JPEG format and less than 200 kB in size.
|
||||
@@ -111,9 +113,11 @@ class InputMediaPhoto(InputMedia):
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): ``photo``.
|
||||
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.
|
||||
media (:obj:`str` | `filelike object` | :class:`telegram.PhotoSize`): Photo to send.
|
||||
Pass a file_id as String to send a photo that exists on the Telegram servers
|
||||
(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.
|
||||
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
|
||||
@@ -150,9 +154,11 @@ class InputMediaVideo(InputMedia):
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): ``video``.
|
||||
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.
|
||||
media (:obj:`str` | `filelike object` | :class:`telegram.Video`): Video file to send.
|
||||
Pass a file_id as String to send an video file that exists on the Telegram servers
|
||||
(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.
|
||||
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
|
||||
@@ -229,9 +235,11 @@ class InputMediaAudio(InputMedia):
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): ``audio``.
|
||||
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.Audio` object to send.
|
||||
media (:obj:`str` | `filelike object` | :class:`telegram.Audio`): Audio file to send.
|
||||
Pass a file_id as String to send an audio file that exists on the Telegram servers
|
||||
(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.
|
||||
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
|
||||
@@ -304,9 +312,11 @@ class InputMediaDocument(InputMedia):
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): ``document``.
|
||||
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.
|
||||
media (:obj:`str` | `filelike object` | :class:`telegram.Document`): File to send.
|
||||
Pass a file_id as String to send a file that exists on the Telegram servers
|
||||
(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.
|
||||
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
|
||||
|
||||
@@ -28,6 +28,7 @@ class Sticker(TelegramObject):
|
||||
file_id (:obj:`str`): Unique identifier for this file.
|
||||
width (:obj:`int`): Sticker width.
|
||||
height (:obj:`int`): Sticker height.
|
||||
is_animated (:obj:`bool`): True, if the sticker is animated.
|
||||
thumb (:class:`telegram.PhotoSize`): Optional. Sticker thumbnail in the .webp or .jpg
|
||||
format.
|
||||
emoji (:obj:`str`): Optional. Emoji associated with the sticker.
|
||||
@@ -41,6 +42,7 @@ class Sticker(TelegramObject):
|
||||
file_id (:obj:`str`): Unique identifier for this file.
|
||||
width (:obj:`int`): Sticker width.
|
||||
height (:obj:`int`): Sticker height.
|
||||
is_animated (:obj:`bool`): True, if the sticker is animated.
|
||||
thumb (:class:`telegram.PhotoSize`, optional): Sticker thumbnail in the .webp or .jpg
|
||||
format.
|
||||
emoji (:obj:`str`, optional): Emoji associated with the sticker
|
||||
@@ -58,6 +60,7 @@ class Sticker(TelegramObject):
|
||||
file_id,
|
||||
width,
|
||||
height,
|
||||
is_animated,
|
||||
thumb=None,
|
||||
emoji=None,
|
||||
file_size=None,
|
||||
@@ -69,6 +72,7 @@ class Sticker(TelegramObject):
|
||||
self.file_id = str(file_id)
|
||||
self.width = int(width)
|
||||
self.height = int(height)
|
||||
self.is_animated = is_animated
|
||||
# Optionals
|
||||
self.thumb = thumb
|
||||
self.emoji = emoji
|
||||
@@ -123,20 +127,23 @@ class StickerSet(TelegramObject):
|
||||
Attributes:
|
||||
name (:obj:`str`): Sticker set name.
|
||||
title (:obj:`str`): Sticker set title.
|
||||
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.
|
||||
|
||||
Args:
|
||||
name (:obj:`str`): Sticker set name.
|
||||
title (:obj:`str`): Sticker set title.
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name, title, contains_masks, stickers, bot=None, **kwargs):
|
||||
def __init__(self, name, title, is_animated, contains_masks, stickers, bot=None, **kwargs):
|
||||
self.name = name
|
||||
self.title = title
|
||||
self.is_animated = is_animated
|
||||
self.contains_masks = contains_masks
|
||||
self.stickers = stickers
|
||||
|
||||
|
||||
@@ -31,8 +31,10 @@ 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.
|
||||
login_url (:class:`telegram.LoginUrl`) Optional. An HTTP URL used to automatically
|
||||
authorize the user.
|
||||
callback_data (:obj:`str`): Optional. Data to be sent in a callback query to the bot when
|
||||
button is pressed, 1-64 bytes.
|
||||
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.
|
||||
@@ -45,8 +47,10 @@ class InlineKeyboardButton(TelegramObject):
|
||||
Args:
|
||||
text (:obj:`str`): Label text on the button.
|
||||
url (:obj:`str`): HTTP url to be opened when button is pressed.
|
||||
login_url (:class:`telegram.LoginUrl`, optional) An HTTP URL used to automatically
|
||||
authorize the user.
|
||||
callback_data (:obj:`str`, optional): Data to be sent in a callback query to the bot when
|
||||
button is pressed, 1-64 bytes.
|
||||
button is pressed, 1-64 UTF-8 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
|
||||
@@ -76,14 +80,23 @@ class InlineKeyboardButton(TelegramObject):
|
||||
switch_inline_query_current_chat=None,
|
||||
callback_game=None,
|
||||
pay=None,
|
||||
login_url=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
self.text = text
|
||||
|
||||
# Optionals
|
||||
self.url = url
|
||||
self.login_url = login_url
|
||||
self.callback_data = callback_data
|
||||
self.switch_inline_query = switch_inline_query
|
||||
self.switch_inline_query_current_chat = switch_inline_query_current_chat
|
||||
self.callback_game = callback_game
|
||||
self.pay = pay
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, bot):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return cls(**data)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram InlineKeyboardMarkup."""
|
||||
|
||||
from telegram import ReplyMarkup
|
||||
from telegram import ReplyMarkup, InlineKeyboardButton
|
||||
|
||||
|
||||
class InlineKeyboardMarkup(ReplyMarkup):
|
||||
@@ -49,6 +49,19 @@ class InlineKeyboardMarkup(ReplyMarkup):
|
||||
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, bot):
|
||||
if not data:
|
||||
return None
|
||||
keyboard = []
|
||||
for row in data['inline_keyboard']:
|
||||
tmp = []
|
||||
for col in row:
|
||||
tmp.append(InlineKeyboardButton.de_json(col, bot))
|
||||
keyboard.append(tmp)
|
||||
|
||||
return cls(keyboard)
|
||||
|
||||
@classmethod
|
||||
def from_button(cls, button, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
@@ -77,17 +77,10 @@ class InlineQueryResultArticle(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
# Optional
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if url:
|
||||
self.url = url
|
||||
if hide_url:
|
||||
self.hide_url = hide_url
|
||||
if description:
|
||||
self.description = description
|
||||
if thumb_url:
|
||||
self.thumb_url = thumb_url
|
||||
if thumb_width:
|
||||
self.thumb_width = thumb_width
|
||||
if thumb_height:
|
||||
self.thumb_height = thumb_height
|
||||
self.reply_markup = reply_markup
|
||||
self.url = url
|
||||
self.hide_url = hide_url
|
||||
self.description = description
|
||||
self.thumb_url = thumb_url
|
||||
self.thumb_width = thumb_width
|
||||
self.thumb_height = thumb_height
|
||||
|
||||
@@ -79,15 +79,9 @@ class InlineQueryResultAudio(InlineQueryResult):
|
||||
self.title = title
|
||||
|
||||
# Optionals
|
||||
if performer:
|
||||
self.performer = performer
|
||||
if audio_duration:
|
||||
self.audio_duration = audio_duration
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.performer = performer
|
||||
self.audio_duration = audio_duration
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -68,11 +68,7 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
|
||||
self.audio_file_id = audio_file_id
|
||||
|
||||
# Optionals
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -75,13 +75,8 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
|
||||
self.document_file_id = document_file_id
|
||||
|
||||
# Optionals
|
||||
if description:
|
||||
self.description = description
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.description = description
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -72,13 +72,8 @@ class InlineQueryResultCachedGif(InlineQueryResult):
|
||||
self.gif_file_id = gif_file_id
|
||||
|
||||
# Optionals
|
||||
if title:
|
||||
self.title = title
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.title = title
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -72,13 +72,8 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
|
||||
self.mpeg4_file_id = mpeg4_file_id
|
||||
|
||||
# Optionals
|
||||
if title:
|
||||
self.title = title
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.title = title
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -75,15 +75,9 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
|
||||
self.photo_file_id = photo_file_id
|
||||
|
||||
# Optionals
|
||||
if title:
|
||||
self.title = title
|
||||
if description:
|
||||
self.description = description
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -58,7 +58,5 @@ class InlineQueryResultCachedSticker(InlineQueryResult):
|
||||
self.sticker_file_id = sticker_file_id
|
||||
|
||||
# Optionals
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -76,13 +76,8 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
|
||||
self.title = title
|
||||
|
||||
# Optionals
|
||||
if description:
|
||||
self.description = description
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.description = description
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -72,11 +72,7 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
|
||||
self.title = title
|
||||
|
||||
# Optionals
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -79,17 +79,10 @@ class InlineQueryResultContact(InlineQueryResult):
|
||||
self.first_name = first_name
|
||||
|
||||
# Optionals
|
||||
if last_name:
|
||||
self.last_name = last_name
|
||||
if vcard:
|
||||
self.vcard = vcard
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
if thumb_url:
|
||||
self.thumb_url = thumb_url
|
||||
if thumb_width:
|
||||
self.thumb_width = thumb_width
|
||||
if thumb_height:
|
||||
self.thumb_height = thumb_height
|
||||
self.last_name = last_name
|
||||
self.vcard = vcard
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
self.thumb_url = thumb_url
|
||||
self.thumb_width = thumb_width
|
||||
self.thumb_height = thumb_height
|
||||
|
||||
@@ -91,19 +91,11 @@ class InlineQueryResultDocument(InlineQueryResult):
|
||||
self.mime_type = mime_type
|
||||
|
||||
# Optionals
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if description:
|
||||
self.description = description
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
if thumb_url:
|
||||
self.thumb_url = thumb_url
|
||||
if thumb_width:
|
||||
self.thumb_width = thumb_width
|
||||
if thumb_height:
|
||||
self.thumb_height = thumb_height
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.description = description
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
self.thumb_url = thumb_url
|
||||
self.thumb_width = thumb_width
|
||||
self.thumb_height = thumb_height
|
||||
|
||||
@@ -46,5 +46,4 @@ class InlineQueryResultGame(InlineQueryResult):
|
||||
self.id = id
|
||||
self.game_short_name = game_short_name
|
||||
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
@@ -85,19 +85,11 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
self.thumb_url = thumb_url
|
||||
|
||||
# Optionals
|
||||
if gif_width:
|
||||
self.gif_width = gif_width
|
||||
if gif_height:
|
||||
self.gif_height = gif_height
|
||||
if gif_duration:
|
||||
self.gif_duration = gif_duration
|
||||
if title:
|
||||
self.title = title
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.gif_width = gif_width
|
||||
self.gif_height = gif_height
|
||||
self.gif_duration = gif_duration
|
||||
self.title = title
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -80,15 +80,9 @@ class InlineQueryResultLocation(InlineQueryResult):
|
||||
self.title = title
|
||||
|
||||
# Optionals
|
||||
if live_period:
|
||||
self.live_period = live_period
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
if thumb_url:
|
||||
self.thumb_url = thumb_url
|
||||
if thumb_width:
|
||||
self.thumb_width = thumb_width
|
||||
if thumb_height:
|
||||
self.thumb_height = thumb_height
|
||||
self.live_period = live_period
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
self.thumb_url = thumb_url
|
||||
self.thumb_width = thumb_width
|
||||
self.thumb_height = thumb_height
|
||||
|
||||
@@ -86,19 +86,11 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
self.thumb_url = thumb_url
|
||||
|
||||
# Optional
|
||||
if mpeg4_width:
|
||||
self.mpeg4_width = mpeg4_width
|
||||
if mpeg4_height:
|
||||
self.mpeg4_height = mpeg4_height
|
||||
if mpeg4_duration:
|
||||
self.mpeg4_duration = mpeg4_duration
|
||||
if title:
|
||||
self.title = title
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.mpeg4_width = mpeg4_width
|
||||
self.mpeg4_height = mpeg4_height
|
||||
self.mpeg4_duration = mpeg4_duration
|
||||
self.title = title
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -86,19 +86,11 @@ class InlineQueryResultPhoto(InlineQueryResult):
|
||||
self.thumb_url = thumb_url
|
||||
|
||||
# Optionals
|
||||
if photo_width:
|
||||
self.photo_width = int(photo_width)
|
||||
if photo_height:
|
||||
self.photo_height = int(photo_height)
|
||||
if title:
|
||||
self.title = title
|
||||
if description:
|
||||
self.description = description
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.photo_width = int(photo_width)if photo_width is not None else None
|
||||
self.photo_height = int(photo_height) if photo_height is not None else None
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -90,17 +90,10 @@ class InlineQueryResultVenue(InlineQueryResult):
|
||||
self.address = address
|
||||
|
||||
# Optional
|
||||
if foursquare_id:
|
||||
self.foursquare_id = foursquare_id
|
||||
if foursquare_type:
|
||||
self.foursquare_type = foursquare_type
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
if thumb_url:
|
||||
self.thumb_url = thumb_url
|
||||
if thumb_width:
|
||||
self.thumb_width = thumb_width
|
||||
if thumb_height:
|
||||
self.thumb_height = thumb_height
|
||||
self.foursquare_id = foursquare_id
|
||||
self.foursquare_type = foursquare_type
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
self.thumb_url = thumb_url
|
||||
self.thumb_width = thumb_width
|
||||
self.thumb_height = thumb_height
|
||||
|
||||
@@ -94,19 +94,11 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
self.title = title
|
||||
|
||||
# Optional
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if video_width:
|
||||
self.video_width = video_width
|
||||
if video_height:
|
||||
self.video_height = video_height
|
||||
if video_duration:
|
||||
self.video_duration = video_duration
|
||||
if description:
|
||||
self.description = description
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.video_width = video_width
|
||||
self.video_height = video_height
|
||||
self.video_duration = video_duration
|
||||
self.description = description
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -77,13 +77,8 @@ class InlineQueryResultVoice(InlineQueryResult):
|
||||
self.title = title
|
||||
|
||||
# Optional
|
||||
if voice_duration:
|
||||
self.voice_duration = voice_duration
|
||||
if caption:
|
||||
self.caption = caption
|
||||
if parse_mode:
|
||||
self.parse_mode = parse_mode
|
||||
if reply_markup:
|
||||
self.reply_markup = reply_markup
|
||||
if input_message_content:
|
||||
self.input_message_content = input_message_content
|
||||
self.voice_duration = voice_duration
|
||||
self.caption = caption
|
||||
self.parse_mode = parse_mode
|
||||
self.reply_markup = reply_markup
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=R0903
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-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/].
|
||||
"""This module contains an object that represents a Telegram LoginUrl."""
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
class LoginUrl(TelegramObject):
|
||||
"""This object represents a parameter of the inline keyboard button used to automatically
|
||||
authorize a user. Serves as a great replacement for the Telegram Login Widget when the user is
|
||||
coming from Telegram. All the user needs to do is tap/click a button and confirm that they want
|
||||
to log in. Telegram apps support these buttons as of version 5.7.
|
||||
|
||||
Sample bot: `@discussbot <https://t.me/dicussbot>`_
|
||||
|
||||
Attributes:
|
||||
url (:obj:`str`): An HTTP URL to be opened with user authorization data.
|
||||
forward_text (:obj:`str`): Optional. New text of the button in forwarded messages.
|
||||
bot_username (:obj:`str`): Optional. Username of a bot, which will be used for user
|
||||
authorization.
|
||||
request_write_access (:obj:`bool`): Optional. Pass True to request the permission for your
|
||||
bot to send messages to the user.
|
||||
|
||||
Args:
|
||||
url (:obj:`str`): An HTTP URL to be opened with user authorization data added to the query
|
||||
string when the button is pressed. If the user refuses to provide authorization data,
|
||||
the original URL without information about the user will be opened. The data added is
|
||||
the same as described in Receiving authorization data.
|
||||
NOTE: You must always check the hash of the received data to verify the authentication
|
||||
and the integrity of the data as described in Checking authorization.
|
||||
forward_text (:obj:`str`, optional): New text of the button in forwarded messages.
|
||||
bot_username (:obj:`str`, optional): Username of a bot, which will be used for user
|
||||
authorization. See Setting up a bot for more details. If not specified, the current
|
||||
bot's username will be assumed. The url's domain must be the same as the domain linked
|
||||
with the bot. See Linking your domain to the bot for more details.
|
||||
request_write_access (:obj:`bool`, optional): Pass True to request the permission for your
|
||||
bot to send messages to the user.
|
||||
"""
|
||||
|
||||
def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None):
|
||||
# Required
|
||||
self.url = url
|
||||
# Optional
|
||||
self.forward_text = forward_text
|
||||
self.bot_username = bot_username
|
||||
self.request_write_access = request_write_access
|
||||
|
||||
self._id_attrs = (self.url,)
|
||||
+75
-17
@@ -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)
|
||||
SuccessfulPayment, VideoNote, PassportData, Poll, InlineKeyboardMarkup)
|
||||
from telegram import ParseMode
|
||||
from telegram.utils.helpers import escape_markdown, to_timestamp, from_timestamp
|
||||
|
||||
@@ -99,9 +99,15 @@ class Message(TelegramObject):
|
||||
has logged in.
|
||||
forward_signature (:obj:`str`): Optional. Signature of the post author for messages
|
||||
forwarded from channels.
|
||||
forward_sender_name (:obj:`str`): Optional. Sender's name for messages forwarded from users
|
||||
who disallow adding a link to their account in forwarded messages.
|
||||
author_signature (:obj:`str`): Optional. Signature of the post author for messages
|
||||
in channels.
|
||||
passport_data (:class:`telegram.PassportData`): Optional. Telegram Passport data
|
||||
passport_data (:class:`telegram.PassportData`): Optional. Telegram Passport data.
|
||||
poll (:class:`telegram.Poll`): Optional. Message is a native poll,
|
||||
information about the poll.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
|
||||
|
||||
Args:
|
||||
@@ -117,6 +123,8 @@ class Message(TelegramObject):
|
||||
channel, information about the original channel.
|
||||
forward_from_message_id (:obj:`int`, optional): For forwarded channel posts, identifier of
|
||||
the original message in the channel.
|
||||
forward_sender_name (:obj:`str`, optional): Sender's name for messages forwarded from users
|
||||
who disallow adding a link to their account in forwarded messages.
|
||||
forward_date (:class:`datetime.datetime`, optional): For forwarded messages, date the
|
||||
original message was sent in Unix time. Converted to :class:`datetime.datetime`.
|
||||
reply_to_message (:class:`telegram.Message`, optional): For replies, the original message.
|
||||
@@ -201,7 +209,12 @@ class Message(TelegramObject):
|
||||
forwarded from channels.
|
||||
author_signature (:obj:`str`, optional): Signature of the post author for messages
|
||||
in channels.
|
||||
passport_data (:class:`telegram.PassportData`, optional): Telegram Passport data
|
||||
passport_data (:class:`telegram.PassportData`, optional): Telegram Passport data.
|
||||
poll (:class:`telegram.Poll`, optional): Message is a native poll,
|
||||
information about the poll.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message. login_url buttons are represented as ordinary url buttons.
|
||||
|
||||
"""
|
||||
|
||||
_effective_attachment = _UNDEFINED
|
||||
@@ -260,6 +273,9 @@ class Message(TelegramObject):
|
||||
connected_website=None,
|
||||
animation=None,
|
||||
passport_data=None,
|
||||
poll=None,
|
||||
forward_sender_name=None,
|
||||
reply_markup=None,
|
||||
bot=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
@@ -304,11 +320,13 @@ class Message(TelegramObject):
|
||||
self.successful_payment = successful_payment
|
||||
self.connected_website = connected_website
|
||||
self.forward_signature = forward_signature
|
||||
self.forward_sender_name = forward_sender_name
|
||||
self.author_signature = author_signature
|
||||
self.media_group_id = media_group_id
|
||||
self.animation = animation
|
||||
self.passport_data = passport_data
|
||||
|
||||
self.poll = poll
|
||||
self.reply_markup = reply_markup
|
||||
self.bot = bot
|
||||
|
||||
self._id_attrs = (self.message_id,)
|
||||
@@ -320,10 +338,20 @@ class Message(TelegramObject):
|
||||
|
||||
@property
|
||||
def link(self):
|
||||
""":obj:`str`: Convenience property. If the chat of the message is a supergroup or a
|
||||
channel and has a :attr:`Chat.username`, returns a t.me link of the message."""
|
||||
if self.chat.type in (Chat.SUPERGROUP, Chat.CHANNEL) and self.chat.username:
|
||||
return "https://t.me/{}/{}".format(self.chat.username, self.message_id)
|
||||
""":obj:`str`: Convenience property. If the chat of the message is not
|
||||
a private chat, returns a t.me link of the message."""
|
||||
if self.chat.type != Chat.PRIVATE:
|
||||
if self.chat.username:
|
||||
to_link = self.chat.username
|
||||
else:
|
||||
if self.chat.type != Chat.GROUP:
|
||||
# Get rid of leading -100 for supergroups
|
||||
id_to_link = str(self.chat.id)[4:]
|
||||
else:
|
||||
# Get rid of leading minus for regular groups
|
||||
id_to_link = str(self.chat.id)[1:]
|
||||
to_link = "c/{}".format(id_to_link)
|
||||
return "https://t.me/{}/{}".format(to_link, self.message_id)
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
@@ -362,6 +390,8 @@ class Message(TelegramObject):
|
||||
data['invoice'] = Invoice.de_json(data.get('invoice'), bot)
|
||||
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['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
|
||||
return cls(bot=bot, **data)
|
||||
|
||||
@@ -453,6 +483,9 @@ class Message(TelegramObject):
|
||||
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_message(self.chat_id, *args, **kwargs)
|
||||
@@ -470,6 +503,9 @@ class Message(TelegramObject):
|
||||
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
|
||||
@@ -490,6 +526,9 @@ class Message(TelegramObject):
|
||||
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.HTML
|
||||
@@ -705,13 +744,31 @@ class Message(TelegramObject):
|
||||
self._quote(kwargs)
|
||||
return self.bot.send_contact(self.chat_id, *args, **kwargs)
|
||||
|
||||
def forward(self, chat_id, disable_notification=False):
|
||||
def reply_poll(self, *args, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
bot.send_poll(update.message.chat_id, *args, **kwargs)
|
||||
|
||||
Keyword Args:
|
||||
quote (:obj:`bool`, optional): If set to ``True``, the photo 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_poll(self.chat_id, *args, **kwargs)
|
||||
|
||||
def forward(self, chat_id, *args, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
bot.forward_message(chat_id=chat_id,
|
||||
from_chat_id=update.message.chat_id,
|
||||
disable_notification=disable_notification,
|
||||
message_id=update.message.message_id)
|
||||
message_id=update.message.message_id,
|
||||
*args,
|
||||
**kwargs)
|
||||
|
||||
Returns:
|
||||
:class:`telegram.Message`: On success, instance representing the message forwarded.
|
||||
@@ -720,8 +777,9 @@ class Message(TelegramObject):
|
||||
return self.bot.forward_message(
|
||||
chat_id=chat_id,
|
||||
from_chat_id=self.chat_id,
|
||||
disable_notification=disable_notification,
|
||||
message_id=self.message_id)
|
||||
message_id=self.message_id,
|
||||
*args,
|
||||
**kwargs)
|
||||
|
||||
def edit_text(self, *args, **kwargs):
|
||||
"""Shortcut for::
|
||||
@@ -828,7 +886,7 @@ class Message(TelegramObject):
|
||||
|
||||
Args:
|
||||
entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must
|
||||
be an entity that belongs to this message.
|
||||
be an entity that belongs to this message.
|
||||
|
||||
Returns:
|
||||
:obj:`str`: The text of the given entity
|
||||
@@ -841,7 +899,7 @@ class Message(TelegramObject):
|
||||
entity_text = self.text.encode('utf-16-le')
|
||||
entity_text = entity_text[entity.offset * 2:(entity.offset + entity.length) * 2]
|
||||
|
||||
return entity_text.decode('utf-16-le')
|
||||
return entity_text.decode('utf-16-le')
|
||||
|
||||
def parse_caption_entity(self, entity):
|
||||
"""Returns the text from a given :class:`telegram.MessageEntity`.
|
||||
@@ -853,7 +911,7 @@ class Message(TelegramObject):
|
||||
|
||||
Args:
|
||||
entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must
|
||||
be an entity that belongs to this message.
|
||||
be an entity that belongs to this message.
|
||||
|
||||
Returns:
|
||||
:obj:`str`: The text of the given entity
|
||||
@@ -866,7 +924,7 @@ class Message(TelegramObject):
|
||||
entity_text = self.caption.encode('utf-16-le')
|
||||
entity_text = entity_text[entity.offset * 2:(entity.offset + entity.length) * 2]
|
||||
|
||||
return entity_text.decode('utf-16-le')
|
||||
return entity_text.decode('utf-16-le')
|
||||
|
||||
def parse_entities(self, types=None):
|
||||
"""
|
||||
|
||||
@@ -55,6 +55,8 @@ class MessageEntity(TelegramObject):
|
||||
self.url = url
|
||||
self.user = user
|
||||
|
||||
self._id_attrs = (self.type, self.offset, self.length)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, bot):
|
||||
data = super(MessageEntity, cls).de_json(data, bot)
|
||||
|
||||
@@ -64,7 +64,7 @@ class ShippingQuery(TelegramObject):
|
||||
data['from_user'] = User.de_json(data.pop('from'), bot)
|
||||
data['shipping_address'] = ShippingAddress.de_json(data.get('shipping_address'), bot)
|
||||
|
||||
return cls(**data)
|
||||
return cls(bot=bot, **data)
|
||||
|
||||
def answer(self, *args, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=R0903
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2018
|
||||
# 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 Poll."""
|
||||
|
||||
from telegram import (TelegramObject)
|
||||
|
||||
|
||||
class PollOption(TelegramObject):
|
||||
"""
|
||||
This object contains information about one answer option in a poll.
|
||||
|
||||
Attributes:
|
||||
text (:obj:`str`): Option text, 1-100 characters.
|
||||
voter_count (:obj:`int`): Number of users that voted for this option.
|
||||
|
||||
Args:
|
||||
text (:obj:`str`): Option text, 1-100 characters.
|
||||
voter_count (:obj:`int`): Number of users that voted for this option.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, text, voter_count, **kwargs):
|
||||
self.text = text
|
||||
self.voter_count = voter_count
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, bot):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return cls(**data)
|
||||
|
||||
|
||||
class Poll(TelegramObject):
|
||||
"""
|
||||
This object contains information about a poll.
|
||||
|
||||
Attributes:
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, id, question, options, is_closed, **kwargs):
|
||||
self.id = id
|
||||
self.question = question
|
||||
self.options = options
|
||||
self.is_closed = is_closed
|
||||
|
||||
self._id_attrs = (self.id,)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, bot):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Poll, cls).de_json(data, bot)
|
||||
|
||||
data['options'] = [PollOption.de_json(option, bot) for option in data['options']]
|
||||
|
||||
return cls(**data)
|
||||
|
||||
def to_dict(self):
|
||||
data = super(Poll, self).to_dict()
|
||||
|
||||
data['options'] = [x.to_dict() for x in self.options]
|
||||
|
||||
return data
|
||||
+11
-4
@@ -19,7 +19,7 @@
|
||||
"""This module contains an object that represents a Telegram Update."""
|
||||
|
||||
from telegram import (Message, TelegramObject, InlineQuery, ChosenInlineResult,
|
||||
CallbackQuery, ShippingQuery, PreCheckoutQuery)
|
||||
CallbackQuery, ShippingQuery, PreCheckoutQuery, Poll)
|
||||
|
||||
|
||||
class Update(TelegramObject):
|
||||
@@ -41,6 +41,8 @@ class Update(TelegramObject):
|
||||
shipping_query (:class:`telegram.ShippingQuery`): Optional. New incoming shipping query.
|
||||
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
|
||||
|
||||
Args:
|
||||
update_id (:obj:`int`): The update's unique identifier. Update identifiers start from a
|
||||
@@ -63,6 +65,8 @@ class Update(TelegramObject):
|
||||
Only for invoices with flexible price.
|
||||
pre_checkout_query (:class:`telegram.PreCheckoutQuery`, optional): New incoming
|
||||
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
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
@@ -78,6 +82,7 @@ class Update(TelegramObject):
|
||||
callback_query=None,
|
||||
shipping_query=None,
|
||||
pre_checkout_query=None,
|
||||
poll=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
self.update_id = int(update_id)
|
||||
@@ -91,6 +96,7 @@ class Update(TelegramObject):
|
||||
self.pre_checkout_query = pre_checkout_query
|
||||
self.channel_post = channel_post
|
||||
self.edited_channel_post = edited_channel_post
|
||||
self.poll = poll
|
||||
|
||||
self._effective_user = None
|
||||
self._effective_chat = None
|
||||
@@ -102,7 +108,7 @@ class Update(TelegramObject):
|
||||
def effective_user(self):
|
||||
"""
|
||||
:class:`telegram.User`: The user that sent this update, no matter what kind of update this
|
||||
is. Will be ``None`` for :attr:`channel_post`.
|
||||
is. Will be ``None`` for :attr:`channel_post` and :attr:`poll`.
|
||||
|
||||
"""
|
||||
if self._effective_user:
|
||||
@@ -140,7 +146,7 @@ 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` and :attr:`pre_checkout_query`.
|
||||
:attr:`shipping_query`, :attr:`pre_checkout_query` and :attr:`poll`.
|
||||
|
||||
"""
|
||||
if self._effective_chat:
|
||||
@@ -172,7 +178,7 @@ 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` and :attr:`pre_checkout_query`.
|
||||
:attr:`shipping_query`, :attr:`pre_checkout_query` and :attr:`poll`.
|
||||
|
||||
"""
|
||||
if self._effective_message:
|
||||
@@ -215,5 +221,6 @@ class Update(TelegramObject):
|
||||
data['pre_checkout_query'] = PreCheckoutQuery.de_json(data.get('pre_checkout_query'), bot)
|
||||
data['channel_post'] = Message.de_json(data.get('channel_post'), bot)
|
||||
data['edited_channel_post'] = Message.de_json(data.get('edited_channel_post'), bot)
|
||||
data['poll'] = Poll.de_json(data.get('poll'), bot)
|
||||
|
||||
return cls(**data)
|
||||
|
||||
+179
-26
@@ -17,7 +17,12 @@
|
||||
# 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 helper functions."""
|
||||
|
||||
import datetime as dtm # dtm = "DateTime Module"
|
||||
import time
|
||||
|
||||
from collections import defaultdict
|
||||
from numbers import Number
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
@@ -27,7 +32,6 @@ from html import escape
|
||||
|
||||
import re
|
||||
import signal
|
||||
from datetime import datetime
|
||||
|
||||
# From https://stackoverflow.com/questions/2549939/get-signal-names-from-numbers-in-python
|
||||
_signames = {v: k
|
||||
@@ -40,54 +44,154 @@ def get_signal_name(signum):
|
||||
return _signames[signum]
|
||||
|
||||
|
||||
# Not using future.backports.datetime here as datetime value might be an input from the user,
|
||||
# making every isinstace() call more delicate. So we just use our own compat layer.
|
||||
if hasattr(datetime, 'timestamp'):
|
||||
# Python 3.3+
|
||||
def _timestamp(dt_obj):
|
||||
return dt_obj.timestamp()
|
||||
else:
|
||||
# Python < 3.3 (incl 2.7)
|
||||
from time import mktime
|
||||
|
||||
def _timestamp(dt_obj):
|
||||
return mktime(dt_obj.timetuple())
|
||||
|
||||
|
||||
def escape_markdown(text):
|
||||
"""Helper function to escape telegram markup symbols."""
|
||||
escape_chars = '\*_`\['
|
||||
return re.sub(r'([%s])' % escape_chars, r'\\\1', text)
|
||||
|
||||
|
||||
def to_timestamp(dt_obj):
|
||||
# -------- date/time related helpers --------
|
||||
# TODO: add generic specification of UTC for naive datetimes to docs
|
||||
|
||||
if hasattr(dtm, 'timezone'):
|
||||
# Python 3.3+
|
||||
def _datetime_to_float_timestamp(dt_obj):
|
||||
if dt_obj.tzinfo is None:
|
||||
dt_obj = dt_obj.replace(tzinfo=_UTC)
|
||||
return dt_obj.timestamp()
|
||||
|
||||
_UtcOffsetTimezone = dtm.timezone
|
||||
_UTC = dtm.timezone.utc
|
||||
else:
|
||||
# Python < 3.3 (incl 2.7)
|
||||
|
||||
# hardcoded timezone class (`datetime.timezone` isn't available in py2)
|
||||
class _UtcOffsetTimezone(dtm.tzinfo):
|
||||
def __init__(self, offset):
|
||||
self.offset = offset
|
||||
|
||||
def tzname(self, dt):
|
||||
return 'UTC +{}'.format(self.offset)
|
||||
|
||||
def utcoffset(self, dt):
|
||||
return self.offset
|
||||
|
||||
def dst(self, dt):
|
||||
return dtm.timedelta(0)
|
||||
|
||||
_UTC = _UtcOffsetTimezone(dtm.timedelta(0))
|
||||
__EPOCH_DT = dtm.datetime.fromtimestamp(0, tz=_UTC)
|
||||
__NAIVE_EPOCH_DT = __EPOCH_DT.replace(tzinfo=None)
|
||||
|
||||
# _datetime_to_float_timestamp
|
||||
# Not using future.backports.datetime here as datetime value might be an input from the user,
|
||||
# making every isinstace() call more delicate. So we just use our own compat layer.
|
||||
def _datetime_to_float_timestamp(dt_obj):
|
||||
epoch_dt = __EPOCH_DT if dt_obj.tzinfo is not None else __NAIVE_EPOCH_DT
|
||||
return (dt_obj - epoch_dt).total_seconds()
|
||||
|
||||
_datetime_to_float_timestamp.__doc__ = \
|
||||
"""Converts a datetime object to a float timestamp (with sub-second precision).
|
||||
If the datetime object is timezone-naive, it is assumed to be in UTC."""
|
||||
|
||||
|
||||
def to_float_timestamp(t, reference_timestamp=None):
|
||||
"""
|
||||
Converts a given time object to a float POSIX timestamp.
|
||||
Used to convert different time specifications to a common format. The time object
|
||||
can be relative (i.e. indicate a time increment, or a time of day) or absolute.
|
||||
Any objects from the :module:`datetime` module that are timezone-naive will be assumed
|
||||
to be in UTC.
|
||||
|
||||
``None`` s are left alone (i.e. ``to_float_timestamp(None)`` is ``None``).
|
||||
|
||||
Args:
|
||||
dt_obj (:class:`datetime.datetime`):
|
||||
t (int | float | datetime.timedelta | datetime.datetime | datetime.time):
|
||||
Time value to convert. The semantics of this parameter will depend on its type:
|
||||
|
||||
* :obj:`int` or :obj:`float` will be interpreted as "seconds from ``reference_t``"
|
||||
* :obj:`datetime.timedelta` will be interpreted as
|
||||
"time increment from ``reference_t``"
|
||||
* :obj:`datetime.datetime` will be interpreted as an absolute date/time value
|
||||
* :obj:`datetime.time` will be interpreted as a specific time of day
|
||||
|
||||
reference_timestamp (float, optional): POSIX timestamp that indicates the absolute time
|
||||
from which relative calculations are to be performed (e.g. when ``t`` is given as an
|
||||
:obj:`int`, indicating "seconds from ``reference_t``"). Defaults to now (the time at
|
||||
which this function is called).
|
||||
|
||||
If ``t`` is given as an absolute representation of date & time (i.e. a
|
||||
``datetime.datetime`` object), ``reference_timestamp`` is not relevant and so its
|
||||
value should be ``None``. If this is not the case, a ``ValueError`` will be raised.
|
||||
|
||||
Returns:
|
||||
int:
|
||||
(float | None) The return value depends on the type of argument ``t``. If ``t`` is
|
||||
given as a time increment (i.e. as a obj:`int`, :obj:`float` or
|
||||
:obj:`datetime.timedelta`), then the return value will be ``reference_t`` + ``t``.
|
||||
|
||||
Else if it is given as an absolute date/time value (i.e. a :obj:`datetime.datetime`
|
||||
object), the equivalent value as a POSIX timestamp will be returned.
|
||||
|
||||
Finally, if it is a time of the day without date (i.e. a :obj:`datetime.time`
|
||||
object), the return value is the nearest future occurrence of that time of day.
|
||||
|
||||
Raises:
|
||||
TypeError: if `t`'s type is not one of those described above
|
||||
"""
|
||||
if not dt_obj:
|
||||
return None
|
||||
|
||||
return int(_timestamp(dt_obj))
|
||||
if reference_timestamp is None:
|
||||
reference_timestamp = time.time()
|
||||
elif isinstance(t, dtm.datetime):
|
||||
raise ValueError('t is an (absolute) datetime while reference_timestamp is not None')
|
||||
|
||||
if isinstance(t, dtm.timedelta):
|
||||
return reference_timestamp + t.total_seconds()
|
||||
elif isinstance(t, Number):
|
||||
return reference_timestamp + t
|
||||
elif isinstance(t, dtm.time):
|
||||
if t.tzinfo is not None:
|
||||
reference_dt = dtm.datetime.fromtimestamp(reference_timestamp, tz=t.tzinfo)
|
||||
else:
|
||||
reference_dt = dtm.datetime.utcfromtimestamp(reference_timestamp) # assume UTC
|
||||
reference_date = reference_dt.date()
|
||||
reference_time = reference_dt.timetz()
|
||||
if reference_time > t: # if the time of day has passed today, use tomorrow
|
||||
reference_date += dtm.timedelta(days=1)
|
||||
return _datetime_to_float_timestamp(dtm.datetime.combine(reference_date, t))
|
||||
elif isinstance(t, dtm.datetime):
|
||||
return _datetime_to_float_timestamp(t)
|
||||
|
||||
raise TypeError('Unable to convert {} object to timestamp'.format(type(t).__name__))
|
||||
|
||||
|
||||
def to_timestamp(dt_obj, reference_timestamp=None):
|
||||
"""
|
||||
Wrapper over :func:`to_float_timestamp` which returns an integer (the float value truncated
|
||||
down to the nearest integer).
|
||||
|
||||
See the documentation for :func:`to_float_timestamp` for more details.
|
||||
"""
|
||||
return int(to_float_timestamp(dt_obj, reference_timestamp)) if dt_obj is not None else None
|
||||
|
||||
|
||||
def from_timestamp(unixtime):
|
||||
"""
|
||||
Converts an (integer) unix timestamp to a naive datetime object in UTC.
|
||||
``None`` s are left alone (i.e. ``from_timestamp(None)`` is ``None``).
|
||||
|
||||
Args:
|
||||
unixtime (int):
|
||||
unixtime (int): integer POSIX timestamp
|
||||
|
||||
Returns:
|
||||
datetime.datetime:
|
||||
|
||||
equivalent :obj:`datetime.datetime` value in naive UTC if ``timestamp`` is not
|
||||
``None``; else ``None``
|
||||
"""
|
||||
if not unixtime:
|
||||
if unixtime is None:
|
||||
return None
|
||||
|
||||
return datetime.fromtimestamp(unixtime)
|
||||
return dtm.datetime.utcfromtimestamp(unixtime)
|
||||
|
||||
# -------- end --------
|
||||
|
||||
|
||||
def mention_html(user_id, name):
|
||||
@@ -147,6 +251,55 @@ def effective_message_type(entity):
|
||||
return None
|
||||
|
||||
|
||||
def create_deep_linked_url(bot_username, payload=None, group=False):
|
||||
"""
|
||||
Creates a deep-linked URL for this ``bot_username`` with the specified ``payload``.
|
||||
See https://core.telegram.org/bots#deep-linking to learn more.
|
||||
|
||||
The ``payload`` may consist of the following characters: ``A-Z, a-z, 0-9, _, -``
|
||||
|
||||
Note:
|
||||
Works well in conjunction with
|
||||
``CommandHandler("start", callback, filters = Filters.regex('payload'))``
|
||||
|
||||
Examples:
|
||||
``create_deep_linked_url(bot.get_me().username, "some-params")``
|
||||
|
||||
Args:
|
||||
bot_username (:obj:`str`): The username to link to
|
||||
payload (:obj:`str`, optional): Parameters to encode in the created URL
|
||||
group (:obj:`bool`, optional): If `True` the user is prompted to select a group to add the
|
||||
bot to. If `False`, opens a one-on-one conversation with the bot. Defaults to `False`.
|
||||
|
||||
Returns:
|
||||
:obj:`str`: An URL to start the bot with specific parameters
|
||||
"""
|
||||
if bot_username is None or len(bot_username) <= 3:
|
||||
raise ValueError("You must provide a valid bot_username.")
|
||||
|
||||
base_url = 'https://t.me/{}'.format(bot_username)
|
||||
if not payload:
|
||||
return base_url
|
||||
|
||||
if len(payload) > 64:
|
||||
raise ValueError("The deep-linking payload must not exceed 64 characters.")
|
||||
|
||||
if not re.match(r'^[A-Za-z0-9_-]+$', payload):
|
||||
raise ValueError("Only the following characters are allowed for deep-linked "
|
||||
"URLs: A-Z, a-z, 0-9, _ and -")
|
||||
|
||||
if group:
|
||||
key = 'startgroup'
|
||||
else:
|
||||
key = 'start'
|
||||
|
||||
return '{0}?{1}={2}'.format(
|
||||
base_url,
|
||||
key,
|
||||
payload
|
||||
)
|
||||
|
||||
|
||||
def enocde_conversations_to_json(conversations):
|
||||
"""Helper method to encode a conversations dict (that uses tuples as keys) to a
|
||||
JSON-serializable way. Use :attr:`_decode_conversations_from_json` to decode.
|
||||
|
||||
@@ -34,8 +34,9 @@ logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||
|
||||
class WebhookServer(object):
|
||||
|
||||
def __init__(self, port, webhook_app, ssl_ctx):
|
||||
def __init__(self, listen, port, webhook_app, ssl_ctx):
|
||||
self.http_server = HTTPServer(webhook_app, ssl_options=ssl_ctx)
|
||||
self.listen = listen
|
||||
self.port = port
|
||||
self.loop = None
|
||||
self.logger = logging.getLogger(__name__)
|
||||
@@ -48,7 +49,7 @@ class WebhookServer(object):
|
||||
IOLoop().make_current()
|
||||
self.is_running = True
|
||||
self.logger.debug('Webhook Server started.')
|
||||
self.http_server.listen(self.port)
|
||||
self.http_server.listen(self.port, address=self.listen)
|
||||
self.loop = IOLoop.current()
|
||||
self.loop.start()
|
||||
self.logger.debug('Webhook Server stopped.')
|
||||
|
||||
Vendored
+1
-1
Submodule telegram/vendor/ptb_urllib3 updated: d2403a79fc...1954df0395
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user