Compare commits

..

1 Commits

Author SHA1 Message Date
Jannes Höke 208e9c6809 Merge pull request #348 from python-telegram-bot/urllib3_fix_proxy_auth
Urllib3 fix proxy auth
2016-07-13 14:51:39 +02:00
178 changed files with 2692 additions and 6223 deletions
+52 -67
View File
@@ -1,41 +1,34 @@
How To Contribute
===================
=================
Every open source project lives from the generous help by contributors that sacrifice their time and ``python-telegram-bot`` is no different. To make participation as pleasant as possible, this project adheres to the `Code of Conduct`_ by the Python Software Foundation.
Setting things up
-------------------
-----------------
1. Fork the ``python-telegram-bot`` repository to your GitHub account.
2. Clone your forked repository of ``python-telegram-bot`` to your computer:
.. code-block:: bash
``$ git clone https://github.com/<your username>/python-telegram-bot``
$ git clone https://github.com/<your username>/python-telegram-bot
$ cd python-telegram-bot
``$ cd python-telegram-bot``
3. Add a track to the original repository:
.. code-block:: bash
$ git remote add upstream https://github.com/python-telegram-bot/python-telegram-bot
``$ git remote add upstream https://github.com/python-telegram-bot/python-telegram-bot``
4. Install dependencies:
.. 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:
.. code-block:: bash
$ pre-commit install
``$ pre-commit install``
Finding something to do
###################
-----------------------
If you already know what you'd like to work on, you can skip this section.
@@ -44,7 +37,7 @@ If you have an idea for something to do, first check if it's already been filed
Another great way to start contributing is by writing tests. Tests are really important because they help prevent developers from accidentally breaking existing code, allowing them to build cool things faster. If you're interested in helping out, let the development team know by posting to the `developers' mailing list`_, and we'll help you get started.
Instructions for making a code change
####################
-------------------------------------
The central development branch is ``master``, which should be clean and ready for release at any time. In general, all changes should be done as feature branches based off of ``master``.
@@ -54,12 +47,13 @@ Here's how to make a one-off code change.
2. **Create a new branch with this name, starting from** ``master``. In other words, run:
.. code-block:: bash
``$ git fetch upstream``
$ git fetch upstream
$ git checkout master
$ git merge upstream/master
$ git checkout -b your-branch-name
``$ git checkout master``
``$ git merge upstream/master``
``$ git checkout -b your-branch-name``
3. **Make a commit to your feature branch**. Each commit should be self-contained and have a descriptive commit message that helps other developers understand why the changes were made.
@@ -81,27 +75,19 @@ Here's how to make a one-off code change.
- Before making a commit ensure that all automated tests still pass:
.. code-block::
$ make test
``$ make test``
- To actually make the commit (this will trigger tests for yapf, lint and pep8 automatically):
.. code-block:: bash
``$ git add your-file-changed.py``
$ git add your-file-changed.py
- yapf may change code formatting, make sure to re-add them to your commit.
- yapf may change code formatting, make sure to re-add them to your commit.
.. code-block:: bash
$ git commit -a -m "your-commit-message-here"
``$ git commit -a -m "your-commit-message-here"``
- Finally, push it to your GitHub fork, run:
.. code-block:: bash
$ git push origin your-branch-name
``$ git push origin your-branch-name``
4. **When your feature is ready to merge, create a pull request.**
@@ -121,71 +107,70 @@ Here's how to make a one-off code change.
- Resolve any merge conflicts that arise. To resolve conflicts between 'your-branch-name' (in your fork) and 'master' (in the ``python-telegram-bot`` repository), run:
.. code-block:: bash
``$ git checkout your-branch-name``
$ git checkout your-branch-name
$ git fetch upstream
$ git merge upstream/master
$ ...[fix the conflicts]...
$ ...[make sure the tests pass before committing]...
$ git commit -a
$ git push origin your-branch-name
``$ git fetch upstream``
``$ git merge upstream/master``
``$ ...[fix the conflicts]...``
``$ ...[make sure the tests pass before committing]...``
``$ git commit -a``
``$ git push origin your-branch-name``
- At the end, the reviewer will merge the pull request.
6. **Tidy up!** Delete the feature branch from both your local clone and the GitHub repository:
.. code-block:: bash
``$ git branch -D your-branch-name``
$ git branch -D your-branch-name
$ git push origin --delete your-branch-name
``$ git push origin --delete your-branch-name``
7. **Celebrate.** Congratulations, you have contributed to ``python-telegram-bot``!
Style commandments
---------------------
==================
Specific commandments
#####################
---------------------
- Avoid using "double quotes" where you can reasonably use 'single quotes'.
AssertEqual argument order
######################
--------------------------
- assertEqual method's arguments should be in ('actual', 'expected') order.
assertEqual method's arguments should be in ('actual', 'expected') order.
Properly calling callables
#######################
--------------------------
Methods, functions and classes can specify optional parameters (with default
values) using Python's keyword arg syntax. When providing a value to such a
callable we prefer that the call also uses keyword arg syntax. For example:
callable we prefer that the call also uses keyword arg syntax. For example::
.. code-block:: python
# GOOD
f(0, optional=True)
# GOOD
f(0, optional=True)
# BAD
f(0, True)
# BAD
f(0, True)
This gives us the flexibility to re-order arguments and more importantly
to add new required arguments. It's also more explicit and easier to read.
Properly defining optional arguments
########################
------------------------------------
It's always good to not initialize optional arguments at class creation,
instead use ``**kwargs`` to get them. It's well known Telegram API can
change without notice, in that case if a new argument is added it won't
break the API classes. For example:
.. code-block:: python
It's always good to not initialize optional arguments at class creation,
instead use ``**kwargs`` to get them. It's well known Telegram API can
change without notice, in that case if a new argument is added it won't
break the API classes. For example::
# GOOD
def __init__(self, id, name, last_name='', **kwargs):
self.last_name = last_name
def __init__(self, id, name, **kwargs):
self.last_name = kwargs.get('last_name', '')
# BAD
def __init__(self, id, name, last_name=''):
@@ -198,4 +183,4 @@ break the API classes. For example:
.. _`PEP 8 Style Guide`: https://www.python.org/dev/peps/pep-0008/
.. _`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
.. _AUTHORS.rst: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/AUTHORS.rst
+6 -6
View File
@@ -1,10 +1,6 @@
<!--
Thanks for reporting issues of python-telegram-bot!
To make it easier for us to help you please enter detailed information below.
Please note, we only support the latest version of python-telegram-bot and
master branch. Please make sure to upgrade & recreate the issue on the latest
version prior to opening an issue.
-->
### Steps to reproduce
1.
@@ -23,9 +19,13 @@ Tell us what happens instead
**Operating System:**
**Version of Python, python-telegram-bot & dependencies:**
**Version of Python:**
``$ python -m telegram``
``$ python -V``
**Version of python-telegram-bot:**
``$ python -c 'import telegram; print(telegram.__version__)'``
### Logs
Insert logs here (if necessary)
-3
View File
@@ -68,6 +68,3 @@ telegram.mp4
telegram.ogg
telegram.png
telegram.webp
# original files from merges
*.orig
+4 -4
View File
@@ -1,15 +1,15 @@
- repo: git://github.com/python-telegram-bot/mirrors-yapf
sha: v0.12.2
- repo: git://github.com/pre-commit/mirrors-yapf
sha: 316b795b2f32cbe80047aff7e842b72368d5a2c1
hooks:
- id: yapf
files: ^(telegram|tests)/.*\.py$
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: 18d7035de5388cc7775be57f529c154bf541aab9
sha: 6dfcb89af3c9b4d172cc2e5a8a2fa0f54615a338
hooks:
- id: flake8
files: ^telegram/.*\.py$
- repo: git://github.com/pre-commit/mirrors-pylint
sha: v1.5.5
sha: 4de6c8dfadef1a271a814561ce05b8bc1c446d22
hooks:
- id: pylint
files: ^telegram/.*\.py$
+2 -5
View File
@@ -1,21 +1,18 @@
language: python
python:
- "2.6"
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "pypy"
- "pypy3"
branches:
only:
- master
install:
- pip install coveralls
- pip install -r requirements.txt
- pip install -r requirements-dev.txt
- if [[ $TRAVIS_PYTHON_VERSION != 'pypy'* ]]; then pip install ujson; fi
script:
- nosetests -v --with-flaky --no-flaky-report --with-coverage --cover-package=telegram/
- if [[ $TRAVIS_PYTHON_VERSION == 3.5 ]]; then pre-commit run --all-files; fi
- 'if [ $TRAVIS_PYTHON_VERSION != 2.6 ] && [ $TRAVIS_PYTHON_VERSION != 3.3 ] && [ $TRAVIS_PYTHON_VERSION != pypy3 ]; then pre-commit run --all-files; fi'
after_success:
coveralls
-7
View File
@@ -11,27 +11,20 @@ The following wonderful people contributed directly or indirectly to this projec
- `Avanatiker <https://github.com/Avanatiker>`_
- `Balduro <https://github.com/Balduro>`_
- `bimmlerd <https://github.com/bimmlerd>`_
- `Eli Gao <https://github.com/eligao>`_
- `ErgoZ Riftbit Vaper <https://github.com/ergoz>`_
- `franciscod <https://github.com/franciscod>`_
- `Jacob Bom <https://github.com/bomjacob>`_
- `JASON0916 <https://github.com/JASON0916>`_
- `jh0ker <https://github.com/jh0ker>`_
- `JRoot3D <https://github.com/JRoot3D>`_
- `jlmadurga <https://github.com/jlmadurga>`_
- `Li-aung Yip <https://github.com/LiaungYip>`_
- `macrojames <https://github.com/macrojames>`_
- `Michael Elovskikh <https://github.com/wronglink>`_
- `naveenvhegde <https://github.com/naveenvhegde>`_
- `njittam <https://github.com/njittam>`_
- `Noam Meltzer <https://github.com/tsnoam>`_
- `Oleg Shlyazhko <https://github.com/ollmer>`_
- `overquota <https://github.com/overquota>`_
- `Patrick Hofmann <https://github.com/PH89>`_
- `Rahiel Kasim <https://github.com/rahiel>`_
- `Shelomentsev D <https://github.com/shelomentsevd>`_
- `sooyhwang <https://github.com/sooyhwang>`_
- `Valentijn <https://github.com/Faalentijn>`_
- `wjt <https://github.com/wjt>`_
Please add yourself here alphabetically when you submit your first pull request.
-38
View File
@@ -1,41 +1,3 @@
=======
Changes
=======
**2016-10-25**
*Released 5.2*
- Implement API changes of October 3rd (games update)
- Add ``Message.edit_*`` methods
- Filters for the ``MessageHandler`` can now be combined using bitwise operators (``& and |``)
- Add a way to save user- and chat-related data temporarily
- Other bugfixes and improvements
**2016-09-24**
*Released 5.1*
- Drop Python 2.6 support
- Deprecate ``telegram.Emoji``
- Use ``ujson`` if available
- Add instance methods to ``Message``, ``Chat``, ``User``, ``InlineQuery`` and ``CallbackQuery``
- RegEx filtering for ``CallbackQueryHandler`` and ``InlineQueryHandler``
- New ``MessageHandler`` filters: ``forwarded`` and ``entity``
- Add ``Message.get_entity`` to correctly handle UTF-16 codepoints and ``MessageEntity`` offsets
- Fix bug in ``ConversationHandler`` when first handler ends the conversation
- Allow multiple ``Dispatcher`` instances
- Add ``ChatMigrated`` Exception
- Properly split and handle arguments in ``CommandHandler``
**2016-07-15**
*Released 5.0*
- Rework ``JobQueue``
- Introduce ``ConversationHandler``
**2016-07-12**
*Released 4.3.4*
+21 -18
View File
@@ -1,9 +1,9 @@
.. image:: https://github.com/python-telegram-bot/logos/blob/master/logo-text/png/ptb-logo-text_768.png?raw=true
:align: center
:target: https://python-telegram-bot.org
:target: https://github.com/python-telegram-bot/logos
:alt: python-telegram-bot Logo
We have made you a wrapper you can't refuse
Not **just** a Python wrapper around the Telegram Bot API
*Stay tuned for library updates and new releases on our* `Telegram Channel <https://telegram.me/pythontelegrambotchannel>`_.
@@ -16,7 +16,7 @@ We have made you a wrapper you can't refuse
:alt: Supported python versions
.. image:: https://img.shields.io/badge/docs-latest-af1a97.svg
:target: https://python-telegram-bot.readthedocs.io/
:target: https://pythonhosted.org/python-telegram-bot/
:alt: Documentation Status
.. image:: https://img.shields.io/pypi/l/python-telegram-bot.svg
@@ -37,7 +37,7 @@ We have made you a wrapper you can't refuse
.. image:: http://isitmaintained.com/badge/resolution/python-telegram-bot/python-telegram-bot.svg
:target: http://isitmaintained.com/project/python-telegram-bot/python-telegram-bot
:alt: Median time to resolve an issue
:alt: Average time to resolve an issue
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg
:target: https://telegram.me/pythontelegrambotgroup
@@ -73,7 +73,8 @@ Introduction
This library provides a pure Python interface for the
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
It's compatible with Python versions 2.7, 3.3+ and `PyPy <http://pypy.org/>`_.
It works with Python versions from 2.6+ (**Note:** Support for 2.6 will be dropped at some point
this year. 2.7 will still be supported).
It also works with `Google App Engine <https://cloud.google.com/appengine>`_.
In addition to the pure API implementation, this library features a number of high-level classes to
@@ -84,7 +85,7 @@ make the development of bots easy and straightforward. These classes are contain
Telegram API support
====================
As of **3. Oct 2016**, all types and methods of the Telegram Bot API are supported.
As of **28. May 2016**, all types and methods of the Telegram Bot API are supported.
==========
Installing
@@ -96,14 +97,6 @@ You can install or upgrade python-telegram-bot with:
$ pip install python-telegram-bot --upgrade
Or you can install from source with:
.. code:: shell
$ git clone https://github.com/python-telegram-bot/python-telegram-bot
$ cd python-telegram-bot
$ python setup.py install
===============
Getting started
===============
@@ -116,7 +109,7 @@ Our Wiki contains a lot of resources to get you started with ``python-telegram-b
Other references:
- `Telegram API documentation <https://core.telegram.org/bots/api>`_
- `python-telegram-bot documentation <https://python-telegram-bot.readthedocs.io/>`_
- `python-telegram-bot documentation <https://pythonhosted.org/python-telegram-bot/>`_
-------------------
Learning by example
@@ -124,11 +117,21 @@ Learning by example
We believe that the best way to learn and understand this simple package is by example. So here
are some examples for you to review. Even if it's not your approach for learning, please take a
look at ``echobot2``, it is de facto the base for most of the bots out there. Best of all,
look at ``echobot2`` (below), it is de facto the base for most of the bots out there. Best of all,
the code for these examples are released to the public domain, so you can start by grabbing the
code and building on top of it.
Visit `this page <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/README.md>`_ to discover the official examples or look at the examples on the `wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Examples>`_ to see other bots the community has built.
- `echobot2 <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot2.py>`_ replies back messages.
- `inlinebot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinebot.py>`_ basic example of an `inline bot <https://core.telegram.org/bots/inline>`_.
- `state machine bot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/state_machine_bot.py>`_ keeps the state for individual users, useful for multipart conversations.
- `timerbot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py>`_ uses the ``JobQueue`` to send timed messages.
- `echobot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/legacy/echobot.py>`_ uses only the pure API to echo messages.
Look at the examples on the `wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Examples>`_ to see other bots the community has built.
-------
Logging
@@ -162,7 +165,7 @@ If you want DEBUG logs instead:
Documentation
=============
``python-telegram-bot``'s documentation lives at `readthedocs.io <https://python-telegram-bot.readthedocs.io/>`_.
``python-telegram-bot``'s documentation lives at `pythonhosted.org <https://pythonhosted.org/python-telegram-bot/>`_.
============
Getting help
+1 -1
View File
@@ -189,4 +189,4 @@ xml:
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+3 -3
View File
@@ -15,7 +15,7 @@
import sys
import os
import shlex
# import telegram
import telegram
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -59,9 +59,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '5.2' # telegram.__version__[:3]
version = telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = '5.2.0' # telegram.__version__
release = telegram.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
-1
View File
@@ -7,7 +7,6 @@ Welcome to Python Telegram Bot's documentation!
===============================================
Contents:
telegram
.. toctree::
:maxdepth: 2
-7
View File
@@ -1,7 +0,0 @@
telegram.constants module
=========================
.. automodule:: telegram.constants
:members:
:undoc-members:
:show-inheritance:
+7
View File
@@ -0,0 +1,7 @@
telegram.emoji module
=====================
.. automodule:: telegram.emoji
:members:
:undoc-members:
:show-inheritance:
@@ -1,7 +1,7 @@
telegram.ext.callbackqueryhandler module
========================================
telegram.ext.handler module
===========================
.. automodule:: telegram.ext.callbackqueryhandler
.. automodule:: telegram.ext.handler
:members:
:undoc-members:
:show-inheritance:
@@ -1,7 +0,0 @@
telegram.ext.conversationhandler module
=======================================
.. automodule:: telegram.ext.conversationhandler
:members:
:undoc-members:
:show-inheritance:
-7
View File
@@ -1,7 +0,0 @@
telegram.ext.filters module
===========================
.. automodule:: telegram.ext.filters
:members:
:undoc-members:
:show-inheritance:
-2
View File
@@ -11,11 +11,9 @@ Submodules
telegram.ext.jobqueue
telegram.ext.handler
telegram.ext.choseninlineresulthandler
telegram.ext.conversationhandler
telegram.ext.commandhandler
telegram.ext.inlinequeryhandler
telegram.ext.messagehandler
telegram.ext.filters
telegram.ext.regexhandler
telegram.ext.stringcommandhandler
telegram.ext.stringregexhandler
-26
View File
@@ -1,26 +0,0 @@
# Examples
The examples in this folder are small bots meant to show you how a bot that is 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` example, they all use the high-level framework this library provides with the `telegram.ext` submodule.
All examples are licensed under the [CC0 License](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/LICENSE.txt) and are therefore fully dedicated to the public domain. You can use them as the base for your own bots without worrying about copyrights.
### [`echobot2.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot2.py)
This is probably the base for most of the bots made with `python-telegram-bot`. It simply replies to each text message with a message that contains the same text.
### [`timerbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py)
This bot uses the [`JobQueue`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.jobqueue.html) class to send timed messages. The user sets a timer by using `/set` command with a specific time, for example `/set 30`. The bot then sets up a job to send a message to that user after 30 seconds. The user can also cancel the timer by sending `/unset`. To learn more about the `JobQueue`, read [this wiki article](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue).
### [`conversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot.py)
A common task for a bot is to ask information from the user. In v5.0 of this library, we introduced the [`ConversationHandler`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.conversationhandler.html) for that exact purpose. This example uses it to retrieve user-information in a conversation-like style.
### [`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`
### [`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.
### [`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).
## 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.
-157
View File
@@ -1,157 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Simple Bot to reply to Telegram messages
# 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 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 ConversationHandler.
Send /start to initiate the conversation.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram import (ReplyKeyboardMarkup)
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
ConversationHandler)
import logging
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
GENDER, PHOTO, LOCATION, BIO = range(4)
def start(bot, update):
reply_keyboard = [['Boy', 'Girl', 'Other']]
update.message.reply_text(
'Hi! My name is Professor Bot. I will hold a conversation with you. '
'Send /cancel to stop talking to me.\n\n'
'Are you a boy or a girl?',
reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
return GENDER
def gender(bot, update):
user = update.message.from_user
logger.info("Gender of %s: %s" % (user.first_name, update.message.text))
update.message.reply_text('I see! Please send me a photo of yourself, '
'so I know what you look like, or send /skip if you don\'t want to.')
return PHOTO
def photo(bot, update):
user = update.message.from_user
photo_file = bot.getFile(update.message.photo[-1].file_id)
photo_file.download('user_photo.jpg')
logger.info("Photo of %s: %s" % (user.first_name, 'user_photo.jpg'))
update.message.reply_text('Gorgeous! Now, send me your location please, '
'or send /skip if you don\'t want to.')
return LOCATION
def skip_photo(bot, update):
user = update.message.from_user
logger.info("User %s did not send a photo." % user.first_name)
update.message.reply_text('I bet you look great! Now, send me your location please, '
'or send /skip.')
return LOCATION
def location(bot, update):
user = update.message.from_user
user_location = update.message.location
logger.info("Location of %s: %f / %f"
% (user.first_name, user_location.latitude, user_location.longitude))
update.message.reply_text('Maybe I can visit you sometime! '
'At last, tell me something about yourself.')
return BIO
def skip_location(bot, update):
user = update.message.from_user
logger.info("User %s did not send a location." % user.first_name)
update.message.reply_text('You seem a bit paranoid! '
'At last, tell me something about yourself.')
return BIO
def bio(bot, update):
user = update.message.from_user
logger.info("Bio of %s: %s" % (user.first_name, update.message.text))
update.message.reply_text('Thank you! I hope we can talk again some day.')
return ConversationHandler.END
def cancel(bot, update):
user = update.message.from_user
logger.info("User %s canceled the conversation." % user.first_name)
update.message.reply_text('Bye! I hope we can talk again some day.')
return ConversationHandler.END
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
def main():
# Create the EventHandler and pass it your bot's token.
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
# Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
GENDER: [RegexHandler('^(Boy|Girl|Other)$', gender)],
PHOTO: [MessageHandler(Filters.photo, photo),
CommandHandler('skip', skip_photo)],
LOCATION: [MessageHandler(Filters.location, location),
CommandHandler('skip', skip_location)],
BIO: [MessageHandler(Filters.text, bio)]
},
fallbacks=[CommandHandler('cancel', cancel)]
)
dp.add_handler(conv_handler)
# log all errors
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
# Run the bot until the you presses 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()
-152
View File
@@ -1,152 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Simple Bot to reply to Telegram messages
# 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 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 ConversationHandler.
Send /start to initiate the conversation.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram import ReplyKeyboardMarkup
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
ConversationHandler)
import logging
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3)
reply_keyboard = [['Age', 'Favourite colour'],
['Number of siblings', 'Something else...'],
['Done']]
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
def facts_to_str(user_data):
facts = list()
for key, value in user_data.items():
facts.append('%s - %s' % (key, value))
return "\n".join(facts).join(['\n', '\n'])
def start(bot, update):
update.message.reply_text(
"Hi! My name is Doctor Botter. I will hold a more complex conversation with you. "
"Why don't you tell me something about yourself?",
reply_markup=markup)
return CHOOSING
def regular_choice(bot, update, user_data):
text = update.message.text
user_data['choice'] = text
update.message.reply_text('Your %s? Yes, I would love to hear about that!' % text.lower())
return TYPING_REPLY
def custom_choice(bot, update):
update.message.reply_text('Alright, please send me the category first, '
'for example "Most impressive skill"')
return TYPING_CHOICE
def received_information(bot, update, user_data):
text = update.message.text
category = user_data['choice']
user_data[category] = text
del user_data['choice']
update.message.reply_text("Neat! Just so you know, this is what you already told me:"
"%s"
"You can tell me more, or change your opinion on something."
% facts_to_str(user_data),
reply_markup=markup)
return CHOOSING
def done(bot, update, user_data):
if 'choice' in user_data:
del user_data['choice']
update.message.reply_text("I learned these facts about you:"
"%s"
"Until next time!" % facts_to_str(user_data))
user_data.clear()
return ConversationHandler.END
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
def main():
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
# Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO
conv_handler = ConversationHandler(
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),
],
TYPING_CHOICE: [MessageHandler(Filters.text,
regular_choice,
pass_user_data=True),
],
TYPING_REPLY: [MessageHandler(Filters.text,
received_information,
pass_user_data=True),
],
},
fallbacks=[RegexHandler('^Done$', done, pass_user_data=True)]
)
dp.add_handler(conv_handler)
# log all errors
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
# Run the bot until the you presses 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 -1
View File
@@ -46,7 +46,7 @@ def echo(bot):
if update.message: # your bot can receive updates without messages
# Reply to the message
update.message.reply_text(update.message.text)
bot.sendMessage(chat_id=chat_id, text=update.message.text)
if __name__ == '__main__':
+4 -4
View File
@@ -29,15 +29,15 @@ 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.
def start(bot, update):
update.message.reply_text('Hi!')
bot.sendMessage(update.message.chat_id, text='Hi!')
def help(bot, update):
update.message.reply_text('Help!')
bot.sendMessage(update.message.chat_id, text='Help!')
def echo(bot, update):
update.message.reply_text(update.message.text)
bot.sendMessage(update.message.chat_id, text=update.message.text)
def error(bot, update, error):
@@ -56,7 +56,7 @@ def main():
dp.add_handler(CommandHandler("help", help))
# on noncommand i.e message - echo the message on Telegram
dp.add_handler(MessageHandler(Filters.text, echo))
dp.add_handler(MessageHandler([Filters.text], echo))
# log all errors
dp.add_error_handler(error)
+3 -3
View File
@@ -34,11 +34,11 @@ 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.
def start(bot, update):
update.message.reply_text('Hi!')
bot.sendMessage(update.message.chat_id, text='Hi!')
def help(bot, update):
update.message.reply_text('Help!')
bot.sendMessage(update.message.chat_id, text='Help!')
def escape_markdown(text):
@@ -68,7 +68,7 @@ def inlinequery(bot, update):
"_%s_" % escape_markdown(query),
parse_mode=ParseMode.MARKDOWN)))
update.inline_query.answer(results)
bot.answerInlineQuery(update.inline_query.id, results=results)
def error(bot, update, error):
+79 -20
View File
@@ -1,49 +1,108 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Basic example for a bot that uses inline keyboards.
# Basic example for a bot that awaits an answer from the user. It's built upon
# the state_machine_bot.py example
# This program is dedicated to the public domain under the CC0 license.
import logging
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
from telegram import Emoji, ForceReply, InlineKeyboardButton, \
InlineKeyboardMarkup
from telegram.ext import Updater, CommandHandler, MessageHandler, \
CallbackQueryHandler, Filters
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
level=logging.DEBUG)
# Define the different states a chat can be in
MENU, AWAIT_CONFIRMATION, AWAIT_INPUT = range(3)
# Python 2 and 3 unicode differences
try:
YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'), Emoji.THUMBS_DOWN_SIGN.decode('utf-8'))
except AttributeError:
YES, NO = (Emoji.THUMBS_UP_SIGN, Emoji.THUMBS_DOWN_SIGN)
# States are saved in a dict that maps chat_id -> state
state = dict()
# Sometimes you need to save data temporarily
context = dict()
# This dict is used to store the settings value for the chat.
# Usually, you'd use persistence for this (e.g. sqlite).
values = dict()
def start(bot, update):
keyboard = [[InlineKeyboardButton("Option 1", callback_data='1'),
InlineKeyboardButton("Option 2", callback_data='2')],
# Example handler. Will be called on the /set command and on regular messages
def set_value(bot, update):
chat_id = update.message.chat_id
user_id = update.message.from_user.id
user_state = state.get(chat_id, MENU)
[InlineKeyboardButton("Option 3", callback_data='3')]]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('Please choose:', reply_markup=reply_markup)
if user_state == MENU:
state[user_id] = AWAIT_INPUT # set the state
bot.sendMessage(chat_id,
text="Please enter your settings value",
reply_markup=ForceReply())
def button(bot, update):
def entered_value(bot, update):
chat_id = update.message.chat_id
user_id = update.message.from_user.id
chat_state = state.get(user_id, MENU)
# Check if we are waiting for input
if chat_state == AWAIT_INPUT:
state[user_id] = AWAIT_CONFIRMATION
# Save the user id and the answer to context
context[user_id] = update.message.text
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton(YES, callback_data=YES),
InlineKeyboardButton(NO, callback_data=NO)]])
bot.sendMessage(chat_id, text="Are you sure?", reply_markup=reply_markup)
def confirm_value(bot, update):
query = update.callback_query
chat_id = query.message.chat_id
user_id = query.from_user.id
text = query.data
user_state = state.get(user_id, MENU)
user_context = context.get(user_id, None)
bot.editMessageText(text="Selected option: %s" % query.data,
chat_id=query.message.chat_id,
message_id=query.message.message_id)
# Check if we are waiting for confirmation and the right user answered
if user_state == AWAIT_CONFIRMATION:
del state[user_id]
del context[user_id]
bot.answerCallbackQuery(query.id, text="Ok!")
if text == YES:
values[user_id] = user_context
bot.editMessageText(text="Changed value to %s." % values[user_id],
chat_id=chat_id,
message_id=query.message.message_id)
else:
bot.editMessageText(text="Alright, value is still %s." %
values.get(user_id, 'not set'),
chat_id=chat_id,
message_id=query.message.message_id)
def help(bot, update):
update.message.reply_text("Use /start to test this bot.")
bot.sendMessage(update.message.chat_id, text="Use /set to test this bot.")
def error(bot, update, error):
logging.warning('Update "%s" caused error "%s"' % (update, error))
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
updater.dispatcher.add_handler(CommandHandler('start', start))
updater.dispatcher.add_handler(CallbackQueryHandler(button))
# The command
updater.dispatcher.add_handler(CommandHandler('set', set_value))
# The answer
updater.dispatcher.add_handler(MessageHandler([Filters.text], entered_value))
# The confirmation
updater.dispatcher.add_handler(CallbackQueryHandler(confirm_value))
updater.dispatcher.add_handler(CommandHandler('start', help))
updater.dispatcher.add_handler(CommandHandler('help', help))
updater.dispatcher.add_error_handler(error)
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Basic example for a bot that awaits an answer from the user
# This program is dedicated to the public domain under the CC0 license.
import logging
from telegram import Emoji, ForceReply, ReplyKeyboardMarkup, KeyboardButton
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
# Define the different states a chat can be in
MENU, AWAIT_CONFIRMATION, AWAIT_INPUT = range(3)
# Python 2 and 3 unicode differences
try:
YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'), Emoji.THUMBS_DOWN_SIGN.decode('utf-8'))
except AttributeError:
YES, NO = (Emoji.THUMBS_UP_SIGN, Emoji.THUMBS_DOWN_SIGN)
# States are saved in a dict that maps chat_id -> state
state = dict()
# Sometimes you need to save data temporarily
context = dict()
# This dict is used to store the settings value for the chat.
# Usually, you'd use persistence for this (e.g. sqlite).
values = dict()
# Example handler. Will be called on the /set command and on regular messages
def set_value(bot, update):
chat_id = update.message.chat_id
user_id = update.message.from_user.id
text = update.message.text
chat_state = state.get(chat_id, MENU)
chat_context = context.get(chat_id, None)
# Since the handler will also be called on messages, we need to check if
# the message is actually a command
if chat_state == MENU and text[0] == '/':
state[chat_id] = AWAIT_INPUT # set the state
context[chat_id] = user_id # save the user id to context
bot.sendMessage(chat_id,
text="Please enter your settings value or send "
"/cancel to abort",
reply_markup=ForceReply())
# If we are waiting for input and the right user answered
elif chat_state == AWAIT_INPUT and chat_context == user_id:
state[chat_id] = AWAIT_CONFIRMATION
# Save the user id and the answer to context
context[chat_id] = (user_id, update.message.text)
reply_markup = ReplyKeyboardMarkup(
[[KeyboardButton(YES), KeyboardButton(NO)]],
one_time_keyboard=True)
bot.sendMessage(chat_id, text="Are you sure?", reply_markup=reply_markup)
# If we are waiting for confirmation and the right user answered
elif chat_state == AWAIT_CONFIRMATION and chat_context[0] == user_id:
del state[chat_id]
del context[chat_id]
if text == YES:
values[chat_id] = chat_context[1]
bot.sendMessage(chat_id, text="Changed value to %s." % values[chat_id])
else:
bot.sendMessage(chat_id,
text="Value not changed: %s." % values.get(chat_id, '<not set>'))
# Handler for the /cancel command.
# Sets the state back to MENU and clears the context
def cancel(bot, update):
chat_id = update.message.chat_id
del state[chat_id]
del context[chat_id]
def help(bot, update):
bot.sendMessage(update.message.chat_id, text="Use /set to test this bot.")
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
# The command
updater.dispatcher.add_handler(CommandHandler('set', set_value))
# The answer and confirmation
updater.dispatcher.add_handler(MessageHandler([Filters.text], set_value))
updater.dispatcher.add_handler(CommandHandler('cancel', cancel))
updater.dispatcher.add_handler(CommandHandler('start', help))
updater.dispatcher.add_handler(CommandHandler('help', help))
# Start the Bot
updater.start_polling()
# Run the bot until the user presses Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT
updater.idle()
+21 -37
View File
@@ -17,62 +17,44 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram.ext import Updater, CommandHandler, Job
from telegram.ext import Updater, CommandHandler
import logging
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.DEBUG)
level=logging.INFO)
logger = logging.getLogger(__name__)
timers = dict()
job_queue = None
# Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error.
def start(bot, update):
update.message.reply_text('Hi! Use /set <seconds> to set a timer')
bot.sendMessage(update.message.chat_id, text='Hi! Use /set <seconds> to ' 'set a timer')
def alarm(bot, job):
"""Function to send the alarm message"""
bot.sendMessage(job.context, text='Beep!')
def set(bot, update, args, job_queue):
"""Adds a job to the queue"""
def set(bot, update, args):
""" Adds a job to the queue """
chat_id = update.message.chat_id
try:
# args[0] should contain the time for the timer in seconds
due = int(args[0])
if due < 0:
update.message.reply_text('Sorry we can not go back to future!')
return
bot.sendMessage(chat_id, text='Sorry we can not go back to future!')
def alarm(bot):
""" Inner function to send the alarm message """
bot.sendMessage(chat_id, text='Beep!')
# Add job to queue
job = Job(alarm, due, repeat=False, context=chat_id)
timers[chat_id] = job
job_queue.put(job)
job_queue.put(alarm, due, repeat=False)
bot.sendMessage(chat_id, text='Timer successfully set!')
update.message.reply_text('Timer successfully set!')
except (IndexError, ValueError):
update.message.reply_text('Usage: /set <seconds>')
def unset(bot, update):
"""Removes the job if the user changed their mind"""
chat_id = update.message.chat_id
if chat_id not in timers:
update.message.reply_text('You have no active timer')
return
job = timers[chat_id]
job.schedule_removal()
del timers[chat_id]
update.message.reply_text('Timer successfully unset!')
except IndexError:
bot.sendMessage(chat_id, text='Usage: /set <seconds>')
except ValueError:
bot.sendMessage(chat_id, text='Usage: /set <seconds>')
def error(bot, update, error):
@@ -80,7 +62,10 @@ def error(bot, update, error):
def main():
global job_queue
updater = Updater("TOKEN")
job_queue = updater.job_queue
# Get the dispatcher to register handlers
dp = updater.dispatcher
@@ -88,8 +73,7 @@ def main():
# on different commands - answer in Telegram
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", start))
dp.add_handler(CommandHandler("set", set, pass_args=True, pass_job_queue=True))
dp.add_handler(CommandHandler("unset", unset))
dp.add_handler(CommandHandler("set", set, pass_args=True))
# log all errors
dp.add_error_handler(error)
+1
View File
@@ -2,6 +2,7 @@ flake8
nose
pep257
pylint
unittest2
flaky
yapf
pre-commit
+4 -12
View File
@@ -2,7 +2,7 @@
"""The setup and build script for the python-telegram-bot library."""
import codecs
import os
import telegram
from setuptools import setup, find_packages
@@ -16,27 +16,18 @@ def requirements():
return requirements_list
with codecs.open('README.rst', 'r', 'utf-8') as fd:
fn = os.path.join('telegram', 'version.py')
with open(fn) as fh:
code = compile(fh.read(), fn, 'exec')
exec(code)
setup(name='python-telegram-bot',
version=__version__,
version=telegram.__version__,
author='Leandro Toledo',
author_email='devs@python-telegram-bot.org',
license='LGPLv3',
url='https://python-telegram-bot.org/',
url='https://github.com/python-telegram-bot/python-telegram-bot',
keywords='python telegram bot api wrapper',
description='Not just a Python wrapper around the Telegram Bot API',
long_description=fd.read(),
packages=find_packages(exclude=['tests*']),
install_requires=requirements(),
extras_require={
'json': 'ujson',
},
include_package_data=True,
classifiers=[
'Development Status :: 5 - Production/Stable',
@@ -48,6 +39,7 @@ with codecs.open('README.rst', 'r', 'utf-8') as fd:
'Topic :: Internet',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
+23 -31
View File
@@ -43,11 +43,10 @@ from .forcereply import ForceReply
from .error import TelegramError
from .inputfile import InputFile
from .file import File
from .nullhandler import NullHandler
from .emoji import Emoji
from .parsemode import ParseMode
from .messageentity import MessageEntity
from .animation import Animation
from .game import Game
from .message import Message
from .inputmessagecontent import InputMessageContent
from .callbackquery import CallbackQuery
@@ -75,41 +74,34 @@ from .inlinequeryresultphoto import InlineQueryResultPhoto
from .inlinequeryresultvenue import InlineQueryResultVenue
from .inlinequeryresultvideo import InlineQueryResultVideo
from .inlinequeryresultvoice import InlineQueryResultVoice
from .inlinequeryresultgame import InlineQueryResultGame
from .inputtextmessagecontent import InputTextMessageContent
from .inputlocationmessagecontent import InputLocationMessageContent
from .inputvenuemessagecontent import InputVenueMessageContent
from .inputcontactmessagecontent import InputContactMessageContent
from .webhookinfo import WebhookInfo
from .gamehighscore import GameHighScore
from .update import Update
from .bot import Bot
from .constants import (MAX_MESSAGE_LENGTH, MAX_CAPTION_LENGTH, SUPPORTED_WEBHOOK_PORTS,
MAX_FILESIZE_DOWNLOAD, MAX_FILESIZE_UPLOAD,
MAX_MESSAGES_PER_SECOND_PER_CHAT, MAX_MESSAGES_PER_SECOND,
MAX_MESSAGES_PER_MINUTE_PER_GROUP)
from .version import __version__ # flake8: noqa
__author__ = 'devs@python-telegram-bot.org'
__version__ = '4.3.4'
__all__ = ['Audio', 'Bot', 'Chat', 'ChatMember', 'ChatAction', 'ChosenInlineResult',
'CallbackQuery', 'Contact', 'Document', 'Emoji', 'File', 'ForceReply',
'InlineKeyboardButton', 'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult',
'InlineQueryResult', 'InlineQueryResultArticle', 'InlineQueryResultAudio',
'InlineQueryResultCachedAudio', 'InlineQueryResultCachedDocument',
'InlineQueryResultCachedGif', 'InlineQueryResultCachedMpeg4Gif',
'InlineQueryResultCachedPhoto', 'InlineQueryResultCachedSticker',
'InlineQueryResultCachedVideo', 'InlineQueryResultCachedVoice',
'InlineQueryResultContact', 'InlineQueryResultDocument', 'InlineQueryResultGif',
'InlineQueryResultLocation', 'InlineQueryResultMpeg4Gif', 'InlineQueryResultPhoto',
'InlineQueryResultVenue', 'InlineQueryResultVideo', 'InlineQueryResultVoice',
'InputContactMessageContent', 'InputFile', 'InputLocationMessageContent',
'InputMessageContent', 'InputTextMessageContent', 'InputVenueMessageContent',
'KeyboardButton', 'Location', 'Message', 'MessageEntity', 'NullHandler', 'ParseMode',
'PhotoSize', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup', 'ReplyMarkup', 'Sticker',
'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue',
'Video', 'Voice']
__all__ = [
'Audio', 'Bot', 'Chat', 'ChatMember', 'ChatAction', 'ChosenInlineResult', 'CallbackQuery',
'Contact', 'Document', 'Emoji', 'File', 'ForceReply', 'InlineKeyboardButton',
'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult', 'InlineQueryResult',
'InlineQueryResultArticle', 'InlineQueryResultAudio', 'InlineQueryResultCachedAudio',
'InlineQueryResultCachedDocument', 'InlineQueryResultCachedGif',
'InlineQueryResultCachedMpeg4Gif', 'InlineQueryResultCachedPhoto',
'InlineQueryResultCachedSticker', 'InlineQueryResultCachedVideo',
'InlineQueryResultCachedVoice', 'InlineQueryResultContact', 'InlineQueryResultDocument',
'InlineQueryResultGif', 'InlineQueryResultLocation', 'InlineQueryResultMpeg4Gif',
'InlineQueryResultPhoto', 'InlineQueryResultVenue', 'InlineQueryResultVideo',
'InlineQueryResultVoice', 'InlineQueryResultGame', 'InputContactMessageContent', 'InputFile',
'InputLocationMessageContent', 'InputMessageContent', 'InputTextMessageContent',
'InputVenueMessageContent', 'KeyboardButton', 'Location', 'Message', 'MessageEntity',
'ParseMode', 'PhotoSize', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup', 'ReplyMarkup', 'Sticker',
'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue', 'Video',
'Voice', 'MAX_MESSAGE_LENGTH', 'MAX_CAPTION_LENGTH', 'SUPPORTED_WEBHOOK_PORTS',
'MAX_FILESIZE_DOWNLOAD', 'MAX_FILESIZE_UPLOAD', 'MAX_MESSAGES_PER_SECOND_PER_CHAT',
'MAX_MESSAGES_PER_SECOND', 'MAX_MESSAGES_PER_MINUTE_PER_GROUP', 'WebhookInfo', 'Animation',
'Game', 'GameHighScore'
]
if version_info < (2, 7):
from warnings import warn
warn("python-telegram-bot will stop supporting Python 2.6 in a future release. "
"Please upgrade your Python version to at least Python 2.7!")
-18
View File
@@ -1,18 +0,0 @@
import sys
import urllib3
import certifi
import future
from . import __version__ as telegram_ver
def print_ver_info():
print('python-telegram-bot {0}'.format(telegram_ver))
print('urllib3 {0}'.format(urllib3.__version__))
print('certifi {0}'.format(certifi.__version__))
print('future {0}'.format(future.__version__))
print('Python {0}'.format(sys.version.replace('\n', ' ')))
# main
print_ver_info()
-60
View File
@@ -1,60 +0,0 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# 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 Animation."""
from telegram import PhotoSize
from telegram import TelegramObject
class Animation(TelegramObject):
"""This object represents a Telegram Animation.
Attributes:
file_id (str): Unique file identifier.
Keyword Args:
thumb (Optional[:class:`telegram.PhotoSize`]): Animation thumbnail as defined by sender.
file_name (Optional[str]): Original animation filename as defined by sender.
mime_type (Optional[str]): MIME type of the file as defined by sender.
file_size (Optional[int]): File size.
"""
def __init__(self, file_id, **kwargs):
self.file_id = file_id
self.thumb = kwargs.get('thumb')
self.file_name = kwargs.get('file_name')
self.mime_type = kwargs.get('mime_type')
self.file_size = kwargs.get('file_size')
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.Game:
"""
if not data:
return None
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
return Animation(**data)
+11 -18
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Audio."""
"""This module contains a object that represents a Telegram Audio."""
from telegram import TelegramObject
@@ -35,37 +35,30 @@ class Audio(TelegramObject):
Args:
file_id (str):
duration (int):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
performer (Optional[str]):
title (Optional[str]):
mime_type (Optional[str]):
file_size (Optional[int]):
**kwargs: Arbitrary keyword arguments.
"""
def __init__(self,
file_id,
duration,
performer='',
title='',
mime_type='',
file_size=0,
**kwargs):
def __init__(self, file_id, duration, **kwargs):
# Required
self.file_id = str(file_id)
self.duration = int(duration)
# Optionals
self.performer = performer
self.title = title
self.mime_type = str(mime_type)
self.file_size = int(file_size)
self.performer = kwargs.get('performer', '')
self.title = kwargs.get('title', '')
self.mime_type = str(kwargs.get('mime_type', ''))
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
def de_json(data, bot):
def de_json(data):
"""
Args:
data (dict):
bot (telegram.Bot):
data (str):
Returns:
telegram.Audio:
+4 -12
View File
@@ -18,11 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""Base class for Telegram Objects."""
try:
import ujson as json
except ImportError:
import json
import json
from abc import ABCMeta
@@ -38,14 +34,13 @@ class TelegramObject(object):
return self.__dict__[item]
@staticmethod
def de_json(data, bot):
def de_json(data):
"""
Args:
data (dict):
bot (telegram.Bot):
data (str):
Returns:
dict:
telegram.TelegramObject:
"""
if not data:
return None
@@ -69,9 +64,6 @@ class TelegramObject(object):
data = dict()
for key in iter(self.__dict__):
if key == 'bot':
continue
value = self.__dict__[key]
if value is not None:
if hasattr(value, 'to_dict'):
+608 -677
View File
File diff suppressed because it is too large Load Diff
+10 -84
View File
@@ -16,7 +16,8 @@
#
# 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 CallbackQuery"""
"""This module contains a object that represents a Telegram
CallbackQuery"""
from telegram import TelegramObject, Message, User
@@ -24,46 +25,24 @@ from telegram import TelegramObject, Message, User
class CallbackQuery(TelegramObject):
"""This object represents a Telegram CallbackQuery."""
def __init__(self,
id,
from_user,
chat_instance,
message=None,
data=None,
inline_message_id=None,
game_short_name=None,
bot=None,
**kwargs):
def __init__(self, id, from_user, data, **kwargs):
# Required
self.id = id
self.from_user = from_user
self.chat_instance = chat_instance
# Optionals
self.message = message
self.data = data
self.inline_message_id = inline_message_id
self.game_short_name = game_short_name
self.bot = bot
# Optionals
self.message = kwargs.get('message')
self.inline_message_id = kwargs.get('inline_message_id', '')
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.CallbackQuery:
"""
def de_json(data):
if not data:
return None
data['from_user'] = User.de_json(data.get('from'), bot)
data['message'] = Message.de_json(data.get('message'), bot)
data['from_user'] = User.de_json(data.get('from'))
data['message'] = Message.de_json(data.get('message'))
return CallbackQuery(bot=bot, **data)
return CallbackQuery(**data)
def to_dict(self):
"""
@@ -75,56 +54,3 @@ class CallbackQuery(TelegramObject):
# Required
data['from'] = data.pop('from_user', None)
return data
def answer(self, *args, **kwargs):
"""Shortcut for ``bot.answerCallbackQuery(update.callback_query.id, *args, **kwargs)``"""
return self.bot.answerCallbackQuery(self.id, *args, **kwargs)
def edit_message_text(self, *args, **kwargs):
"""
Shortcut for either ``bot.editMessageText(chat_id=update.callback_query.message.chat_id, \
message_id=update.callback_query.message.message_id, \
*args, **kwargs)``
or ``bot.editMessageText(inline_message_id=update.callback_query.inline_message_id, \
*args, **kwargs)``
"""
if self.inline_message_id:
return self.bot.edit_message_text(
inline_message_id=self.inline_message_id, *args, **kwargs)
else:
return self.bot.edit_message_text(
chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs)
def edit_message_caption(self, *args, **kwargs):
"""
Shortcut for either
``bot.editMessageCaption(chat_id=update.callback_query.message.chat_id, \
message_id=update.callback_query.message.message_id, \
*args, **kwargs)``
or
``bot.editMessageCaption(inline_message_id=update.callback_query.inline_message_id, \
*args, **kwargs)``
"""
if self.inline_message_id:
return self.bot.edit_message_caption(
inline_message_id=self.inline_message_id, *args, **kwargs)
else:
return self.bot.edit_message_caption(
chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs)
def edit_message_reply_markup(self, *args, **kwargs):
"""
Shortcut for either
``bot.editMessageReplyMarkup(chat_id=update.callback_query.message.chat_id, \
message_id=update.callback_query.message.message_id, \
*args, **kwargs)``
or
``bot.editMessageReplyMarkup(inline_message_id=update.callback_query.inline_message_id, \
*args, **kwargs)``
"""
if self.inline_message_id:
return self.bot.edit_message_reply_markup(
inline_message_id=self.inline_message_id, *args, **kwargs)
else:
return self.bot.edit_message_reply_markup(
chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs)
+12 -56
View File
@@ -17,7 +17,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Chat."""
"""This module contains a object that represents a Telegram Chat."""
from telegram import TelegramObject
@@ -32,52 +32,36 @@ class Chat(TelegramObject):
username (str): Username, for private chats and channels if available
first_name (str): First name of the other party in a private chat
last_name (str): Last name of the other party in a private chat
all_members_are_admins (bool): True if a group has 'All Members Are Admins' enabled.
Args:
id (int):
type (str):
title (Optional[str]):
username(Optional[str]):
first_name(Optional[str]):
last_name(Optional[str]):
bot (Optional[Bot]): The Bot to use for instance methods
**kwargs (dict): Arbitrary keyword arguments.
**kwargs: Arbitrary keyword arguments.
Keyword Args:
type (Optional[str]):
"""
PRIVATE = 'private'
GROUP = 'group'
SUPERGROUP = 'supergroup'
CHANNEL = 'channel'
def __init__(self,
id,
type,
title='',
username='',
first_name='',
last_name='',
all_members_are_admins=False,
bot=None,
**kwargs):
def __init__(self, id, type, **kwargs):
# Required
self.id = int(id)
self.type = type
# Optionals
self.title = title
self.username = username
self.first_name = first_name
self.last_name = last_name
self.all_members_are_admins = all_members_are_admins
self.bot = bot
self.title = kwargs.get('title', '')
self.username = kwargs.get('username', '')
self.first_name = kwargs.get('first_name', '')
self.last_name = kwargs.get('last_name', '')
@staticmethod
def de_json(data, bot):
def de_json(data):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.Chat:
@@ -85,32 +69,4 @@ class Chat(TelegramObject):
if not data:
return None
return Chat(bot=bot, **data)
def send_action(self, *args, **kwargs):
"""Shortcut for ``bot.sendChatAction(update.message.chat.id, *args, **kwargs)``"""
return self.bot.sendChatAction(self.id, *args, **kwargs)
def leave(self, *args, **kwargs):
"""Shortcut for ``bot.leaveChat(update.message.chat.id, *args, **kwargs)``"""
return self.bot.leaveChat(self.id, *args, **kwargs)
def get_administrators(self, *args, **kwargs):
"""Shortcut for ``bot.getChatAdministrators(update.message.chat.id, *args, **kwargs)``"""
return self.bot.getChatAdministrators(self.id, *args, **kwargs)
def get_members_count(self, *args, **kwargs):
"""Shortcut for ``bot.getChatMembersCount(update.message.chat.id, *args, **kwargs)``"""
return self.bot.getChatMembersCount(self.id, *args, **kwargs)
def get_member(self, *args, **kwargs):
"""Shortcut for ``bot.getChatMember(update.message.chat.id, *args, **kwargs)``"""
return self.bot.getChatMember(self.id, *args, **kwargs)
def kick_member(self, *args, **kwargs):
"""Shortcut for ``bot.kickChatMember(update.message.chat.id, *args, **kwargs)``"""
return self.bot.kickChatMember(self.id, *args, **kwargs)
def unban_member(self, *args, **kwargs):
"""Shortcut for ``bot.unbanChatMember(update.message.chat.id, *args, **kwargs)``"""
return self.bot.unbanChatMember(self.id, *args, **kwargs)
return Chat(**data)
+1 -1
View File
@@ -17,7 +17,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ChatAction."""
"""This module contains a object that represents a Telegram ChatAction."""
class ChatAction(object):
+4 -6
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ChatMember."""
"""This module contains a object that represents a Telegram ChatMember."""
from telegram import User, TelegramObject
@@ -32,9 +32,8 @@ class ChatMember(TelegramObject):
Args:
user (:class:`telegram.User`):
status (str):
**kwargs (dict): Arbitrary keyword arguments.
"""
CREATOR = 'creator'
ADMINISTRATOR = 'administrator'
MEMBER = 'member'
@@ -47,11 +46,10 @@ class ChatMember(TelegramObject):
self.status = status
@staticmethod
def de_json(data, bot):
def de_json(data):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.ChatMember:
@@ -59,6 +57,6 @@ class ChatMember(TelegramObject):
if not data:
return None
data['user'] = User.de_json(data.get('user'), bot)
data['user'] = User.de_json(data.get('user'))
return ChatMember(**data)
+5 -11
View File
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""
This module contains an object that represents a Telegram ChosenInlineResult
This module contains a object that represents a Telegram ChosenInlineResult
"""
from telegram import TelegramObject, User, Location
@@ -33,16 +33,11 @@ class ChosenInlineResult(TelegramObject):
result_id (str):
from_user (:class:`telegram.User`):
query (str):
location (:class:`telegram.Location`):
inline_message_id (str):
Args:
result_id (str):
from_user (:class:`telegram.User`):
query (str):
location (Optional[:class:`telegram.Location`]):
inline_message_id (Optional[str]):
**kwargs (dict): Arbitrary keyword arguments.
"""
@@ -62,11 +57,10 @@ class ChosenInlineResult(TelegramObject):
self.inline_message_id = inline_message_id
@staticmethod
def de_json(data, bot):
def de_json(data):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.ChosenInlineResult:
@@ -74,10 +68,10 @@ class ChosenInlineResult(TelegramObject):
if not data:
return None
# Required
data['from_user'] = User.de_json(data.pop('from'), bot)
# Required
data['from_user'] = User.de_json(data.pop('from'))
# Optionals
data['location'] = Location.de_json(data.get('location'), bot)
data['location'] = Location.de_json(data.get('location'))
return ChosenInlineResult(**data)
-54
View File
@@ -1,54 +0,0 @@
# python-telegram-bot - a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# by the python-telegram-bot contributors <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/].
"""Constants in the Telegram network.
Attributes:
MAX_MESSAGE_LENGTH (int): from
https://core.telegram.org/method/messages.sendMessage#return-errors
MAX_CAPTION_LENGTH (int): from https://core.telegram.org/bots/api#sendphoto
The following constants were extracted from the
`Telegram Bots FAQ <https://core.telegram.org/bots/faq>`_.
Attributes:
SUPPORTED_WEBHOOK_PORTS (List[int])
MAX_FILESIZE_DOWNLOAD (int): In bytes.
MAX_FILESIZE_UPLOAD (int): Official limit, the actual limit can be a bit higher.
MAX_MESSAGES_PER_SECOND_PER_CHAT (int): Telegram may allow short bursts that go over this
limit, but eventually you'll begin receiving 429 errors.
MAX_MESSAGES_PER_SECOND (int)
MAX_MESSAGES_PER_MINUTE_PER_GROUP (int)
The following constant have been found by experimentation:
Attributes:
MAX_MESSAGE_ENTITIES (int): Max number of entities that can be in a message.
(Beyond this cap telegram will simply ignore further formatting styles)
"""
MAX_MESSAGE_LENGTH = 4096
MAX_CAPTION_LENGTH = 200
# constants above this line are tested
SUPPORTED_WEBHOOK_PORTS = [443, 80, 88, 8443]
MAX_FILESIZE_DOWNLOAD = int(20E6) # (20MB)
MAX_FILESIZE_UPLOAD = int(50E6) # (50MB)
MAX_MESSAGES_PER_SECOND_PER_CHAT = 1
MAX_MESSAGES_PER_SECOND = 30
MAX_MESSAGES_PER_MINUTE_PER_GROUP = 20
MAX_MESSAGE_ENTITIES = 100
+9 -9
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Contact."""
"""This module contains a object that represents a Telegram Contact."""
from telegram import TelegramObject
@@ -33,26 +33,26 @@ class Contact(TelegramObject):
Args:
phone_number (str):
first_name (str):
last_name (Optional[str]):
user_id (Optional[int]):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
last_name (Optional[str]):
user_id (Optional[int]):
"""
def __init__(self, phone_number, first_name, last_name='', user_id=0, **kwargs):
def __init__(self, phone_number, first_name, **kwargs):
# Required
self.phone_number = str(phone_number)
self.first_name = first_name
# Optionals
self.last_name = last_name
self.user_id = int(user_id)
self.last_name = kwargs.get('last_name', '')
self.user_id = int(kwargs.get('user_id', 0))
@staticmethod
def de_json(data, bot):
def de_json(data):
"""
Args:
data (dict):
bot (telegram.Bot):
data (str):
Returns:
telegram.Contact:
+8 -5
View File
@@ -1,10 +1,11 @@
import logging
from telegram import NullHandler
from future.moves.urllib.parse import quote
from future.moves.urllib.error import HTTPError, URLError
from future.moves.urllib.request import urlopen, Request
logging.getLogger(__name__).addHandler(logging.NullHandler())
logging.getLogger(__name__).addHandler(NullHandler())
class Botan(object):
@@ -28,10 +29,12 @@ class Botan(object):
return False
data = message.to_json()
try:
url = self.url_template.format(
token=str(self.token), uid=str(uid), name=quote(event_name))
request = Request(
url, data=data.encode(), headers={'Content-Type': 'application/json'})
url = self.url_template.format(token=str(self.token),
uid=str(uid),
name=quote(event_name))
request = Request(url,
data=data.encode(),
headers={'Content-Type': 'application/json'})
urlopen(request)
return True
except HTTPError as error:
+12 -12
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Document."""
"""This module contains a object that represents a Telegram Document."""
from telegram import PhotoSize, TelegramObject
@@ -33,29 +33,29 @@ class Document(TelegramObject):
Args:
file_id (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
thumb (Optional[:class:`telegram.PhotoSize`]):
file_name (Optional[str]):
mime_type (Optional[str]):
file_size (Optional[int]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, file_id, thumb=None, file_name='', mime_type='', file_size=0, **kwargs):
def __init__(self, file_id, **kwargs):
# Required
self.file_id = str(file_id)
# Optionals
self.thumb = thumb
self.file_name = file_name
self.mime_type = str(mime_type)
self.file_size = int(file_size)
self.thumb = kwargs.get('thumb')
self.file_name = kwargs.get('file_name', '')
self.mime_type = str(kwargs.get('mime_type', ''))
self.file_size = int(kwargs.get('file_size', 0))
@staticmethod
def de_json(data, bot):
def de_json(data):
"""
Args:
data (dict):
bot (telegram.Bot):
data (str):
Returns:
telegram.Document:
@@ -63,6 +63,6 @@ class Document(TelegramObject):
if not data:
return None
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
data['thumb'] = PhotoSize.de_json(data.get('thumb'))
return Document(**data)
+4 -20
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python
# flake8: noqa
# pylint: disable=C0103,R0903,E0213
# pylint: disable=C0103,R0903
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
@@ -18,27 +18,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents an Emoji.
"""This module contains a object that represents an Emoji."""
This module will be removed in the future.
"""
import warnings
from future.utils import bytes_to_native_str
from future.utils import bytes_to_native_str as n
class Emoji2(object):
class Emoji(object):
"""This object represents an Emoji."""
def n(b):
def e(cls):
warnings.warn("telegram.Emoji is being deprecated, please see https://git.io/v6DeB")
return bytes_to_native_str(b)
return property(e)
GRINNING_FACE_WITH_SMILING_EYES = n(b'\xF0\x9F\x98\x81')
FACE_WITH_TEARS_OF_JOY = n(b'\xF0\x9F\x98\x82')
SMILING_FACE_WITH_OPEN_MOUTH = n(b'\xF0\x9F\x98\x83')
@@ -892,6 +879,3 @@ class Emoji2(object):
CLOCK_FACE_TEN_THIRTY = n(b'\xF0\x9F\x95\xA5')
CLOCK_FACE_ELEVEN_THIRTY = n(b'\xF0\x9F\x95\xA6')
CLOCK_FACE_TWELVE_THIRTY = n(b'\xF0\x9F\x95\xA7')
Emoji = Emoji2()
+1 -27
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Error."""
"""This module contains a object that represents a Telegram Error."""
def _lstrip_str(in_s, lstr):
@@ -85,29 +85,3 @@ class TimedOut(NetworkError):
def __init__(self):
super(TimedOut, self).__init__('Timed out')
class ChatMigrated(TelegramError):
def __init__(self, new_chat_id):
"""
Args:
new_chat_id (int):
"""
super(ChatMigrated,
self).__init__('Group migrated to supergroup. New chat id: {}'.format(new_chat_id))
self.new_chat_id = new_chat_id
class RetryAfter(TelegramError):
def __init__(self, retry_after):
"""
Args:
retry_after (int):
"""
super(RetryAfter,
self).__init__('Flood control exceeded. Retry in {} seconds'.format(retry_after))
self.retry_after = float(retry_after)
+5 -7
View File
@@ -19,22 +19,20 @@
"""Extensions over the Telegram Bot API to facilitate bot making"""
from .dispatcher import Dispatcher
from .jobqueue import JobQueue, Job
from .jobqueue import JobQueue
from .updater import Updater
from .callbackqueryhandler import CallbackQueryHandler
from .choseninlineresulthandler import ChosenInlineResultHandler
from .commandhandler import CommandHandler
from .handler import Handler
from .inlinequeryhandler import InlineQueryHandler
from .messagehandler import MessageHandler
from .filters import BaseFilter, Filters
from .messagehandler import MessageHandler, Filters
from .regexhandler import RegexHandler
from .stringcommandhandler import StringCommandHandler
from .stringregexhandler import StringRegexHandler
from .typehandler import TypeHandler
from .conversationhandler import ConversationHandler
__all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
__all__ = ('Dispatcher', 'JobQueue', 'Updater', 'CallbackQueryHandler',
'ChosenInlineResultHandler', 'CommandHandler', 'Handler', 'InlineQueryHandler',
'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler',
'StringRegexHandler', 'TypeHandler', 'ConversationHandler')
'MessageHandler', 'Filters', 'RegexHandler', 'StringCommandHandler',
'StringRegexHandler', 'TypeHandler')
+10 -69
View File
@@ -18,93 +18,34 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the CallbackQueryHandler class """
import re
from future.utils import string_types
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
from .handler import Handler
class CallbackQueryHandler(Handler):
"""
Handler class to handle Telegram callback queries. Optionally based on a regex.
Read the documentation of the ``re`` module for more information.
Handler class to handle Telegram callback queries.
Args:
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
pattern (optional[str or Pattern]): Optional regex pattern. If not ``None`` ``re.match``
is used to determine if an update should be handled by this handler.
pass_groups (optional[bool]): If the callback should be passed the
result of ``re.match(pattern, data).groups()`` as a keyword
argument called ``groups``. Default is ``False``
pass_groupdict (optional[bool]): If the callback should be passed the
result of ``re.match(pattern, data).groupdict()`` as a keyword
argument called ``groupdict``. Default is ``False``
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
"""
def __init__(self,
callback,
pass_update_queue=False,
pass_job_queue=False,
pattern=None,
pass_groups=False,
pass_groupdict=False,
pass_user_data=False,
pass_chat_data=False):
super(CallbackQueryHandler, self).__init__(
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
if isinstance(pattern, string_types):
pattern = re.compile(pattern)
self.pattern = pattern
self.pass_groups = pass_groups
self.pass_groupdict = pass_groupdict
def __init__(self, callback, pass_update_queue=False):
super(CallbackQueryHandler, self).__init__(callback, pass_update_queue)
def check_update(self, update):
if isinstance(update, Update) and update.callback_query:
if self.pattern:
if update.callback_query.data:
match = re.match(self.pattern, update.callback_query.data)
return bool(match)
else:
return True
return isinstance(update, Update) and update.callback_query
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher, update)
if self.pattern:
match = re.match(self.pattern, update.callback_query.data)
optional_args = self.collect_optional_args(dispatcher)
if self.pass_groups:
optional_args['groups'] = match.groups()
if self.pass_groupdict:
optional_args['groupdict'] = match.groupdict()
return self.callback(dispatcher.bot, update, **optional_args)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.CallbackQueryHandler."
+7 -30
View File
@@ -32,44 +32,21 @@ class ChosenInlineResultHandler(Handler):
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
"""
def __init__(self,
callback,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(ChosenInlineResultHandler, self).__init__(
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
def __init__(self, callback, pass_update_queue=False):
super(ChosenInlineResultHandler, self).__init__(callback, pass_update_queue)
def check_update(self, update):
return isinstance(update, Update) and update.chosen_inline_result
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher, update)
optional_args = self.collect_optional_args(dispatcher)
return self.callback(dispatcher.bot, update, **optional_args)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.ChosenInlineResultHandler."
+9 -31
View File
@@ -39,24 +39,10 @@ class CommandHandler(Handler):
pass_args (optional[bool]): If the handler should be passed the
arguments passed to the command as a keyword argument called `
``args``. It will contain a list of strings, which is the text
following the command split on single or consecutive whitespace characters.
Default is ``False``
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
following the command split on spaces. Default is ``False``
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
"""
def __init__(self,
@@ -64,16 +50,8 @@ class CommandHandler(Handler):
callback,
allow_edited=False,
pass_args=False,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(CommandHandler, self).__init__(
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
pass_update_queue=False):
super(CommandHandler, self).__init__(callback, pass_update_queue)
self.command = command
self.allow_edited = allow_edited
self.pass_args = pass_args
@@ -90,14 +68,14 @@ class CommandHandler(Handler):
return False
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher, update)
optional_args = self.collect_optional_args(dispatcher)
message = update.message or update.edited_message
if self.pass_args:
optional_args['args'] = message.text.split()[1:]
optional_args['args'] = message.text.split(' ')[1:]
return self.callback(dispatcher.bot, update, **optional_args)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.CommandHandler."
-204
View File
@@ -1,204 +0,0 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the ConversationHandler """
import logging
from telegram import Update
from telegram.ext import Handler
from telegram.utils.helpers import extract_chat_and_user
from telegram.utils.promise import Promise
class ConversationHandler(Handler):
"""
A handler to hold a conversation with a user by managing four collections of other handlers.
The first collection, a ``list`` named ``entry_points``, is used to initiate the conversation,
for example with a ``CommandHandler`` or ``RegexHandler``.
The second collection, a ``dict`` named ``states``, contains the different conversation steps
and one or more associated handlers that should be used if the user sends a message when the
conversation with them is currently in that state. You will probably use mostly
``MessageHandler`` and ``RegexHandler`` here.
The third collection, a ``list`` named ``fallbacks``, is used if the user is currently in a
conversation but the state has either no associated handler or the handler that is associated
to the state is inappropriate for the update, for example if the update contains a command, but
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 ``timed_out_behavior`` is used if
the wait for ``run_async`` takes longer than defined in ``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. To end the conversation, the callback function must
return ``CallbackHandler.END`` or ``-1``.
Args:
entry_points (list): A list of ``Handler`` objects that can trigger the start of the
conversation. The first handler which ``check_update`` method returns ``True`` will be
used. If all return ``False``, the update is not handled.
states (dict): A ``dict[object: list[Handler]]`` that defines the different states of
conversation a user can be in and one or more associated ``Handler`` objects that
should be used in that state. The first handler which ``check_update`` method returns
``True`` will be used.
fallbacks (list): A list of handlers that might be used if the user is in a conversation,
but every handler for their current state returned ``False`` on ``check_update``.
The first handler which ``check_update`` method returns ``True`` will be used. If all
return ``False``, the update is not handled.
allow_reentry (Optional[bool]): If set to ``True``, a user that is currently in a
conversation can restart the conversation by triggering one of the entry points.
run_async_timeout (Optional[float]): If the previous handler for this user was running
asynchronously using the ``run_async`` decorator, it might not be finished when the
next message arrives. This timeout defines how long the conversation handler should
wait for the next state to be computed. The default is ``None`` which means it will
wait indefinitely.
timed_out_behavior (Optional[list]): A list of handlers that might be used if
the wait for ``run_async`` timed out. The first handler which ``check_update`` method
returns ``True`` will be used. If all return ``False``, the update is not handled.
"""
END = -1
def __init__(self,
entry_points,
states,
fallbacks,
allow_reentry=False,
run_async_timeout=None,
timed_out_behavior=None):
self.entry_points = entry_points
""":type: list[telegram.ext.Handler]"""
self.states = states
""":type: dict[str: telegram.ext.Handler]"""
self.fallbacks = fallbacks
""":type: list[telegram.ext.Handler]"""
self.allow_reentry = allow_reentry
self.run_async_timeout = run_async_timeout
self.timed_out_behavior = timed_out_behavior
""":type: list[telegram.ext.Handler]"""
self.conversations = dict()
""":type: dict[(int, int): str]"""
self.current_conversation = None
self.current_handler = None
self.logger = logging.getLogger(__name__)
def check_update(self, update):
if not isinstance(update, Update):
return False
chat, user = extract_chat_and_user(update)
key = (chat.id, user.id) if chat else (None, user.id)
state = self.conversations.get(key)
# Resolve promises
if isinstance(state, tuple) and len(state) is 2 and isinstance(state[1], Promise):
self.logger.debug('waiting for promise...')
old_state, new_state = state
new_state.result(timeout=self.run_async_timeout)
if new_state.done.is_set():
self.update_state(new_state.result(), key)
state = self.conversations.get(key)
else:
for candidate in (self.timed_out_behavior or []):
if candidate.check_update(update):
# Save the current user and the selected handler for handle_update
self.current_conversation = key
self.current_handler = candidate
return True
else:
return False
self.logger.debug('selecting conversation %s with state %s' % (str(key), str(state)))
handler = None
# Search entry points for a match
if state is None or self.allow_reentry:
for entry_point in self.entry_points:
if entry_point.check_update(update):
handler = entry_point
break
else:
if state is None:
return False
# Get the handler list for current state, if we didn't find one yet and we're still here
if state is not None and not handler:
handlers = self.states.get(state)
for candidate in (handlers or []):
if candidate.check_update(update):
handler = candidate
break
# Find a fallback handler if all other handlers fail
else:
for fallback in self.fallbacks:
if fallback.check_update(update):
handler = fallback
break
else:
return False
# Save the current user and the selected handler for handle_update
self.current_conversation = key
self.current_handler = handler
return True
def handle_update(self, update, dispatcher):
new_state = self.current_handler.handle_update(update, dispatcher)
self.update_state(new_state, self.current_conversation)
def update_state(self, new_state, key):
if new_state == self.END:
if key in self.conversations:
del self.conversations[key]
else:
pass
elif isinstance(new_state, Promise):
self.conversations[key] = (self.conversations.get(key), new_state)
elif new_state is not None:
self.conversations[key] = new_state
+51 -130
View File
@@ -19,44 +19,68 @@
"""This module contains the Dispatcher class."""
import logging
import weakref
from functools import wraps
from threading import Thread, Lock, Event, current_thread, BoundedSemaphore
from threading import Thread, Lock, Event, current_thread
from time import sleep
from uuid import uuid4
from collections import defaultdict
from queue import Queue, Empty
from future.builtins import range
from telegram import TelegramError
from telegram import (TelegramError, NullHandler)
from telegram.utils import request
from telegram.ext.handler import Handler
from telegram.utils.deprecate import deprecate
from telegram.utils.promise import Promise
logging.getLogger(__name__).addHandler(logging.NullHandler())
logging.getLogger(__name__).addHandler(NullHandler())
ASYNC_QUEUE = Queue()
ASYNC_THREADS = set()
""":type: set[Thread]"""
ASYNC_LOCK = Lock() # guards ASYNC_THREADS
DEFAULT_GROUP = 0
def run_async(func):
"""Function decorator that will run the function in a new thread.
def _pooled():
"""
A wrapper to run a thread in a thread pool
"""
while 1:
try:
func, args, kwargs = ASYNC_QUEUE.get()
Using this decorator is only possible when only a single Dispatcher exist in the system.
# If unpacking fails, the thread pool is being closed from Updater._join_async_threads
except TypeError:
logging.getLogger(__name__).debug("Closing run_async thread %s/%d" %
(current_thread().getName(), len(ASYNC_THREADS)))
break
try:
func(*args, **kwargs)
except:
logging.getLogger(__name__).exception("run_async function raised exception")
def run_async(func):
"""
Function decorator that will run the function in a new thread.
Args:
func (function): The function to run in the thread.
async_queue (Queue): The queue of the functions to be executed asynchronously.
Returns:
function:
"""
# TODO: handle exception in async threads
# set a threading.Event to notify caller thread
@wraps(func)
def async_func(*args, **kwargs):
return Dispatcher.get_instance().run_async(func, *args, **kwargs)
"""
A wrapper to run a function in a thread
"""
ASYNC_QUEUE.put((func, args, kwargs))
return async_func
@@ -70,27 +94,11 @@ class Dispatcher(object):
handlers
update_queue (Queue): The synchronized queue that will contain the
updates.
job_queue (Optional[telegram.ext.JobQueue]): The ``JobQueue`` instance to pass onto handler
callbacks
workers (Optional[int]): Number of maximum concurrent worker threads for the ``@run_async``
decorator
"""
__singleton_lock = Lock()
__singleton_semaphore = BoundedSemaphore()
__singleton = None
logger = logging.getLogger(__name__)
def __init__(self, bot, update_queue, workers=4, exception_event=None, job_queue=None):
def __init__(self, bot, update_queue, workers=4, exception_event=None):
self.bot = bot
self.update_queue = update_queue
self.job_queue = job_queue
self.workers = workers
self.user_data = defaultdict(dict)
""":type: dict[int, dict]"""
self.chat_data = defaultdict(dict)
""":type: dict[int, dict]"""
self.handlers = {}
""":type: dict[int, list[Handler]"""
@@ -98,90 +106,23 @@ class Dispatcher(object):
""":type: list[int]"""
self.error_handlers = []
self.logger = logging.getLogger(__name__)
self.running = False
self.__stop_event = Event()
self.__exception_event = exception_event or Event()
self.__async_queue = Queue()
self.__async_threads = set()
# For backward compatibility, we allow a "singleton" mode for the dispatcher. When there's
# only one instance of Dispatcher, it will be possible to use the `run_async` decorator.
with self.__singleton_lock:
if self.__singleton_semaphore.acquire(blocking=0):
self._set_singleton(self)
with ASYNC_LOCK:
if not ASYNC_THREADS:
if request.is_con_pool_initialized():
raise RuntimeError('Connection Pool already initialized')
request.CON_POOL_SIZE = workers + 3
for i in range(workers):
thread = Thread(target=_pooled, name=str(i))
ASYNC_THREADS.add(thread)
thread.start()
else:
self._set_singleton(None)
@classmethod
def _reset_singleton(cls):
# NOTE: This method was added mainly for test_updater benefit and specifically pypy. Never
# call it in production code.
cls.__singleton_semaphore.release()
def _init_async_threads(self, base_name, workers):
base_name = '{}_'.format(base_name) if base_name else ''
for i in range(workers):
thread = Thread(target=self._pooled, name='{}{}'.format(base_name, i))
self.__async_threads.add(thread)
thread.start()
@classmethod
def _set_singleton(cls, val):
cls.logger.debug('Setting singleton dispatcher as %s', val)
cls.__singleton = weakref.ref(val) if val else None
@classmethod
def get_instance(cls):
"""Get the singleton instance of this class.
Returns:
Dispatcher
"""
if cls.__singleton is not None:
return cls.__singleton()
else:
raise RuntimeError('{} not initialized or multiple instances exist'.format(
cls.__name__))
def _pooled(self):
"""
A wrapper to run a thread in a thread pool
"""
thr_name = current_thread().getName()
while 1:
promise = self.__async_queue.get()
# If unpacking fails, the thread pool is being closed from Updater._join_async_threads
if not isinstance(promise, Promise):
self.logger.debug("Closing run_async thread %s/%d", thr_name,
len(self.__async_threads))
break
try:
promise.run()
except:
self.logger.exception("run_async function raised exception")
def run_async(self, func, *args, **kwargs):
"""Queue a function (with given args/kwargs) to be run asynchronously.
Args:
func (function): The function to run in the thread.
args (Optional[tuple]): Arguments to `func`.
kwargs (Optional[dict]): Keyword arguments to `func`.
Returns:
Promise
"""
# TODO: handle exception in async threads
# set a threading.Event to notify caller thread
promise = Promise(func, args, kwargs)
self.__async_queue.put(promise)
return promise
self.logger.debug('Thread pool already initialized, skipping.')
def start(self):
"""
@@ -198,7 +139,6 @@ class Dispatcher(object):
self.logger.error(msg)
raise TelegramError(msg)
self._init_async_threads(uuid4(), self.workers)
self.running = True
self.logger.debug('Dispatcher started')
@@ -231,25 +171,6 @@ class Dispatcher(object):
sleep(0.1)
self.__stop_event.clear()
# async threads must be join()ed only after the dispatcher thread was joined,
# otherwise we can still have new async threads dispatched
threads = list(self.__async_threads)
total = len(threads)
# Stop all threads in the thread pool by put()ting one non-tuple per thread
for i in range(total):
self.__async_queue.put(None)
for i, thr in enumerate(threads):
self.logger.debug('Waiting for async thread {0}/{1} to end'.format(i + 1, total))
thr.join()
self.__async_threads.remove(thr)
self.logger.debug('async thread {0}/{1} has ended'.format(i + 1, total))
@property
def has_running_threads(self):
return self.running or bool(self.__async_threads)
def process_update(self, update):
"""
Processes a single update.
-216
View File
@@ -1,216 +0,0 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the Filters for use with the MessageHandler class """
class BaseFilter(object):
"""Base class for all Message Filters
Subclassing from this class filters to be combined using bitwise operators:
And:
>>> (Filters.text & Filters.entity(MENTION))
Or:
>>> (Filters.audio | Filters.video)
Also works with more than two filters:
>>> (Filters.text & (Filters.entity(URL) | Filters.entity(TEXT_LINK)))
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
(so remember to initialize your filter classes).
"""
def __call__(self, message):
return self.filter(message)
def __and__(self, other):
return MergedFilter(self, and_filter=other)
def __or__(self, other):
return MergedFilter(self, or_filter=other)
def filter(self, message):
raise NotImplementedError
class MergedFilter(BaseFilter):
"""Represents a filter consisting of two other filters.
Args:
base_filter: Filter 1 of the merged filter
and_filter: Optional filter to "and" with base_filter. Mutually exclusive with or_filter.
or_filter: Optional filter to "or" with base_filter. Mutually exclusive with and_filter.
"""
def __init__(self, base_filter, and_filter=None, or_filter=None):
self.base_filter = base_filter
self.and_filter = and_filter
self.or_filter = or_filter
def filter(self, message):
if self.and_filter:
return self.base_filter(message) and self.and_filter(message)
elif self.or_filter:
return self.base_filter(message) or self.or_filter(message)
def __str__(self):
return ("<telegram.ext.filters.MergedFilter consisting of"
" {} {} {}>").format(self.base_filter, "and" if self.and_filter else "or",
self.and_filter or self.or_filter)
__repr__ = __str__
class Filters(object):
"""
Predefined filters for use with the `filter` argument of :class:`telegram.ext.MessageHandler`.
"""
class _All(BaseFilter):
def filter(self, message):
return True
all = _All()
class _Text(BaseFilter):
def filter(self, message):
return bool(message.text and not message.text.startswith('/'))
text = _Text()
class _Command(BaseFilter):
def filter(self, message):
return bool(message.text and message.text.startswith('/'))
command = _Command()
class _Audio(BaseFilter):
def filter(self, message):
return bool(message.audio)
audio = _Audio()
class _Document(BaseFilter):
def filter(self, message):
return bool(message.document)
document = _Document()
class _Photo(BaseFilter):
def filter(self, message):
return bool(message.photo)
photo = _Photo()
class _Sticker(BaseFilter):
def filter(self, message):
return bool(message.sticker)
sticker = _Sticker()
class _Video(BaseFilter):
def filter(self, message):
return bool(message.video)
video = _Video()
class _Voice(BaseFilter):
def filter(self, message):
return bool(message.voice)
voice = _Voice()
class _Contact(BaseFilter):
def filter(self, message):
return bool(message.contact)
contact = _Contact()
class _Location(BaseFilter):
def filter(self, message):
return bool(message.location)
location = _Location()
class _Venue(BaseFilter):
def filter(self, message):
return bool(message.venue)
venue = _Venue()
class _StatusUpdate(BaseFilter):
def filter(self, message):
return bool(message.new_chat_member or message.left_chat_member
or message.new_chat_title or message.new_chat_photo
or message.delete_chat_photo or message.group_chat_created
or message.supergroup_chat_created or message.channel_chat_created
or message.migrate_to_chat_id or message.migrate_from_chat_id
or message.pinned_message)
status_update = _StatusUpdate()
class _Forwarded(BaseFilter):
def filter(self, message):
return bool(message.forward_date)
forwarded = _Forwarded()
class _Game(BaseFilter):
def filter(self, message):
return bool(message.game)
game = _Game()
class entity(BaseFilter):
"""Filters messages to only allow those which have a :class:`telegram.MessageEntity`
where their `type` matches `entity_type`.
Args:
entity_type: Entity type to check for. All types can be found as constants
in :class:`telegram.MessageEntity`.
Returns: function to use as filter
"""
def __init__(self, entity_type):
self.entity_type = entity_type
def filter(self, message):
return any([entity.type == self.entity_type for entity in message.entities])
+7 -42
View File
@@ -20,7 +20,6 @@
Dispatcher """
from telegram.utils.deprecate import deprecate
from telegram.utils.helpers import extract_chat_and_user
class Handler(object):
@@ -32,35 +31,14 @@ class Handler(object):
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
pass_update_queue (optional[bool]): If the callback should be passed
the update queue as a keyword argument called ``update_queue``. It
can be used to insert updates. Default is ``False``
"""
def __init__(self,
callback,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
def __init__(self, callback, pass_update_queue=False):
self.callback = callback
self.pass_update_queue = pass_update_queue
self.pass_job_queue = pass_job_queue
self.pass_user_data = pass_user_data
self.pass_chat_data = pass_chat_data
def check_update(self, update):
"""
@@ -79,18 +57,16 @@ class Handler(object):
"""
This method is called if it was determined that an update should indeed
be handled by this instance. It should also be overridden, but in most
cases call ``self.callback(dispatcher.bot, update)``, possibly along with
optional arguments. To work with the ``ConversationHandler``, this method should return the
value returned from ``self.callback``
cases call self.callback(dispatcher.bot, update), possibly along with
optional arguments.
Args:
update (object): The update to be handled
dispatcher (Dispatcher): The dispatcher to collect optional args
"""
raise NotImplementedError
def collect_optional_args(self, dispatcher, update=None):
def collect_optional_args(self, dispatcher):
"""
Prepares the optional arguments that are the same for all types of
handlers
@@ -99,19 +75,8 @@ class Handler(object):
dispatcher (Dispatcher):
"""
optional_args = dict()
if self.pass_update_queue:
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, user = extract_chat_and_user(update)
if self.pass_user_data:
optional_args['user_data'] = dispatcher.user_data[user.id]
if self.pass_chat_data:
optional_args['chat_data'] = dispatcher.chat_data[chat.id if chat else None]
return optional_args
+10 -68
View File
@@ -17,93 +17,35 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the InlineQueryHandler class """
import re
from future.utils import string_types
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
from .handler import Handler
class InlineQueryHandler(Handler):
"""
Handler class to handle Telegram inline queries. Optionally based on a regex. Read the
documentation of the ``re`` module for more information.
Handler class to handle Telegram inline queries.
Args:
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
pattern (optional[str or Pattern]): Optional regex pattern. If not ``None`` ``re.match``
is used to determine if an update should be handled by this handler.
pass_groups (optional[bool]): If the callback should be passed the
result of ``re.match(pattern, query).groups()`` as a keyword
argument called ``groups``. Default is ``False``
pass_groupdict (optional[bool]): If the callback should be passed the
result of ``re.match(pattern, query).groupdict()`` as a keyword
argument called ``groupdict``. Default is ``False``
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
"""
def __init__(self,
callback,
pass_update_queue=False,
pass_job_queue=False,
pattern=None,
pass_groups=False,
pass_groupdict=False,
pass_user_data=False,
pass_chat_data=False):
super(InlineQueryHandler, self).__init__(
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
if isinstance(pattern, string_types):
pattern = re.compile(pattern)
self.pattern = pattern
self.pass_groups = pass_groups
self.pass_groupdict = pass_groupdict
def __init__(self, callback, pass_update_queue=False):
super(InlineQueryHandler, self).__init__(callback, pass_update_queue)
def check_update(self, update):
if isinstance(update, Update) and update.inline_query:
if self.pattern:
if update.inline_query.query:
match = re.match(self.pattern, update.inline_query.query)
return bool(match)
else:
return True
return isinstance(update, Update) and update.inline_query
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher, update)
if self.pattern:
match = re.match(self.pattern, update.inline_query.query)
optional_args = self.collect_optional_args(dispatcher)
if self.pass_groups:
optional_args['groups'] = match.groups()
if self.pass_groupdict:
optional_args['groupdict'] = match.groupdict()
return self.callback(dispatcher.bot, update, **optional_args)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.InlineQueryHandler."
+80 -182
View File
@@ -16,241 +16,139 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes JobQueue and Job."""
"""This module contains the class JobQueue."""
import logging
import time
import warnings
from threading import Thread, Lock, Event
from queue import PriorityQueue, Empty
from threading import Thread, Lock
from queue import PriorityQueue
class JobQueue(object):
"""This class allows you to periodically perform tasks with the bot.
"""
This class allows you to periodically perform tasks with the bot.
Attributes:
tick_interval (float):
queue (PriorityQueue):
bot (Bot):
running (bool):
Args:
bot (Bot): The bot instance that should be passed to the jobs
tick_interval (Optional[float]): The interval this queue should check
the newest task in seconds. Defaults to 1.0
Deprecated: 5.2
prevent_autostart (Optional[bool]): Thread does not start during initialisation.
Use `start` method instead.
"""
def __init__(self, bot, prevent_autostart=None):
if prevent_autostart is not None:
warnings.warn("prevent_autostart is being deprecated, use `start` method instead.")
def __init__(self, bot, tick_interval=1.0):
self.tick_interval = tick_interval
self.queue = PriorityQueue()
self.bot = bot
self.logger = logging.getLogger(self.__class__.__name__)
self.__start_lock = Lock()
self.__next_peek_lock = Lock() # to protect self._next_peek & self.__tick
self.__tick = Event()
self.__thread = None
""":type: Thread"""
self._next_peek = None
""":type: float"""
self._running = False
self.logger = logging.getLogger(__name__)
self.__lock = Lock()
self.running = False
def put(self, job, next_t=None):
"""Queue a new job.
def put(self, run, interval, repeat=True, next_t=None, prevent_autostart=False):
"""
Queue a new job. If the JobQueue is not running, it will be started.
Args:
job (Job): The ``Job`` instance representing the new job
next_t (Optional[float]): Time in seconds in which the job should be executed first.
Defaults to ``job.interval``
run (function): A function that takes the parameter `bot`
interval (float): The interval in seconds in which `run` should be
executed
repeat (Optional[bool]): If `False`, job will only be executed once
next_t (Optional[float]): Time in seconds in which run should be
executed first. Defaults to `interval`
prevent_autostart (Optional[bool]): If `True`, the job queue will
not be started automatically if it is not running.
"""
job.job_queue = self
name = run.__name__
job = JobQueue.Job()
job.run = run
job.interval = interval
job.name = name
job.repeat = repeat
if next_t is None:
next_t = job.interval
next_t = interval
now = time.time()
next_t += now
next_t += time.time()
self.logger.debug('Putting job %s with t=%f', job.name, next_t)
self.logger.debug('Putting a %s with t=%f' % (job.name, next_t))
self.queue.put((next_t, job))
# Wake up the loop if this job should be executed next
self._set_next_peek(next_t)
def _set_next_peek(self, t):
"""
Set next peek if not defined or `t` is before next peek.
In case the next peek was set, also trigger the `self.__tick` event.
"""
with self.__next_peek_lock:
if not self._next_peek or self._next_peek > t:
self._next_peek = t
self.__tick.set()
if not self.running and not prevent_autostart:
self.logger.debug('Auto-starting JobQueue')
self.start()
def tick(self):
"""
Run all jobs that are due and re-enqueue them with their interval.
Run all jobs that are due and re-enqueue them with their interval
"""
now = time.time()
self.logger.debug('Ticking jobs with t=%f', now)
self.logger.debug('Ticking jobs with t=%f' % now)
while not self.queue.empty():
t, j = self.queue.queue[0]
self.logger.debug('Peeked at %s with t=%f' % (j.name, t))
while True:
try:
t, job = self.queue.get(False)
except Empty:
break
self.logger.debug('Peeked at %s with t=%f', job.name, t)
if t > now:
# we can get here in two conditions:
# 1. At the second or later pass of the while loop, after we've already processed
# the job(s) we were supposed to at this time.
# 2. At the first iteration of the loop only if `self.put()` had triggered
# `self.__tick` because `self._next_peek` wasn't set
self.logger.debug("Next task isn't due yet. Finished!")
self.queue.put((t, job))
self._set_next_peek(t)
break
if job._remove.is_set():
self.logger.debug('Removing job %s', job.name)
if t < now:
self.queue.get()
self.logger.debug('Running job %s' % j.name)
try:
j.run(self.bot)
except:
self.logger.exception('An uncaught error was raised while '
'executing job %s' % j.name)
if j.repeat:
self.put(j.run, j.interval)
continue
if job.enabled:
self.logger.debug('Running job %s', job.name)
try:
job.run(self.bot)
except:
self.logger.exception('An uncaught error was raised while executing job %s',
job.name)
else:
self.logger.debug('Skipping disabled job %s', job.name)
if job.repeat:
self.put(job)
self.logger.debug('Next task isn\'t due yet. Finished!')
break
def start(self):
"""
Starts the job_queue thread.
"""
self.__start_lock.acquire()
if not self._running:
self._running = True
self.__start_lock.release()
self.__thread = Thread(target=self._main_loop, name="job_queue")
self.__thread.start()
self.logger.debug('%s thread started', self.__class__.__name__)
self.__lock.acquire()
if not self.running:
self.running = True
self.__lock.release()
job_queue_thread = Thread(target=self._start, name="job_queue")
job_queue_thread.start()
self.logger.debug('Job Queue thread started')
else:
self.__start_lock.release()
self.__lock.release()
def _main_loop(self):
def _start(self):
"""
Thread target of thread ``job_queue``. Runs in background and performs ticks on the job
queue.
Thread target of thread 'job_queue'. Runs in background and performs
ticks on the job queue.
"""
while self._running:
# self._next_peek may be (re)scheduled during self.tick() or self.put()
with self.__next_peek_lock:
tmout = self._next_peek and self._next_peek - time.time()
self._next_peek = None
self.__tick.clear()
self.__tick.wait(tmout)
# If we were woken up by self.stop(), just bail out
if not self._running:
break
while self.running:
self.tick()
time.sleep(self.tick_interval)
self.logger.debug('%s thread stopped', self.__class__.__name__)
self.logger.debug('Job Queue thread stopped')
def stop(self):
"""
Stops the thread
"""
with self.__start_lock:
self._running = False
with self.__lock:
self.running = False
self.__tick.set()
if self.__thread is not None:
self.__thread.join()
class Job(object):
""" Inner class that represents a job """
interval = None
name = None
repeat = None
def jobs(self):
"""Returns a tuple of all jobs that are currently in the ``JobQueue``"""
return tuple(job[1] for job in self.queue.queue if job)
def run(self):
pass
class Job(object):
"""This class encapsulates a Job
Attributes:
callback (function):
interval (float):
repeat (bool):
name (str):
enabled (bool): Boolean property that decides if this job is currently active
Args:
callback (function): The callback function that should be executed by the Job. It should
take two parameters ``bot`` and ``job``, where ``job`` is the ``Job`` instance. It
can be used to terminate the job or modify its interval.
interval (float): The interval in which this job should execute its callback function in
seconds.
repeat (Optional[bool]): If this job should be periodically execute its callback function
(``True``) or only once (``False``). Defaults to ``True``
context (Optional[object]): Additional data needed for the callback function. Can be
accessed through ``job.context`` in the callback. Defaults to ``None``
"""
job_queue = None
def __init__(self, callback, interval, repeat=True, context=None):
self.callback = callback
self.interval = interval
self.repeat = repeat
self.context = context
self.name = callback.__name__
self._remove = Event()
self._enabled = Event()
self._enabled.set()
def run(self, bot):
"""Executes the callback function"""
self.callback(bot, self)
def schedule_removal(self):
"""
Schedules this job for removal from the ``JobQueue``. It will be removed without executing
its callback function again.
"""
self._remove.set()
def is_enabled(self):
return self._enabled.is_set()
def set_enabled(self, status):
if status:
self._enabled.set()
else:
self._enabled.clear()
enabled = property(is_enabled, set_enabled)
def __lt__(self, other):
return False
def __lt__(self, other):
return False
+70 -40
View File
@@ -17,13 +17,71 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the MessageHandler class """
import warnings
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
class Filters(object):
"""
Convenient namespace (class) & methods for the filter funcs of the
MessageHandler class.
"""
@staticmethod
def text(message):
return message.text and not message.text.startswith('/')
@staticmethod
def command(message):
return message.text and message.text.startswith('/')
@staticmethod
def audio(message):
return bool(message.audio)
@staticmethod
def document(message):
return bool(message.document)
@staticmethod
def photo(message):
return bool(message.photo)
@staticmethod
def sticker(message):
return bool(message.sticker)
@staticmethod
def video(message):
return bool(message.video)
@staticmethod
def voice(message):
return bool(message.voice)
@staticmethod
def contact(message):
return bool(message.contact)
@staticmethod
def location(message):
return bool(message.location)
@staticmethod
def venue(message):
return bool(message.venue)
@staticmethod
def status_update(message):
return bool(message.new_chat_member or message.left_chat_member or message.new_chat_title
or message.new_chat_photo or message.delete_chat_photo
or message.group_chat_created or message.supergroup_chat_created
or message.channel_chat_created or message.migrate_to_chat_id
or message.migrate_from_chat_id or message.pinned_message)
class MessageHandler(Handler):
"""
Handler class to handle telegram messages. Messages are Telegram Updates
@@ -31,10 +89,12 @@ class MessageHandler(Handler):
updates.
Args:
filters (telegram.ext.BaseFilter): A filter inheriting from
:class:`telegram.filters.BaseFilter`. Standard filters can be found in
:class:`telegram.filters.Filters`. Filters can be combined using bitwise
operators (& for and, | for or).
filters (list[function]): A list of filter functions. Standard filters
can be found in the Filters class above.
| Each `function` takes ``Update`` as arg and returns ``bool``.
| All messages that match at least one of those filters will be
accepted. If ``bool(filters)`` evaluates to ``False``, messages are
not filtered.
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
@@ -43,40 +103,13 @@ class MessageHandler(Handler):
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
"""
def __init__(self,
filters,
callback,
allow_edited=False,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(MessageHandler, self).__init__(
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
def __init__(self, filters, callback, allow_edited=False, pass_update_queue=False):
super(MessageHandler, self).__init__(callback, pass_update_queue)
self.filters = filters
self.allow_edited = allow_edited
# We put this up here instead of with the rest of checking code
# in check_update since we don't wanna spam a ton
if isinstance(self.filters, list):
warnings.warn('Using a list of filters in MessageHandler is getting '
'deprecated, please use bitwise operators (& and |) '
'instead. More info: https://git.io/vPTbc.')
def check_update(self, update):
if (isinstance(update, Update)
and (update.message or update.edited_message and self.allow_edited)):
@@ -86,10 +119,7 @@ class MessageHandler(Handler):
else:
message = update.message or update.edited_message
if isinstance(self.filters, list):
res = any(func(message) for func in self.filters)
else:
res = self.filters(message)
res = any(func(message) for func in self.filters)
else:
res = False
@@ -97,9 +127,9 @@ class MessageHandler(Handler):
return res
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher, update)
optional_args = self.collect_optional_args(dispatcher)
return self.callback(dispatcher.bot, update, **optional_args)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.MessageHandler."
+8 -29
View File
@@ -45,22 +45,9 @@ class RegexHandler(Handler):
pass_groupdict (optional[bool]): If the callback should be passed the
result of ``re.match(pattern, text).groupdict()`` as a keyword
argument called ``groupdict``. Default is ``False``
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
"""
def __init__(self,
@@ -68,16 +55,8 @@ class RegexHandler(Handler):
callback,
pass_groups=False,
pass_groupdict=False,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(RegexHandler, self).__init__(
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
pass_update_queue=False):
super(RegexHandler, self).__init__(callback, pass_update_queue)
if isinstance(pattern, string_types):
pattern = re.compile(pattern)
@@ -87,14 +66,14 @@ class RegexHandler(Handler):
self.pass_groupdict = pass_groupdict
def check_update(self, update):
if isinstance(update, Update) and update.message and update.message.text:
if (isinstance(update, Update) and update.message and update.message.text):
match = re.match(self.pattern, update.message.text)
return bool(match)
else:
return False
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher, update)
optional_args = self.collect_optional_args(dispatcher)
match = re.match(self.pattern, update.message.text)
if self.pass_groups:
@@ -102,7 +81,7 @@ class RegexHandler(Handler):
if self.pass_groupdict:
optional_args['groupdict'] = match.groupdict()
return self.callback(dispatcher.bot, update, **optional_args)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.RegexHandler."
+8 -20
View File
@@ -35,26 +35,14 @@ class StringCommandHandler(Handler):
pass_args (optional[bool]): If the handler should be passed the
arguments passed to the command as a keyword argument called `
``args``. It will contain a list of strings, which is the text
following the command split on single or consecutive whitespace characters.
Default is ``False``
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
following the command split on spaces. Default is ``False``
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
"""
def __init__(self,
command,
callback,
pass_args=False,
pass_update_queue=False,
pass_job_queue=False):
super(StringCommandHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
def __init__(self, command, callback, pass_args=False, pass_update_queue=False):
super(StringCommandHandler, self).__init__(callback, pass_update_queue)
self.command = command
self.pass_args = pass_args
@@ -66,9 +54,9 @@ class StringCommandHandler(Handler):
optional_args = self.collect_optional_args(dispatcher)
if self.pass_args:
optional_args['args'] = update.split()[1:]
optional_args['args'] = update.split(' ')[1:]
return self.callback(dispatcher.bot, update, **optional_args)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.StringCommandHandler."
+6 -13
View File
@@ -44,14 +44,9 @@ class StringRegexHandler(Handler):
pass_groupdict (optional[bool]): If the callback should be passed the
result of ``re.match(pattern, update).groupdict()`` as a keyword
argument called ``groupdict``. Default is ``False``
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
"""
def __init__(self,
@@ -59,10 +54,8 @@ class StringRegexHandler(Handler):
callback,
pass_groups=False,
pass_groupdict=False,
pass_update_queue=False,
pass_job_queue=False):
super(StringRegexHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
pass_update_queue=False):
super(StringRegexHandler, self).__init__(callback, pass_update_queue)
if isinstance(pattern, string_types):
pattern = re.compile(pattern)
@@ -83,7 +76,7 @@ class StringRegexHandler(Handler):
if self.pass_groupdict:
optional_args['groupdict'] = match.groupdict()
return self.callback(dispatcher.bot, update, **optional_args)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.StringRegexHandler."
+6 -13
View File
@@ -34,20 +34,13 @@ class TypeHandler(Handler):
has determined that an update should be processed by this handler.
strict (optional[bool]): Use ``type`` instead of ``isinstance``.
Default is ``False``
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
"""
def __init__(self, type, callback, strict=False, pass_update_queue=False,
pass_job_queue=False):
super(TypeHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
def __init__(self, type, callback, strict=False, pass_update_queue=False):
super(TypeHandler, self).__init__(callback, pass_update_queue)
self.type = type
self.strict = strict
@@ -60,7 +53,7 @@ class TypeHandler(Handler):
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
return self.callback(dispatcher.bot, update, **optional_args)
self.callback(dispatcher.bot, update, **optional_args)
# old non-PEP8 Handler methods
m = "telegram.TypeHandler."
+47 -48
View File
@@ -28,13 +28,12 @@ import subprocess
from signal import signal, SIGINT, SIGTERM, SIGABRT
from queue import Queue
from telegram import Bot, TelegramError
from telegram.ext import Dispatcher, JobQueue
from telegram.error import Unauthorized, InvalidToken, RetryAfter
from telegram.utils.request import Request
from telegram import Bot, TelegramError, NullHandler
from telegram.ext import dispatcher, Dispatcher, JobQueue
from telegram.error import Unauthorized, InvalidToken
from telegram.utils.webhookhandler import (WebhookServer, WebhookHandler)
logging.getLogger(__name__).addHandler(logging.NullHandler())
logging.getLogger(__name__).addHandler(NullHandler())
class Updater(object):
@@ -58,17 +57,20 @@ class Updater(object):
base_url (Optional[str]):
workers (Optional[int]): Amount of threads in the thread pool for
functions decorated with @run_async
bot (Optional[Bot]): A pre-initialized bot instance. If a pre-initizlied bot is used, it is
the user's responsibility to create it using a `Request` instance with a large enough
connection pool.
bot (Optional[Bot]):
job_queue_tick_interval(Optional[float]): The interval the queue should
be checked for new tasks. Defaults to 1.0
Raises:
ValueError: If both `token` and `bot` are passed or none of them.
"""
_request = None
def __init__(self, token=None, base_url=None, workers=4, bot=None):
def __init__(self,
token=None,
base_url=None,
workers=4,
bot=None,
job_queue_tick_interval=1.0):
if (token is None) and (bot is None):
raise ValueError('`token` or `bot` must be passed')
if (token is not None) and (bot is not None):
@@ -77,23 +79,11 @@ class Updater(object):
if bot is not None:
self.bot = bot
else:
# we need a connection pool the size of:
# * for each of the workers
# * 1 for Dispatcher
# * 1 for polling Updater (even if webhook is used, we can spare a connection)
# * 1 for JobQueue
# * 1 for main thread
self._request = Request(con_pool_size=workers + 4)
self.bot = Bot(token, base_url, request=self._request)
self.bot = Bot(token, base_url)
self.update_queue = Queue()
self.job_queue = JobQueue(self.bot)
self.job_queue = JobQueue(self.bot, job_queue_tick_interval)
self.__exception_event = Event()
self.dispatcher = Dispatcher(
self.bot,
self.update_queue,
job_queue=self.job_queue,
workers=workers,
exception_event=self.__exception_event)
self.dispatcher = Dispatcher(self.bot, self.update_queue, workers, self.__exception_event)
self.last_update_id = 0
self.logger = logging.getLogger(__name__)
self.running = False
@@ -155,7 +145,6 @@ class Updater(object):
self.running = True
# Create & start threads
self.job_queue.start()
self._init_thread(self.dispatcher.start, "dispatcher")
self._init_thread(self._start_polling, "updater", poll_interval, timeout,
network_delay, bootstrap_retries, clean)
@@ -207,7 +196,6 @@ class Updater(object):
self.running = True
# Create & start threads
self.job_queue.start()
self._init_thread(self.dispatcher.start, "dispatcher"),
self._init_thread(self._start_webhook, "updater", listen, port, url_path, cert,
key, bootstrap_retries, clean, webhook_url)
@@ -229,11 +217,9 @@ class Updater(object):
while self.running:
try:
updates = self.bot.getUpdates(
self.last_update_id, timeout=timeout, network_delay=network_delay)
except RetryAfter as e:
self.logger.info(str(e))
cur_interval = 0.5 + e.retry_after
updates = self.bot.getUpdates(self.last_update_id,
timeout=timeout,
network_delay=network_delay)
except TelegramError as te:
self.logger.error("Error while getting Updates: {0}".format(te))
@@ -277,8 +263,7 @@ class Updater(object):
url_path = '/{0}'.format(url_path)
# Create and start server
self.httpd = WebhookServer((listen, port), WebhookHandler, self.update_queue, url_path,
self.bot)
self.httpd = WebhookServer((listen, port), WebhookHandler, self.update_queue, url_path)
if use_ssl:
self._check_ssl_cert(cert, key)
@@ -287,11 +272,10 @@ class Updater(object):
if not webhook_url:
webhook_url = self._gen_webhook_url(listen, port, url_path)
self._bootstrap(
max_retries=bootstrap_retries,
clean=clean,
webhook_url=webhook_url,
cert=open(cert, 'rb'))
self._bootstrap(max_retries=bootstrap_retries,
clean=clean,
webhook_url=webhook_url,
cert=open(cert, 'rb'))
elif clean:
self.logger.warning("cleaning updates is not supported if "
"SSL-termination happens elsewhere; skipping")
@@ -309,8 +293,10 @@ class Updater(object):
exit_code = 0
if exit_code is 0:
try:
self.httpd.socket = ssl.wrap_socket(
self.httpd.socket, certfile=cert, keyfile=key, server_side=True)
self.httpd.socket = ssl.wrap_socket(self.httpd.socket,
certfile=cert,
keyfile=key,
server_side=True)
except ssl.SSLError as error:
self.logger.exception('Failed to init SSL socket')
raise TelegramError(str(error))
@@ -330,7 +316,6 @@ class Updater(object):
# Disable webhook for cleaning
self.bot.setWebhook(webhook_url='')
self._clean_updates()
sleep(1)
self.bot.setWebhook(webhook_url=webhook_url, certificate=cert)
except (Unauthorized, InvalidToken):
@@ -361,7 +346,7 @@ class Updater(object):
self.job_queue.stop()
with self.__lock:
if self.running or self.dispatcher.has_running_threads:
if self.running or dispatcher.ASYNC_THREADS:
self.logger.debug('Stopping Updater and Dispatcher...')
self.running = False
@@ -369,10 +354,9 @@ class Updater(object):
self._stop_httpd()
self._stop_dispatcher()
self._join_threads()
# Stop the Request instance only if it was created by the Updater
if self._request:
self._request.stop()
# async threads must be join()ed only after the dispatcher thread was joined,
# otherwise we can still have new async threads dispatched
self._join_async_threads()
def _stop_httpd(self):
if self.httpd:
@@ -386,6 +370,21 @@ class Updater(object):
self.logger.debug('Requesting Dispatcher to stop...')
self.dispatcher.stop()
def _join_async_threads(self):
with dispatcher.ASYNC_LOCK:
threads = list(dispatcher.ASYNC_THREADS)
total = len(threads)
# Stop all threads in the thread pool by put()ting one non-tuple per thread
for i in range(total):
dispatcher.ASYNC_QUEUE.put(None)
for i, thr in enumerate(threads):
self.logger.debug('Waiting for async thread {0}/{1} to end'.format(i + 1, total))
thr.join()
dispatcher.ASYNC_THREADS.remove(thr)
self.logger.debug('async thread {0}/{1} has ended'.format(i + 1, total))
def _join_threads(self):
for thr in self.__threads:
self.logger.debug('Waiting for {0} thread to end'.format(thr.name))
+12 -16
View File
@@ -16,11 +16,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 an object that represents a Telegram File."""
"""This module contains a object that represents a Telegram File."""
from os.path import basename
from telegram import TelegramObject
from telegram.utils.request import download as _download
class File(TelegramObject):
@@ -33,29 +34,25 @@ class File(TelegramObject):
Args:
file_id (str):
bot (telegram.Bot):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
file_size (Optional[int]):
file_path (Optional[str]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, file_id, bot, file_size=0, file_path='', **kwargs):
def __init__(self, file_id, **kwargs):
# Required
self.file_id = str(file_id)
# Optionals
self.file_size = int(file_size)
self.file_path = str(file_path)
self.bot = bot
self.file_size = int(kwargs.get('file_size', 0))
self.file_path = str(kwargs.get('file_path', ''))
@staticmethod
def de_json(data, bot):
def de_json(data):
"""
Args:
data (dict):
bot (telegram.Bot):
data (str):
Returns:
telegram.File:
@@ -63,13 +60,12 @@ class File(TelegramObject):
if not data:
return None
return File(bot=bot, **data)
return File(**data)
def download(self, custom_path=None):
"""
Args:
custom_path (str):
"""
url = self.file_path
@@ -78,4 +74,4 @@ class File(TelegramObject):
else:
filename = basename(url)
self.bot.request.download(url, filename)
_download(url, filename)
+8 -8
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ForceReply."""
"""This module contains a object that represents a Telegram ForceReply."""
from telegram import ReplyMarkup
@@ -30,23 +30,23 @@ class ForceReply(ReplyMarkup):
Args:
force_reply (bool):
selective (Optional[bool]):
**kwargs (dict): Arbitrary keyword arguments.
**kwargs: Arbitrary keyword arguments.
Keyword Args:
selective (Optional[bool]):
"""
def __init__(self, force_reply=True, selective=False, **kwargs):
def __init__(self, force_reply=True, **kwargs):
# Required
self.force_reply = bool(force_reply)
# Optionals
self.selective = bool(selective)
self.selective = bool(kwargs.get('selective', False))
@staticmethod
def de_json(data, bot):
def de_json(data):
"""
Args:
data (dict):
bot (telegram.Bot):
data (str):
Returns:
telegram.ForceReply:
-146
View File
@@ -1,146 +0,0 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# 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 Game."""
import sys
from telegram import MessageEntity, TelegramObject, Animation, PhotoSize
class Game(TelegramObject):
"""This object represents a Telegram Game.
Attributes:
title (str): Title of the game.
description (str): Description of the game.
photo (list[:class:`telegram.PhotoSize`]): List of photos that will be displayed in the
game message in chats.
Keyword Args:
text (Optional[str]): Brief description of the game or high scores included in the game
message. Can be automatically edited to include current high scores for the game when
the bot calls setGameScore, or manually edited using editMessageText.
0-4096 characters.
text_entities (Optional[list[:class:`telegram.MessageEntity`]]): Special entities that
appear in text, such as usernames, URLs, bot commands, etc.
animation (Optional[:class:`telegram.Animation`]): Animation that will be displayed in the
game message in chats. Upload via BotFather.
"""
def __init__(self,
title,
description,
photo,
text='',
text_entities=None,
animation=None,
**kwargs):
self.title = title
self.description = description
self.photo = photo
self.text = text
self.text_entities = text_entities
self.animation = animation
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.Game:
"""
if not data:
return None
data['photo'] = PhotoSize.de_list(data.get('photo'), bot)
data['text_entities'] = MessageEntity.de_list(data.get('text_entities'), bot)
data['animation'] = Animation.de_json(data.get('animation'), bot)
return Game(**data)
def to_dict(self):
"""
Returns:
dict:
"""
data = super(Game, self).to_dict()
data['photo'] = [p.to_dict() for p in self.photo]
data['text_entities'] = [x.to_dict() for x in self.text_entities]
return data
def parse_text_entity(self, entity):
"""
Returns the text from a given :class:`telegram.MessageEntity`.
Note:
This method is present because Telegram calculates the offset and length in
UTF-16 codepoint pairs, which some versions of Python don't handle automatically.
(That is, you can't just slice ``Message.text`` with the offset and length.)
Args:
entity (MessageEntity): The entity to extract the text from. It must be an entity that
belongs to this message.
Returns:
str: The text of the given entity
"""
# Is it a narrow build, if so we don't need to convert
if sys.maxunicode == 0xffff:
return self.text[entity.offset:entity.offset + entity.length]
else:
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')
def parse_text_entities(self, types=None):
"""
Returns a ``dict`` that maps :class:`telegram.MessageEntity` to ``str``.
It contains entities from this message filtered by their ``type`` attribute as the key, and
the text that each entity belongs to as the value of the ``dict``.
Note:
This method should always be used instead of the ``entities`` attribute, since it
calculates the correct substring from the message text based on UTF-16 codepoints.
See ``get_entity_text`` for more info.
Args:
types (Optional[list]): List of ``MessageEntity`` types as strings. If the ``type``
attribute of an entity is contained in this list, it will be returned.
Defaults to a list of all types. All types can be found as constants in
:class:`telegram.MessageEntity`.
Returns:
dict[:class:`telegram.MessageEntity`, ``str``]: A dictionary of entities mapped to the
text that belongs to them, calculated based on UTF-16 codepoints.
"""
if types is None:
types = MessageEntity.ALL_TYPES
return {
entity: self.parse_text_entity(entity)
for entity in self.text_entities if entity.type in types
}
-54
View File
@@ -1,54 +0,0 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# 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 GameHighScore."""
from telegram import TelegramObject, User
class GameHighScore(TelegramObject):
"""This object represents a Telegram GameHighScore.
Attributes:
position (int): Position in high score table for the game.
user (:class:`telegram.User`): User object.
score (int): Score.
"""
def __init__(self, position, user, score):
self.position = position
self.user = user
self.score = score
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.Game:
"""
if not data:
return None
data['user'] = User.de_json(data.get('user'), bot)
return GameHighScore(**data)
+16 -35
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram
"""This module contains a object that represents a Telegram
InlineKeyboardButton"""
from telegram import TelegramObject
@@ -30,49 +30,30 @@ class InlineKeyboardButton(TelegramObject):
url (str):
callback_data (str):
switch_inline_query (str):
switch_inline_query_current_chat (str):
callback_game (:class:`telegram.CallbackGame`):
Args:
text (str): Label text on the button.
url (Optional[str]): HTTP url to be opened when button is pressed.
callback_data (Optional[str]): Data to be sent in a callback query to the bot when button
is pressed, 1-64 bytes.
switch_inline_query (Optional[str]): 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
username will be inserted.
switch_inline_query_current_chat (Optional[str]): If set, pressing the button will insert
the bot's username and the specified inline query in the current chat's input field.
Can be empty, in which case only the bot's username will be inserted.
callback_game (Optional[:class:`telegram.CallbackGame`]): Description of the game that will
be launched when the user presses the button.
**kwargs (dict): Arbitrary keyword arguments.
text (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
url (Optional[str]):
callback_data (Optional[str]):
switch_inline_query (Optional[str]):
"""
def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, **kwargs):
def __init__(self, text, **kwargs):
# Required
self.text = text
# Optionals
self.url = url
self.callback_data = callback_data
self.switch_inline_query = switch_inline_query
self.switch_inline_query_current_chat = kwargs.get('switch_inline_query_current_chat')
self.callback_game = kwargs.get('callback_game')
self.url = kwargs.get('url')
self.callback_data = kwargs.get('callback_data')
self.switch_inline_query = kwargs.get('switch_inline_query')
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.InlineKeyboardButton:
"""
data = super(InlineKeyboardButton, InlineKeyboardButton).de_json(data, bot)
def de_json(data):
data = super(InlineKeyboardButton, InlineKeyboardButton).de_json(data)
if not data:
return None
@@ -80,12 +61,12 @@ class InlineKeyboardButton(TelegramObject):
return InlineKeyboardButton(**data)
@staticmethod
def de_list(data, bot):
def de_list(data):
if not data:
return []
inline_keyboards = list()
for inline_keyboard in data:
inline_keyboards.append(InlineKeyboardButton.de_json(inline_keyboard, bot))
inline_keyboards.append(InlineKeyboardButton.de_json(inline_keyboard))
return inline_keyboards
+5 -17
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram
"""This module contains a object that represents a Telegram
InlineKeyboardMarkup"""
from telegram import ReplyMarkup, InlineKeyboardButton
@@ -30,7 +30,6 @@ class InlineKeyboardMarkup(ReplyMarkup):
Args:
inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]):
**kwargs (dict): Arbitrary keyword arguments.
"""
@@ -39,25 +38,14 @@ class InlineKeyboardMarkup(ReplyMarkup):
self.inline_keyboard = inline_keyboard
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.InlineKeyboardMarkup:
"""
data = super(InlineKeyboardMarkup, InlineKeyboardMarkup).de_json(data, bot)
def de_json(data):
data = super(InlineKeyboardMarkup, InlineKeyboardMarkup).de_json(data)
if not data:
return None
data['inline_keyboard'] = [
InlineKeyboardButton.de_list(inline_keyboard, bot)
for inline_keyboard in data['inline_keyboard']
]
data['inline_keyboard'] = [InlineKeyboardButton.de_list(inline_keyboard)
for inline_keyboard in data['inline_keyboard']]
return InlineKeyboardMarkup(**data)
+11 -18
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram InlineQuery"""
"""This module contains a object that represents a Telegram InlineQuery"""
from telegram import TelegramObject, User, Location
@@ -38,13 +38,13 @@ class InlineQuery(TelegramObject):
from_user (:class:`telegram.User`):
query (str):
offset (str):
location (optional[:class:`telegram.Location`]):
bot (Optional[Bot]): The Bot to use for instance methods
**kwargs (dict): Arbitrary keyword arguments.
**kwargs: Arbitrary keyword arguments.
Keyword Args:
location (optional[:class:`telegram.Location`]):
"""
def __init__(self, id, from_user, query, offset, location=None, bot=None, **kwargs):
def __init__(self, id, from_user, query, offset, **kwargs):
# Required
self.id = id
self.from_user = from_user
@@ -52,29 +52,26 @@ class InlineQuery(TelegramObject):
self.offset = offset
# Optional
self.location = location
self.bot = bot
self.location = kwargs.get('location')
@staticmethod
def de_json(data, bot):
def de_json(data):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.InlineQuery:
"""
data = super(InlineQuery, InlineQuery).de_json(data, bot)
data = super(InlineQuery, InlineQuery).de_json(data)
if not data:
return None
data['from_user'] = User.de_json(data.get('from'), bot)
data['location'] = Location.de_json(data.get('location'), bot)
data['from_user'] = User.de_json(data.get('from'))
data['location'] = Location.de_json(data.get('location'))
return InlineQuery(bot=bot, **data)
return InlineQuery(**data)
def to_dict(self):
"""
@@ -87,7 +84,3 @@ class InlineQuery(TelegramObject):
data['from'] = data.pop('from_user', None)
return data
def answer(self, *args, **kwargs):
"""Shortcut for ``bot.answerInlineQuery(update.inline_query.id, *args, **kwargs)``"""
return self.bot.answerInlineQuery(self.id, *args, **kwargs)
+6 -7
View File
@@ -26,21 +26,20 @@ class InlineQueryResult(TelegramObject):
"""This object represents a Telegram InlineQueryResult.
Attributes:
type (str): Type of the result.
id (str): Unique identifier for this result, 1-64 Bytes
type (str):
id (str):
Args:
type (str): Type of the result.
type (str):
id (str): Unique identifier for this result, 1-64 Bytes
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, type, id, **kwargs):
def __init__(self, type, id):
# Required
self.type = str(type)
self.id = str(id)
@staticmethod
def de_json(data, bot):
return super(InlineQueryResult, InlineQueryResult).de_json(data, bot)
def de_json(data):
return super(InlineQueryResult, InlineQueryResult).de_json(data)
+9 -8
View File
@@ -42,20 +42,21 @@ class InlineQueryResultArticle(InlineQueryResult):
parse_mode (str): Use :class:`InputTextMessageContent` instead.
disable_web_page_preview (bool): Use :class:`InputTextMessageContent` instead.
disable_web_page_preview (bool): Use :class:`InputTextMessageContent`
instead.
Args:
id (str): Unique identifier for this result, 1-64 Bytes
title (str):
reply_markup (:class:`telegram.ReplyMarkup`):
Keyword Args:
url (Optional[str]):
hide_url (Optional[bool]):
description (Optional[str]):
thumb_url (Optional[str]):
thumb_width (Optional[int]):
thumb_height (Optional[int]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
@@ -93,11 +94,11 @@ class InlineQueryResultArticle(InlineQueryResult):
self.thumb_height = thumb_height
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultArticle, InlineQueryResultArticle).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultArticle, InlineQueryResultArticle).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultArticle(**data)
+14 -15
View File
@@ -33,27 +33,29 @@ class InlineQueryResultAudio(InlineQueryResult):
title (str):
performer (Optional[str]):
audio_duration (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.input_message_content`]):
input_message_content (Optional[
:class:`telegram.input_message_content`]):
Deprecated: 4.0
message_text (str): Use :class:`InputTextMessageContent` instead.
parse_mode (str): Use :class:`InputTextMessageContent` instead.
disable_web_page_preview (bool): Use :class:`InputTextMessageContent` instead.
disable_web_page_preview (bool): Use :class:`InputTextMessageContent`
instead.
Args:
audio_url (str):
title (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
performer (Optional[str]):
audio_duration (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.input_message_content`]):
**kwargs (dict): Arbitrary keyword arguments.
input_message_content (Optional[
:class:`telegram.input_message_content`]):
"""
def __init__(self,
@@ -62,7 +64,6 @@ class InlineQueryResultAudio(InlineQueryResult):
title,
performer=None,
audio_duration=None,
caption=None,
reply_markup=None,
input_message_content=None,
**kwargs):
@@ -77,19 +78,17 @@ class InlineQueryResultAudio(InlineQueryResult):
self.performer = performer
if audio_duration:
self.audio_duration = audio_duration
if caption:
self.caption = caption
if reply_markup:
self.reply_markup = reply_markup
if input_message_content:
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultAudio, InlineQueryResultAudio).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultAudio, InlineQueryResultAudio).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultAudio(**data)
+19 -24
View File
@@ -23,58 +23,53 @@ from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageConten
class InlineQueryResultCachedAudio(InlineQueryResult):
"""Represents a link to an mp3 audio file stored on the Telegram servers. By default, this
audio file will be sent by the user. Alternatively, you can use input_message_content to send a
message with the specified content instead of the audio.
"""Represents a link to an mp3 audio file stored on the Telegram
servers. By default, this audio file will be sent by the user.
Alternatively, you can use input_message_content to send a message with
the specified content instead of the audio.
Attributes:
id (str):
audio_file_id (str):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.input_message_content`]):
input_message_content (Optional[
:class:`telegram.input_message_content`]):
Deprecated: 4.0
message_text (str): Use :class:`InputTextMessageContent` instead.
parse_mode (str): Use :class:`InputTextMessageContent` instead.
disable_web_page_preview (bool): Use :class:`InputTextMessageContent` instead.
disable_web_page_preview (bool): Use :class:`InputTextMessageContent`
instead.
Args:
audio_file_id (str):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.input_message_content`]):
**kwargs (dict): Arbitrary keyword arguments.
**kwargs: Arbitrary keyword arguments.
Keyword Args:
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[
:class:`telegram.input_message_content`]):
"""
def __init__(self,
id,
audio_file_id,
caption=None,
reply_markup=None,
input_message_content=None,
**kwargs):
def __init__(self, id, audio_file_id, reply_markup=None, input_message_content=None, **kwargs):
# Required
super(InlineQueryResultCachedAudio, self).__init__('audio', id)
self.audio_file_id = audio_file_id
# Optionals
if caption:
self.caption = caption
if reply_markup:
self.reply_markup = reply_markup
if input_message_content:
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultCachedAudio, InlineQueryResultCachedAudio).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultCachedAudio, InlineQueryResultCachedAudio).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedAudio(**data)
+7 -32
View File
@@ -16,38 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram InlineQueryResultCachedDocument"""
"""This module contains the classes that represent Telegram
InlineQueryResultCachedDocument"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedDocument(InlineQueryResult):
"""Represents a link to a file stored on the Telegram servers. By default, this file will be
sent by the user with an optional caption. Alternatively, you can use input_message_content to
send a message with the specified content instead of the file. Currently, only pdf-files and
zip archives can be sent using this method.
Attributes:
title (str): Title for the result.
document_file_id (str): A valid file identifier for the file.
description (Optional[str]): Short description of the result.
caption (Optional[str]): Caption of the document to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the file.
Args:
id (str):
title (str):
document_file_id (str):
description (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
@@ -74,12 +49,12 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
def de_json(data):
data = super(InlineQueryResultCachedDocument,
InlineQueryResultCachedDocument).de_json(data, bot)
InlineQueryResultCachedDocument).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedDocument(**data)
+5 -28
View File
@@ -23,29 +23,6 @@ from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageConten
class InlineQueryResultCachedGif(InlineQueryResult):
"""Represents a link to an animated GIF file stored on the Telegram servers. By default, this
animated GIF file will be sent by the user with an optional caption. Alternatively, you can use
input_message_content to send a message with specified content instead of the animation.
Attributes:
gif_file_id (str): A valid file identifier for the GIF file.
title (Optional[str]): Title for the result.
caption (Optional[str]): Caption of the GIF file to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the GIF animation.
Args:
id (str):
gif_file_id (str):
title (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
@@ -70,11 +47,11 @@ class InlineQueryResultCachedGif(InlineQueryResult):
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultCachedGif, InlineQueryResultCachedGif).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultCachedGif, InlineQueryResultCachedGif).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedGif(**data)
+5 -29
View File
@@ -23,30 +23,6 @@ from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageConten
class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
"""Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the
Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an
optional caption. Alternatively, you can use input_message_content to send a message with the
specified content instead of the animation.
Attributes:
mpeg4_file_id (str): A valid file identifier for the MP4 file.
title (Optional[str]): Title for the result.
caption (Optional[str]): Caption of the MPEG-4 file to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the video animation
Args:
id (str):
mpeg4_file_id (str):
title (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
@@ -71,12 +47,12 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
def de_json(data):
data = super(InlineQueryResultCachedMpeg4Gif,
InlineQueryResultCachedMpeg4Gif).de_json(data, bot)
InlineQueryResultCachedMpeg4Gif).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedMpeg4Gif(**data)
+7 -31
View File
@@ -16,37 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram InlineQueryResultPhoto"""
"""This module contains the classes that represent Telegram
InlineQueryResultPhoto"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedPhoto(InlineQueryResult):
"""Represents a link to a photo stored on the Telegram servers. By default, this photo will be
sent by the user with an optional caption. Alternatively, you can use input_message_content to
send a message with the specified content instead of the photo.
Attributes:
photo_file_id (str): A valid file identifier of the photo.
title (Optional[str]): Title for the result.
description (Optional[str]): Short description of the result.
caption (Optional[str]): Caption of the photo to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the photo
Args:
id (str):
photo_file_id (str):
title (Optional[str]):
description (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
@@ -74,11 +50,11 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultCachedPhoto, InlineQueryResultCachedPhoto).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultCachedPhoto, InlineQueryResultCachedPhoto).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedPhoto(**data)
+7 -26
View File
@@ -16,31 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram InlineQueryResultCachedSticker"""
"""This module contains the classes that represent Telegram
InlineQueryResultCachedSticker"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedSticker(InlineQueryResult):
"""Represents a link to a sticker stored on the Telegram servers. By default, this sticker will
be sent by the user. Alternatively, you can use input_message_content to send a message with
the specified content instead of the sticker.
Attributes:
sticker_file_id (str): A valid file identifier of the sticker.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the sticker.
Args:
id (str):
sticker_file_id (str):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
@@ -59,12 +41,11 @@ class InlineQueryResultCachedSticker(InlineQueryResult):
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultCachedSticker,
InlineQueryResultCachedSticker).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultCachedSticker, InlineQueryResultCachedSticker).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedSticker(**data)
+7 -31
View File
@@ -16,37 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram InlineQueryResultCachedVideo"""
"""This module contains the classes that represent Telegram
InlineQueryResultCachedVideo"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedVideo(InlineQueryResult):
"""Represents a link to a video file stored on the Telegram servers. By default, this video
file will be sent by the user with an optional caption. Alternatively, you can use
input_message_content to send a message with the specified content instead of the video.
Attributes:
video_file_id (str): A valid file identifier for the video file.
title (str): Title for the result.
description (Optional[str]): Short description of the result.
caption (Optional[str]): Caption of the video to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the video
Args:
id (str):
video_file_id (str):
title (str):
description (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
@@ -73,11 +49,11 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultCachedVideo, InlineQueryResultCachedVideo).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultCachedVideo, InlineQueryResultCachedVideo).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedVideo(**data)
+10 -32
View File
@@ -16,41 +16,19 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram InlineQueryResultCachedVoice"""
"""This module contains the classes that represent Telegram
InlineQueryResultCachedVoice"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedVoice(InlineQueryResult):
"""Represents a link to a voice message stored on the Telegram servers. By default, this voice
message will be sent by the user. Alternatively, you can use input_message_content to send a
message with the specified content instead of the voice message.
Attributes:
voice_file_id (str): A valid file identifier for the voice message.
title (str): Voice message title.
caption (Optional[str]): Caption, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the voice message.
Args:
id (str):
voice_file_id (str):
title (str):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
voice_file_id,
title,
caption=None,
description=None,
reply_markup=None,
input_message_content=None,
**kwargs):
@@ -60,19 +38,19 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
self.title = title
# Optionals
if caption:
self.caption = caption
if description:
self.description = description
if reply_markup:
self.reply_markup = reply_markup
if input_message_content:
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultCachedVoice, InlineQueryResultCachedVoice).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultCachedVoice, InlineQueryResultCachedVoice).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultCachedVoice(**data)
+7 -35
View File
@@ -16,41 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram InlineQueryResultContact"""
"""This module contains the classes that represent Telegram
InlineQueryResultContact"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultContact(InlineQueryResult):
"""Represents a contact with a phone number. By default, this contact will be sent by the user.
Alternatively, you can use input_message_content to send a message with the specified content
instead of the contact.
Attributes:
phone_number (str): Contact's phone number.
first_name (str): Contact's first name.
last_name (Optional[str]): Contact's last name.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the contact.
thumb_url (Optional[str]): Url of the thumbnail for the result.
thumb_width (Optional[int]): Thumbnail width.
thumb_height (Optional[int]): Thumbnail height.
Args:
id (str):
phone_number (str):
first_name (str):
last_name (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
thumb_url (Optional[str]): Url of the thumbnail for the result.
thumb_width (Optional[int]):
thumb_height (Optional[int]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
@@ -83,11 +55,11 @@ class InlineQueryResultContact(InlineQueryResult):
self.thumb_height = thumb_height
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultContact, InlineQueryResultContact).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultContact, InlineQueryResultContact).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultContact(**data)
+7 -40
View File
@@ -16,46 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram InlineQueryResultDocument"""
"""This module contains the classes that represent Telegram
InlineQueryResultDocument"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultDocument(InlineQueryResult):
"""Represents a link to a file. By default, this file will be sent by the user with an optional
caption. Alternatively, you can use input_message_content to send a message with the specified
content instead of the file. Currently, only .PDF and .ZIP files can be sent using this method.
Attributes:
title (str): Title for the result.
caption (Optional[str]): Caption of the document to be sent, 0-200 characters.
document_url (Optional[str]): A valid URL for the file.
mime_type (Optional[str]): Mime type of the content of the file, either "application/pdf"
or "application/zip".
description (Optional[str]): Short description of the result.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the file.
thumb_url (Optional[str]): URL of the thumbnail (jpeg only) for the file.
thumb_width (Optional[int]): Thumbnail width.
thumb_height (Optional[int]): Thumbnail height.
Args:
id (str):
document_url (str):
title (str):
mime_type (str):
caption (Optional[str]):
description (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
thumb_url (Optional[str]):
thumb_width (Optional[int]):
thumb_height (Optional[int]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
@@ -93,11 +60,11 @@ class InlineQueryResultDocument(InlineQueryResult):
self.thumb_height = thumb_height
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultDocument, InlineQueryResultDocument).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultDocument, InlineQueryResultDocument).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultDocument(**data)
-42
View File
@@ -1,42 +0,0 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultGame"""
from telegram import InlineQueryResult, InlineKeyboardMarkup
class InlineQueryResultGame(InlineQueryResult):
def __init__(self, id, game_short_name, reply_markup=None, **kwargs):
# Required
super(InlineQueryResultGame, self).__init__('game', id)
self.id = id
self.game_short_name = game_short_name
if reply_markup:
self.reply_markup = reply_markup
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultGame, InlineQueryResultGame).de_json(data, bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
return InlineQueryResultGame(**data)
+5 -34
View File
@@ -23,35 +23,6 @@ from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageConten
class InlineQueryResultGif(InlineQueryResult):
"""Represents a link to an animated GIF file. By default, this animated GIF file will be sent
by the user with optional caption. Alternatively, you can use input_message_content to send a
message with the specified content instead of the animation.
Attributes:
gif_url (str): A valid URL for the GIF file. File size must not exceed 1MB.
thumb_url (str): URL of the static thumbnail for the result (jpeg or gif).
gif_width (Optional[int]): Width of the GIF.
gif_height (Optional[int]): Height of the GIF.
title (Optional[str]): Title for the result.
caption (Optional[str]): Caption of the GIF file to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the GIF animation.
Args:
id (str):
gif_url (str):
thumb_url (str):
gif_width (Optional[int]):
gif_height (Optional[int]):
title (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
@@ -85,11 +56,11 @@ class InlineQueryResultGif(InlineQueryResult):
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultGif, InlineQueryResultGif).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultGif, InlineQueryResultGif).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultGif(**data)
+7 -36
View File
@@ -16,42 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram InlineQueryResultLocation"""
"""This module contains the classes that represent Telegram
InlineQueryResultLocation"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultLocation(InlineQueryResult):
"""Represents a location on a map. By default, the location will be sent by the user.
Alternatively, you can use input_message_content to send a message with the specified content
instead of the location.
Attributes:
latitude (float): Location latitude in degrees.
longitude (float): Location longitude in degrees.
title (str): Location title.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the location.
thumb_url (Optional[str]): Url of the thumbnail for the result.
thumb_width (Optional[int]): Thumbnail width.
thumb_height (Optional[int]): Thumbnail height.
Args:
latitude (float): Location latitude in degrees.
longitude (float): Location longitude in degrees.
title (str): Location title.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the location.
thumb_url (Optional[str]): Url of the thumbnail for the result.
thumb_width (Optional[int]): Thumbnail width.
thumb_height (Optional[int]): Thumbnail height.
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
@@ -83,11 +54,11 @@ class InlineQueryResultLocation(InlineQueryResult):
self.thumb_height = thumb_height
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultLocation, InlineQueryResultLocation).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultLocation, InlineQueryResultLocation).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultLocation(**data)
+7 -37
View File
@@ -16,43 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif"""
"""This module contains the classes that represent Telegram
InlineQueryResultMpeg4Gif"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultMpeg4Gif(InlineQueryResult):
"""Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default,
this animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you
can use input_message_content to send a message with the specified content instead of the
animation.
Attributes:
mpeg4_url (str): A valid URL for the MP4 file. File size must not exceed 1MB.
thumb_url (str): URL of the static thumbnail (jpeg or gif) for the result.
mpeg4_width (Optional[int]): Video width.
mpeg4_height (Optional[int]): Video height.
title (Optional[str]): Title for the result.
caption (Optional[str]): Caption of the MPEG-4 file to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the video animation.
Args:
mpeg4_url (str): A valid URL for the MP4 file. File size must not exceed 1MB.
thumb_url (str): URL of the static thumbnail (jpeg or gif) for the result.
mpeg4_width (Optional[int]): Video width.
mpeg4_height (Optional[int]): Video height.
title (Optional[str]): Title for the result.
caption (Optional[str]): Caption of the MPEG-4 file to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the video animation.
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
@@ -86,11 +56,11 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultMpeg4Gif, InlineQueryResultMpeg4Gif).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultMpeg4Gif, InlineQueryResultMpeg4Gif).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultMpeg4Gif(**data)
+7 -25
View File
@@ -16,31 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram InlineQueryResultPhoto"""
"""This module contains the classes that represent Telegram
InlineQueryResultPhoto"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultPhoto(InlineQueryResult):
"""Represents a link to a photo. By default, this photo will be sent by the user with optional
caption. Alternatively, you can use input_message_content to send a message with the specified
content instead of the photo.
Attributes:
photo_url (str): A valid URL of the photo. Photo must be in jpeg format. Photo size must
not exceed 5MB.
thumb_url (str): URL of the thumbnail for the photo.
photo_width (Optional[int]): Width of the photo.
photo_height (Optional[int]): Height of the photo.
title (Optional[str]): Title for the result.
description (Optional[str]): Short description of the result.
caption (Optional[str]): Caption of the photo to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the photo.
"""
def __init__(self,
id,
@@ -76,11 +58,11 @@ class InlineQueryResultPhoto(InlineQueryResult):
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultPhoto, InlineQueryResultPhoto).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultPhoto, InlineQueryResultPhoto).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultPhoto(**data)
+5 -5
View File
@@ -60,11 +60,11 @@ class InlineQueryResultVenue(InlineQueryResult):
self.thumb_height = thumb_height
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultVenue, InlineQueryResultVenue).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultVenue, InlineQueryResultVenue).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultVenue(**data)
+5 -5
View File
@@ -63,11 +63,11 @@ class InlineQueryResultVideo(InlineQueryResult):
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultVideo, InlineQueryResultVideo).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultVideo, InlineQueryResultVideo).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultVideo(**data)
+5 -8
View File
@@ -29,7 +29,6 @@ class InlineQueryResultVoice(InlineQueryResult):
voice_url,
title,
voice_duration=None,
caption=None,
reply_markup=None,
input_message_content=None,
**kwargs):
@@ -42,19 +41,17 @@ class InlineQueryResultVoice(InlineQueryResult):
# Optional
if voice_duration:
self.voice_duration = voice_duration
if caption:
self.caption = caption
if reply_markup:
self.reply_markup = reply_markup
if input_message_content:
self.input_message_content = input_message_content
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultVoice, InlineQueryResultVoice).de_json(data, bot)
def de_json(data):
data = super(InlineQueryResultVoice, InlineQueryResultVoice).de_json(data)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
data['input_message_content'] = InputMessageContent.de_json(
data.get('input_message_content'), bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
data['input_message_content'] = InputMessageContent.de_json(data.get(
'input_message_content'))
return InlineQueryResultVoice(**data)
+1 -1
View File
@@ -33,5 +33,5 @@ class InputContactMessageContent(InputMessageContent):
self.last_name = last_name
@staticmethod
def de_json(data, bot):
def de_json(data):
return InputContactMessageContent(**data)
+28 -21
View File
@@ -17,7 +17,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram InputFile."""
"""This module contains a object that represents a Telegram InputFile."""
try:
# python 3
@@ -31,11 +31,12 @@ import mimetypes
import os
import sys
from future.moves.urllib.request import urlopen
from telegram import TelegramError
DEFAULT_MIME_TYPE = 'application/octet-stream'
USER_AGENT = 'Python Telegram Bot (https://github.com/python-telegram-bot/python-telegram-bot)'
FILE_TYPES = ('audio', 'document', 'photo', 'sticker', 'video', 'voice', 'certificate')
class InputFile(object):
@@ -48,28 +49,32 @@ class InputFile(object):
if 'audio' in data:
self.input_name = 'audio'
self.input_file = data.pop('audio')
elif 'document' in data:
if 'document' in data:
self.input_name = 'document'
self.input_file = data.pop('document')
elif 'photo' in data:
if 'photo' in data:
self.input_name = 'photo'
self.input_file = data.pop('photo')
elif 'sticker' in data:
if 'sticker' in data:
self.input_name = 'sticker'
self.input_file = data.pop('sticker')
elif 'video' in data:
if 'video' in data:
self.input_name = 'video'
self.input_file = data.pop('video')
elif 'voice' in data:
if 'voice' in data:
self.input_name = 'voice'
self.input_file = data.pop('voice')
elif 'certificate' in data:
if 'certificate' in data:
self.input_name = 'certificate'
self.input_file = data.pop('certificate')
else:
raise TelegramError('Unknown inputfile type')
if hasattr(self.input_file, 'read'):
if str(self.input_file).startswith('http'):
from_url = True
self.input_file = urlopen(self.input_file)
else:
from_url = False
if hasattr(self.input_file, 'read') or from_url:
self.filename = None
self.input_file_content = self.input_file.read()
if 'filename' in data:
@@ -78,9 +83,11 @@ class InputFile(object):
# on py2.7, pylint fails to understand this properly
# pylint: disable=E1101
self.filename = os.path.basename(self.input_file.name)
elif from_url:
self.filename = os.path.basename(self.input_file.url).split('?')[0].split('&')[0]
try:
self.mimetype = self.is_image(self.input_file_content)
self.mimetype = InputFile.is_image(self.input_file_content)
if not self.filename or '.' not in self.filename:
self.filename = self.mimetype.replace('/', '.')
except TelegramError:
@@ -111,23 +118,22 @@ class InputFile(object):
form_boundary = '--' + self.boundary
# Add data fields
for name in iter(self.data):
value = self.data[name]
for name, value in self.data.items():
form.extend([
form_boundary, 'Content-Disposition: form-data; name="%s"' % name, '', str(value)
])
# Add input_file to upload
form.extend([
form_boundary, 'Content-Disposition: form-data; name="%s"; filename="%s"' %
(self.input_name,
self.filename), 'Content-Type: %s' % self.mimetype, '', self.input_file_content
form_boundary, 'Content-Disposition: form-data; name="%s"; filename="%s"' % (
self.input_name, self.filename
), 'Content-Type: %s' % self.mimetype, '', self.input_file_content
])
form.append('--' + self.boundary + '--')
form.append('')
return self._parse(form)
return InputFile._parse(form)
@staticmethod
def _parse(form):
@@ -168,17 +174,18 @@ class InputFile(object):
"""Check if the request is a file request.
Args:
data (dict): A dict of (str, unicode) key/value pairs
data (str): A dict of (str, unicode) key/value pairs
Returns:
bool
"""
if data:
file_type = [i for i in iter(data) if i in FILE_TYPES]
file_types = ['audio', 'document', 'photo', 'sticker', 'video', 'voice', 'certificate']
file_type = [i for i in list(data.keys()) if i in file_types]
if file_type:
file_content = data[file_type[0]]
return hasattr(file_content, 'read')
return hasattr(file_content, 'read') or str(file_content).startswith('http')
return False
+1 -1
View File
@@ -31,5 +31,5 @@ class InputLocationMessageContent(InputMessageContent):
self.longitude = longitude
@staticmethod
def de_json(data, bot):
def de_json(data):
return InputLocationMessageContent(**data)
+6 -6
View File
@@ -26,33 +26,33 @@ class InputMessageContent(TelegramObject):
"""Base class for Telegram InputMessageContent Objects"""
@staticmethod
def de_json(data, bot):
data = super(InputMessageContent, InputMessageContent).de_json(data, bot)
def de_json(data):
data = super(InputMessageContent, InputMessageContent).de_json(data)
if not data:
return None
try:
from telegram import InputTextMessageContent
return InputTextMessageContent.de_json(data, bot)
return InputTextMessageContent.de_json(data)
except TypeError:
pass
try:
from telegram import InputVenueMessageContent
return InputVenueMessageContent.de_json(data, bot)
return InputVenueMessageContent.de_json(data)
except TypeError:
pass
try:
from telegram import InputLocationMessageContent
return InputLocationMessageContent.de_json(data, bot)
return InputLocationMessageContent.de_json(data)
except TypeError:
pass
try:
from telegram import InputContactMessageContent
return InputContactMessageContent.de_json(data, bot)
return InputContactMessageContent.de_json(data)
except TypeError:
pass
+1 -1
View File
@@ -33,5 +33,5 @@ class InputTextMessageContent(InputMessageContent):
self.disable_web_page_preview = disable_web_page_preview
@staticmethod
def de_json(data, bot):
def de_json(data):
return InputTextMessageContent(**data)
+1 -1
View File
@@ -35,5 +35,5 @@ class InputVenueMessageContent(InputMessageContent):
self.foursquare_id = foursquare_id
@staticmethod
def de_json(data, bot):
def de_json(data):
return InputVenueMessageContent(**data)
+4 -4
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram KeyboardButton."""
"""This module contains a object that represents a Telegram KeyboardButton."""
from telegram import TelegramObject
@@ -43,19 +43,19 @@ class KeyboardButton(TelegramObject):
self.request_location = request_location
@staticmethod
def de_json(data, bot):
def de_json(data):
if not data:
return None
return KeyboardButton(**data)
@staticmethod
def de_list(data, bot):
def de_list(data):
if not data:
return []
keyboards = list()
for keyboard in data:
keyboards.append(KeyboardButton.de_json(keyboard, bot))
keyboards.append(KeyboardButton.de_json(keyboard))
return keyboards
+3 -4
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Location."""
"""This module contains a object that represents a Telegram Location."""
from telegram import TelegramObject
@@ -39,11 +39,10 @@ class Location(TelegramObject):
self.latitude = float(latitude)
@staticmethod
def de_json(data, bot):
def de_json(data):
"""
Args:
data (dict):
bot (telegram.Bot):
data (str):
Returns:
telegram.Location:
+52 -353
View File
@@ -17,14 +17,13 @@
#
# 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 Message."""
"""This module contains a object that represents a Telegram Message."""
import sys
from datetime import datetime
from time import mktime
from telegram import (Audio, Contact, Document, Chat, Location, PhotoSize, Sticker, TelegramObject,
User, Video, Voice, Venue, MessageEntity, Game)
User, Video, Voice, Venue, MessageEntity)
class Message(TelegramObject):
@@ -45,7 +44,6 @@ class Message(TelegramObject):
text (str):
audio (:class:`telegram.Audio`):
document (:class:`telegram.Document`):
game (:class:`telegram.Game`):
photo (List[:class:`telegram.PhotoSize`]):
sticker (:class:`telegram.Sticker`):
video (:class:`telegram.Video`):
@@ -87,7 +85,6 @@ class Message(TelegramObject):
text (Optional[str]):
audio (Optional[:class:`telegram.Audio`]):
document (Optional[:class:`telegram.Document`]):
game (Optional[:class:`telegram.Game`]):
photo (Optional[List[:class:`telegram.PhotoSize`]]):
sticker (Optional[:class:`telegram.Sticker`]):
video (Optional[:class:`telegram.Video`]):
@@ -105,81 +102,43 @@ class Message(TelegramObject):
migrate_to_chat_id (Optional[int]):
migrate_from_chat_id (Optional[int]):
channel_chat_created (Optional[bool]):
bot (Optional[Bot]): The Bot to use for instance methods
"""
def __init__(self,
message_id,
from_user,
date,
chat,
forward_from=None,
forward_from_chat=None,
forward_date=None,
reply_to_message=None,
edit_date=None,
text='',
entities=None,
audio=None,
document=None,
photo=None,
sticker=None,
video=None,
voice=None,
caption='',
contact=None,
location=None,
venue=None,
new_chat_member=None,
left_chat_member=None,
new_chat_title='',
new_chat_photo=None,
delete_chat_photo=False,
group_chat_created=False,
supergroup_chat_created=False,
migrate_to_chat_id=0,
migrate_from_chat_id=0,
channel_chat_created=False,
pinned_message=None,
bot=None,
**kwargs):
def __init__(self, message_id, from_user, date, chat, **kwargs):
# Required
self.message_id = int(message_id)
self.from_user = from_user
self.date = date
self.chat = chat
# Optionals
self.forward_from = forward_from
self.forward_from_chat = forward_from_chat
self.forward_date = forward_date
self.reply_to_message = reply_to_message
self.edit_date = edit_date
self.text = text
self.entities = entities or list()
self.audio = audio
self.game = kwargs.get('game')
self.document = document
self.photo = photo
self.sticker = sticker
self.video = video
self.voice = voice
self.caption = caption
self.contact = contact
self.location = location
self.venue = venue
self.new_chat_member = new_chat_member
self.left_chat_member = left_chat_member
self.new_chat_title = new_chat_title
self.new_chat_photo = new_chat_photo
self.delete_chat_photo = bool(delete_chat_photo)
self.group_chat_created = bool(group_chat_created)
self.supergroup_chat_created = bool(supergroup_chat_created)
self.migrate_to_chat_id = int(migrate_to_chat_id)
self.migrate_from_chat_id = int(migrate_from_chat_id)
self.channel_chat_created = bool(channel_chat_created)
self.pinned_message = pinned_message
self.bot = bot
self.forward_from = kwargs.get('forward_from')
self.forward_from_chat = kwargs.get('forward_from_chat')
self.forward_date = kwargs.get('forward_date')
self.reply_to_message = kwargs.get('reply_to_message')
self.edit_date = kwargs.get('edit_date')
self.text = kwargs.get('text', '')
self.entities = kwargs.get('entities', list())
self.audio = kwargs.get('audio')
self.document = kwargs.get('document')
self.photo = kwargs.get('photo')
self.sticker = kwargs.get('sticker')
self.video = kwargs.get('video')
self.voice = kwargs.get('voice')
self.caption = kwargs.get('caption', '')
self.contact = kwargs.get('contact')
self.location = kwargs.get('location')
self.venue = kwargs.get('venue')
self.new_chat_member = kwargs.get('new_chat_member')
self.left_chat_member = kwargs.get('left_chat_member')
self.new_chat_title = kwargs.get('new_chat_title', '')
self.new_chat_photo = kwargs.get('new_chat_photo')
self.delete_chat_photo = bool(kwargs.get('delete_chat_photo', False))
self.group_chat_created = bool(kwargs.get('group_chat_created', False))
self.supergroup_chat_created = bool(kwargs.get('supergroup_chat_created', False))
self.migrate_to_chat_id = int(kwargs.get('migrate_to_chat_id', 0))
self.migrate_from_chat_id = int(kwargs.get('migrate_from_chat_id', 0))
self.channel_chat_created = bool(kwargs.get('channel_chat_created', False))
self.pinned_message = kwargs.get('pinned_message')
@property
def chat_id(self):
@@ -187,11 +146,10 @@ class Message(TelegramObject):
return self.chat.id
@staticmethod
def de_json(data, bot):
def de_json(data):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.Message:
@@ -199,31 +157,30 @@ class Message(TelegramObject):
if not data:
return None
data['from_user'] = User.de_json(data.get('from'), bot)
data['from_user'] = User.de_json(data.get('from'))
data['date'] = datetime.fromtimestamp(data['date'])
data['chat'] = Chat.de_json(data.get('chat'), bot)
data['entities'] = MessageEntity.de_list(data.get('entities'), bot)
data['forward_from'] = User.de_json(data.get('forward_from'), bot)
data['forward_from_chat'] = Chat.de_json(data.get('forward_from_chat'), bot)
data['chat'] = Chat.de_json(data.get('chat'))
data['entities'] = MessageEntity.de_list(data.get('entities'))
data['forward_from'] = User.de_json(data.get('forward_from'))
data['forward_from_chat'] = Chat.de_json(data.get('forward_from_chat'))
data['forward_date'] = Message._fromtimestamp(data.get('forward_date'))
data['reply_to_message'] = Message.de_json(data.get('reply_to_message'), bot)
data['reply_to_message'] = Message.de_json(data.get('reply_to_message'))
data['edit_date'] = Message._fromtimestamp(data.get('edit_date'))
data['audio'] = Audio.de_json(data.get('audio'), bot)
data['document'] = Document.de_json(data.get('document'), bot)
data['game'] = Game.de_json(data.get('game'), bot)
data['photo'] = PhotoSize.de_list(data.get('photo'), bot)
data['sticker'] = Sticker.de_json(data.get('sticker'), bot)
data['video'] = Video.de_json(data.get('video'), bot)
data['voice'] = Voice.de_json(data.get('voice'), bot)
data['contact'] = Contact.de_json(data.get('contact'), bot)
data['location'] = Location.de_json(data.get('location'), bot)
data['venue'] = Venue.de_json(data.get('venue'), bot)
data['new_chat_member'] = User.de_json(data.get('new_chat_member'), bot)
data['left_chat_member'] = User.de_json(data.get('left_chat_member'), bot)
data['new_chat_photo'] = PhotoSize.de_list(data.get('new_chat_photo'), bot)
data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot)
data['audio'] = Audio.de_json(data.get('audio'))
data['document'] = Document.de_json(data.get('document'))
data['photo'] = PhotoSize.de_list(data.get('photo'))
data['sticker'] = Sticker.de_json(data.get('sticker'))
data['video'] = Video.de_json(data.get('video'))
data['voice'] = Voice.de_json(data.get('voice'))
data['contact'] = Contact.de_json(data.get('contact'))
data['location'] = Location.de_json(data.get('location'))
data['venue'] = Venue.de_json(data.get('venue'))
data['new_chat_member'] = User.de_json(data.get('new_chat_member'))
data['left_chat_member'] = User.de_json(data.get('left_chat_member'))
data['new_chat_photo'] = PhotoSize.de_list(data.get('new_chat_photo'))
data['pinned_message'] = Message.de_json(data.get('pinned_message'))
return Message(bot=bot, **data)
return Message(**data)
def __getitem__(self, item):
if item in self.__dict__.keys():
@@ -287,261 +244,3 @@ class Message(TelegramObject):
except AttributeError:
# Python 3 (< 3.3) and Python 2
return int(mktime(dt_obj.timetuple()))
def _quote(self, kwargs):
"""Modify kwargs for replying with or without quoting"""
if 'reply_to_message_id' in kwargs:
if 'quote' in kwargs:
del kwargs['quote']
elif 'quote' in kwargs:
if kwargs['quote']:
kwargs['reply_to_message_id'] = self.message_id
del kwargs['quote']
else:
if self.chat.type != Chat.PRIVATE:
kwargs['reply_to_message_id'] = self.message_id
def reply_text(self, *args, **kwargs):
"""
Shortcut for ``bot.sendMessage(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the message is sent as an actual reply to
this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
"""
self._quote(kwargs)
return self.bot.sendMessage(self.chat_id, *args, **kwargs)
def reply_photo(self, *args, **kwargs):
"""
Shortcut for ``bot.sendPhoto(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): 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.
"""
self._quote(kwargs)
return self.bot.sendPhoto(self.chat_id, *args, **kwargs)
def reply_audio(self, *args, **kwargs):
"""
Shortcut for ``bot.sendAudio(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the audio 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.
"""
self._quote(kwargs)
return self.bot.sendAudio(self.chat_id, *args, **kwargs)
def reply_document(self, *args, **kwargs):
"""
Shortcut for ``bot.sendDocument(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the document 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.
"""
self._quote(kwargs)
return self.bot.sendDocument(self.chat_id, *args, **kwargs)
def reply_sticker(self, *args, **kwargs):
"""
Shortcut for ``bot.sendSticker(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the sticker 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.
"""
self._quote(kwargs)
return self.bot.sendSticker(self.chat_id, *args, **kwargs)
def reply_video(self, *args, **kwargs):
"""
Shortcut for ``bot.sendVideo(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the video 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.
"""
self._quote(kwargs)
return self.bot.sendVideo(self.chat_id, *args, **kwargs)
def reply_voice(self, *args, **kwargs):
"""
Shortcut for ``bot.sendVoice(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the voice 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.
"""
self._quote(kwargs)
return self.bot.sendVoice(self.chat_id, *args, **kwargs)
def reply_location(self, *args, **kwargs):
"""
Shortcut for ``bot.sendLocation(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the location 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.
"""
self._quote(kwargs)
return self.bot.sendLocation(self.chat_id, *args, **kwargs)
def reply_venue(self, *args, **kwargs):
"""
Shortcut for ``bot.sendVenue(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the venue 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.
"""
self._quote(kwargs)
return self.bot.sendVenue(self.chat_id, *args, **kwargs)
def reply_contact(self, *args, **kwargs):
"""
Shortcut for ``bot.sendContact(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the contact 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.
"""
self._quote(kwargs)
return self.bot.sendContact(self.chat_id, *args, **kwargs)
def forward(self, chat_id, disable_notification=False):
"""Shortcut for
bot.forwardMessage(chat_id=chat_id,
from_chat_id=update.message.chat_id,
disable_notification=disable_notification,
message_id=update.message.message_id)
"""
return self.bot.forwardMessage(
chat_id=chat_id,
from_chat_id=self.chat_id,
disable_notification=disable_notification,
message_id=self.message_id)
def edit_text(self, *args, **kwargs):
"""
Shortcut for ``bot.editMessageText(chat_id=message.chat_id,
message_id=message.message_id,
*args, **kwargs)``
Note:
You can only edit messages that the bot sent itself,
therefore this method can only be used on the
return value of the bot.send_* family of methods.
"""
return self.bot.edit_message_text(
chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs)
def edit_caption(self, *args, **kwargs):
"""
Shortcut for ``bot.editMessageCaption(chat_id=message.chat_id,
message_id=message.message_id,
*args, **kwargs)``
Note:
You can only edit messages that the bot sent itself,
therefore this method can only be used on the
return value of the bot.send_* family of methods.
"""
return self.bot.edit_message_caption(
chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs)
def edit_reply_markup(self, *args, **kwargs):
"""
Shortcut for ``bot.editReplyMarkup(chat_id=message.chat_id,
message_id=message.message_id,
*args, **kwargs)``
Note:
You can only edit messages that the bot sent itself,
therefore this method can only be used on the
return value of the bot.send_* family of methods.
"""
return self.bot.edit_message_caption(
chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs)
def parse_entity(self, entity):
"""
Returns the text from a given :class:`telegram.MessageEntity`.
Note:
This method is present because Telegram calculates the offset and length in
UTF-16 codepoint pairs, which some versions of Python don't handle automatically.
(That is, you can't just slice ``Message.text`` with the offset and length.)
Args:
entity (MessageEntity): The entity to extract the text from. It must be an entity that
belongs to this message.
Returns:
str: The text of the given entity
"""
# Is it a narrow build, if so we don't need to convert
if sys.maxunicode == 0xffff:
return self.text[entity.offset:entity.offset + entity.length]
else:
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')
def parse_entities(self, types=None):
"""
Returns a ``dict`` that maps :class:`telegram.MessageEntity` to ``str``.
It contains entities from this message filtered by their ``type`` attribute as the key, and
the text that each entity belongs to as the value of the ``dict``.
Note:
This method should always be used instead of the ``entities`` attribute, since it
calculates the correct substring from the message text based on UTF-16 codepoints.
See ``get_entity_text`` for more info.
Args:
types (Optional[list]): List of ``MessageEntity`` types as strings. If the ``type``
attribute of an entity is contained in this list, it will be returned.
Defaults to a list of all types. All types can be found as constants in
:class:`telegram.MessageEntity`.
Returns:
dict[:class:`telegram.MessageEntity`, ``str``]: A dictionary of entities mapped to the
text that belongs to them, calculated based on UTF-16 codepoints.
"""
if types is None:
types = MessageEntity.ALL_TYPES
return {
entity: self.parse_entity(entity)
for entity in self.entities if entity.type in types
}
+9 -24
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram MessageEntity."""
"""This module contains a object that represents a Telegram MessageEntity."""
from telegram import User, TelegramObject
@@ -34,25 +34,25 @@ class MessageEntity(TelegramObject):
user (Optional[:class:`telegram.User`]):
"""
def __init__(self, type, offset, length, url=None, user=None, **kwargs):
def __init__(self, type, offset, length, **kwargs):
# Required
self.type = type
self.offset = offset
self.length = length
# Optionals
self.url = url
self.user = user
self.url = kwargs.get('url')
self.user = kwargs.get('user')
@staticmethod
def de_json(data, bot):
data = super(MessageEntity, MessageEntity).de_json(data, bot)
def de_json(data):
data = super(MessageEntity, MessageEntity).de_json(data)
data['user'] = User.de_json(data.get('user'), bot)
data['user'] = User.de_json(data.get('user'))
return MessageEntity(**data)
@staticmethod
def de_list(data, bot):
def de_list(data):
"""
Args:
data (list):
@@ -65,21 +65,6 @@ class MessageEntity(TelegramObject):
entities = list()
for entity in data:
entities.append(MessageEntity.de_json(entity, bot))
entities.append(MessageEntity.de_json(entity))
return entities
MENTION = 'mention'
HASHTAG = 'hashtag'
BOT_COMMAND = 'bot_command'
URL = 'url'
EMAIL = 'email'
BOLD = 'bold'
ITALIC = 'italic'
CODE = 'code'
PRE = 'pre'
TEXT_LINK = 'text_link'
TEXT_MENTION = 'text_mention'
ALL_TYPES = [
MENTION, HASHTAG, BOT_COMMAND, URL, EMAIL, BOLD, ITALIC, CODE, PRE, TEXT_LINK, TEXT_MENTION
]
@@ -16,10 +16,17 @@
#
# 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 CallbackGame."""
"""This module contains a object that represents a logging NullHandler."""
from telegram import TelegramObject
import logging
class CallbackGame(TelegramObject):
"""A placeholder, currently holds no information. Use BotFather to set up your game."""
class NullHandler(logging.Handler):
"""This object represents a logging NullHandler."""
def emit(self, record):
"""
Args:
record (str):
"""
pass

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