mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-22 17:34:11 +00:00
Compare commits
109 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6479e15578 | |||
| 5dd3a660e3 | |||
| e2a651afc8 | |||
| 33512ffd2e | |||
| c2c5452829 | |||
| 9aa5522694 | |||
| 9720f59d7e | |||
| 578627feca | |||
| 257b8321f8 | |||
| 4f2ea5b0f1 | |||
| 9a2d5e0410 | |||
| 1b7f83625c | |||
| da01601ff9 | |||
| 752b64769d | |||
| f3b75d95c4 | |||
| 36b98b0ab4 | |||
| 05b85d4334 | |||
| f27807552f | |||
| f3aca42e69 | |||
| ed49bdb19c | |||
| 9f3afa5fa2 | |||
| cdf36a20b7 | |||
| c5598b96bc | |||
| ca4351079f | |||
| 34059c951d | |||
| c7dbdce3dc | |||
| fe5ae8ed84 | |||
| a6b28b022a | |||
| 284f16b87b | |||
| 785245a57e | |||
| 1d905d567c | |||
| 4541476143 | |||
| 11a3de67ea | |||
| 90bf26c09b | |||
| b5b09884b1 | |||
| 35132271af | |||
| 44d7bad11c | |||
| ad5f009ce7 | |||
| 8fe6e13ff2 | |||
| ff39e2436e | |||
| 5b14b134dc | |||
| 5897affa07 | |||
| 9982f3c908 | |||
| 22142e7cbd | |||
| 4c8d1c9a5e | |||
| bef6651da0 | |||
| 8c2ee0b439 | |||
| bfb99a688a | |||
| d239f4ee62 | |||
| 2b930c221d | |||
| 2a1d40bd28 | |||
| 1d4464ddbd | |||
| 85b9236641 | |||
| e78d11a99b | |||
| cc73469dab | |||
| 853d823964 | |||
| 69bfb85298 | |||
| f267646828 | |||
| 6b7144bbab | |||
| f8b13440c1 | |||
| a1ade408b0 | |||
| 2954ca2bad | |||
| 34ebb7fe5a | |||
| 78094b796d | |||
| fb378775a4 | |||
| 264b9bd08c | |||
| d714da4b36 | |||
| e39afad321 | |||
| 0507378509 | |||
| 659ac52d92 | |||
| 924c241680 | |||
| ac59f2f37c | |||
| 86c8ebbfb7 | |||
| cd38bdbed5 | |||
| 975d193441 | |||
| 728ffa432d | |||
| 8ac66698b5 | |||
| c2c93f5d51 | |||
| fc9f36d4db | |||
| e69e99ce23 | |||
| 27b2fd64b7 | |||
| 4dee785fba | |||
| 4c7cc3a05b | |||
| 1bb5dd224b | |||
| bd96771a7a | |||
| 191e442e59 | |||
| 58dddfd9c3 | |||
| 68a7d9fa1b | |||
| 38f2064639 | |||
| 651119fd69 | |||
| 232a0b0286 | |||
| 48bcc3129a | |||
| 6a01164897 | |||
| 7f6b017ce2 | |||
| 09cb33f52d | |||
| 423251f66c | |||
| ed1785981d | |||
| a8fecc527d | |||
| c3984e1bf1 | |||
| 93bf21a0a4 | |||
| cbf93e1046 | |||
| a1a8628c75 | |||
| d5ce32c672 | |||
| 09ddc1b1a8 | |||
| c7cd379016 | |||
| 84f3bc0c79 | |||
| 92dc9b81ce | |||
| 8ead72e3ef | |||
| a37add39f4 |
+4
-1
@@ -1,5 +1,8 @@
|
||||
[run]
|
||||
source = telegram
|
||||
omit = telegram/vendor/*
|
||||
|
||||
[report]
|
||||
omit = tests/
|
||||
omit =
|
||||
tests/
|
||||
telegram/vendor/*
|
||||
|
||||
@@ -85,6 +85,12 @@ Here's how to make a one-off code change.
|
||||
|
||||
$ make test
|
||||
|
||||
If you don't have ``make``, do:
|
||||
|
||||
.. code-block::
|
||||
|
||||
$ nosetests -v
|
||||
|
||||
- To actually make the commit (this will trigger tests for yapf, lint and pep8 automatically):
|
||||
|
||||
.. code-block:: bash
|
||||
@@ -184,11 +190,11 @@ break the API classes. For example:
|
||||
.. code-block:: python
|
||||
|
||||
# GOOD
|
||||
def __init__(self, id, name, last_name='', **kwargs):
|
||||
def __init__(self, id, name, last_name=None, **kwargs):
|
||||
self.last_name = last_name
|
||||
|
||||
# BAD
|
||||
def __init__(self, id, name, last_name=''):
|
||||
def __init__(self, id, name, last_name=None):
|
||||
self.last_name = last_name
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
[submodule "telegram/vendor/urllib3"]
|
||||
path = telegram/vendor/urllib3
|
||||
url = https://github.com/python-telegram-bot/urllib3.git
|
||||
branch = ptb
|
||||
+2
-1
@@ -4,6 +4,7 @@ python:
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
- "pypy"
|
||||
- "pypy3"
|
||||
branches:
|
||||
@@ -15,7 +16,7 @@ install:
|
||||
- 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/
|
||||
- nosetests -v --with-flaky --no-flaky-report --with-coverage --cover-package=telegram/ tests
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 3.5 ]]; then pre-commit run --all-files; fi
|
||||
after_success:
|
||||
coveralls
|
||||
|
||||
+19
-1
@@ -1,7 +1,14 @@
|
||||
Credits
|
||||
=======
|
||||
|
||||
``python-telegram-bot`` is written and maintained by `Leandro Toledo <https://github.com/leandrotoledo>`_.
|
||||
``python-telegram-bot`` was originally created by
|
||||
`Leandro Toledo <https://github.com/leandrotoledo>`_ and is now maintained by
|
||||
`Jannes Höke <https://github.com/jh0ker>`_ (`@jh0ker <https://t.me/jh0ker>`_ on Telegram) and
|
||||
`Noam Meltzer <https://github.com/tsnoam>`_.
|
||||
|
||||
We're vendoring urllib3 as part of ``python-telegram-bot`` which is distributed under the MIT
|
||||
license. For more info, full credits & license terms, the sources can be found here:
|
||||
`https://github.com/python-telegram-bot/urllib3`.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
@@ -9,15 +16,22 @@ Contributors
|
||||
The following wonderful people contributed directly or indirectly to this project:
|
||||
|
||||
- `Avanatiker <https://github.com/Avanatiker>`_
|
||||
- `Anton Tagunov <https://github.com/anton-tagunov>`_
|
||||
- `Balduro <https://github.com/Balduro>`_
|
||||
- `bimmlerd <https://github.com/bimmlerd>`_
|
||||
- `d-qoi <https://github.com/d-qoi>`_
|
||||
- `daimajia <https://github.com/daimajia>`_
|
||||
- `Eli Gao <https://github.com/eligao>`_
|
||||
- `ErgoZ Riftbit Vaper <https://github.com/ergoz>`_
|
||||
- `Eugene Lisitsky <https://github.com/lisitsky>`_
|
||||
- `evgfilim1 <https://github.com/evgfilim1>`_
|
||||
- `franciscod <https://github.com/franciscod>`_
|
||||
- `Hugo Damer <https://github.com/HakimusGIT>`_
|
||||
- `Jacob Bom <https://github.com/bomjacob>`_
|
||||
- `JASON0916 <https://github.com/JASON0916>`_
|
||||
- `jh0ker <https://github.com/jh0ker>`_
|
||||
- `John Yong <https://github.com/whipermr5>`_
|
||||
- `jossalgon <https://github.com/jossalgon>`_
|
||||
- `JRoot3D <https://github.com/JRoot3D>`_
|
||||
- `jlmadurga <https://github.com/jlmadurga>`_
|
||||
- `Kjwon15 <https://github.com/kjwon15>`_
|
||||
@@ -25,14 +39,18 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||
- `macrojames <https://github.com/macrojames>`_
|
||||
- `Michael Elovskikh <https://github.com/wronglink>`_
|
||||
- `naveenvhegde <https://github.com/naveenvhegde>`_
|
||||
- `neurrone <https://github.com/neurrone>`_
|
||||
- `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>`_
|
||||
- `Pieter Schutz <https://github.com/eldinnie>`_
|
||||
- `Rahiel Kasim <https://github.com/rahiel>`_
|
||||
- `Joscha Götzer <https://github.com/Rostgnom>`_
|
||||
- `Shelomentsev D <https://github.com/shelomentsevd>`_
|
||||
- `sooyhwang <https://github.com/sooyhwang>`_
|
||||
- `thodnev <https://github.com/thodnev>`_
|
||||
- `Valentijn <https://github.com/Faalentijn>`_
|
||||
- `voider1 <https://github.com/voider1>`_
|
||||
- `wjt <https://github.com/wjt>`_
|
||||
|
||||
+27
@@ -2,6 +2,33 @@
|
||||
Changes
|
||||
=======
|
||||
|
||||
**2017-05-19**
|
||||
|
||||
*Released 6.0.0*
|
||||
|
||||
- Add support for Bot API 2.3.1
|
||||
- Add support for ``deleteMessage`` API method
|
||||
- New, simpler API for ``JobQueue`` - https://github.com/python-telegram-bot/python-telegram-bot/pull/484
|
||||
- Download files into file-like objects - https://github.com/python-telegram-bot/python-telegram-bot/pull/459
|
||||
- Use vendor ``urllib3`` to address issues with timeouts
|
||||
- The default timeout for messages is now 5 seconds. For sending media, the default timeout is now 20 seconds.
|
||||
- String attributes that are not set are now ``None`` by default, instead of empty strings
|
||||
- Add ``text_markdown`` and ``text_html`` properties to ``Message`` - https://github.com/python-telegram-bot/python-telegram-bot/pull/507
|
||||
- Add support for Socks5 proxy - https://github.com/python-telegram-bot/python-telegram-bot/pull/518
|
||||
- Add support for filters in ``CommandHandler`` - https://github.com/python-telegram-bot/python-telegram-bot/pull/536
|
||||
- Add the ability to invert (not) filters - https://github.com/python-telegram-bot/python-telegram-bot/pull/552
|
||||
- Add ``Filters.group`` and ``Filters.private``
|
||||
- Compatibility with GAE via ``urllib3.contrib`` package - https://github.com/python-telegram-bot/python-telegram-bot/pull/583
|
||||
- Add equality rich comparision operators to telegram objects - https://github.com/python-telegram-bot/python-telegram-bot/pull/604
|
||||
- Several bugfixes and other improvements
|
||||
- Remove some deprecated code
|
||||
|
||||
**2017-04-17**
|
||||
|
||||
*Released 5.3.1*
|
||||
|
||||
- Hotfix release due to bug introduced by urllib3 version 1.21
|
||||
|
||||
**2016-12-11**
|
||||
|
||||
*Released 5.3*
|
||||
|
||||
+8
-2
@@ -84,7 +84,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 **4. Dec 2016**, all types and methods of the Telegram Bot API are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
@@ -100,9 +100,15 @@ Or you can install from source with:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
$ git clone https://github.com/python-telegram-bot/python-telegram-bot
|
||||
$ git clone https://github.com/python-telegram-bot/python-telegram-bot --recursive
|
||||
$ cd python-telegram-bot
|
||||
$ python setup.py install
|
||||
|
||||
In case you have a previously cloned local repository already, you should initialize the added urllib3 submodule before installing with:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
$ git submodule update --init --recursive
|
||||
|
||||
===============
|
||||
Getting started
|
||||
|
||||
+3
-3
@@ -51,7 +51,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Python Telegram Bot'
|
||||
copyright = u'2015-2016, Leandro Toledo'
|
||||
copyright = u'2015-2017, Leandro Toledo'
|
||||
author = u'Leandro Toledo'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
@@ -59,9 +59,9 @@ author = u'Leandro Toledo'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '5.3' # telegram.__version__[:3]
|
||||
version = '6.0' # telegram.__version__[:3]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '5.3.0' # telegram.__version__
|
||||
release = '6.0.0' # telegram.__version__
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.ext.messagequeue module
|
||||
================================
|
||||
|
||||
.. automodule:: telegram.ext.messagequeue
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -16,6 +16,7 @@ Submodules
|
||||
telegram.ext.commandhandler
|
||||
telegram.ext.inlinequeryhandler
|
||||
telegram.ext.messagehandler
|
||||
telegram.ext.messagequeue
|
||||
telegram.ext.filters
|
||||
telegram.ext.regexhandler
|
||||
telegram.ext.stringcommandhandler
|
||||
|
||||
@@ -56,7 +56,7 @@ def gender(bot, update):
|
||||
|
||||
def photo(bot, update):
|
||||
user = update.message.from_user
|
||||
photo_file = bot.getFile(update.message.photo[-1].file_id)
|
||||
photo_file = bot.get_file(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, '
|
||||
@@ -149,7 +149,7 @@ def main():
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
|
||||
# Run the bot until the you presses Ctrl-C or the process receives SIGINT,
|
||||
# Run the bot until you press Ctrl-C or the process receives SIGINT,
|
||||
# SIGTERM or SIGABRT. This should be used most of the time, since
|
||||
# start_polling() is non-blocking and will stop the bot gracefully.
|
||||
updater.idle()
|
||||
|
||||
@@ -142,7 +142,7 @@ def main():
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
|
||||
# Run the bot until the you presses Ctrl-C or the process receives SIGINT,
|
||||
# Run the bot until you press Ctrl-C or the process receives SIGINT,
|
||||
# SIGTERM or SIGABRT. This should be used most of the time, since
|
||||
# start_polling() is non-blocking and will stop the bot gracefully.
|
||||
updater.idle()
|
||||
|
||||
+2
-4
@@ -20,7 +20,7 @@ def main():
|
||||
# get the first pending update_id, this is so we can skip over it in case
|
||||
# we get an "Unauthorized" exception.
|
||||
try:
|
||||
update_id = bot.getUpdates()[0].update_id
|
||||
update_id = bot.get_updates()[0].update_id
|
||||
except IndexError:
|
||||
update_id = None
|
||||
|
||||
@@ -39,9 +39,7 @@ def main():
|
||||
def echo(bot):
|
||||
global update_id
|
||||
# Request updates after the last update_id
|
||||
for update in bot.getUpdates(offset=update_id, timeout=10):
|
||||
# chat_id is required to reply to any message
|
||||
chat_id = update.message.chat_id
|
||||
for update in bot.get_updates(offset=update_id, timeout=10):
|
||||
update_id = update.update_id + 1
|
||||
|
||||
if update.message: # your bot can receive updates without messages
|
||||
|
||||
@@ -64,7 +64,7 @@ def main():
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
|
||||
# Run the bot until the you presses Ctrl-C or the process receives SIGINT,
|
||||
# Run the bot until you press Ctrl-C or the process receives SIGINT,
|
||||
# SIGTERM or SIGABRT. This should be used most of the time, since
|
||||
# start_polling() is non-blocking and will stop the bot gracefully.
|
||||
updater.idle()
|
||||
|
||||
@@ -72,7 +72,7 @@ def inlinequery(bot, update):
|
||||
|
||||
|
||||
def error(bot, update, error):
|
||||
logger.warn('Update "%s" caused error "%s"' % (update, error))
|
||||
logger.warning('Update "%s" caused error "%s"' % (update, error))
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -26,9 +26,9 @@ def start(bot, update):
|
||||
def button(bot, update):
|
||||
query = update.callback_query
|
||||
|
||||
bot.editMessageText(text="Selected option: %s" % query.data,
|
||||
chat_id=query.message.chat_id,
|
||||
message_id=query.message.message_id)
|
||||
bot.edit_message_text(text="Selected option: %s" % query.data,
|
||||
chat_id=query.message.chat_id,
|
||||
message_id=query.message.message_id)
|
||||
|
||||
|
||||
def help(bot, update):
|
||||
|
||||
+18
-18
@@ -22,10 +22,9 @@ 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()
|
||||
|
||||
|
||||
# Define a few command handlers. These usually take the two arguments bot and
|
||||
@@ -36,10 +35,10 @@ def start(bot, update):
|
||||
|
||||
def alarm(bot, job):
|
||||
"""Function to send the alarm message"""
|
||||
bot.sendMessage(job.context, text='Beep!')
|
||||
bot.send_message(job.context, text='Beep!')
|
||||
|
||||
|
||||
def set(bot, update, args, job_queue):
|
||||
def set(bot, update, args, job_queue, chat_data):
|
||||
"""Adds a job to the queue"""
|
||||
chat_id = update.message.chat_id
|
||||
try:
|
||||
@@ -50,9 +49,8 @@ def set(bot, update, args, job_queue):
|
||||
return
|
||||
|
||||
# Add job to queue
|
||||
job = Job(alarm, due, repeat=False, context=chat_id)
|
||||
timers[chat_id] = job
|
||||
job_queue.put(job)
|
||||
job = job_queue.run_once(alarm, due, context=chat_id)
|
||||
chat_data['job'] = job
|
||||
|
||||
update.message.reply_text('Timer successfully set!')
|
||||
|
||||
@@ -60,23 +58,22 @@ def set(bot, update, args, job_queue):
|
||||
update.message.reply_text('Usage: /set <seconds>')
|
||||
|
||||
|
||||
def unset(bot, update):
|
||||
def unset(bot, update, chat_data):
|
||||
"""Removes the job if the user changed their mind"""
|
||||
chat_id = update.message.chat_id
|
||||
|
||||
if chat_id not in timers:
|
||||
if 'job' not in chat_data:
|
||||
update.message.reply_text('You have no active timer')
|
||||
return
|
||||
|
||||
job = timers[chat_id]
|
||||
job = chat_data['job']
|
||||
job.schedule_removal()
|
||||
del timers[chat_id]
|
||||
del chat_data['job']
|
||||
|
||||
update.message.reply_text('Timer successfully unset!')
|
||||
|
||||
|
||||
def error(bot, update, error):
|
||||
logger.warn('Update "%s" caused error "%s"' % (update, error))
|
||||
logger.warning('Update "%s" caused error "%s"' % (update, error))
|
||||
|
||||
|
||||
def main():
|
||||
@@ -88,8 +85,11 @@ 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,
|
||||
pass_job_queue=True,
|
||||
pass_chat_data=True))
|
||||
dp.add_handler(CommandHandler("unset", unset, pass_chat_data=True))
|
||||
|
||||
# log all errors
|
||||
dp.add_error_handler(error)
|
||||
@@ -97,9 +97,9 @@ def main():
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
|
||||
# Block 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.
|
||||
# Block until you press Ctrl-C or the process receives SIGINT, SIGTERM or
|
||||
# SIGABRT. This should be used most of the time, since start_polling() is
|
||||
# non-blocking and will stop the bot gracefully.
|
||||
updater.idle()
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
future>=0.15.2
|
||||
urllib3>=1.10
|
||||
certifi
|
||||
|
||||
@@ -17,6 +17,13 @@ def requirements():
|
||||
return requirements_list
|
||||
|
||||
|
||||
packages = find_packages(exclude=['tests*'])
|
||||
packages.extend(['telegram.vendor.urllib3.urllib3',
|
||||
'telegram.vendor.urllib3.urllib3.packages', 'telegram.vendor.urllib3.urllib3.packages.ssl_match_hostname',
|
||||
'telegram.vendor.urllib3.urllib3.packages.backports', 'telegram.vendor.urllib3.urllib3.contrib',
|
||||
'telegram.vendor.urllib3.urllib3.util',
|
||||
])
|
||||
|
||||
with codecs.open('README.rst', 'r', 'utf-8') as fd:
|
||||
fn = os.path.join('telegram', 'version.py')
|
||||
with open(fn) as fh:
|
||||
@@ -32,10 +39,11 @@ with codecs.open('README.rst', 'r', 'utf-8') as fd:
|
||||
keywords='python telegram bot api wrapper',
|
||||
description="We have made you a wrapper you can't refuse",
|
||||
long_description=fd.read(),
|
||||
packages=find_packages(exclude=['tests*']),
|
||||
packages=packages,
|
||||
install_requires=requirements(),
|
||||
extras_require={
|
||||
'json': 'ujson',
|
||||
'socks': 'PySocks'
|
||||
},
|
||||
include_package_data=True,
|
||||
classifiers=[
|
||||
@@ -53,4 +61,5 @@ with codecs.open('README.rst', 'r', 'utf-8') as fd:
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6'
|
||||
],)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -19,6 +19,10 @@
|
||||
"""A library that provides a Python interface to the Telegram Bot API"""
|
||||
|
||||
from sys import version_info
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'vendor', 'urllib3'))
|
||||
|
||||
from .base import TelegramObject
|
||||
from .user import User
|
||||
|
||||
+16
-6
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -35,12 +35,20 @@ class Animation(TelegramObject):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, file_id, **kwargs):
|
||||
def __init__(self,
|
||||
file_id,
|
||||
thumb=None,
|
||||
file_name=None,
|
||||
mime_type=None,
|
||||
file_size=None,
|
||||
**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')
|
||||
self.thumb = thumb
|
||||
self.file_name = file_name
|
||||
self.mime_type = mime_type
|
||||
self.file_size = file_size
|
||||
|
||||
self._id_attrs = (self.file_id,)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
@@ -55,6 +63,8 @@ class Animation(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Animation, Animation).de_json(data, bot)
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
|
||||
return Animation(**data)
|
||||
|
||||
+9
-7
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -46,10 +46,10 @@ class Audio(TelegramObject):
|
||||
def __init__(self,
|
||||
file_id,
|
||||
duration,
|
||||
performer='',
|
||||
title='',
|
||||
mime_type='',
|
||||
file_size=0,
|
||||
performer=None,
|
||||
title=None,
|
||||
mime_type=None,
|
||||
file_size=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
self.file_id = str(file_id)
|
||||
@@ -57,8 +57,10 @@ class Audio(TelegramObject):
|
||||
# Optionals
|
||||
self.performer = performer
|
||||
self.title = title
|
||||
self.mime_type = str(mime_type)
|
||||
self.file_size = int(file_size)
|
||||
self.mime_type = mime_type
|
||||
self.file_size = file_size
|
||||
|
||||
self._id_attrs = (self.file_id,)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
|
||||
+13
-3
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -28,8 +28,8 @@ from abc import ABCMeta
|
||||
|
||||
class TelegramObject(object):
|
||||
"""Base class for most telegram objects."""
|
||||
|
||||
__metaclass__ = ABCMeta
|
||||
_id_attrs = ()
|
||||
|
||||
def __str__(self):
|
||||
return str(self.to_dict())
|
||||
@@ -69,7 +69,7 @@ class TelegramObject(object):
|
||||
data = dict()
|
||||
|
||||
for key in iter(self.__dict__):
|
||||
if key == 'bot':
|
||||
if key in ('bot', '_id_attrs'):
|
||||
continue
|
||||
|
||||
value = self.__dict__[key]
|
||||
@@ -80,3 +80,13 @@ class TelegramObject(object):
|
||||
data[key] = value
|
||||
|
||||
return data
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, self.__class__):
|
||||
return self._id_attrs == other._id_attrs
|
||||
return super(TelegramObject, self).__eq__(other)
|
||||
|
||||
def __hash__(self):
|
||||
if self._id_attrs:
|
||||
return hash((self.__class__, self._id_attrs))
|
||||
return super(TelegramObject, self).__hash__()
|
||||
|
||||
+610
-378
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -60,6 +60,8 @@ class CallbackQuery(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(CallbackQuery, CallbackQuery).de_json(data, bot)
|
||||
|
||||
data['from_user'] = User.de_json(data.get('from'), bot)
|
||||
data['message'] = Message.de_json(data.get('message'), bot)
|
||||
|
||||
|
||||
+9
-8
@@ -2,7 +2,7 @@
|
||||
# pylint: disable=C0103,W0622
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -32,7 +32,7 @@ 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.
|
||||
all_members_are_administrators (bool): True if group has 'All Members Are Administrators'
|
||||
|
||||
Args:
|
||||
id (int):
|
||||
@@ -53,11 +53,11 @@ class Chat(TelegramObject):
|
||||
def __init__(self,
|
||||
id,
|
||||
type,
|
||||
title='',
|
||||
username='',
|
||||
first_name='',
|
||||
last_name='',
|
||||
all_members_are_admins=False,
|
||||
title=None,
|
||||
username=None,
|
||||
first_name=None,
|
||||
last_name=None,
|
||||
all_members_are_administrators=None,
|
||||
bot=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
@@ -68,9 +68,10 @@ class Chat(TelegramObject):
|
||||
self.username = username
|
||||
self.first_name = first_name
|
||||
self.last_name = last_name
|
||||
self.all_members_are_admins = all_members_are_admins
|
||||
self.all_members_are_administrators = all_members_are_administrators
|
||||
|
||||
self.bot = bot
|
||||
self._id_attrs = (self.id,)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# pylint: disable=R0903
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -46,6 +46,8 @@ class ChatMember(TelegramObject):
|
||||
self.user = user
|
||||
self.status = status
|
||||
|
||||
self._id_attrs = (self.user, self.status)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
@@ -59,6 +61,8 @@ class ChatMember(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(ChatMember, ChatMember).de_json(data, bot)
|
||||
|
||||
data['user'] = User.de_json(data.get('user'), bot)
|
||||
|
||||
return ChatMember(**data)
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
# pylint: disable=R0902,R0912,R0913
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015 Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
# Copyright (C) 2015-2017
|
||||
# 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
|
||||
@@ -61,6 +62,8 @@ class ChosenInlineResult(TelegramObject):
|
||||
self.location = location
|
||||
self.inline_message_id = inline_message_id
|
||||
|
||||
self._id_attrs = (self.result_id,)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
@@ -74,7 +77,8 @@ class ChosenInlineResult(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
# Required
|
||||
data = super(ChosenInlineResult, ChosenInlineResult).de_json(data, bot)
|
||||
# Required
|
||||
data['from_user'] = User.de_json(data.pop('from'), bot)
|
||||
# Optionals
|
||||
data['location'] = Location.de_json(data.get('location'), bot)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# python-telegram-bot - a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# by the python-telegram-bot contributors <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -32,6 +32,7 @@ Attributes:
|
||||
limit, but eventually you'll begin receiving 429 errors.
|
||||
MAX_MESSAGES_PER_SECOND (int)
|
||||
MAX_MESSAGES_PER_MINUTE_PER_GROUP (int)
|
||||
MAX_INLINE_QUERY_RESULTS (int)
|
||||
|
||||
The following constant have been found by experimentation:
|
||||
|
||||
@@ -52,3 +53,4 @@ MAX_MESSAGES_PER_SECOND_PER_CHAT = 1
|
||||
MAX_MESSAGES_PER_SECOND = 30
|
||||
MAX_MESSAGES_PER_MINUTE_PER_GROUP = 20
|
||||
MAX_MESSAGE_ENTITIES = 100
|
||||
MAX_INLINE_QUERY_RESULTS = 50
|
||||
|
||||
+5
-3
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -39,13 +39,15 @@ class Contact(TelegramObject):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, phone_number, first_name, last_name='', user_id=0, **kwargs):
|
||||
def __init__(self, phone_number, first_name, last_name=None, user_id=None, **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.user_id = user_id
|
||||
|
||||
self._id_attrs = (self.phone_number,)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
|
||||
+16
-4
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -41,14 +41,24 @@ class Document(TelegramObject):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, file_id, thumb=None, file_name='', mime_type='', file_size=0, **kwargs):
|
||||
_id_keys = ('file_id',)
|
||||
|
||||
def __init__(self,
|
||||
file_id,
|
||||
thumb=None,
|
||||
file_name=None,
|
||||
mime_type=None,
|
||||
file_size=None,
|
||||
**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.mime_type = mime_type
|
||||
self.file_size = file_size
|
||||
|
||||
self._id_attrs = (self.file_id,)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
@@ -63,6 +73,8 @@ class Document(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Document, Document).de_json(data, bot)
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
|
||||
return Document(**data)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
# pylint: disable=C0103,R0903,E0213
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
+2
-4
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -62,9 +62,7 @@ class TelegramError(Exception):
|
||||
|
||||
|
||||
class Unauthorized(TelegramError):
|
||||
|
||||
def __init__(self):
|
||||
super(Unauthorized, self).__init__('Unauthorized')
|
||||
pass
|
||||
|
||||
|
||||
class InvalidToken(TelegramError):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -23,7 +23,6 @@ import re
|
||||
from future.utils import string_types
|
||||
|
||||
from telegram import Update
|
||||
from telegram.utils.deprecate import deprecate
|
||||
from .handler import Handler
|
||||
|
||||
|
||||
@@ -105,8 +104,3 @@ class CallbackQueryHandler(Handler):
|
||||
optional_args['groupdict'] = match.groupdict()
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.CallbackQueryHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -17,10 +17,10 @@
|
||||
# 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 CommandHandler class """
|
||||
import warnings
|
||||
|
||||
from .handler import Handler
|
||||
from telegram import Update
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class CommandHandler(Handler):
|
||||
@@ -30,10 +30,15 @@ class CommandHandler(Handler):
|
||||
name and/or some additional text.
|
||||
|
||||
Args:
|
||||
command (str): The name of the command this handler should listen for.
|
||||
command (str|list): The name of the command or list of command this handler should
|
||||
listen for.
|
||||
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.
|
||||
filters (telegram.ext.BaseFilter): A filter inheriting from
|
||||
:class:`telegram.ext.filters.BaseFilter`. Standard filters can be found in
|
||||
:class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise
|
||||
operators (& for and, | for or).
|
||||
allow_edited (Optional[bool]): If the handler should also accept edited messages.
|
||||
Default is ``False``
|
||||
pass_args (optional[bool]): If the handler should be passed the
|
||||
@@ -62,6 +67,7 @@ class CommandHandler(Handler):
|
||||
def __init__(self,
|
||||
command,
|
||||
callback,
|
||||
filters=None,
|
||||
allow_edited=False,
|
||||
pass_args=False,
|
||||
pass_update_queue=False,
|
||||
@@ -74,17 +80,47 @@ class CommandHandler(Handler):
|
||||
pass_job_queue=pass_job_queue,
|
||||
pass_user_data=pass_user_data,
|
||||
pass_chat_data=pass_chat_data)
|
||||
self.command = command
|
||||
try:
|
||||
_str = basestring # Python 2
|
||||
except NameError:
|
||||
_str = str # Python 3
|
||||
|
||||
if isinstance(command, _str):
|
||||
self.command = [command]
|
||||
else:
|
||||
self.command = command
|
||||
self.filters = filters
|
||||
self.allow_edited = allow_edited
|
||||
self.pass_args = pass_args
|
||||
|
||||
# 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)):
|
||||
message = update.message or update.edited_message
|
||||
|
||||
return (message.text and message.text.startswith('/')
|
||||
and message.text[1:].split(' ')[0].split('@')[0] == self.command)
|
||||
if message.text:
|
||||
command = message.text[1:].split(' ')[0].split('@')
|
||||
command.append(
|
||||
message.bot.username) # in case the command was send without a username
|
||||
|
||||
if self.filters is None:
|
||||
res = True
|
||||
elif isinstance(self.filters, list):
|
||||
res = any(func(message) for func in self.filters)
|
||||
else:
|
||||
res = self.filters(message)
|
||||
|
||||
return res and (message.text.startswith('/') and command[0] in self.command
|
||||
and command[1].lower() == message.bot.username.lower())
|
||||
else:
|
||||
return False
|
||||
|
||||
else:
|
||||
return False
|
||||
@@ -98,8 +134,3 @@ class CommandHandler(Handler):
|
||||
optional_args['args'] = message.text.split()[1:]
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.CommandHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -21,14 +21,16 @@
|
||||
import logging
|
||||
|
||||
from telegram import Update
|
||||
from telegram.ext import Handler
|
||||
from telegram.utils.helpers import extract_chat_and_user
|
||||
from telegram.ext import (Handler, CallbackQueryHandler, InlineQueryHandler,
|
||||
ChosenInlineResultHandler)
|
||||
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.
|
||||
A handler to hold a conversation with a single user by managing four collections of other
|
||||
handlers. Note that neither posts in Telegram Channels, nor group interactions with multiple
|
||||
users are managed by instances of this class.
|
||||
|
||||
The first collection, a ``list`` named ``entry_points``, is used to initiate the conversation,
|
||||
for example with a ``CommandHandler`` or ``RegexHandler``.
|
||||
@@ -86,7 +88,10 @@ class ConversationHandler(Handler):
|
||||
fallbacks,
|
||||
allow_reentry=False,
|
||||
run_async_timeout=None,
|
||||
timed_out_behavior=None):
|
||||
timed_out_behavior=None,
|
||||
per_chat=True,
|
||||
per_user=True,
|
||||
per_message=False):
|
||||
|
||||
self.entry_points = entry_points
|
||||
""":type: list[telegram.ext.Handler]"""
|
||||
@@ -104,21 +109,76 @@ class ConversationHandler(Handler):
|
||||
""":type: list[telegram.ext.Handler]"""
|
||||
|
||||
self.conversations = dict()
|
||||
""":type: dict[(int, int): str]"""
|
||||
self.per_user = per_user
|
||||
self.per_chat = per_chat
|
||||
self.per_message = per_message
|
||||
""":type: dict[tuple: object]"""
|
||||
|
||||
self.current_conversation = None
|
||||
self.current_handler = None
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
if not any((self.per_user, self.per_chat, self.per_message)):
|
||||
raise ValueError("'per_user', 'per_chat' and 'per_message' can't all be 'False'")
|
||||
|
||||
if self.per_message and not self.per_chat:
|
||||
logging.warning("If 'per_message=True' is used, 'per_chat=True' should also be used, "
|
||||
"since message IDs are not globally unique.")
|
||||
|
||||
all_handlers = list()
|
||||
all_handlers.extend(entry_points)
|
||||
all_handlers.extend(fallbacks)
|
||||
|
||||
for state_handlers in states.values():
|
||||
all_handlers.extend(state_handlers)
|
||||
|
||||
if self.per_message:
|
||||
for handler in all_handlers:
|
||||
if not isinstance(handler, CallbackQueryHandler):
|
||||
logging.warning("If 'per_message=True', all entry points and state handlers"
|
||||
" must be 'CallbackQueryHandler', since no other handlers "
|
||||
"have a message context.")
|
||||
else:
|
||||
for handler in all_handlers:
|
||||
if isinstance(handler, CallbackQueryHandler):
|
||||
logging.warning("If 'per_message=False', 'CallbackQueryHandler' will not be "
|
||||
"tracked for every message.")
|
||||
|
||||
if self.per_chat:
|
||||
for handler in all_handlers:
|
||||
if isinstance(handler, (InlineQueryHandler, ChosenInlineResultHandler)):
|
||||
logging.warning("If 'per_chat=True', 'InlineQueryHandler' can not be used, "
|
||||
"since inline queries have no chat context.")
|
||||
|
||||
def _get_key(self, update):
|
||||
chat = update.effective_chat
|
||||
user = update.effective_user
|
||||
|
||||
key = list()
|
||||
|
||||
if self.per_chat:
|
||||
key.append(chat.id)
|
||||
|
||||
if self.per_user:
|
||||
key.append(user.id)
|
||||
|
||||
if self.per_message:
|
||||
key.append(update.callback_query.inline_message_id
|
||||
or update.callback_query.message.message_id)
|
||||
|
||||
return tuple(key)
|
||||
|
||||
def check_update(self, update):
|
||||
|
||||
if not isinstance(update, Update):
|
||||
# Ignore messages in channels
|
||||
if (not isinstance(update, Update) or update.channel_post or self.per_chat
|
||||
and (update.inline_query or update.chosen_inline_result) or self.per_message
|
||||
and not update.callback_query or update.callback_query and self.per_chat
|
||||
and not update.callback_query.message):
|
||||
return False
|
||||
|
||||
chat, user = extract_chat_and_user(update)
|
||||
|
||||
key = (chat.id, user.id) if chat else (None, user.id)
|
||||
key = self._get_key(update)
|
||||
state = self.conversations.get(key)
|
||||
|
||||
# Resolve promises
|
||||
@@ -126,10 +186,16 @@ class ConversationHandler(Handler):
|
||||
self.logger.debug('waiting for promise...')
|
||||
|
||||
old_state, new_state = state
|
||||
new_state.result(timeout=self.run_async_timeout)
|
||||
error = False
|
||||
try:
|
||||
res = new_state.result(timeout=self.run_async_timeout)
|
||||
except Exception as exc:
|
||||
self.logger.exception("Promise function raised exception")
|
||||
self.logger.exception("{}".format(exc))
|
||||
error = True
|
||||
|
||||
if new_state.done.is_set():
|
||||
self.update_state(new_state.result(), key)
|
||||
if not error and new_state.done.is_set():
|
||||
self.update_state(res, key)
|
||||
state = self.conversations.get(key)
|
||||
|
||||
else:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -32,7 +32,6 @@ from future.builtins import range
|
||||
|
||||
from telegram import TelegramError
|
||||
from telegram.ext.handler import Handler
|
||||
from telegram.utils.deprecate import deprecate
|
||||
from telegram.utils.promise import Promise
|
||||
|
||||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||
@@ -159,11 +158,7 @@ class Dispatcher(object):
|
||||
len(self.__async_threads))
|
||||
break
|
||||
|
||||
try:
|
||||
promise.run()
|
||||
|
||||
except:
|
||||
self.logger.exception("run_async function raised exception")
|
||||
promise.run()
|
||||
|
||||
def run_async(self, func, *args, **kwargs):
|
||||
"""Queue a function (with given args/kwargs) to be run asynchronously.
|
||||
@@ -372,11 +367,3 @@ class Dispatcher(object):
|
||||
|
||||
for callback in self.error_handlers:
|
||||
callback(self.bot, update, error)
|
||||
|
||||
# old non-PEP8 Dispatcher methods
|
||||
m = "telegram.dispatcher."
|
||||
addHandler = deprecate(add_handler, m + "AddHandler", m + "add_handler")
|
||||
removeHandler = deprecate(remove_handler, m + "removeHandler", m + "remove_handler")
|
||||
addErrorHandler = deprecate(add_error_handler, m + "addErrorHandler", m + "add_error_handler")
|
||||
removeErrorHandler = deprecate(remove_error_handler, m + "removeErrorHandler",
|
||||
m + "remove_error_handler")
|
||||
|
||||
+43
-1
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -17,6 +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 the Filters for use with the MessageHandler class """
|
||||
from telegram import Chat
|
||||
|
||||
|
||||
class BaseFilter(object):
|
||||
@@ -32,9 +33,14 @@ class BaseFilter(object):
|
||||
|
||||
>>> (Filters.audio | Filters.video)
|
||||
|
||||
Not:
|
||||
|
||||
>>> ~ Filters.command
|
||||
|
||||
Also works with more than two filters:
|
||||
|
||||
>>> (Filters.text & (Filters.entity(URL) | Filters.entity(TEXT_LINK)))
|
||||
>>> Filters.text & (~ Filters.forwarded)
|
||||
|
||||
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`
|
||||
@@ -51,10 +57,32 @@ class BaseFilter(object):
|
||||
def __or__(self, other):
|
||||
return MergedFilter(self, or_filter=other)
|
||||
|
||||
def __invert__(self):
|
||||
return InvertedFilter(self)
|
||||
|
||||
def filter(self, message):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class InvertedFilter(BaseFilter):
|
||||
"""Represents a filter that has been inverted.
|
||||
|
||||
Args:
|
||||
f: The filter to invert
|
||||
"""
|
||||
|
||||
def __init__(self, f):
|
||||
self.f = f
|
||||
|
||||
def filter(self, message):
|
||||
return not self.f(message)
|
||||
|
||||
def __str__(self):
|
||||
return "<telegram.ext.filters.InvertedFilter inverting {}>".format(self.f)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
class MergedFilter(BaseFilter):
|
||||
"""Represents a filter consisting of two other filters.
|
||||
|
||||
@@ -221,3 +249,17 @@ class Filters(object):
|
||||
|
||||
def filter(self, message):
|
||||
return any([entity.type == self.entity_type for entity in message.entities])
|
||||
|
||||
class _Private(BaseFilter):
|
||||
|
||||
def filter(self, message):
|
||||
return message.chat.type == Chat.PRIVATE
|
||||
|
||||
private = _Private()
|
||||
|
||||
class _Group(BaseFilter):
|
||||
|
||||
def filter(self, message):
|
||||
return message.chat.type in [Chat.GROUP, Chat.SUPERGROUP]
|
||||
|
||||
group = _Group()
|
||||
|
||||
+4
-13
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -19,9 +19,6 @@
|
||||
""" This module contains the base class for handlers as used by the
|
||||
Dispatcher """
|
||||
|
||||
from telegram.utils.deprecate import deprecate
|
||||
from telegram.utils.helpers import extract_chat_and_user
|
||||
|
||||
|
||||
class Handler(object):
|
||||
"""
|
||||
@@ -105,19 +102,13 @@ class Handler(object):
|
||||
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)
|
||||
chat = update.effective_chat
|
||||
user = update.effective_user
|
||||
|
||||
if self.pass_user_data:
|
||||
optional_args['user_data'] = dispatcher.user_data[user.id]
|
||||
optional_args['user_data'] = dispatcher.user_data[user.id if user else None]
|
||||
|
||||
if self.pass_chat_data:
|
||||
optional_args['chat_data'] = dispatcher.chat_data[chat.id if chat else None]
|
||||
|
||||
return optional_args
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.Handler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
collectOptionalArgs = deprecate(collect_optional_args, m + "collectOptionalArgs",
|
||||
m + "collect_optional_args")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
+275
-53
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -22,6 +22,7 @@ import logging
|
||||
import time
|
||||
import warnings
|
||||
import datetime
|
||||
import weakref
|
||||
from numbers import Number
|
||||
from threading import Thread, Lock, Event
|
||||
from queue import PriorityQueue, Empty
|
||||
@@ -68,40 +69,182 @@ class JobQueue(object):
|
||||
|
||||
Args:
|
||||
job (telegram.ext.Job): The ``Job`` instance representing the new job
|
||||
next_t (Optional[int, float, datetime.timedelta]): Time in which the job
|
||||
should be executed first. Defaults to ``job.interval``. ``int`` and ``float``
|
||||
will be interpreted as seconds.
|
||||
next_t (Optional[int, float, datetime.timedelta, datetime.datetime, datetime.time]):
|
||||
Time in or at which the job should run for the first time. This parameter will be
|
||||
interpreted depending on its type.
|
||||
``int`` or ``float`` will be interpreted as "seconds from now" in which the job
|
||||
should run.
|
||||
``datetime.timedelta`` will be interpreted as "time from now" in which the job
|
||||
should run.
|
||||
``datetime.datetime`` will be interpreted as a specific date and time at which the
|
||||
job should run.
|
||||
``datetime.time`` will be interpreted as a specific time at which the job should
|
||||
run. This could be either today or, if the time has already passed, tomorrow.
|
||||
"""
|
||||
warnings.warn("'JobQueue.put' is being deprecated, use 'JobQueue.run_once', "
|
||||
"'JobQueue.run_daily' or 'JobQueue.run_repeating' instead")
|
||||
if job.job_queue is None:
|
||||
job.job_queue = self
|
||||
self._put(job, next_t=next_t)
|
||||
|
||||
def _put(self, job, next_t=None, last_t=None):
|
||||
"""Queue a new job.
|
||||
|
||||
Args:
|
||||
job (telegram.ext.Job): The ``Job`` instance representing the new job
|
||||
next_t (Optional[int, float, datetime.timedelta, datetime.datetime, datetime.time]):
|
||||
Time in or at which the job should run for the first time. This parameter will be
|
||||
interpreted depending on its type.
|
||||
|
||||
* ``int`` or ``float`` will be interpreted as "seconds from now" in which the job
|
||||
should run.
|
||||
* ``datetime.timedelta`` will be interpreted as "time from now" in which the job
|
||||
should run.
|
||||
* ``datetime.datetime`` will be interpreted as a specific date and time at which
|
||||
the job should run.
|
||||
* ``datetime.time`` will be interpreted as a specific time of day at which the job
|
||||
should run. This could be either today or, if the time has already passed,
|
||||
tomorrow.
|
||||
last_t (Optional[float]): Timestamp of the time when ``job`` was scheduled for in the
|
||||
last ``put`` call. If provided, it will be used to calculate the next timestamp
|
||||
more accurately by accounting for the execution time of the job (and possibly
|
||||
others). If None, `now` will be assumed.
|
||||
|
||||
"""
|
||||
job.job_queue = self
|
||||
|
||||
if next_t is None:
|
||||
interval = job.interval
|
||||
next_t = job.interval
|
||||
if next_t is None:
|
||||
raise ValueError('next_t is None')
|
||||
|
||||
if isinstance(next_t, datetime.datetime):
|
||||
next_t = (next_t - datetime.datetime.now()).total_seconds()
|
||||
|
||||
elif isinstance(next_t, datetime.time):
|
||||
next_datetime = datetime.datetime.combine(datetime.date.today(), next_t)
|
||||
|
||||
if datetime.datetime.now().time() > next_t:
|
||||
next_datetime += datetime.timedelta(days=1)
|
||||
|
||||
next_t = (next_datetime - datetime.datetime.now()).total_seconds()
|
||||
|
||||
if isinstance(interval, Number):
|
||||
next_t = interval
|
||||
elif isinstance(interval, datetime.timedelta):
|
||||
next_t = interval.total_seconds()
|
||||
else:
|
||||
raise ValueError("The interval argument should be of type datetime.timedelta,"
|
||||
" int or float")
|
||||
elif isinstance(next_t, datetime.timedelta):
|
||||
next_t = next_t.total_second()
|
||||
next_t = next_t.total_seconds()
|
||||
|
||||
now = time.time()
|
||||
next_t += now
|
||||
next_t += last_t or time.time()
|
||||
|
||||
self.logger.debug('Putting job %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 run_once(self, callback, when, context=None, name=None):
|
||||
"""Creates a new ``Job`` that runs once and adds it to the queue.
|
||||
|
||||
Args:
|
||||
callback (function): The callback function that should be executed by the new job. It
|
||||
should take two parameters ``bot`` and ``job``, where ``job`` is the ``Job``
|
||||
instance. It can be used to access it's ``context`` or change it to a repeating
|
||||
job.
|
||||
when (int, float, datetime.timedelta, datetime.datetime, datetime.time):
|
||||
Time in or at which the job should run. This parameter will be interpreted
|
||||
depending on its type.
|
||||
|
||||
* ``int`` or ``float`` will be interpreted as "seconds from now" in which the job
|
||||
should run.
|
||||
* ``datetime.timedelta`` will be interpreted as "time from now" in which the job
|
||||
should run.
|
||||
* ``datetime.datetime`` will be interpreted as a specific date and time at which
|
||||
the job should run.
|
||||
* ``datetime.time`` will be interpreted as a specific time of day at which the job
|
||||
should run. This could be either today or, if the time has already passed,
|
||||
tomorrow.
|
||||
|
||||
context (Optional[object]): Additional data needed for the callback function. Can be
|
||||
accessed through ``job.context`` in the callback. Defaults to ``None``
|
||||
name (Optional[str]): The name of the new job. Defaults to ``callback.__name__``
|
||||
|
||||
Returns:
|
||||
Job: The new ``Job`` instance that has been added to the job queue.
|
||||
|
||||
"""
|
||||
job = Job(callback, repeat=False, context=context, name=name, job_queue=self)
|
||||
self._put(job, next_t=when)
|
||||
return job
|
||||
|
||||
def run_repeating(self, callback, interval, first=None, context=None, name=None):
|
||||
"""Creates a new ``Job`` that runs once and adds it to the queue.
|
||||
|
||||
Args:
|
||||
callback (function): The callback function that should be executed by the new job. It
|
||||
should take two parameters ``bot`` and ``job``, where ``job`` is the ``Job``
|
||||
instance. It can be used to access it's ``context``, terminate the job or change
|
||||
its interval.
|
||||
interval (int, float, datetime.timedelta): The interval in which the job will run.
|
||||
If it is an ``int`` or a ``float``, it will be interpreted as seconds.
|
||||
first (int, float, datetime.timedelta, datetime.datetime, datetime.time):
|
||||
|
||||
* ``int`` or ``float`` will be interpreted as "seconds from now" in which the job
|
||||
should run.
|
||||
* ``datetime.timedelta`` will be interpreted as "time from now" in which the job
|
||||
should run.
|
||||
* ``datetime.datetime`` will be interpreted as a specific date and time at which
|
||||
the job should run.
|
||||
* ``datetime.time`` will be interpreted as a specific time of day at which the job
|
||||
should run. This could be either today or, if the time has already passed,
|
||||
tomorrow.
|
||||
|
||||
Defaults to ``interval``
|
||||
context (Optional[object]): Additional data needed for the callback function. Can be
|
||||
accessed through ``job.context`` in the callback. Defaults to ``None``
|
||||
name (Optional[str]): The name of the new job. Defaults to ``callback.__name__``
|
||||
|
||||
Returns:
|
||||
Job: The new ``Job`` instance that has been added to the job queue.
|
||||
|
||||
"""
|
||||
job = Job(callback,
|
||||
interval=interval,
|
||||
repeat=True,
|
||||
context=context,
|
||||
name=name,
|
||||
job_queue=self)
|
||||
self._put(job, next_t=first)
|
||||
return job
|
||||
|
||||
def run_daily(self, callback, time, days=Days.EVERY_DAY, context=None, name=None):
|
||||
"""Creates a new ``Job`` that runs once and adds it to the queue.
|
||||
|
||||
Args:
|
||||
callback (function): The callback function that should be executed by the new job. It
|
||||
should take two parameters ``bot`` and ``job``, where ``job`` is the ``Job``
|
||||
instance. It can be used to access it's ``context`` or terminate the job.
|
||||
time (datetime.time): Time of day at which the job should run.
|
||||
days (Optional[tuple[int]]): Defines on which days of the week the job should run.
|
||||
Defaults to ``Days.EVERY_DAY``
|
||||
context (Optional[object]): Additional data needed for the callback function. Can be
|
||||
accessed through ``job.context`` in the callback. Defaults to ``None``
|
||||
name (Optional[str]): The name of the new job. Defaults to ``callback.__name__``
|
||||
|
||||
Returns:
|
||||
Job: The new ``Job`` instance that has been added to the job queue.
|
||||
|
||||
"""
|
||||
job = Job(callback,
|
||||
interval=datetime.timedelta(days=1),
|
||||
repeat=True,
|
||||
days=days,
|
||||
context=context,
|
||||
name=name,
|
||||
job_queue=self)
|
||||
self._put(job, next_t=time)
|
||||
return job
|
||||
|
||||
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:
|
||||
@@ -126,9 +269,9 @@ class JobQueue(object):
|
||||
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.
|
||||
# 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!")
|
||||
@@ -136,7 +279,7 @@ class JobQueue(object):
|
||||
self._set_next_peek(t)
|
||||
break
|
||||
|
||||
if job._remove.is_set():
|
||||
if job.removed:
|
||||
self.logger.debug('Removing job %s', job.name)
|
||||
continue
|
||||
|
||||
@@ -146,15 +289,17 @@ class JobQueue(object):
|
||||
if any(day == current_week_day for day in job.days):
|
||||
self.logger.debug('Running job %s', job.name)
|
||||
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)
|
||||
if job.repeat and not job.removed:
|
||||
self._put(job, last_t=t)
|
||||
else:
|
||||
self.logger.debug('Dropping non-repeating or removed job %s', job.name)
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
@@ -169,7 +314,6 @@ class JobQueue(object):
|
||||
self.__thread = Thread(target=self._main_loop, name="job_queue")
|
||||
self.__thread.start()
|
||||
self.logger.debug('%s thread started', self.__class__.__name__)
|
||||
|
||||
else:
|
||||
self.__start_lock.release()
|
||||
|
||||
@@ -182,7 +326,7 @@ class JobQueue(object):
|
||||
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()
|
||||
tmout = self._next_peek - time.time() if self._next_peek else None
|
||||
self._next_peek = None
|
||||
self.__tick.clear()
|
||||
|
||||
@@ -216,46 +360,58 @@ class Job(object):
|
||||
"""This class encapsulates a Job
|
||||
|
||||
Attributes:
|
||||
callback (function):
|
||||
interval (float):
|
||||
days: (tuple)
|
||||
repeat (bool):
|
||||
name (str):
|
||||
callback (function): The function that the job executes when it's due
|
||||
interval (int, float, datetime.timedelta): The interval in which the job runs
|
||||
days (tuple[int]): A tuple of ``int`` values that determine on which days of the week the
|
||||
job runs
|
||||
repeat (bool): If the job runs periodically or only once
|
||||
name (str): The name of this job
|
||||
job_queue (JobQueue): The ``JobQueue`` this job belongs to
|
||||
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 ([int, float, datetime.timedelta]): The interval in which the job will execute its
|
||||
callback function. ``int`` and ``float`` will be interpreted as seconds.
|
||||
interval (Optional[int, float, datetime.timedelta]): The interval in which the job will
|
||||
execute its callback function. ``int`` and ``float`` will be interpreted as seconds.
|
||||
If you don't set this value, you must set ``repeat=False`` and specify ``next_t`` when
|
||||
you put the job into the job queue.
|
||||
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``
|
||||
days (Tuple): Defines on which days the job should be ran.
|
||||
days (Optional[tuple[int]]): Defines on which days of the week the job should run.
|
||||
Defaults to ``Days.EVERY_DAY``
|
||||
name (Optional[str]): The name of this job. Defaults to ``callback.__name__``
|
||||
job_queue (Optional[class:`telegram.ext.JobQueue`]): The ``JobQueue`` this job belongs to.
|
||||
Only optional for backward compatibility with ``JobQueue.put()``.
|
||||
|
||||
"""
|
||||
job_queue = None
|
||||
|
||||
def __init__(self, callback, interval, repeat=True, context=None, days=Days.EVERY_DAY):
|
||||
def __init__(self,
|
||||
callback,
|
||||
interval=None,
|
||||
repeat=True,
|
||||
context=None,
|
||||
days=Days.EVERY_DAY,
|
||||
name=None,
|
||||
job_queue=None):
|
||||
|
||||
self.callback = callback
|
||||
self.context = context
|
||||
self.name = name or callback.__name__
|
||||
|
||||
self._repeat = repeat
|
||||
self._interval = None
|
||||
self.interval = interval
|
||||
self.repeat = repeat
|
||||
self.context = context
|
||||
|
||||
if not isinstance(days, tuple):
|
||||
raise ValueError("The 'days argument should be of type 'tuple'")
|
||||
|
||||
if not all(isinstance(day, int) for day in days):
|
||||
raise ValueError("The elements of the 'days' argument should be of type 'int'")
|
||||
|
||||
if not all(day >= 0 and day <= 6 for day in days):
|
||||
raise ValueError("The elements of the 'days' argument should be from 0 up to and "
|
||||
"including 6")
|
||||
|
||||
self._days = None
|
||||
self.days = days
|
||||
self.name = callback.__name__
|
||||
|
||||
self._job_queue = weakref.proxy(job_queue) if job_queue is not None else None
|
||||
|
||||
self._remove = Event()
|
||||
self._enabled = Event()
|
||||
self._enabled.set()
|
||||
@@ -268,20 +424,86 @@ class Job(object):
|
||||
"""
|
||||
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):
|
||||
@property
|
||||
def removed(self):
|
||||
return self._remove.is_set()
|
||||
|
||||
@property
|
||||
def enabled(self):
|
||||
return self._enabled.is_set()
|
||||
|
||||
def set_enabled(self, status):
|
||||
@enabled.setter
|
||||
def enabled(self, status):
|
||||
if status:
|
||||
self._enabled.set()
|
||||
else:
|
||||
self._enabled.clear()
|
||||
|
||||
enabled = property(is_enabled, set_enabled)
|
||||
@property
|
||||
def interval(self):
|
||||
return self._interval
|
||||
|
||||
@interval.setter
|
||||
def interval(self, interval):
|
||||
if interval is None and self.repeat:
|
||||
raise ValueError("The 'interval' can not be 'None' when 'repeat' is set to 'True'")
|
||||
|
||||
if not (interval is None or isinstance(interval, (Number, datetime.timedelta))):
|
||||
raise ValueError("The 'interval' must be of type 'datetime.timedelta',"
|
||||
" 'int' or 'float'")
|
||||
|
||||
self._interval = interval
|
||||
|
||||
@property
|
||||
def interval_seconds(self):
|
||||
if isinstance(self.interval, datetime.timedelta):
|
||||
return self.interval.total_seconds()
|
||||
else:
|
||||
return self.interval
|
||||
|
||||
@property
|
||||
def repeat(self):
|
||||
return self._repeat
|
||||
|
||||
@repeat.setter
|
||||
def repeat(self, repeat):
|
||||
if self.interval is None and repeat:
|
||||
raise ValueError("'repeat' can not be set to 'True' when no 'interval' is set")
|
||||
self._repeat = repeat
|
||||
|
||||
@property
|
||||
def days(self):
|
||||
return self._days
|
||||
|
||||
@days.setter
|
||||
def days(self, days):
|
||||
if not isinstance(days, tuple):
|
||||
raise ValueError("The 'days' argument should be of type 'tuple'")
|
||||
|
||||
if not all(isinstance(day, int) for day in days):
|
||||
raise ValueError("The elements of the 'days' argument should be of type 'int'")
|
||||
|
||||
if not all(0 <= day <= 6 for day in days):
|
||||
raise ValueError("The elements of the 'days' argument should be from 0 up to and "
|
||||
"including 6")
|
||||
|
||||
self._days = days
|
||||
|
||||
@property
|
||||
def job_queue(self):
|
||||
""" :rtype: JobQueue """
|
||||
return self._job_queue
|
||||
|
||||
@job_queue.setter
|
||||
def job_queue(self, job_queue):
|
||||
# Property setter for backward compatibility with JobQueue.put()
|
||||
if not self._job_queue:
|
||||
self._job_queue = weakref.proxy(job_queue)
|
||||
else:
|
||||
raise RuntimeError("The 'job_queue' attribute can only be set once.")
|
||||
|
||||
def __lt__(self, other):
|
||||
return False
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -21,7 +21,6 @@ import warnings
|
||||
|
||||
from .handler import Handler
|
||||
from telegram import Update
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class MessageHandler(Handler):
|
||||
@@ -38,8 +37,6 @@ class MessageHandler(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.
|
||||
allow_edited (Optional[bool]): If the handler should also accept edited messages.
|
||||
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``
|
||||
@@ -53,8 +50,12 @@ class MessageHandler(Handler):
|
||||
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
|
||||
message_updates (Optional[bool]): Should "normal" message updates be handled? Default is
|
||||
``True``.
|
||||
channel_posts_updates (Optional[bool]): Should channel posts updates be handled? Default is
|
||||
allow_edited (Optional[bool]): If the handler should also accept edited messages.
|
||||
Default is ``False`` - Deprecated. use edited updates instead.
|
||||
channel_post_updates (Optional[bool]): Should channel posts updates be handled? Default is
|
||||
``True``.
|
||||
edited_updates (Optional[bool]): Should "edited" message updates be handled? Default is
|
||||
``False``.
|
||||
|
||||
"""
|
||||
|
||||
@@ -67,9 +68,14 @@ class MessageHandler(Handler):
|
||||
pass_user_data=False,
|
||||
pass_chat_data=False,
|
||||
message_updates=True,
|
||||
channel_posts_updates=True):
|
||||
if not message_updates and not channel_posts_updates:
|
||||
raise ValueError('Both message_updates & channel_post_updates are False')
|
||||
channel_post_updates=True,
|
||||
edited_updates=False):
|
||||
if not message_updates and not channel_post_updates and not edited_updates:
|
||||
raise ValueError(
|
||||
'message_updates, channel_post_updates and edited_updates are all False')
|
||||
if allow_edited:
|
||||
warnings.warn('allow_edited is getting deprecated, please use edited_updates instead')
|
||||
edited_updates = allow_edited
|
||||
|
||||
super(MessageHandler, self).__init__(
|
||||
callback,
|
||||
@@ -78,9 +84,9 @@ class MessageHandler(Handler):
|
||||
pass_user_data=pass_user_data,
|
||||
pass_chat_data=pass_chat_data)
|
||||
self.filters = filters
|
||||
self.allow_edited = allow_edited
|
||||
self.message_updates = message_updates
|
||||
self.channel_posts_updates = channel_posts_updates
|
||||
self.channel_post_updates = channel_post_updates
|
||||
self.edited_updates = edited_updates
|
||||
|
||||
# We put this up here instead of with the rest of checking code
|
||||
# in check_update since we don't wanna spam a ton
|
||||
@@ -89,24 +95,19 @@ class MessageHandler(Handler):
|
||||
'deprecated, please use bitwise operators (& and |) '
|
||||
'instead. More info: https://git.io/vPTbc.')
|
||||
|
||||
def _is_allowed_message(self, update):
|
||||
return (self.message_updates
|
||||
and (update.message or (update.edited_message and self.allow_edited)))
|
||||
|
||||
def _is_allowed_channel_post(self, update):
|
||||
return (self.channel_posts_updates
|
||||
and (update.channel_post or (update.edited_channel_post and self.allow_edited)))
|
||||
def _is_allowed_update(self, update):
|
||||
return any([(self.message_updates and update.message),
|
||||
(self.edited_updates and update.edited_message),
|
||||
(self.channel_post_updates and update.channel_post)])
|
||||
|
||||
def check_update(self, update):
|
||||
if (isinstance(update, Update)
|
||||
and (self._is_allowed_message(update) or self._is_allowed_channel_post(update))):
|
||||
if isinstance(update, Update) and self._is_allowed_update(update):
|
||||
|
||||
if not self.filters:
|
||||
res = True
|
||||
|
||||
else:
|
||||
message = (update.message or update.edited_message or update.channel_post
|
||||
or update.edited_channel_post)
|
||||
message = update.effective_message
|
||||
if isinstance(self.filters, list):
|
||||
res = any(func(message) for func in self.filters)
|
||||
else:
|
||||
@@ -121,8 +122,3 @@ class MessageHandler(Handler):
|
||||
optional_args = self.collect_optional_args(dispatcher, update)
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.MessageHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Module author:
|
||||
# Tymofii A. Khodniev (thodnev) <thodnev@mail.ru>
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2017
|
||||
# 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/]
|
||||
'''A throughput-limiting message processor for Telegram bots'''
|
||||
from telegram.utils import promise
|
||||
|
||||
import functools
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
if sys.version_info.major > 2:
|
||||
import queue as q
|
||||
else:
|
||||
import Queue as q
|
||||
|
||||
# We need to count < 1s intervals, so the most accurate timer is needed
|
||||
# Starting from Python 3.3 we have time.perf_counter which is the clock
|
||||
# with the highest resolution available to the system, so let's use it there.
|
||||
# In Python 2.7, there's no perf_counter yet, so fallback on what we have:
|
||||
# on Windows, the best available is time.clock while time.time is on
|
||||
# another platforms (M. Lutz, "Learning Python," 4ed, p.630-634)
|
||||
if sys.version_info.major == 3 and sys.version_info.minor >= 3:
|
||||
curtime = time.perf_counter # pylint: disable=E1101
|
||||
else:
|
||||
curtime = time.clock if sys.platform[:3] == 'win' else time.time
|
||||
|
||||
|
||||
class DelayQueueError(RuntimeError):
|
||||
'''Indicates processing errors'''
|
||||
pass
|
||||
|
||||
|
||||
class DelayQueue(threading.Thread):
|
||||
'''Processes callbacks from queue with specified throughput limits.
|
||||
Creates a separate thread to process callbacks with delays.
|
||||
|
||||
Args:
|
||||
queue (:obj:`queue.Queue`, optional): used to pass callbacks to
|
||||
thread.
|
||||
Creates `queue.Queue` implicitly if not provided.
|
||||
burst_limit (:obj:`int`, optional): number of maximum callbacks to
|
||||
process per time-window defined by `time_limit_ms`.
|
||||
Defaults to 30.
|
||||
time_limit_ms (:obj:`int`, optional): defines width of time-window
|
||||
used when each processing limit is calculated.
|
||||
Defaults to 1000.
|
||||
exc_route (:obj:`callable`, optional): a callable, accepting 1
|
||||
positional argument; used to route exceptions from processor
|
||||
thread to main thread; is called on `Exception` subclass
|
||||
exceptions.
|
||||
If not provided, exceptions are routed through dummy handler,
|
||||
which re-raises them.
|
||||
autostart (:obj:`bool`, optional): if True, processor is started
|
||||
immediately after object's creation; if False, should be
|
||||
started manually by `start` method.
|
||||
Defaults to True.
|
||||
name (:obj:`str`, optional): thread's name.
|
||||
Defaults to ``'DelayQueue-N'``, where N is sequential
|
||||
number of object created.
|
||||
'''
|
||||
_instcnt = 0 # instance counter
|
||||
|
||||
def __init__(self,
|
||||
queue=None,
|
||||
burst_limit=30,
|
||||
time_limit_ms=1000,
|
||||
exc_route=None,
|
||||
autostart=True,
|
||||
name=None):
|
||||
self._queue = queue if queue is not None else q.Queue()
|
||||
self.burst_limit = burst_limit
|
||||
self.time_limit = time_limit_ms / 1000
|
||||
self.exc_route = (exc_route if exc_route is not None else self._default_exception_handler)
|
||||
self.__exit_req = False # flag to gently exit thread
|
||||
self.__class__._instcnt += 1
|
||||
if name is None:
|
||||
name = '%s-%s' % (self.__class__.__name__, self.__class__._instcnt)
|
||||
super(DelayQueue, self).__init__(name=name)
|
||||
self.daemon = False
|
||||
if autostart: # immediately start processing
|
||||
super(DelayQueue, self).start()
|
||||
|
||||
def run(self):
|
||||
'''Do not use the method except for unthreaded testing purposes,
|
||||
the method normally is automatically called by `start` method.
|
||||
'''
|
||||
times = [] # used to store each callable processing time
|
||||
while True:
|
||||
item = self._queue.get()
|
||||
if self.__exit_req:
|
||||
return # shutdown thread
|
||||
# delay routine
|
||||
now = curtime()
|
||||
t_delta = now - self.time_limit # calculate early to improve perf.
|
||||
if times and t_delta > times[-1]:
|
||||
# if last call was before the limit time-window
|
||||
# used to impr. perf. in long-interval calls case
|
||||
times = [now]
|
||||
else:
|
||||
# collect last in current limit time-window
|
||||
times = [t for t in times if t >= t_delta]
|
||||
times.append(now)
|
||||
if len(times) >= self.burst_limit: # if throughput limit was hit
|
||||
time.sleep(times[1] - t_delta)
|
||||
# finally process one
|
||||
try:
|
||||
func, args, kwargs = item
|
||||
func(*args, **kwargs)
|
||||
except Exception as exc: # re-route any exceptions
|
||||
self.exc_route(exc) # to prevent thread exit
|
||||
|
||||
def stop(self, timeout=None):
|
||||
'''Used to gently stop processor and shutdown its thread.
|
||||
|
||||
Args:
|
||||
timeout (:obj:`float`): indicates maximum time to wait for
|
||||
processor to stop and its thread to exit.
|
||||
If timeout exceeds and processor has not stopped, method
|
||||
silently returns. `is_alive` could be used afterwards
|
||||
to check the actual status. If `timeout` set to None, blocks
|
||||
until processor is shut down.
|
||||
Defaults to None.
|
||||
Returns:
|
||||
None
|
||||
'''
|
||||
self.__exit_req = True # gently request
|
||||
self._queue.put(None) # put something to unfreeze if frozen
|
||||
super(DelayQueue, self).join(timeout=timeout)
|
||||
|
||||
@staticmethod
|
||||
def _default_exception_handler(exc):
|
||||
'''Dummy exception handler which re-raises exception in thread.
|
||||
Could be possibly overwritten by subclasses.
|
||||
'''
|
||||
raise exc
|
||||
|
||||
def __call__(self, func, *args, **kwargs):
|
||||
'''Used to process callbacks in throughput-limiting thread
|
||||
through queue.
|
||||
Args:
|
||||
func (:obj:`callable`): the actual function (or any callable) that
|
||||
is processed through queue.
|
||||
*args: variable-length `func` arguments.
|
||||
**kwargs: arbitrary keyword-arguments to `func`.
|
||||
Returns:
|
||||
None
|
||||
'''
|
||||
if not self.is_alive() or self.__exit_req:
|
||||
raise DelayQueueError('Could not process callback in stopped thread')
|
||||
self._queue.put((func, args, kwargs))
|
||||
|
||||
|
||||
# The most straightforward way to implement this is to use 2 sequenital delay
|
||||
# queues, like on classic delay chain schematics in electronics.
|
||||
# So, message path is:
|
||||
# msg --> group delay if group msg, else no delay --> normal msg delay --> out
|
||||
# This way OS threading scheduler cares of timings accuracy.
|
||||
# (see time.time, time.clock, time.perf_counter, time.sleep @ docs.python.org)
|
||||
class MessageQueue(object):
|
||||
'''Implements callback processing with proper delays to avoid hitting
|
||||
Telegram's message limits.
|
||||
Contains two `DelayQueue`s, for group and for all messages, interconnected
|
||||
in delay chain. Callables are processed through *group* `DelayQueue`, then
|
||||
through *all* `DelayQueue` for group-type messages. For non-group messages,
|
||||
only the *all* `DelayQueue` is used.
|
||||
|
||||
Args:
|
||||
all_burst_limit (:obj:`int`, optional): numer of maximum *all-type*
|
||||
callbacks to process per time-window defined by
|
||||
`all_time_limit_ms`.
|
||||
Defaults to 30.
|
||||
all_time_limit_ms (:obj:`int`, optional): defines width of *all-type*
|
||||
time-window used when each processing limit is calculated.
|
||||
Defaults to 1000 ms.
|
||||
group_burst_limit (:obj:`int`, optional): numer of maximum *group-type*
|
||||
callbacks to process per time-window defined by
|
||||
`group_time_limit_ms`.
|
||||
Defaults to 20.
|
||||
group_time_limit_ms (:obj:`int`, optional): defines width of
|
||||
*group-type* time-window used when each processing limit is
|
||||
calculated.
|
||||
Defaults to 60000 ms.
|
||||
exc_route (:obj:`callable`, optional): a callable, accepting one
|
||||
positional argument; used to route exceptions from processor
|
||||
threads to main thread; is called on `Exception` subclass
|
||||
exceptions.
|
||||
If not provided, exceptions are routed through dummy handler,
|
||||
which re-raises them.
|
||||
autostart (:obj:`bool`, optional): if True, processors are started
|
||||
immediately after object's creation; if False, should be
|
||||
started manually by `start` method.
|
||||
Defaults to True.
|
||||
|
||||
Attributes:
|
||||
_all_delayq (:obj:`telegram.ext.messagequeue.DelayQueue`): actual
|
||||
`DelayQueue` used for *all-type* callback processing
|
||||
_group_delayq (:obj:`telegram.ext.messagequeue.DelayQueue`): actual
|
||||
`DelayQueue` used for *group-type* callback processing
|
||||
'''
|
||||
|
||||
def __init__(self,
|
||||
all_burst_limit=30,
|
||||
all_time_limit_ms=1000,
|
||||
group_burst_limit=20,
|
||||
group_time_limit_ms=60000,
|
||||
exc_route=None,
|
||||
autostart=True):
|
||||
# create accoring delay queues, use composition
|
||||
self._all_delayq = DelayQueue(
|
||||
burst_limit=all_burst_limit,
|
||||
time_limit_ms=all_time_limit_ms,
|
||||
exc_route=exc_route,
|
||||
autostart=autostart)
|
||||
self._group_delayq = DelayQueue(
|
||||
burst_limit=group_burst_limit,
|
||||
time_limit_ms=group_time_limit_ms,
|
||||
exc_route=exc_route,
|
||||
autostart=autostart)
|
||||
|
||||
def start(self):
|
||||
'''Method is used to manually start the `MessageQueue` processing
|
||||
|
||||
Returns:
|
||||
None
|
||||
'''
|
||||
self._all_delayq.start()
|
||||
self._group_delayq.start()
|
||||
|
||||
def stop(self, timeout=None):
|
||||
self._group_delayq.stop(timeout=timeout)
|
||||
self._all_delayq.stop(timeout=timeout)
|
||||
|
||||
stop.__doc__ = DelayQueue.stop.__doc__ or '' # reuse docsting if any
|
||||
|
||||
def __call__(self, promise, is_group_msg=False):
|
||||
'''Processes callables in troughput-limiting queues to avoid
|
||||
hitting limits (specified with \*_burst_limit and *\_time_limit_ms).
|
||||
Args:
|
||||
promise (:obj:`callable`): mainly the
|
||||
:obj:`telegram.utils.promise.Promise` (see Notes for other
|
||||
callables), that is processed in delay queues
|
||||
is_group_msg (:obj:`bool`, optional): defines whether `promise`
|
||||
would be processed in *group*+*all* `DelayQueue`s
|
||||
(if set to ``True``), or only through *all* `DelayQueue`
|
||||
(if set to ``False``), resulting in needed delays to avoid
|
||||
hitting specified limits.
|
||||
Defaults to ``True``.
|
||||
|
||||
Notes:
|
||||
Method is designed to accept :obj:`telegram.utils.promise.Promise`
|
||||
as `promise` argument, but other callables could be used too.
|
||||
For example, lambdas or simple functions could be used to wrap
|
||||
original func to be called with needed args.
|
||||
In that case, be sure that either wrapper func does not raise
|
||||
outside exceptions or the proper `exc_route` handler is provided.
|
||||
|
||||
Returns:
|
||||
:obj:`callable` used as `promise` argument.
|
||||
'''
|
||||
if not is_group_msg: # ignore middle group delay
|
||||
self._all_delayq(promise)
|
||||
else: # use middle group delay
|
||||
self._group_delayq(self._all_delayq, promise)
|
||||
return promise
|
||||
|
||||
|
||||
def queuedmessage(method):
|
||||
'''A decorator to be used with `telegram.bot.Bot` send* methods.
|
||||
|
||||
Note:
|
||||
As it probably wouldn't be a good idea to make this decorator a
|
||||
property, it had been coded as decorator function, so it implies that
|
||||
**first positional argument to wrapped MUST be self**\.
|
||||
|
||||
The next object attributes are used by decorator:
|
||||
|
||||
Attributes:
|
||||
self._is_messages_queued_default (:obj:`bool`): Value to provide
|
||||
class-defaults to `queued` kwarg if not provided during wrapped
|
||||
method call.
|
||||
self._msg_queue (:obj:`telegram.ext.messagequeue.MessageQueue`):
|
||||
The actual `MessageQueue` used to delay outbond messages according
|
||||
to specified time-limits.
|
||||
|
||||
Wrapped method starts accepting the next kwargs:
|
||||
|
||||
Args:
|
||||
queued (:obj:`bool`, optional): if set to ``True``, the `MessageQueue`
|
||||
is used to process output messages.
|
||||
Defaults to `self._is_queued_out`.
|
||||
isgroup (:obj:`bool`, optional): if set to ``True``, the message is
|
||||
meant to be group-type (as there's no obvious way to determine its
|
||||
type in other way at the moment). Group-type messages could have
|
||||
additional processing delay according to limits set in
|
||||
`self._out_queue`.
|
||||
Defaults to ``False``.
|
||||
|
||||
Returns:
|
||||
Either :obj:`telegram.utils.promise.Promise` in case call is queued,
|
||||
or original method's return value if it's not.
|
||||
'''
|
||||
|
||||
@functools.wraps(method)
|
||||
def wrapped(self, *args, **kwargs):
|
||||
queued = kwargs.pop('queued', self._is_messages_queued_default)
|
||||
isgroup = kwargs.pop('isgroup', False)
|
||||
if queued:
|
||||
prom = promise.Promise(method, args, kwargs)
|
||||
return self._msg_queue(prom, isgroup)
|
||||
return method(self, *args, **kwargs)
|
||||
|
||||
return wrapped
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -71,7 +71,10 @@ class RegexHandler(Handler):
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False,
|
||||
pass_user_data=False,
|
||||
pass_chat_data=False):
|
||||
pass_chat_data=False,
|
||||
allow_edited=False,
|
||||
message_updates=True,
|
||||
channel_post_updates=False):
|
||||
super(RegexHandler, self).__init__(
|
||||
callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
@@ -85,17 +88,30 @@ class RegexHandler(Handler):
|
||||
self.pattern = pattern
|
||||
self.pass_groups = pass_groups
|
||||
self.pass_groupdict = pass_groupdict
|
||||
self.allow_edited = allow_edited
|
||||
self.message_updates = message_updates
|
||||
self.channel_post_updates = channel_post_updates
|
||||
|
||||
def _is_allowed_message(self, update):
|
||||
return (self.message_updates
|
||||
and (update.message or (update.edited_message and self.allow_edited)))
|
||||
|
||||
def _is_allowed_channel_post(self, update):
|
||||
return (self.channel_post_updates
|
||||
and (update.channel_post or (update.edited_channel_post and self.allow_edited)))
|
||||
|
||||
def check_update(self, update):
|
||||
if isinstance(update, Update) and update.message and update.message.text:
|
||||
match = re.match(self.pattern, update.message.text)
|
||||
if (isinstance(update, Update)
|
||||
and (self._is_allowed_message(update) or self._is_allowed_channel_post(update))
|
||||
and update.effective_message.text):
|
||||
match = re.match(self.pattern, update.effective_message.text)
|
||||
return bool(match)
|
||||
else:
|
||||
return False
|
||||
|
||||
def handle_update(self, update, dispatcher):
|
||||
optional_args = self.collect_optional_args(dispatcher, update)
|
||||
match = re.match(self.pattern, update.message.text)
|
||||
match = re.match(self.pattern, update.effective_message.text)
|
||||
|
||||
if self.pass_groups:
|
||||
optional_args['groups'] = match.groups()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -19,7 +19,6 @@
|
||||
""" This module contains the StringCommandHandler class """
|
||||
|
||||
from .handler import Handler
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class StringCommandHandler(Handler):
|
||||
@@ -69,8 +68,3 @@ class StringCommandHandler(Handler):
|
||||
optional_args['args'] = update.split()[1:]
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.StringCommandHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -23,7 +23,6 @@ import re
|
||||
from future.utils import string_types
|
||||
|
||||
from .handler import Handler
|
||||
from telegram.utils.deprecate import deprecate
|
||||
|
||||
|
||||
class StringRegexHandler(Handler):
|
||||
@@ -84,8 +83,3 @@ class StringRegexHandler(Handler):
|
||||
optional_args['groupdict'] = match.groupdict()
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
m = "telegram.StringRegexHandler."
|
||||
checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
|
||||
handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
+61
-19
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -22,6 +22,7 @@ Telegram bots intuitive."""
|
||||
import logging
|
||||
import os
|
||||
import ssl
|
||||
import warnings
|
||||
from threading import Thread, Lock, current_thread, Event
|
||||
from time import sleep
|
||||
import subprocess
|
||||
@@ -61,6 +62,11 @@ class Updater(object):
|
||||
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.
|
||||
user_sig_handler (Optional[function]): Takes ``signum, frame`` as positional arguments.
|
||||
This will be called when a signal is received, defaults are (SIGINT, SIGTERM, SIGABRT)
|
||||
setable with Updater.idle(stop_signals=(signals))
|
||||
request_kwargs (Optional[dict]): Keyword args to control the creation of a request object
|
||||
(ignored if `bot` argument is used).
|
||||
|
||||
Raises:
|
||||
ValueError: If both `token` and `bot` are passed or none of them.
|
||||
@@ -68,7 +74,14 @@ class Updater(object):
|
||||
"""
|
||||
_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,
|
||||
user_sig_handler=None,
|
||||
request_kwargs=None):
|
||||
|
||||
if (token is None) and (bot is None):
|
||||
raise ValueError('`token` or `bot` must be passed')
|
||||
if (token is not None) and (bot is not None):
|
||||
@@ -83,8 +96,13 @@ class Updater(object):
|
||||
# * 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)
|
||||
if request_kwargs is None:
|
||||
request_kwargs = {}
|
||||
if 'con_pool_size' not in request_kwargs:
|
||||
request_kwargs['con_pool_size'] = workers + 4
|
||||
self._request = Request(**request_kwargs)
|
||||
self.bot = Bot(token, base_url, request=self._request)
|
||||
self.user_sig_handler = user_sig_handler
|
||||
self.update_queue = Queue()
|
||||
self.job_queue = JobQueue(self.bot)
|
||||
self.__exception_event = Event()
|
||||
@@ -122,9 +140,11 @@ class Updater(object):
|
||||
def start_polling(self,
|
||||
poll_interval=0.0,
|
||||
timeout=10,
|
||||
network_delay=5.,
|
||||
network_delay=None,
|
||||
clean=False,
|
||||
bootstrap_retries=0):
|
||||
bootstrap_retries=0,
|
||||
read_latency=2.,
|
||||
allowed_updates=None):
|
||||
"""
|
||||
Starts polling updates from Telegram.
|
||||
|
||||
@@ -134,7 +154,8 @@ class Updater(object):
|
||||
|
||||
timeout (Optional[float]): Passed to Bot.getUpdates
|
||||
|
||||
network_delay (Optional[float]): Passed to Bot.getUpdates
|
||||
network_delay: Deprecated. Will be honoured as `read_latency` for a while but will be
|
||||
removed in the future.
|
||||
|
||||
clean (Optional[bool]): Whether to clean any pending updates on Telegram servers before
|
||||
actually starting to poll. Default is False.
|
||||
@@ -143,13 +164,24 @@ class Updater(object):
|
||||
will retry on failures on the Telegram server.
|
||||
|
||||
| < 0 - retry indefinitely
|
||||
| 0 - no retries (default)
|
||||
| 0 - no retries (default)
|
||||
| > 0 - retry up to X times
|
||||
|
||||
allowed_updates (Optional[list[str]]): Passed to Bot.getUpdates
|
||||
|
||||
read_latency (Optional[float|int]): Grace time in seconds for receiving the reply from
|
||||
server. Will be added to the `timeout` value and used as the read timeout from
|
||||
server (Default: 2).
|
||||
|
||||
|
||||
Returns:
|
||||
Queue: The update queue that can be filled from the main thread
|
||||
|
||||
"""
|
||||
if network_delay is not None:
|
||||
warnings.warn('network_delay is deprecated, use read_latency instead')
|
||||
read_latency = network_delay
|
||||
|
||||
with self.__lock:
|
||||
if not self.running:
|
||||
self.running = True
|
||||
@@ -158,7 +190,7 @@ class Updater(object):
|
||||
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)
|
||||
read_latency, bootstrap_retries, clean, allowed_updates)
|
||||
|
||||
# Return the update queue so the main thread can insert updates
|
||||
return self.update_queue
|
||||
@@ -171,7 +203,8 @@ class Updater(object):
|
||||
key=None,
|
||||
clean=False,
|
||||
bootstrap_retries=0,
|
||||
webhook_url=None):
|
||||
webhook_url=None,
|
||||
allowed_updates=None):
|
||||
"""
|
||||
Starts a small http server to listen for updates via webhook. If cert
|
||||
and key are not provided, the webhook will be started directly on
|
||||
@@ -194,9 +227,10 @@ class Updater(object):
|
||||
| < 0 - retry indefinitely
|
||||
| 0 - no retries (default)
|
||||
| > 0 - retry up to X times
|
||||
webhook_url (Optional[str]): Explicitly specifiy the webhook url.
|
||||
webhook_url (Optional[str]): Explicitly specify the webhook url.
|
||||
Useful behind NAT, reverse proxy, etc. Default is derived from
|
||||
`listen`, `port` & `url_path`.
|
||||
allowed_updates (Optional[list[str]]): Passed to Bot.setWebhook
|
||||
|
||||
Returns:
|
||||
Queue: The update queue that can be filled from the main thread
|
||||
@@ -210,12 +244,13 @@ class Updater(object):
|
||||
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)
|
||||
key, bootstrap_retries, clean, webhook_url, allowed_updates)
|
||||
|
||||
# Return the update queue so the main thread can insert updates
|
||||
return self.update_queue
|
||||
|
||||
def _start_polling(self, poll_interval, timeout, network_delay, bootstrap_retries, clean):
|
||||
def _start_polling(self, poll_interval, timeout, read_latency, bootstrap_retries, clean,
|
||||
allowed_updates):
|
||||
"""
|
||||
Thread target of thread 'updater'. Runs in background, pulls
|
||||
updates from Telegram and inserts them in the update queue of the
|
||||
@@ -225,12 +260,15 @@ class Updater(object):
|
||||
cur_interval = poll_interval
|
||||
self.logger.debug('Updater thread started')
|
||||
|
||||
self._bootstrap(bootstrap_retries, clean=clean, webhook_url='')
|
||||
self._bootstrap(bootstrap_retries, clean=clean, webhook_url='', allowed_updates=None)
|
||||
|
||||
while self.running:
|
||||
try:
|
||||
updates = self.bot.getUpdates(
|
||||
self.last_update_id, timeout=timeout, network_delay=network_delay)
|
||||
self.last_update_id,
|
||||
timeout=timeout,
|
||||
read_latency=read_latency,
|
||||
allowed_updates=allowed_updates)
|
||||
except RetryAfter as e:
|
||||
self.logger.info(str(e))
|
||||
cur_interval = 0.5 + e.retry_after
|
||||
@@ -270,7 +308,7 @@ class Updater(object):
|
||||
return current_interval
|
||||
|
||||
def _start_webhook(self, listen, port, url_path, cert, key, bootstrap_retries, clean,
|
||||
webhook_url):
|
||||
webhook_url, allowed_updates):
|
||||
self.logger.debug('Updater thread started')
|
||||
use_ssl = cert is not None and key is not None
|
||||
if not url_path.startswith('/'):
|
||||
@@ -291,7 +329,8 @@ class Updater(object):
|
||||
max_retries=bootstrap_retries,
|
||||
clean=clean,
|
||||
webhook_url=webhook_url,
|
||||
cert=open(cert, 'rb'))
|
||||
cert=open(cert, 'rb'),
|
||||
allowed_updates=allowed_updates)
|
||||
elif clean:
|
||||
self.logger.warning("cleaning updates is not supported if "
|
||||
"SSL-termination happens elsewhere; skipping")
|
||||
@@ -321,18 +360,19 @@ class Updater(object):
|
||||
def _gen_webhook_url(listen, port, url_path):
|
||||
return 'https://{listen}:{port}{path}'.format(listen=listen, port=port, path=url_path)
|
||||
|
||||
def _bootstrap(self, max_retries, clean, webhook_url, cert=None):
|
||||
def _bootstrap(self, max_retries, clean, webhook_url, allowed_updates, cert=None):
|
||||
retries = 0
|
||||
while 1:
|
||||
|
||||
try:
|
||||
if clean:
|
||||
# Disable webhook for cleaning
|
||||
self.bot.setWebhook(webhook_url='')
|
||||
self.bot.deleteWebhook()
|
||||
self._clean_updates()
|
||||
sleep(1)
|
||||
|
||||
self.bot.setWebhook(webhook_url=webhook_url, certificate=cert)
|
||||
self.bot.setWebhook(
|
||||
url=webhook_url, certificate=cert, allowed_updates=allowed_updates)
|
||||
except (Unauthorized, InvalidToken):
|
||||
raise
|
||||
except TelegramError:
|
||||
@@ -397,6 +437,8 @@ class Updater(object):
|
||||
self.is_idle = False
|
||||
if self.running:
|
||||
self.stop()
|
||||
if self.user_sig_handler:
|
||||
self.user_sig_handler(signum, frame)
|
||||
else:
|
||||
self.logger.warning('Exiting immediately!')
|
||||
import os
|
||||
|
||||
+36
-11
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -40,16 +40,19 @@ class File(TelegramObject):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, file_id, bot, file_size=0, file_path='', **kwargs):
|
||||
def __init__(self, file_id, bot, file_size=None, file_path=None, **kwargs):
|
||||
# Required
|
||||
self.file_id = str(file_id)
|
||||
|
||||
# Optionals
|
||||
self.file_size = int(file_size)
|
||||
self.file_path = str(file_path)
|
||||
self.file_size = file_size
|
||||
if file_path:
|
||||
self.file_path = str(file_path)
|
||||
|
||||
self.bot = bot
|
||||
|
||||
self._id_attrs = (self.file_id,)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
@@ -65,17 +68,39 @@ class File(TelegramObject):
|
||||
|
||||
return File(bot=bot, **data)
|
||||
|
||||
def download(self, custom_path=None):
|
||||
def download(self, custom_path=None, out=None, timeout=None):
|
||||
"""
|
||||
Download this file. By default, the file is saved in the current working directory with its
|
||||
original filename as reported by Telegram. If a ``custom_path`` is supplied, it will be
|
||||
saved to that path instead. If ``out`` is defined, the file contents will be saved to that
|
||||
object using the ``out.write`` method. ``custom_path`` and ``out`` are mutually exclusive.
|
||||
|
||||
Args:
|
||||
custom_path (str):
|
||||
custom_path (Optional[str]): Custom path.
|
||||
out (Optional[object]): A file-like object. Must be opened in binary mode, if
|
||||
applicable.
|
||||
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
|
||||
from the server (instead of the one specified during creation of the connection
|
||||
pool).
|
||||
|
||||
Raises:
|
||||
ValueError: If both ``custom_path`` and ``out`` are passed.
|
||||
|
||||
"""
|
||||
|
||||
if custom_path is not None and out is not None:
|
||||
raise ValueError('custom_path and out are mutually exclusive')
|
||||
|
||||
url = self.file_path
|
||||
|
||||
if custom_path:
|
||||
filename = custom_path
|
||||
else:
|
||||
filename = basename(url)
|
||||
if out:
|
||||
buf = self.bot.request.retrieve(url)
|
||||
out.write(buf)
|
||||
|
||||
self.bot.request.download(url, filename)
|
||||
else:
|
||||
if custom_path:
|
||||
filename = custom_path
|
||||
else:
|
||||
filename = basename(url)
|
||||
|
||||
self.bot.request.download(url, filename, timeout=timeout)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
+4
-2
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -48,7 +48,7 @@ class Game(TelegramObject):
|
||||
title,
|
||||
description,
|
||||
photo,
|
||||
text='',
|
||||
text=None,
|
||||
text_entities=None,
|
||||
animation=None,
|
||||
**kwargs):
|
||||
@@ -73,6 +73,8 @@ class Game(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Game, Game).de_json(data, bot)
|
||||
|
||||
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)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -49,6 +49,8 @@ class GameHighScore(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(GameHighScore, GameHighScore).de_json(data, bot)
|
||||
|
||||
data['user'] = User.de_json(data.get('user'), bot)
|
||||
|
||||
return GameHighScore(**data)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -51,7 +51,14 @@ class InlineKeyboardButton(TelegramObject):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, **kwargs):
|
||||
def __init__(self,
|
||||
text,
|
||||
url=None,
|
||||
callback_data=None,
|
||||
switch_inline_query=None,
|
||||
switch_inline_query_current_chat=None,
|
||||
callback_game=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
self.text = text
|
||||
|
||||
@@ -59,8 +66,8 @@ class InlineKeyboardButton(TelegramObject):
|
||||
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.switch_inline_query_current_chat = switch_inline_query_current_chat
|
||||
self.callback_game = callback_game
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
# pylint: disable=R0902,R0912,R0913
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015 Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
# Copyright (C) 2015-2017
|
||||
# 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
|
||||
@@ -55,6 +56,7 @@ class InlineQuery(TelegramObject):
|
||||
self.location = location
|
||||
|
||||
self.bot = bot
|
||||
self._id_attrs = (self.id,)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -41,6 +41,8 @@ class InlineQueryResult(TelegramObject):
|
||||
self.type = str(type)
|
||||
self.id = str(id)
|
||||
|
||||
self._id_attrs = (self.id,)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
return super(InlineQueryResult, InlineQueryResult).de_json(data, bot)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# pylint: disable=W0622,E0611
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -37,10 +37,8 @@ class KeyboardButton(TelegramObject):
|
||||
# Required
|
||||
self.text = text
|
||||
# Optionals
|
||||
if request_contact:
|
||||
self.request_contact = request_contact
|
||||
if request_location:
|
||||
self.request_location = request_location
|
||||
self.request_contact = request_contact
|
||||
self.request_location = request_location
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -38,6 +38,8 @@ class Location(TelegramObject):
|
||||
self.longitude = float(longitude)
|
||||
self.latitude = float(latitude)
|
||||
|
||||
self._id_attrs = (self.longitude, self.latitude)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
|
||||
+109
-10
@@ -2,7 +2,7 @@
|
||||
# pylint: disable=R0902,R0912,R0913
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -18,13 +18,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."""
|
||||
|
||||
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)
|
||||
from telegram.utils.helpers import escape_html, escape_markdown
|
||||
|
||||
|
||||
class Message(TelegramObject):
|
||||
@@ -117,7 +117,7 @@ class Message(TelegramObject):
|
||||
forward_date=None,
|
||||
reply_to_message=None,
|
||||
edit_date=None,
|
||||
text='',
|
||||
text=None,
|
||||
entities=None,
|
||||
audio=None,
|
||||
document=None,
|
||||
@@ -125,19 +125,19 @@ class Message(TelegramObject):
|
||||
sticker=None,
|
||||
video=None,
|
||||
voice=None,
|
||||
caption='',
|
||||
caption=None,
|
||||
contact=None,
|
||||
location=None,
|
||||
venue=None,
|
||||
new_chat_member=None,
|
||||
left_chat_member=None,
|
||||
new_chat_title='',
|
||||
new_chat_title=None,
|
||||
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,
|
||||
migrate_to_chat_id=None,
|
||||
migrate_from_chat_id=None,
|
||||
channel_chat_created=False,
|
||||
pinned_message=None,
|
||||
forward_from_message_id=None,
|
||||
@@ -174,14 +174,16 @@ class Message(TelegramObject):
|
||||
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.migrate_to_chat_id = migrate_to_chat_id
|
||||
self.migrate_from_chat_id = migrate_from_chat_id
|
||||
self.channel_chat_created = bool(channel_chat_created)
|
||||
self.pinned_message = pinned_message
|
||||
self.forward_from_message_id = forward_from_message_id
|
||||
|
||||
self.bot = bot
|
||||
|
||||
self._id_attrs = (self.message_id,)
|
||||
|
||||
@property
|
||||
def chat_id(self):
|
||||
"""int: Short for :attr:`Message.chat.id`"""
|
||||
@@ -200,8 +202,10 @@ class Message(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Message, Message).de_json(data, bot)
|
||||
|
||||
data['from_user'] = User.de_json(data.get('from'), bot)
|
||||
data['date'] = datetime.fromtimestamp(data['date'])
|
||||
data['date'] = Message._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)
|
||||
@@ -539,6 +543,21 @@ class Message(TelegramObject):
|
||||
return self.bot.edit_message_reply_markup(
|
||||
chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
"""
|
||||
Shortcut for
|
||||
|
||||
>>> bot.delete_message(chat_id=message.chat_id,
|
||||
... message_id=message.message_id,
|
||||
... *args, **kwargs)
|
||||
|
||||
Returns:
|
||||
bool: On success, `True` is returned.
|
||||
|
||||
"""
|
||||
return self.bot.delete_message(
|
||||
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`.
|
||||
@@ -584,6 +603,7 @@ class Message(TelegramObject):
|
||||
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
|
||||
@@ -592,3 +612,82 @@ class Message(TelegramObject):
|
||||
entity: self.parse_entity(entity)
|
||||
for entity in self.entities if entity.type in types
|
||||
}
|
||||
|
||||
@property
|
||||
def text_html(self):
|
||||
"""
|
||||
Creates an html-formatted string from the markup entities found in the message
|
||||
(uses ``parse_entities``).
|
||||
|
||||
Use this if you want to retrieve the original string sent by the bot, as opposed to the
|
||||
plain text with corresponding markup entities.
|
||||
|
||||
Returns:
|
||||
str
|
||||
|
||||
"""
|
||||
entities = self.parse_entities()
|
||||
message_text = self.text
|
||||
markdown_text = ''
|
||||
last_offset = 0
|
||||
|
||||
for entity, text in sorted(entities.items(), key=(lambda item: item[0].offset)):
|
||||
text = escape_html(text)
|
||||
|
||||
if entity.type == MessageEntity.TEXT_LINK:
|
||||
insert = '<a href="{}">{}</a>'.format(entity.url, text)
|
||||
elif entity.type == MessageEntity.BOLD:
|
||||
insert = '<b>' + text + '</b>'
|
||||
elif entity.type == MessageEntity.ITALIC:
|
||||
insert = '<i>' + text + '</i>'
|
||||
elif entity.type == MessageEntity.CODE:
|
||||
insert = '<code>' + text + '</code>'
|
||||
elif entity.type == MessageEntity.PRE:
|
||||
insert = '<pre>' + text + '</pre>'
|
||||
else:
|
||||
insert = text
|
||||
|
||||
markdown_text += escape_html(message_text[last_offset:entity.offset]) + insert
|
||||
last_offset = entity.offset + entity.length
|
||||
|
||||
markdown_text += escape_html(message_text[last_offset:])
|
||||
return markdown_text
|
||||
|
||||
@property
|
||||
def text_markdown(self):
|
||||
"""
|
||||
Creates a markdown-formatted string from the markup entities found in the message
|
||||
(uses ``parse_entities``).
|
||||
|
||||
Use this if you want to retrieve the original string sent by the bot, as opposed to the
|
||||
plain text with corresponding markup entities.
|
||||
|
||||
Returns:
|
||||
str
|
||||
"""
|
||||
entities = self.parse_entities()
|
||||
message_text = self.text
|
||||
markdown_text = ''
|
||||
last_offset = 0
|
||||
|
||||
for entity, text in sorted(entities.items(), key=(lambda item: item[0].offset)):
|
||||
text = escape_markdown(text)
|
||||
|
||||
if entity.type == MessageEntity.TEXT_LINK:
|
||||
insert = '[{}]({})'.format(text, entity.url)
|
||||
elif entity.type == MessageEntity.BOLD:
|
||||
insert = '*' + text + '*'
|
||||
elif entity.type == MessageEntity.ITALIC:
|
||||
insert = '_' + text + '_'
|
||||
elif entity.type == MessageEntity.CODE:
|
||||
insert = '`' + text + '`'
|
||||
elif entity.type == MessageEntity.PRE:
|
||||
insert = '```' + text + '```'
|
||||
else:
|
||||
insert = text
|
||||
|
||||
markdown_text += escape_markdown(message_text[last_offset:entity.offset]) + insert
|
||||
last_offset = entity.offset + entity.length
|
||||
|
||||
markdown_text += escape_markdown(message_text[last_offset:])
|
||||
return markdown_text
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# pylint: disable=R0903
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -40,19 +40,15 @@ class PhotoSize(TelegramObject):
|
||||
file_size (Optional[int]):
|
||||
"""
|
||||
|
||||
def __init__(self, file_id, width, height, file_size=0, **kwargs):
|
||||
def __init__(self, file_id, width, height, file_size=None, **kwargs):
|
||||
# Required
|
||||
self.file_id = str(file_id)
|
||||
self.width = int(width)
|
||||
self.height = int(height)
|
||||
# Optionals
|
||||
self.file_size = int(file_size)
|
||||
self.file_size = file_size
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return (self.file_id == other.file_id and self.width == other.width
|
||||
and self.height == other.height and self.file_size == other.file_size)
|
||||
self._id_attrs = (self.file_id,)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -67,6 +67,8 @@ class ReplyKeyboardMarkup(ReplyMarkup):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(ReplyKeyboardMarkup, ReplyKeyboardMarkup).de_json(data, bot)
|
||||
|
||||
data['keyboard'] = [KeyboardButton.de_list(keyboard, bot) for keyboard in data['keyboard']]
|
||||
|
||||
return ReplyKeyboardMarkup(**data)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
+7
-3
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -44,7 +44,7 @@ class Sticker(TelegramObject):
|
||||
file_size (Optional[int]):
|
||||
"""
|
||||
|
||||
def __init__(self, file_id, width, height, thumb=None, emoji='', file_size=0, **kwargs):
|
||||
def __init__(self, file_id, width, height, thumb=None, emoji=None, file_size=None, **kwargs):
|
||||
# Required
|
||||
self.file_id = str(file_id)
|
||||
self.width = int(width)
|
||||
@@ -52,7 +52,9 @@ class Sticker(TelegramObject):
|
||||
# Optionals
|
||||
self.thumb = thumb
|
||||
self.emoji = emoji
|
||||
self.file_size = int(file_size)
|
||||
self.file_size = file_size
|
||||
|
||||
self._id_attrs = (self.file_id,)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
@@ -67,6 +69,8 @@ class Sticker(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Sticker, Sticker).de_json(data, bot)
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
|
||||
return Sticker(**data)
|
||||
|
||||
+100
-1
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -73,6 +73,12 @@ class Update(TelegramObject):
|
||||
self.channel_post = channel_post
|
||||
self.edited_channel_post = edited_channel_post
|
||||
|
||||
self._effective_user = None
|
||||
self._effective_chat = None
|
||||
self._effective_message = None
|
||||
|
||||
self._id_attrs = (self.update_id,)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
@@ -86,6 +92,8 @@ class Update(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(Update, Update).de_json(data, bot)
|
||||
|
||||
data['message'] = Message.de_json(data.get('message'), bot)
|
||||
data['edited_message'] = Message.de_json(data.get('edited_message'), bot)
|
||||
data['inline_query'] = InlineQuery.de_json(data.get('inline_query'), bot)
|
||||
@@ -96,3 +104,94 @@ class Update(TelegramObject):
|
||||
data['edited_channel_post'] = Message.de_json(data.get('edited_channel_post'), bot)
|
||||
|
||||
return Update(**data)
|
||||
|
||||
@property
|
||||
def effective_user(self):
|
||||
"""
|
||||
A property that contains the ``User`` that sent this update, no matter what kind of update
|
||||
this is. Will be ``None`` for channel posts.
|
||||
"""
|
||||
|
||||
if self._effective_user:
|
||||
return self._effective_user
|
||||
|
||||
user = None
|
||||
|
||||
if self.message:
|
||||
user = self.message.from_user
|
||||
|
||||
elif self.edited_message:
|
||||
user = self.edited_message.from_user
|
||||
|
||||
elif self.inline_query:
|
||||
user = self.inline_query.from_user
|
||||
|
||||
elif self.chosen_inline_result:
|
||||
user = self.chosen_inline_result.from_user
|
||||
|
||||
elif self.callback_query:
|
||||
user = self.callback_query.from_user
|
||||
|
||||
self._effective_user = user
|
||||
return user
|
||||
|
||||
@property
|
||||
def effective_chat(self):
|
||||
"""
|
||||
A property that contains the ``Chat`` that this update was sent in, no matter what kind of
|
||||
update this is. Will be ``None`` for inline queries and chosen inline results.
|
||||
"""
|
||||
|
||||
if self._effective_chat:
|
||||
return self._effective_chat
|
||||
|
||||
chat = None
|
||||
|
||||
if self.message:
|
||||
chat = self.message.chat
|
||||
|
||||
elif self.edited_message:
|
||||
chat = self.edited_message.chat
|
||||
|
||||
elif self.callback_query and self.callback_query.message:
|
||||
chat = self.callback_query.message.chat
|
||||
|
||||
elif self.channel_post:
|
||||
chat = self.channel_post.chat
|
||||
|
||||
elif self.edited_channel_post:
|
||||
chat = self.edited_channel_post.chat
|
||||
|
||||
self._effective_chat = chat
|
||||
return chat
|
||||
|
||||
@property
|
||||
def effective_message(self):
|
||||
"""
|
||||
A property that contains the ``Message`` included in this update, no matter what kind
|
||||
of update this is. Will be ``None`` for inline queries, chosen inline results and callback
|
||||
queries from inline messages.
|
||||
"""
|
||||
|
||||
if self._effective_message:
|
||||
return self._effective_message
|
||||
|
||||
message = None
|
||||
|
||||
if self.message:
|
||||
message = self.message
|
||||
|
||||
elif self.edited_message:
|
||||
message = self.edited_message
|
||||
|
||||
elif self.callback_query:
|
||||
message = self.callback_query.message
|
||||
|
||||
elif self.channel_post:
|
||||
message = self.channel_post
|
||||
|
||||
elif self.edited_channel_post:
|
||||
message = self.edited_channel_post
|
||||
|
||||
self._effective_message = message
|
||||
return message
|
||||
|
||||
+13
-2
@@ -2,7 +2,7 @@
|
||||
# pylint: disable=C0103,W0622
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -44,7 +44,14 @@ class User(TelegramObject):
|
||||
bot (Optional[Bot]): The Bot to use for instance methods
|
||||
"""
|
||||
|
||||
def __init__(self, id, first_name, type='', last_name='', username='', bot=None, **kwargs):
|
||||
def __init__(self,
|
||||
id,
|
||||
first_name,
|
||||
type=None,
|
||||
last_name=None,
|
||||
username=None,
|
||||
bot=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
self.id = int(id)
|
||||
self.first_name = first_name
|
||||
@@ -55,6 +62,8 @@ class User(TelegramObject):
|
||||
|
||||
self.bot = bot
|
||||
|
||||
self._id_attrs = (self.id,)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""str: """
|
||||
@@ -77,6 +86,8 @@ class User(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(User, User).de_json(data, bot)
|
||||
|
||||
return User(bot=bot, **data)
|
||||
|
||||
def get_profile_photos(self, *args, **kwargs):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -52,6 +52,8 @@ class UserProfilePhotos(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data = super(UserProfilePhotos, UserProfilePhotos).de_json(data, bot)
|
||||
|
||||
data['photos'] = [PhotoSize.de_list(photo, bot) for photo in data['photos']]
|
||||
|
||||
return UserProfilePhotos(**data)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Copyright (C) 2015-2017
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user