mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-21 16:45:17 +00:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c49058dbb4 | |||
| 8e80a8d273 | |||
| cbe057083f | |||
| 5f6138b06b | |||
| 1b99caa2f9 | |||
| e16c1da6b1 | |||
| e1242b3b4a | |||
| 93dde1ac1d | |||
| 05fb9d161a | |||
| a91fe5f8f6 | |||
| 5116a77221 | |||
| 1f597c6b4a | |||
| 1efd330e59 | |||
| af3e8c6440 | |||
| f34c09dd72 | |||
| 97bb04cd38 | |||
| 7ab007d8d4 | |||
| f7b497c1b4 | |||
| 4e60008086 | |||
| 5285f63e4a | |||
| 6647ae3c25 | |||
| e4a132c0e4 | |||
| ca81a75f29 | |||
| da87d4ba78 | |||
| 4753d27bd5 | |||
| eabfc0b06b | |||
| fcda567f8c | |||
| 1c36ff46ad | |||
| ffff0938f4 | |||
| ab2d6eb494 | |||
| fe14000515 | |||
| 5d27059631 | |||
| 00bba73673 | |||
| e9c5ee7ad6 | |||
| f2f62423ba | |||
| 26a0a173f4 | |||
| b736e1e855 | |||
| bd3fa3bb64 | |||
| c252042ddf | |||
| 8475c322af | |||
| dd4c0f0f1d | |||
| 555e36ee80 | |||
| 5134f71380 | |||
| 32268597d9 | |||
| 4feb2553ff | |||
| cd2f956e56 | |||
| 18fdb5ed13 | |||
| 8c698caa12 | |||
| 587908457e | |||
| 4375820863 | |||
| 171c70b4c2 | |||
| f1ee54fa73 | |||
| 90913724ca | |||
| 9d4691e50d | |||
| c4928229d6 | |||
| 2f2337cac1 | |||
| f5c57cd6c6 | |||
| c51c2224da |
@@ -1,6 +1,10 @@
|
||||
<!--
|
||||
Thanks for reporting issues of python-telegram-bot!
|
||||
To make it easier for us to help you please enter detailed information below.
|
||||
|
||||
Please note, we only support the latest version of python-telegram-bot and
|
||||
master branch. Please make sure to upgrade & recreate the issue on the latest
|
||||
version prior to opening an issue.
|
||||
-->
|
||||
### Steps to reproduce
|
||||
1.
|
||||
@@ -19,13 +23,9 @@ Tell us what happens instead
|
||||
**Operating System:**
|
||||
|
||||
|
||||
**Version of Python:**
|
||||
**Version of Python, python-telegram-bot & dependencies:**
|
||||
|
||||
``$ python -V``
|
||||
|
||||
**Version of python-telegram-bot:**
|
||||
|
||||
``$ python -c 'import telegram; print(telegram.__version__)'``
|
||||
``$ python -m telegram``
|
||||
|
||||
### Logs
|
||||
Insert logs here (if necessary)
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
- repo: git://github.com/pre-commit/mirrors-yapf
|
||||
sha: 316b795b2f32cbe80047aff7e842b72368d5a2c1
|
||||
sha: v0.11.0
|
||||
hooks:
|
||||
- id: yapf
|
||||
files: ^(telegram|tests)/.*\.py$
|
||||
- repo: git://github.com/pre-commit/pre-commit-hooks
|
||||
sha: 3fa02652357ff0dbb42b5bc78c673b7bc105fcf3
|
||||
sha: 18d7035de5388cc7775be57f529c154bf541aab9
|
||||
hooks:
|
||||
- id: flake8
|
||||
files: ^telegram/.*\.py$
|
||||
- repo: git://github.com/pre-commit/mirrors-pylint
|
||||
sha: 4de6c8dfadef1a271a814561ce05b8bc1c446d22
|
||||
sha: v1.5.5
|
||||
hooks:
|
||||
- id: pylint
|
||||
files: ^telegram/.*\.py$
|
||||
|
||||
+2
-2
@@ -1,6 +1,5 @@
|
||||
language: python
|
||||
python:
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
@@ -11,8 +10,9 @@ install:
|
||||
- pip install coveralls
|
||||
- pip install -r requirements.txt
|
||||
- pip install -r requirements-dev.txt
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != 'pypy'* ]]; then pip install ujson; fi
|
||||
script:
|
||||
- nosetests -v --with-flaky --no-flaky-report --with-coverage --cover-package=telegram/
|
||||
- 'if [ $TRAVIS_PYTHON_VERSION != 2.6 ] && [ $TRAVIS_PYTHON_VERSION != 3.3 ] && [ $TRAVIS_PYTHON_VERSION != pypy3 ]; then pre-commit run --all-files; fi'
|
||||
- if [ $TRAVIS_PYTHON_VERSION != 3.3 ] && [ $TRAVIS_PYTHON_VERSION != pypy3 ]; then pre-commit run --all-files; fi
|
||||
after_success:
|
||||
coveralls
|
||||
|
||||
@@ -11,17 +11,21 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||
- `Avanatiker <https://github.com/Avanatiker>`_
|
||||
- `Balduro <https://github.com/Balduro>`_
|
||||
- `bimmlerd <https://github.com/bimmlerd>`_
|
||||
- `Eli Gao <https://github.com/eligao>`_
|
||||
- `ErgoZ Riftbit Vaper <https://github.com/ergoz>`_
|
||||
- `franciscod <https://github.com/franciscod>`_
|
||||
- `Jacob Bom <https://github.com/bomjacob>`_
|
||||
- `JASON0916 <https://github.com/JASON0916>`_
|
||||
- `jh0ker <https://github.com/jh0ker>`_
|
||||
- `JRoot3D <https://github.com/JRoot3D>`_
|
||||
- `jlmadurga <https://github.com/jlmadurga>`_
|
||||
- `Li-aung Yip <https://github.com/LiaungYip>`_
|
||||
- `macrojames <https://github.com/macrojames>`_
|
||||
- `naveenvhegde <https://github.com/naveenvhegde>`_
|
||||
- `njittam <https://github.com/njittam>`_
|
||||
- `Noam Meltzer <https://github.com/tsnoam>`_
|
||||
- `Oleg Shlyazhko <https://github.com/ollmer>`_
|
||||
- `overquota <https://github.com/overquota>`_
|
||||
- `Rahiel Kasim <https://github.com/rahiel>`_
|
||||
- `Shelomentsev D <https://github.com/shelomentsevd>`_
|
||||
- `sooyhwang <https://github.com/sooyhwang>`_
|
||||
|
||||
+17
@@ -2,6 +2,23 @@
|
||||
Changes
|
||||
=======
|
||||
|
||||
**2016-09-24**
|
||||
|
||||
*Released 5.1*
|
||||
|
||||
- Drop Python 2.6 support
|
||||
- Deprecate ``telegram.Emoji``
|
||||
|
||||
- Use ``ujson`` if available
|
||||
- Add instance methods to ``Message``, ``Chat``, ``User``, ``InlineQuery`` and ``CallbackQuery``
|
||||
- RegEx filtering for ``CallbackQueryHandler`` and ``InlineQueryHandler``
|
||||
- New ``MessageHandler`` filters: ``forwarded`` and ``entity``
|
||||
- Add ``Message.get_entity`` to correctly handle UTF-16 codepoints and ``MessageEntity`` offsets
|
||||
- Fix bug in ``ConversationHandler`` when first handler ends the conversation
|
||||
- Allow multiple ``Dispatcher`` instances
|
||||
- Add ``ChatMigrated`` Exception
|
||||
- Properly split and handle arguments in ``CommandHandler``
|
||||
|
||||
**2016-07-15**
|
||||
|
||||
*Released 5.0*
|
||||
|
||||
+12
-16
@@ -1,9 +1,9 @@
|
||||
.. image:: https://github.com/python-telegram-bot/logos/blob/master/logo-text/png/ptb-logo-text_768.png?raw=true
|
||||
:align: center
|
||||
:target: https://github.com/python-telegram-bot/logos
|
||||
:target: https://python-telegram-bot.org
|
||||
:alt: python-telegram-bot Logo
|
||||
|
||||
Not **just** a Python wrapper around the Telegram Bot API
|
||||
We have made you a wrapper you can't refuse
|
||||
|
||||
*Stay tuned for library updates and new releases on our* `Telegram Channel <https://telegram.me/pythontelegrambotchannel>`_.
|
||||
|
||||
@@ -73,8 +73,7 @@ Introduction
|
||||
|
||||
This library provides a pure Python interface for the
|
||||
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
|
||||
It works with Python versions from 2.6+ (**Note:** Support for 2.6 will be dropped at some point
|
||||
this year. 2.7 will still be supported).
|
||||
It's compatible with Python versions 2.7, 3.3+ and `PyPy <http://pypy.org/>`_.
|
||||
It also works with `Google App Engine <https://cloud.google.com/appengine>`_.
|
||||
|
||||
In addition to the pure API implementation, this library features a number of high-level classes to
|
||||
@@ -97,6 +96,13 @@ You can install or upgrade python-telegram-bot with:
|
||||
|
||||
$ pip install python-telegram-bot --upgrade
|
||||
|
||||
Or you can install from source with:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
$ git clone https://github.com/python-telegram-bot/python-telegram-bot
|
||||
$ cd python-telegram-bot
|
||||
$ python setup.py install
|
||||
===============
|
||||
Getting started
|
||||
===============
|
||||
@@ -117,21 +123,11 @@ Learning by example
|
||||
|
||||
We believe that the best way to learn and understand this simple package is by example. So here
|
||||
are some examples for you to review. Even if it's not your approach for learning, please take a
|
||||
look at ``echobot2`` (below), it is de facto the base for most of the bots out there. Best of all,
|
||||
look at ``echobot2``, it is de facto the base for most of the bots out there. Best of all,
|
||||
the code for these examples are released to the public domain, so you can start by grabbing the
|
||||
code and building on top of it.
|
||||
|
||||
- `echobot2 <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot2.py>`_ replies back messages.
|
||||
|
||||
- `inlinebot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinebot.py>`_ basic example of an `inline bot <https://core.telegram.org/bots/inline>`_.
|
||||
|
||||
- `state machine bot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/state_machine_bot.py>`_ keeps the state for individual users, useful for multipart conversations.
|
||||
|
||||
- `timerbot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py>`_ uses the ``JobQueue`` to send timed messages.
|
||||
|
||||
- `echobot <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/legacy/echobot.py>`_ uses only the pure API to echo messages.
|
||||
|
||||
Look at the examples on the `wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Examples>`_ to see other bots the community has built.
|
||||
Visit `this page <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/README.md>`_ to discover the official examples or look at the examples on the `wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Examples>`_ to see other bots the community has built.
|
||||
|
||||
-------
|
||||
Logging
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
telegram.emoji module
|
||||
=====================
|
||||
|
||||
.. automodule:: telegram.emoji
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,23 @@
|
||||
# Examples
|
||||
|
||||
The examples in this folder are small bots meant to show you how a bot that is written with `python-telegram-bot` looks like. Some bots focus on one specific aspect of the Telegram Bot API while others focus on one of the mechanics of this library. Except for the `echobot.py` example, they all use the high-level framework this library provides with the `telegram.ext` submodule.
|
||||
|
||||
All examples are licensed under the [CC0 License](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/LICENSE.txt) and are therefore fully dedicated to the public domain. You can use them as the base for your own bots without worrying about copyrights.
|
||||
|
||||
### [`echobot2.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v5.0/examples/echobot2.py)
|
||||
This is probably the base for most of the bots made with `python-telegram-bot`. It simply replies to each text message with a message that contains the same text.
|
||||
|
||||
### [`timerbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py)
|
||||
This bot uses the [`JobQueue`](https://pythonhosted.org/python-telegram-bot/telegram.ext.jobqueue.html) class to send timed messages. The user sets a timer by using `/set` command with a specific time, for example `/set 30`. The bot then sets up a job to send a message to that user after 30 seconds. The user can also cancel the timer by sending `/unset`. To learn more about the `JobQueue`, read [this wiki article](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue).
|
||||
|
||||
### [`conversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot.py)
|
||||
A common task for a bot is to ask information from the user. In v5.0 of this library, we introduced the [`ConversationHandler`](https://pythonhosted.org/python-telegram-bot/telegram.ext.conversationhandler.html) for that exact purpose. This example uses it to retrieve user-information in a conversation-like style.
|
||||
|
||||
### [`inlinekeyboard.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinekeyboard.py)
|
||||
This example sheds some light on inline keyboards, callback queries and message editing.
|
||||
|
||||
### [`inlinebot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinebot.py)
|
||||
A basic example of an [inline bot](https://core.telegram.org/bots/inline). Don't forget to enable inline mode with [@BotFather](https://telegram.me/BotFather).
|
||||
|
||||
## Pure API
|
||||
The [`echobot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot.py) example uses only the pure, "bare-metal" API wrapper.
|
||||
@@ -29,15 +29,15 @@ logger = logging.getLogger(__name__)
|
||||
# Define a few command handlers. These usually take the two arguments bot and
|
||||
# update. Error handlers also receive the raised TelegramError object in error.
|
||||
def start(bot, update):
|
||||
bot.sendMessage(update.message.chat_id, text='Hi!')
|
||||
update.message.reply_text('Hi!')
|
||||
|
||||
|
||||
def help(bot, update):
|
||||
bot.sendMessage(update.message.chat_id, text='Help!')
|
||||
update.message.reply_text('Help!')
|
||||
|
||||
|
||||
def echo(bot, update):
|
||||
bot.sendMessage(update.message.chat_id, text=update.message.text)
|
||||
update.message.reply_text(update.message.text)
|
||||
|
||||
|
||||
def error(bot, update, error):
|
||||
|
||||
+20
-79
@@ -1,108 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Basic example for a bot that awaits an answer from the user. It's built upon
|
||||
# the state_machine_bot.py example
|
||||
# Basic example for a bot that uses inline keyboards.
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
import logging
|
||||
from telegram import Emoji, ForceReply, InlineKeyboardButton, \
|
||||
InlineKeyboardMarkup
|
||||
from telegram.ext import Updater, CommandHandler, MessageHandler, \
|
||||
CallbackQueryHandler, Filters
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
|
||||
|
||||
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
level=logging.DEBUG)
|
||||
|
||||
# Define the different states a chat can be in
|
||||
MENU, AWAIT_CONFIRMATION, AWAIT_INPUT = range(3)
|
||||
|
||||
# Python 2 and 3 unicode differences
|
||||
try:
|
||||
YES, NO = (Emoji.THUMBS_UP_SIGN.decode('utf-8'), Emoji.THUMBS_DOWN_SIGN.decode('utf-8'))
|
||||
except AttributeError:
|
||||
YES, NO = (Emoji.THUMBS_UP_SIGN, Emoji.THUMBS_DOWN_SIGN)
|
||||
|
||||
# States are saved in a dict that maps chat_id -> state
|
||||
state = dict()
|
||||
# Sometimes you need to save data temporarily
|
||||
context = dict()
|
||||
# This dict is used to store the settings value for the chat.
|
||||
# Usually, you'd use persistence for this (e.g. sqlite).
|
||||
values = dict()
|
||||
level=logging.INFO)
|
||||
|
||||
|
||||
# Example handler. Will be called on the /set command and on regular messages
|
||||
def set_value(bot, update):
|
||||
chat_id = update.message.chat_id
|
||||
user_id = update.message.from_user.id
|
||||
user_state = state.get(chat_id, MENU)
|
||||
def start(bot, update):
|
||||
keyboard = [[InlineKeyboardButton("Option 1", callback_data='1'),
|
||||
InlineKeyboardButton("Option 2", callback_data='2')],
|
||||
|
||||
if user_state == MENU:
|
||||
state[user_id] = AWAIT_INPUT # set the state
|
||||
bot.sendMessage(chat_id,
|
||||
text="Please enter your settings value",
|
||||
reply_markup=ForceReply())
|
||||
[InlineKeyboardButton("Option 3", callback_data='3')]]
|
||||
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
|
||||
bot.sendMessage(update.message.chat_id, text="Please choose:", reply_markup=reply_markup)
|
||||
|
||||
|
||||
def entered_value(bot, update):
|
||||
chat_id = update.message.chat_id
|
||||
user_id = update.message.from_user.id
|
||||
chat_state = state.get(user_id, MENU)
|
||||
|
||||
# Check if we are waiting for input
|
||||
if chat_state == AWAIT_INPUT:
|
||||
state[user_id] = AWAIT_CONFIRMATION
|
||||
|
||||
# Save the user id and the answer to context
|
||||
context[user_id] = update.message.text
|
||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton(YES, callback_data=YES),
|
||||
InlineKeyboardButton(NO, callback_data=NO)]])
|
||||
bot.sendMessage(chat_id, text="Are you sure?", reply_markup=reply_markup)
|
||||
|
||||
|
||||
def confirm_value(bot, update):
|
||||
def button(bot, update):
|
||||
query = update.callback_query
|
||||
chat_id = query.message.chat_id
|
||||
user_id = query.from_user.id
|
||||
text = query.data
|
||||
user_state = state.get(user_id, MENU)
|
||||
user_context = context.get(user_id, None)
|
||||
|
||||
# Check if we are waiting for confirmation and the right user answered
|
||||
if user_state == AWAIT_CONFIRMATION:
|
||||
del state[user_id]
|
||||
del context[user_id]
|
||||
bot.answerCallbackQuery(query.id, text="Ok!")
|
||||
if text == YES:
|
||||
values[user_id] = user_context
|
||||
bot.editMessageText(text="Changed value to %s." % values[user_id],
|
||||
chat_id=chat_id,
|
||||
message_id=query.message.message_id)
|
||||
else:
|
||||
bot.editMessageText(text="Alright, value is still %s." %
|
||||
values.get(user_id, 'not set'),
|
||||
chat_id=chat_id,
|
||||
message_id=query.message.message_id)
|
||||
bot.editMessageText(text="Selected option: %s" % query.data,
|
||||
chat_id=query.message.chat_id,
|
||||
message_id=query.message.message_id)
|
||||
|
||||
|
||||
def help(bot, update):
|
||||
bot.sendMessage(update.message.chat_id, text="Use /set to test this bot.")
|
||||
bot.sendMessage(update.message.chat_id, text="Use /start to test this bot.")
|
||||
|
||||
|
||||
def error(bot, update, error):
|
||||
logging.warning('Update "%s" caused error "%s"' % (update, error))
|
||||
|
||||
|
||||
# Create the Updater and pass it your bot's token.
|
||||
updater = Updater("TOKEN")
|
||||
|
||||
# The command
|
||||
updater.dispatcher.add_handler(CommandHandler('set', set_value))
|
||||
# The answer
|
||||
updater.dispatcher.add_handler(MessageHandler([Filters.text], entered_value))
|
||||
# The confirmation
|
||||
updater.dispatcher.add_handler(CallbackQueryHandler(confirm_value))
|
||||
updater.dispatcher.add_handler(CommandHandler('start', help))
|
||||
updater.dispatcher.add_handler(CommandHandler('start', start))
|
||||
updater.dispatcher.add_handler(CallbackQueryHandler(button))
|
||||
updater.dispatcher.add_handler(CommandHandler('help', help))
|
||||
updater.dispatcher.add_error_handler(error)
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ def set(bot, update, args, job_queue):
|
||||
due = int(args[0])
|
||||
if due < 0:
|
||||
bot.sendMessage(chat_id, text='Sorry we can not go back to future!')
|
||||
return
|
||||
|
||||
# Add job to queue
|
||||
job = Job(alarm, due, repeat=False, context=chat_id)
|
||||
@@ -59,7 +60,7 @@ def set(bot, update, args, job_queue):
|
||||
bot.sendMessage(chat_id, text='Usage: /set <seconds>')
|
||||
|
||||
|
||||
def unset(bot, update):
|
||||
def unset(bot, update, job_queue):
|
||||
"""Removes the job if the user changed their mind"""
|
||||
chat_id = update.message.chat_id
|
||||
|
||||
@@ -88,7 +89,7 @@ def main():
|
||||
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("unset", unset, pass_job_queue=True))
|
||||
|
||||
# log all errors
|
||||
dp.add_error_handler(error)
|
||||
|
||||
@@ -2,7 +2,6 @@ flake8
|
||||
nose
|
||||
pep257
|
||||
pylint
|
||||
unittest2
|
||||
flaky
|
||||
yapf
|
||||
pre-commit
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"""The setup and build script for the python-telegram-bot library."""
|
||||
|
||||
import codecs
|
||||
import telegram
|
||||
import os
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
@@ -16,18 +16,27 @@ def requirements():
|
||||
|
||||
return requirements_list
|
||||
|
||||
|
||||
with codecs.open('README.rst', 'r', 'utf-8') as fd:
|
||||
fn = os.path.join('telegram', 'version.py')
|
||||
with open(fn) as fh:
|
||||
code = compile(fh.read(), fn, 'exec')
|
||||
exec(code)
|
||||
|
||||
setup(name='python-telegram-bot',
|
||||
version=telegram.__version__,
|
||||
version=__version__,
|
||||
author='Leandro Toledo',
|
||||
author_email='devs@python-telegram-bot.org',
|
||||
license='LGPLv3',
|
||||
url='https://github.com/python-telegram-bot/python-telegram-bot',
|
||||
url='https://python-telegram-bot.org/',
|
||||
keywords='python telegram bot api wrapper',
|
||||
description='Not just a Python wrapper around the Telegram Bot API',
|
||||
long_description=fd.read(),
|
||||
packages=find_packages(exclude=['tests*']),
|
||||
install_requires=requirements(),
|
||||
extras_require={
|
||||
'json': 'ujson',
|
||||
},
|
||||
include_package_data=True,
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
@@ -39,7 +48,6 @@ with codecs.open('README.rst', 'r', 'utf-8') as fd:
|
||||
'Topic :: Internet',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
|
||||
+6
-11
@@ -43,7 +43,6 @@ from .forcereply import ForceReply
|
||||
from .error import TelegramError
|
||||
from .inputfile import InputFile
|
||||
from .file import File
|
||||
from .nullhandler import NullHandler
|
||||
from .emoji import Emoji
|
||||
from .parsemode import ParseMode
|
||||
from .messageentity import MessageEntity
|
||||
@@ -84,9 +83,10 @@ from .constants import (MAX_MESSAGE_LENGTH, MAX_CAPTION_LENGTH, SUPPORTED_WEBHOO
|
||||
MAX_FILESIZE_DOWNLOAD, MAX_FILESIZE_UPLOAD,
|
||||
MAX_MESSAGES_PER_SECOND_PER_CHAT, MAX_MESSAGES_PER_SECOND,
|
||||
MAX_MESSAGES_PER_MINUTE_PER_GROUP)
|
||||
from .version import __version__ # flake8: noqa
|
||||
|
||||
__author__ = 'devs@python-telegram-bot.org'
|
||||
__version__ = '5.0.0'
|
||||
|
||||
__all__ = ['Audio', 'Bot', 'Chat', 'ChatMember', 'ChatAction', 'ChosenInlineResult',
|
||||
'CallbackQuery', 'Contact', 'Document', 'Emoji', 'File', 'ForceReply',
|
||||
'InlineKeyboardButton', 'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult',
|
||||
@@ -100,14 +100,9 @@ __all__ = ['Audio', 'Bot', 'Chat', 'ChatMember', 'ChatAction', 'ChosenInlineResu
|
||||
'InlineQueryResultVenue', 'InlineQueryResultVideo', 'InlineQueryResultVoice',
|
||||
'InputContactMessageContent', 'InputFile', 'InputLocationMessageContent',
|
||||
'InputMessageContent', 'InputTextMessageContent', 'InputVenueMessageContent',
|
||||
'KeyboardButton', 'Location', 'Message', 'MessageEntity', 'NullHandler', 'ParseMode',
|
||||
'PhotoSize', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup', 'ReplyMarkup', 'Sticker',
|
||||
'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue',
|
||||
'Video', 'Voice', 'MAX_MESSAGE_LENGTH', 'MAX_CAPTION_LENGTH', 'SUPPORTED_WEBHOOK_PORTS',
|
||||
'KeyboardButton', 'Location', 'Message', 'MessageEntity', 'ParseMode', 'PhotoSize',
|
||||
'ReplyKeyboardHide', 'ReplyKeyboardMarkup', 'ReplyMarkup', 'Sticker', 'TelegramError',
|
||||
'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue', 'Video', 'Voice',
|
||||
'MAX_MESSAGE_LENGTH', 'MAX_CAPTION_LENGTH', 'SUPPORTED_WEBHOOK_PORTS',
|
||||
'MAX_FILESIZE_DOWNLOAD', 'MAX_FILESIZE_UPLOAD', 'MAX_MESSAGES_PER_SECOND_PER_CHAT',
|
||||
'MAX_MESSAGES_PER_SECOND', 'MAX_MESSAGES_PER_MINUTE_PER_GROUP']
|
||||
|
||||
if version_info < (2, 7):
|
||||
from warnings import warn
|
||||
warn("python-telegram-bot will stop supporting Python 2.6 in a future release. "
|
||||
"Please upgrade your Python version to at least Python 2.7!")
|
||||
|
||||
+3
-2
@@ -55,10 +55,11 @@ class Audio(TelegramObject):
|
||||
self.file_size = int(kwargs.get('file_size', 0))
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.Audio:
|
||||
|
||||
+12
-4
@@ -18,7 +18,11 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""Base class for Telegram Objects."""
|
||||
|
||||
import json
|
||||
try:
|
||||
import ujson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
from abc import ABCMeta
|
||||
|
||||
|
||||
@@ -34,13 +38,14 @@ class TelegramObject(object):
|
||||
return self.__dict__[item]
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.TelegramObject:
|
||||
dict:
|
||||
"""
|
||||
if not data:
|
||||
return None
|
||||
@@ -64,6 +69,9 @@ class TelegramObject(object):
|
||||
data = dict()
|
||||
|
||||
for key in iter(self.__dict__):
|
||||
if key == 'bot':
|
||||
continue
|
||||
|
||||
value = self.__dict__[key]
|
||||
if value is not None:
|
||||
if hasattr(value, 'to_dict'):
|
||||
|
||||
+42
-41
@@ -23,11 +23,11 @@ import functools
|
||||
import logging
|
||||
|
||||
from telegram import (User, Message, Update, Chat, ChatMember, UserProfilePhotos, File,
|
||||
ReplyMarkup, TelegramObject, NullHandler)
|
||||
ReplyMarkup, TelegramObject)
|
||||
from telegram.error import InvalidToken
|
||||
from telegram.utils import request
|
||||
from telegram.utils.request import Request
|
||||
|
||||
logging.getLogger(__name__).addHandler(NullHandler())
|
||||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
class Bot(TelegramObject):
|
||||
@@ -44,10 +44,11 @@ class Bot(TelegramObject):
|
||||
token (str): Bot's unique authentication.
|
||||
base_url (Optional[str]): Telegram Bot API service URL.
|
||||
base_file_url (Optional[str]): Telegram Bot API file URL.
|
||||
request (Optional[Request]): Pre initialized `Request` class.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, token, base_url=None, base_file_url=None):
|
||||
def __init__(self, token, base_url=None, base_file_url=None, request=None):
|
||||
self.token = self._validate_token(token)
|
||||
|
||||
if not base_url:
|
||||
@@ -61,9 +62,13 @@ class Bot(TelegramObject):
|
||||
self.base_file_url = base_file_url + self.token
|
||||
|
||||
self.bot = None
|
||||
|
||||
self._request = request or Request()
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
@property
|
||||
def request(self):
|
||||
return self._request
|
||||
|
||||
@staticmethod
|
||||
def _validate_token(token):
|
||||
"""a very basic validation on token"""
|
||||
@@ -144,12 +149,12 @@ class Bot(TelegramObject):
|
||||
else:
|
||||
data['reply_markup'] = reply_markup
|
||||
|
||||
result = request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
|
||||
if result is True:
|
||||
return result
|
||||
|
||||
return Message.de_json(result)
|
||||
return Message.de_json(result, self)
|
||||
|
||||
return decorator
|
||||
|
||||
@@ -169,9 +174,9 @@ class Bot(TelegramObject):
|
||||
|
||||
url = '{0}/getMe'.format(self.base_url)
|
||||
|
||||
result = request.get(url)
|
||||
result = self._request.get(url)
|
||||
|
||||
self.bot = User.de_json(result)
|
||||
self.bot = User.de_json(result, self)
|
||||
|
||||
return self.bot
|
||||
|
||||
@@ -618,13 +623,8 @@ class Bot(TelegramObject):
|
||||
|
||||
@log
|
||||
@message
|
||||
def sendVenue(
|
||||
self, chat_id,
|
||||
latitude,
|
||||
longitude,
|
||||
title, address,
|
||||
foursquare_id=None,
|
||||
**kwargs):
|
||||
def sendVenue(self, chat_id, latitude, longitude, title, address, foursquare_id=None,
|
||||
**kwargs):
|
||||
"""
|
||||
Use this method to send information about a venue.
|
||||
|
||||
@@ -818,7 +818,7 @@ class Bot(TelegramObject):
|
||||
if switch_pm_parameter:
|
||||
data['switch_pm_parameter'] = switch_pm_parameter
|
||||
|
||||
result = request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
|
||||
return result
|
||||
|
||||
@@ -858,9 +858,9 @@ class Bot(TelegramObject):
|
||||
if limit:
|
||||
data['limit'] = limit
|
||||
|
||||
result = request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
|
||||
return UserProfilePhotos.de_json(result)
|
||||
return UserProfilePhotos.de_json(result, self)
|
||||
|
||||
@log
|
||||
def getFile(self, file_id, **kwargs):
|
||||
@@ -889,12 +889,12 @@ class Bot(TelegramObject):
|
||||
|
||||
data = {'file_id': file_id}
|
||||
|
||||
result = request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
|
||||
if result.get('file_path'):
|
||||
result['file_path'] = '%s/%s' % (self.base_file_url, result['file_path'])
|
||||
|
||||
return File.de_json(result)
|
||||
return File.de_json(result, self)
|
||||
|
||||
@log
|
||||
def kickChatMember(self, chat_id, user_id, **kwargs):
|
||||
@@ -926,7 +926,7 @@ class Bot(TelegramObject):
|
||||
|
||||
data = {'chat_id': chat_id, 'user_id': user_id}
|
||||
|
||||
result = request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
|
||||
return result
|
||||
|
||||
@@ -960,7 +960,7 @@ class Bot(TelegramObject):
|
||||
|
||||
data = {'chat_id': chat_id, 'user_id': user_id}
|
||||
|
||||
result = request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
|
||||
return result
|
||||
|
||||
@@ -1004,7 +1004,7 @@ class Bot(TelegramObject):
|
||||
if show_alert:
|
||||
data['show_alert'] = show_alert
|
||||
|
||||
result = request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
|
||||
return result
|
||||
|
||||
@@ -1132,10 +1132,11 @@ class Bot(TelegramObject):
|
||||
|
||||
@log
|
||||
@message
|
||||
def editMessageReplyMarkup(
|
||||
self, chat_id=None,
|
||||
message_id=None, inline_message_id=None,
|
||||
**kwargs):
|
||||
def editMessageReplyMarkup(self,
|
||||
chat_id=None,
|
||||
message_id=None,
|
||||
inline_message_id=None,
|
||||
**kwargs):
|
||||
"""Use this method to edit only the reply markup of messages sent by
|
||||
the bot or via the bot (for inline bots).
|
||||
|
||||
@@ -1217,14 +1218,14 @@ class Bot(TelegramObject):
|
||||
|
||||
urlopen_timeout = timeout + network_delay
|
||||
|
||||
result = request.post(url, data, timeout=urlopen_timeout)
|
||||
result = self._request.post(url, data, timeout=urlopen_timeout)
|
||||
|
||||
if result:
|
||||
self.logger.debug('Getting updates: %s', [u['update_id'] for u in result])
|
||||
else:
|
||||
self.logger.debug('No new updates found.')
|
||||
|
||||
return [Update.de_json(x) for x in result]
|
||||
return [Update.de_json(u, self) for u in result]
|
||||
|
||||
@log
|
||||
def setWebhook(self, webhook_url=None, certificate=None, **kwargs):
|
||||
@@ -1260,7 +1261,7 @@ class Bot(TelegramObject):
|
||||
if certificate:
|
||||
data['certificate'] = certificate
|
||||
|
||||
result = request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
|
||||
return result
|
||||
|
||||
@@ -1290,7 +1291,7 @@ class Bot(TelegramObject):
|
||||
|
||||
data = {'chat_id': chat_id}
|
||||
|
||||
result = request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
|
||||
return result
|
||||
|
||||
@@ -1322,9 +1323,9 @@ class Bot(TelegramObject):
|
||||
|
||||
data = {'chat_id': chat_id}
|
||||
|
||||
result = request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
|
||||
return Chat.de_json(result)
|
||||
return Chat.de_json(result, self)
|
||||
|
||||
@log
|
||||
def getChatAdministrators(self, chat_id, **kwargs):
|
||||
@@ -1357,9 +1358,9 @@ class Bot(TelegramObject):
|
||||
|
||||
data = {'chat_id': chat_id}
|
||||
|
||||
result = request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
|
||||
return [ChatMember.de_json(x) for x in result]
|
||||
return [ChatMember.de_json(x, self) for x in result]
|
||||
|
||||
@log
|
||||
def getChatMembersCount(self, chat_id, **kwargs):
|
||||
@@ -1387,7 +1388,7 @@ class Bot(TelegramObject):
|
||||
|
||||
data = {'chat_id': chat_id}
|
||||
|
||||
result = request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
|
||||
return result
|
||||
|
||||
@@ -1420,13 +1421,13 @@ class Bot(TelegramObject):
|
||||
|
||||
data = {'chat_id': chat_id, 'user_id': user_id}
|
||||
|
||||
result = request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
result = self._request.post(url, data, timeout=kwargs.get('timeout'))
|
||||
|
||||
return ChatMember.de_json(result)
|
||||
return ChatMember.de_json(result, self)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(Bot, Bot).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(Bot, Bot).de_json(data, bot)
|
||||
|
||||
return Bot(**data)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ from telegram import TelegramObject, Message, User
|
||||
class CallbackQuery(TelegramObject):
|
||||
"""This object represents a Telegram CallbackQuery."""
|
||||
|
||||
def __init__(self, id, from_user, data, **kwargs):
|
||||
def __init__(self, id, from_user, data, bot=None, **kwargs):
|
||||
# Required
|
||||
self.id = id
|
||||
self.from_user = from_user
|
||||
@@ -34,15 +34,26 @@ class CallbackQuery(TelegramObject):
|
||||
self.message = kwargs.get('message')
|
||||
self.inline_message_id = kwargs.get('inline_message_id', '')
|
||||
|
||||
self.bot = bot
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.CallbackQuery:
|
||||
"""
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['from_user'] = User.de_json(data.get('from'))
|
||||
data['message'] = Message.de_json(data.get('message'))
|
||||
data['from_user'] = User.de_json(data.get('from'), bot)
|
||||
data['message'] = Message.de_json(data.get('message'), bot)
|
||||
|
||||
return CallbackQuery(**data)
|
||||
return CallbackQuery(bot=bot, **data)
|
||||
|
||||
def to_dict(self):
|
||||
"""
|
||||
@@ -54,3 +65,7 @@ class CallbackQuery(TelegramObject):
|
||||
# Required
|
||||
data['from'] = data.pop('from_user', None)
|
||||
return data
|
||||
|
||||
def answer(self, *args, **kwargs):
|
||||
"""Shortcut for ``bot.answerCallbackQuery(update.callback_query.id, *args, **kwargs)``"""
|
||||
return self.bot.answerCallbackQuery(self.id, *args, **kwargs)
|
||||
|
||||
+35
-3
@@ -40,6 +40,7 @@ class Chat(TelegramObject):
|
||||
|
||||
Keyword Args:
|
||||
type (Optional[str]):
|
||||
bot (Optional[Bot]): The Bot to use for instance methods
|
||||
"""
|
||||
|
||||
PRIVATE = 'private'
|
||||
@@ -47,7 +48,7 @@ class Chat(TelegramObject):
|
||||
SUPERGROUP = 'supergroup'
|
||||
CHANNEL = 'channel'
|
||||
|
||||
def __init__(self, id, type, **kwargs):
|
||||
def __init__(self, id, type, bot=None, **kwargs):
|
||||
# Required
|
||||
self.id = int(id)
|
||||
self.type = type
|
||||
@@ -57,11 +58,14 @@ class Chat(TelegramObject):
|
||||
self.first_name = kwargs.get('first_name', '')
|
||||
self.last_name = kwargs.get('last_name', '')
|
||||
|
||||
self.bot = bot
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.Chat:
|
||||
@@ -69,4 +73,32 @@ class Chat(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return Chat(**data)
|
||||
return Chat(bot=bot, **data)
|
||||
|
||||
def send_action(self, *args, **kwargs):
|
||||
"""Shortcut for ``bot.sendChatAction(update.message.chat.id, *args, **kwargs)``"""
|
||||
return self.bot.sendChatAction(self.id, *args, **kwargs)
|
||||
|
||||
def leave(self, *args, **kwargs):
|
||||
"""Shortcut for ``bot.leaveChat(update.message.chat.id, *args, **kwargs)``"""
|
||||
return self.bot.leaveChat(self.id, *args, **kwargs)
|
||||
|
||||
def get_administrators(self, *args, **kwargs):
|
||||
"""Shortcut for ``bot.getChatAdministrators(update.message.chat.id, *args, **kwargs)``"""
|
||||
return self.bot.getChatAdministrators(self.id, *args, **kwargs)
|
||||
|
||||
def get_members_count(self, *args, **kwargs):
|
||||
"""Shortcut for ``bot.getChatMembersCount(update.message.chat.id, *args, **kwargs)``"""
|
||||
return self.bot.getChatMembersCount(self.id, *args, **kwargs)
|
||||
|
||||
def get_member(self, *args, **kwargs):
|
||||
"""Shortcut for ``bot.getChatMember(update.message.chat.id, *args, **kwargs)``"""
|
||||
return self.bot.getChatMember(self.id, *args, **kwargs)
|
||||
|
||||
def kick_member(self, *args, **kwargs):
|
||||
"""Shortcut for ``bot.kickChatMember(update.message.chat.id, *args, **kwargs)``"""
|
||||
return self.bot.kickChatMember(self.id, *args, **kwargs)
|
||||
|
||||
def unban_member(self, *args, **kwargs):
|
||||
"""Shortcut for ``bot.unbanChatMember(update.message.chat.id, *args, **kwargs)``"""
|
||||
return self.bot.unbanChatMember(self.id, *args, **kwargs)
|
||||
|
||||
@@ -46,10 +46,11 @@ class ChatMember(TelegramObject):
|
||||
self.status = status
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.ChatMember:
|
||||
@@ -57,6 +58,6 @@ class ChatMember(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['user'] = User.de_json(data.get('user'))
|
||||
data['user'] = User.de_json(data.get('user'), bot)
|
||||
|
||||
return ChatMember(**data)
|
||||
|
||||
@@ -57,10 +57,11 @@ class ChosenInlineResult(TelegramObject):
|
||||
self.inline_message_id = inline_message_id
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.ChosenInlineResult:
|
||||
@@ -69,9 +70,9 @@ class ChosenInlineResult(TelegramObject):
|
||||
return None
|
||||
|
||||
# Required
|
||||
data['from_user'] = User.de_json(data.pop('from'))
|
||||
data['from_user'] = User.de_json(data.pop('from'), bot)
|
||||
# Optionals
|
||||
data['location'] = Location.de_json(data.get('location'))
|
||||
data['location'] = Location.de_json(data.get('location'), bot)
|
||||
|
||||
return ChosenInlineResult(**data)
|
||||
|
||||
|
||||
@@ -32,6 +32,12 @@ Attributes:
|
||||
limit, but eventually you'll begin receiving 429 errors.
|
||||
MAX_MESSAGES_PER_SECOND (int)
|
||||
MAX_MESSAGES_PER_MINUTE_PER_GROUP (int)
|
||||
|
||||
The following constant have been found by experimentation:
|
||||
|
||||
Attributes:
|
||||
MAX_MESSAGE_ENTITIES (int): Max number of entities that can be in a message.
|
||||
(Beyond this cap telegram will simply ignore further formatting styles)
|
||||
"""
|
||||
|
||||
MAX_MESSAGE_LENGTH = 4096
|
||||
@@ -45,3 +51,4 @@ MAX_FILESIZE_UPLOAD = int(50E6) # (50MB)
|
||||
MAX_MESSAGES_PER_SECOND_PER_CHAT = 1
|
||||
MAX_MESSAGES_PER_SECOND = 30
|
||||
MAX_MESSAGES_PER_MINUTE_PER_GROUP = 20
|
||||
MAX_MESSAGE_ENTITIES = 100
|
||||
|
||||
+3
-2
@@ -49,10 +49,11 @@ class Contact(TelegramObject):
|
||||
self.user_id = int(kwargs.get('user_id', 0))
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.Contact:
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import logging
|
||||
from telegram import NullHandler
|
||||
|
||||
from future.moves.urllib.parse import quote
|
||||
from future.moves.urllib.error import HTTPError, URLError
|
||||
from future.moves.urllib.request import urlopen, Request
|
||||
|
||||
logging.getLogger(__name__).addHandler(NullHandler())
|
||||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
class Botan(object):
|
||||
@@ -29,12 +28,10 @@ class Botan(object):
|
||||
return False
|
||||
data = message.to_json()
|
||||
try:
|
||||
url = self.url_template.format(token=str(self.token),
|
||||
uid=str(uid),
|
||||
name=quote(event_name))
|
||||
request = Request(url,
|
||||
data=data.encode(),
|
||||
headers={'Content-Type': 'application/json'})
|
||||
url = self.url_template.format(
|
||||
token=str(self.token), uid=str(uid), name=quote(event_name))
|
||||
request = Request(
|
||||
url, data=data.encode(), headers={'Content-Type': 'application/json'})
|
||||
urlopen(request)
|
||||
return True
|
||||
except HTTPError as error:
|
||||
|
||||
@@ -52,10 +52,11 @@ class Document(TelegramObject):
|
||||
self.file_size = int(kwargs.get('file_size', 0))
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.Document:
|
||||
@@ -63,6 +64,6 @@ class Document(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'))
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
|
||||
return Document(**data)
|
||||
|
||||
+20
-4
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# flake8: noqa
|
||||
# pylint: disable=C0103,R0903
|
||||
# pylint: disable=C0103,R0903,E0213
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
@@ -18,14 +18,27 @@
|
||||
#
|
||||
# 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 a object that represents an Emoji."""
|
||||
"""This module contains a object that represents an Emoji.
|
||||
|
||||
from future.utils import bytes_to_native_str as n
|
||||
This module will be removed in the future.
|
||||
"""
|
||||
|
||||
import warnings
|
||||
|
||||
from future.utils import bytes_to_native_str
|
||||
|
||||
|
||||
class Emoji(object):
|
||||
class Emoji2(object):
|
||||
"""This object represents an Emoji."""
|
||||
|
||||
def n(b):
|
||||
|
||||
def e(cls):
|
||||
warnings.warn("telegram.Emoji is being deprecated, please see https://git.io/v6DeB")
|
||||
return bytes_to_native_str(b)
|
||||
|
||||
return property(e)
|
||||
|
||||
GRINNING_FACE_WITH_SMILING_EYES = n(b'\xF0\x9F\x98\x81')
|
||||
FACE_WITH_TEARS_OF_JOY = n(b'\xF0\x9F\x98\x82')
|
||||
SMILING_FACE_WITH_OPEN_MOUTH = n(b'\xF0\x9F\x98\x83')
|
||||
@@ -879,3 +892,6 @@ class Emoji(object):
|
||||
CLOCK_FACE_TEN_THIRTY = n(b'\xF0\x9F\x95\xA5')
|
||||
CLOCK_FACE_ELEVEN_THIRTY = n(b'\xF0\x9F\x95\xA6')
|
||||
CLOCK_FACE_TWELVE_THIRTY = n(b'\xF0\x9F\x95\xA7')
|
||||
|
||||
|
||||
Emoji = Emoji2()
|
||||
|
||||
@@ -85,3 +85,17 @@ class TimedOut(NetworkError):
|
||||
|
||||
def __init__(self):
|
||||
super(TimedOut, self).__init__('Timed out')
|
||||
|
||||
|
||||
class ChatMigrated(TelegramError):
|
||||
|
||||
def __init__(self, new_chat_id):
|
||||
"""
|
||||
Args:
|
||||
new_chat_id (int):
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
super(ChatMigrated, self).__init__('Chat migrated')
|
||||
self.new_chat_id = new_chat_id
|
||||
|
||||
@@ -18,14 +18,19 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
""" This module contains the CallbackQueryHandler class """
|
||||
|
||||
from .handler import Handler
|
||||
import re
|
||||
|
||||
from future.utils import string_types
|
||||
|
||||
from telegram import Update
|
||||
from telegram.utils.deprecate import deprecate
|
||||
from .handler import Handler
|
||||
|
||||
|
||||
class CallbackQueryHandler(Handler):
|
||||
"""
|
||||
Handler class to handle Telegram callback queries.
|
||||
Handler class to handle Telegram callback queries. Optionally based on a regex.
|
||||
Read the documentation of the ``re`` module for more information.
|
||||
|
||||
Args:
|
||||
callback (function): A function that takes ``bot, update`` as
|
||||
@@ -39,18 +44,51 @@ class CallbackQueryHandler(Handler):
|
||||
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
|
||||
instance created by the ``Updater`` which can be used to schedule new jobs.
|
||||
Default is ``False``.
|
||||
pattern (optional[str or Pattern]): Optional regex pattern. If not ``None`` ``re.match``
|
||||
is used to determine if an update should be handled by this handler.
|
||||
pass_groups (optional[bool]): If the callback should be passed the
|
||||
result of ``re.match(pattern, data).groups()`` as a keyword
|
||||
argument called ``groups``. Default is ``False``
|
||||
pass_groupdict (optional[bool]): If the callback should be passed the
|
||||
result of ``re.match(pattern, data).groupdict()`` as a keyword
|
||||
argument called ``groupdict``. Default is ``False``
|
||||
"""
|
||||
|
||||
def __init__(self, callback, pass_update_queue=False, pass_job_queue=False):
|
||||
super(CallbackQueryHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
def __init__(self,
|
||||
callback,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False,
|
||||
pattern=None,
|
||||
pass_groups=False,
|
||||
pass_groupdict=False):
|
||||
super(CallbackQueryHandler, self).__init__(
|
||||
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
|
||||
|
||||
if isinstance(pattern, string_types):
|
||||
pattern = re.compile(pattern)
|
||||
|
||||
self.pattern = pattern
|
||||
self.pass_groups = pass_groups
|
||||
self.pass_groupdict = pass_groupdict
|
||||
|
||||
def check_update(self, update):
|
||||
return isinstance(update, Update) and update.callback_query
|
||||
if isinstance(update, Update) and update.callback_query:
|
||||
if self.pattern:
|
||||
if update.callback_query.data:
|
||||
match = re.match(self.pattern, update.callback_query.data)
|
||||
return bool(match)
|
||||
else:
|
||||
return True
|
||||
|
||||
def handle_update(self, update, dispatcher):
|
||||
optional_args = self.collect_optional_args(dispatcher)
|
||||
if self.pattern:
|
||||
match = re.match(self.pattern, update.callback_query.data)
|
||||
|
||||
if self.pass_groups:
|
||||
optional_args['groups'] = match.groups()
|
||||
if self.pass_groupdict:
|
||||
optional_args['groupdict'] = match.groupdict()
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
|
||||
@@ -43,9 +43,8 @@ class ChosenInlineResultHandler(Handler):
|
||||
"""
|
||||
|
||||
def __init__(self, callback, pass_update_queue=False, pass_job_queue=False):
|
||||
super(ChosenInlineResultHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
super(ChosenInlineResultHandler, self).__init__(
|
||||
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
|
||||
|
||||
def check_update(self, update):
|
||||
return isinstance(update, Update) and update.chosen_inline_result
|
||||
|
||||
@@ -39,7 +39,8 @@ class CommandHandler(Handler):
|
||||
pass_args (optional[bool]): If the handler should be passed the
|
||||
arguments passed to the command as a keyword argument called `
|
||||
``args``. It will contain a list of strings, which is the text
|
||||
following the command split on spaces. Default is ``False``
|
||||
following the command split on single or consecutive whitespace characters.
|
||||
Default is ``False``
|
||||
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
|
||||
@@ -57,9 +58,8 @@ class CommandHandler(Handler):
|
||||
pass_args=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False):
|
||||
super(CommandHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
super(CommandHandler, self).__init__(
|
||||
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
|
||||
self.command = command
|
||||
self.allow_edited = allow_edited
|
||||
self.pass_args = pass_args
|
||||
@@ -81,7 +81,7 @@ class CommandHandler(Handler):
|
||||
message = update.message or update.edited_message
|
||||
|
||||
if self.pass_args:
|
||||
optional_args['args'] = message.text.split(' ')[1:]
|
||||
optional_args['args'] = message.text.split()[1:]
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
|
||||
@@ -213,10 +213,13 @@ class ConversationHandler(Handler):
|
||||
|
||||
def update_state(self, new_state, key):
|
||||
if new_state == self.END:
|
||||
del self.conversations[key]
|
||||
if key in self.conversations:
|
||||
del self.conversations[key]
|
||||
else:
|
||||
pass
|
||||
|
||||
elif isinstance(new_state, Promise):
|
||||
self.conversations[key] = (self.conversations[key], new_state)
|
||||
self.conversations[key] = (self.conversations.get(key), new_state)
|
||||
|
||||
elif new_state is not None:
|
||||
self.conversations[key] = new_state
|
||||
|
||||
+117
-56
@@ -19,70 +19,43 @@
|
||||
"""This module contains the Dispatcher class."""
|
||||
|
||||
import logging
|
||||
import weakref
|
||||
from functools import wraps
|
||||
from threading import Thread, Lock, Event, current_thread
|
||||
from threading import Thread, Lock, Event, current_thread, BoundedSemaphore
|
||||
from time import sleep
|
||||
from uuid import uuid4
|
||||
|
||||
from queue import Queue, Empty
|
||||
|
||||
from future.builtins import range
|
||||
|
||||
from telegram import (TelegramError, NullHandler)
|
||||
from telegram.utils import request
|
||||
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(NullHandler())
|
||||
|
||||
ASYNC_QUEUE = Queue()
|
||||
ASYNC_THREADS = set()
|
||||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||
""":type: set[Thread]"""
|
||||
ASYNC_LOCK = Lock() # guards ASYNC_THREADS
|
||||
DEFAULT_GROUP = 0
|
||||
|
||||
|
||||
def _pooled():
|
||||
"""
|
||||
A wrapper to run a thread in a thread pool
|
||||
"""
|
||||
while 1:
|
||||
promise = ASYNC_QUEUE.get()
|
||||
|
||||
# If unpacking fails, the thread pool is being closed from Updater._join_async_threads
|
||||
if not isinstance(promise, Promise):
|
||||
logging.getLogger(__name__).debug("Closing run_async thread %s/%d" %
|
||||
(current_thread().getName(), len(ASYNC_THREADS)))
|
||||
break
|
||||
|
||||
try:
|
||||
promise.run()
|
||||
|
||||
except:
|
||||
logging.getLogger(__name__).exception("run_async function raised exception")
|
||||
|
||||
|
||||
def run_async(func):
|
||||
"""
|
||||
Function decorator that will run the function in a new thread.
|
||||
"""Function decorator that will run the function in a new thread.
|
||||
|
||||
Using this decorator is only possible when only a single Dispatcher exist in the system.
|
||||
|
||||
Args:
|
||||
func (function): The function to run in the thread.
|
||||
async_queue (Queue): The queue of the functions to be executed asynchronously.
|
||||
|
||||
Returns:
|
||||
function:
|
||||
"""
|
||||
|
||||
# TODO: handle exception in async threads
|
||||
# set a threading.Event to notify caller thread
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def async_func(*args, **kwargs):
|
||||
"""
|
||||
A wrapper to run a function in a thread
|
||||
"""
|
||||
promise = Promise(func, args, kwargs)
|
||||
ASYNC_QUEUE.put(promise)
|
||||
return promise
|
||||
return Dispatcher.get_instance().run_async(func, *args, **kwargs)
|
||||
|
||||
return async_func
|
||||
|
||||
@@ -100,7 +73,12 @@ class Dispatcher(object):
|
||||
callbacks
|
||||
workers (Optional[int]): Number of maximum concurrent worker threads for the ``@run_async``
|
||||
decorator
|
||||
|
||||
"""
|
||||
__singleton_lock = Lock()
|
||||
__singleton_semaphore = BoundedSemaphore()
|
||||
__singleton = None
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def __init__(self, bot, update_queue, workers=4, exception_event=None, job_queue=None):
|
||||
self.bot = bot
|
||||
@@ -113,28 +91,92 @@ class Dispatcher(object):
|
||||
""":type: list[int]"""
|
||||
self.error_handlers = []
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.running = False
|
||||
self.__stop_event = Event()
|
||||
self.__exception_event = exception_event or Event()
|
||||
self.__async_queue = Queue()
|
||||
self.__async_threads = set()
|
||||
|
||||
with ASYNC_LOCK:
|
||||
if not ASYNC_THREADS:
|
||||
if request.is_con_pool_initialized():
|
||||
raise RuntimeError('Connection Pool already initialized')
|
||||
|
||||
# we need a connection pool the size of:
|
||||
# * for each of the workers
|
||||
# * 1 for Dispatcher
|
||||
# * 1 for polling Updater (even if updater is webhook, we can spare a connection)
|
||||
# * 1 for JobQueue
|
||||
request.CON_POOL_SIZE = workers + 3
|
||||
for i in range(workers):
|
||||
thread = Thread(target=_pooled, name=str(i))
|
||||
ASYNC_THREADS.add(thread)
|
||||
thread.start()
|
||||
# For backward compatibility, we allow a "singleton" mode for the dispatcher. When there's
|
||||
# only one instance of Dispatcher, it will be possible to use the `run_async` decorator.
|
||||
with self.__singleton_lock:
|
||||
if self.__singleton_semaphore.acquire(blocking=0):
|
||||
self._set_singleton(self)
|
||||
else:
|
||||
self.logger.debug('Thread pool already initialized, skipping.')
|
||||
self._set_singleton(None)
|
||||
|
||||
self._init_async_threads(uuid4(), workers)
|
||||
|
||||
@classmethod
|
||||
def _reset_singleton(cls):
|
||||
# NOTE: This method was added mainly for test_updater benefit and specifically pypy. Never
|
||||
# call it in production code.
|
||||
cls.__singleton_semaphore.release()
|
||||
|
||||
def _init_async_threads(self, base_name, workers):
|
||||
base_name = '{}_'.format(base_name) if base_name else ''
|
||||
|
||||
for i in range(workers):
|
||||
thread = Thread(target=self._pooled, name='{}{}'.format(base_name, i))
|
||||
self.__async_threads.add(thread)
|
||||
thread.start()
|
||||
|
||||
@classmethod
|
||||
def _set_singleton(cls, val):
|
||||
cls.logger.debug('Setting singleton dispatcher as %s', val)
|
||||
cls.__singleton = weakref.ref(val) if val else None
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls):
|
||||
"""Get the singleton instance of this class.
|
||||
|
||||
Returns:
|
||||
Dispatcher
|
||||
|
||||
"""
|
||||
if cls.__singleton is not None:
|
||||
return cls.__singleton()
|
||||
else:
|
||||
raise RuntimeError('{} not initialized or multiple instances exist'.format(
|
||||
cls.__name__))
|
||||
|
||||
def _pooled(self):
|
||||
"""
|
||||
A wrapper to run a thread in a thread pool
|
||||
"""
|
||||
thr_name = current_thread().getName()
|
||||
while 1:
|
||||
promise = self.__async_queue.get()
|
||||
|
||||
# If unpacking fails, the thread pool is being closed from Updater._join_async_threads
|
||||
if not isinstance(promise, Promise):
|
||||
self.logger.debug("Closing run_async thread %s/%d", thr_name,
|
||||
len(self.__async_threads))
|
||||
break
|
||||
|
||||
try:
|
||||
promise.run()
|
||||
|
||||
except:
|
||||
self.logger.exception("run_async function raised exception")
|
||||
|
||||
def run_async(self, func, *args, **kwargs):
|
||||
"""Queue a function (with given args/kwargs) to be run asynchronously.
|
||||
|
||||
Args:
|
||||
func (function): The function to run in the thread.
|
||||
args (Optional[tuple]): Arguments to `func`.
|
||||
kwargs (Optional[dict]): Keyword arguments to `func`.
|
||||
|
||||
Returns:
|
||||
Promise
|
||||
|
||||
"""
|
||||
# TODO: handle exception in async threads
|
||||
# set a threading.Event to notify caller thread
|
||||
promise = Promise(func, args, kwargs)
|
||||
self.__async_queue.put(promise)
|
||||
return promise
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
@@ -183,6 +225,25 @@ class Dispatcher(object):
|
||||
sleep(0.1)
|
||||
self.__stop_event.clear()
|
||||
|
||||
# async threads must be join()ed only after the dispatcher thread was joined,
|
||||
# otherwise we can still have new async threads dispatched
|
||||
threads = list(self.__async_threads)
|
||||
total = len(threads)
|
||||
|
||||
# Stop all threads in the thread pool by put()ting one non-tuple per thread
|
||||
for i in range(total):
|
||||
self.__async_queue.put(None)
|
||||
|
||||
for i, thr in enumerate(threads):
|
||||
self.logger.debug('Waiting for async thread {0}/{1} to end'.format(i + 1, total))
|
||||
thr.join()
|
||||
self.__async_threads.remove(thr)
|
||||
self.logger.debug('async thread {0}/{1} has ended'.format(i + 1, total))
|
||||
|
||||
@property
|
||||
def has_running_threads(self):
|
||||
return self.running or bool(self.__async_threads)
|
||||
|
||||
def process_update(self, update):
|
||||
"""
|
||||
Processes a single update.
|
||||
|
||||
@@ -17,15 +17,19 @@
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
""" This module contains the InlineQueryHandler class """
|
||||
import re
|
||||
|
||||
from future.utils import string_types
|
||||
|
||||
from .handler import Handler
|
||||
from telegram import Update
|
||||
from telegram.utils.deprecate import deprecate
|
||||
from .handler import Handler
|
||||
|
||||
|
||||
class InlineQueryHandler(Handler):
|
||||
"""
|
||||
Handler class to handle Telegram inline queries.
|
||||
Handler class to handle Telegram inline queries. Optionally based on a regex. Read the
|
||||
documentation of the ``re`` module for more information.
|
||||
|
||||
Args:
|
||||
callback (function): A function that takes ``bot, update`` as
|
||||
@@ -39,18 +43,51 @@ class InlineQueryHandler(Handler):
|
||||
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
|
||||
instance created by the ``Updater`` which can be used to schedule new jobs.
|
||||
Default is ``False``.
|
||||
pattern (optional[str or Pattern]): Optional regex pattern. If not ``None`` ``re.match``
|
||||
is used to determine if an update should be handled by this handler.
|
||||
pass_groups (optional[bool]): If the callback should be passed the
|
||||
result of ``re.match(pattern, query).groups()`` as a keyword
|
||||
argument called ``groups``. Default is ``False``
|
||||
pass_groupdict (optional[bool]): If the callback should be passed the
|
||||
result of ``re.match(pattern, query).groupdict()`` as a keyword
|
||||
argument called ``groupdict``. Default is ``False``
|
||||
"""
|
||||
|
||||
def __init__(self, callback, pass_update_queue=False, pass_job_queue=False):
|
||||
super(InlineQueryHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
def __init__(self,
|
||||
callback,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False,
|
||||
pattern=None,
|
||||
pass_groups=False,
|
||||
pass_groupdict=False):
|
||||
super(InlineQueryHandler, self).__init__(
|
||||
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
|
||||
|
||||
if isinstance(pattern, string_types):
|
||||
pattern = re.compile(pattern)
|
||||
|
||||
self.pattern = pattern
|
||||
self.pass_groups = pass_groups
|
||||
self.pass_groupdict = pass_groupdict
|
||||
|
||||
def check_update(self, update):
|
||||
return isinstance(update, Update) and update.inline_query
|
||||
if isinstance(update, Update) and update.inline_query:
|
||||
if self.pattern:
|
||||
if update.inline_query.query:
|
||||
match = re.match(self.pattern, update.inline_query.query)
|
||||
return bool(match)
|
||||
else:
|
||||
return True
|
||||
|
||||
def handle_update(self, update, dispatcher):
|
||||
optional_args = self.collect_optional_args(dispatcher)
|
||||
if self.pattern:
|
||||
match = re.match(self.pattern, update.inline_query.query)
|
||||
|
||||
if self.pass_groups:
|
||||
optional_args['groups'] = match.groups()
|
||||
if self.pass_groupdict:
|
||||
optional_args['groupdict'] = match.groupdict()
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
|
||||
@@ -81,6 +81,27 @@ class Filters(object):
|
||||
or message.channel_chat_created or message.migrate_to_chat_id
|
||||
or message.migrate_from_chat_id or message.pinned_message)
|
||||
|
||||
@staticmethod
|
||||
def forwarded(message):
|
||||
return bool(message.forward_date)
|
||||
|
||||
@staticmethod
|
||||
def entity(entity_type):
|
||||
"""Filters messages to only allow those which have a :class:`telegram.MessageEntity`
|
||||
where their `type` matches `entity_type`.
|
||||
|
||||
Args:
|
||||
entity_type: Entity type to check for. All types can be found as constants
|
||||
in :class:`telegram.MessageEntity`.
|
||||
|
||||
Returns: function to use as filter
|
||||
"""
|
||||
|
||||
def entities_filter(message):
|
||||
return any([entity.type == entity_type for entity in message.entities])
|
||||
|
||||
return entities_filter
|
||||
|
||||
|
||||
class MessageHandler(Handler):
|
||||
"""
|
||||
@@ -111,9 +132,8 @@ class MessageHandler(Handler):
|
||||
allow_edited=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False):
|
||||
super(MessageHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
super(MessageHandler, self).__init__(
|
||||
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
|
||||
self.filters = filters
|
||||
self.allow_edited = allow_edited
|
||||
|
||||
@@ -138,7 +158,8 @@ class MessageHandler(Handler):
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
# old non-PEP8 Handler methods
|
||||
# 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")
|
||||
|
||||
@@ -62,9 +62,8 @@ class RegexHandler(Handler):
|
||||
pass_groupdict=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False):
|
||||
super(RegexHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
super(RegexHandler, self).__init__(
|
||||
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
|
||||
|
||||
if isinstance(pattern, string_types):
|
||||
pattern = re.compile(pattern)
|
||||
|
||||
@@ -35,7 +35,8 @@ class StringCommandHandler(Handler):
|
||||
pass_args (optional[bool]): If the handler should be passed the
|
||||
arguments passed to the command as a keyword argument called `
|
||||
``args``. It will contain a list of strings, which is the text
|
||||
following the command split on spaces. Default is ``False``
|
||||
following the command split on single or consecutive whitespace characters.
|
||||
Default is ``False``
|
||||
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
|
||||
@@ -52,9 +53,8 @@ class StringCommandHandler(Handler):
|
||||
pass_args=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False):
|
||||
super(StringCommandHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
super(StringCommandHandler, self).__init__(
|
||||
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
|
||||
self.command = command
|
||||
self.pass_args = pass_args
|
||||
|
||||
@@ -66,7 +66,7 @@ class StringCommandHandler(Handler):
|
||||
optional_args = self.collect_optional_args(dispatcher)
|
||||
|
||||
if self.pass_args:
|
||||
optional_args['args'] = update.split(' ')[1:]
|
||||
optional_args['args'] = update.split()[1:]
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
|
||||
@@ -61,9 +61,8 @@ class StringRegexHandler(Handler):
|
||||
pass_groupdict=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False):
|
||||
super(StringRegexHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
super(StringRegexHandler, self).__init__(
|
||||
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
|
||||
|
||||
if isinstance(pattern, string_types):
|
||||
pattern = re.compile(pattern)
|
||||
|
||||
@@ -44,15 +44,10 @@ class TypeHandler(Handler):
|
||||
Default is ``False``.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
type,
|
||||
callback,
|
||||
strict=False,
|
||||
pass_update_queue=False,
|
||||
def __init__(self, type, callback, strict=False, pass_update_queue=False,
|
||||
pass_job_queue=False):
|
||||
super(TypeHandler, self).__init__(callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue)
|
||||
super(TypeHandler, self).__init__(
|
||||
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
|
||||
self.type = type
|
||||
self.strict = strict
|
||||
|
||||
|
||||
+39
-41
@@ -28,12 +28,13 @@ import subprocess
|
||||
from signal import signal, SIGINT, SIGTERM, SIGABRT
|
||||
from queue import Queue
|
||||
|
||||
from telegram import Bot, TelegramError, NullHandler
|
||||
from telegram.ext import dispatcher, Dispatcher, JobQueue
|
||||
from telegram import Bot, TelegramError
|
||||
from telegram.ext import Dispatcher, JobQueue
|
||||
from telegram.error import Unauthorized, InvalidToken
|
||||
from telegram.utils.request import Request
|
||||
from telegram.utils.webhookhandler import (WebhookServer, WebhookHandler)
|
||||
|
||||
logging.getLogger(__name__).addHandler(NullHandler())
|
||||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
class Updater(object):
|
||||
@@ -57,13 +58,17 @@ class Updater(object):
|
||||
base_url (Optional[str]):
|
||||
workers (Optional[int]): Amount of threads in the thread pool for
|
||||
functions decorated with @run_async
|
||||
bot (Optional[Bot]):
|
||||
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.
|
||||
job_queue_tick_interval(Optional[float]): The interval the queue should
|
||||
be checked for new tasks. Defaults to 1.0
|
||||
|
||||
Raises:
|
||||
ValueError: If both `token` and `bot` are passed or none of them.
|
||||
|
||||
"""
|
||||
_request = None
|
||||
|
||||
def __init__(self, token=None, base_url=None, workers=4, bot=None):
|
||||
if (token is None) and (bot is None):
|
||||
@@ -74,15 +79,23 @@ class Updater(object):
|
||||
if bot is not None:
|
||||
self.bot = bot
|
||||
else:
|
||||
self.bot = Bot(token, base_url)
|
||||
# we need a connection pool the size of:
|
||||
# * for each of the workers
|
||||
# * 1 for Dispatcher
|
||||
# * 1 for polling Updater (even if webhook is used, we can spare a connection)
|
||||
# * 1 for JobQueue
|
||||
# * 1 for main thread
|
||||
self._request = Request(con_pool_size=workers + 4)
|
||||
self.bot = Bot(token, base_url, request=self._request)
|
||||
self.update_queue = Queue()
|
||||
self.job_queue = JobQueue(self.bot)
|
||||
self.__exception_event = Event()
|
||||
self.dispatcher = Dispatcher(self.bot,
|
||||
self.update_queue,
|
||||
job_queue=self.job_queue,
|
||||
workers=workers,
|
||||
exception_event=self.__exception_event)
|
||||
self.dispatcher = Dispatcher(
|
||||
self.bot,
|
||||
self.update_queue,
|
||||
job_queue=self.job_queue,
|
||||
workers=workers,
|
||||
exception_event=self.__exception_event)
|
||||
self.last_update_id = 0
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.running = False
|
||||
@@ -216,9 +229,8 @@ class Updater(object):
|
||||
|
||||
while self.running:
|
||||
try:
|
||||
updates = self.bot.getUpdates(self.last_update_id,
|
||||
timeout=timeout,
|
||||
network_delay=network_delay)
|
||||
updates = self.bot.getUpdates(
|
||||
self.last_update_id, timeout=timeout, network_delay=network_delay)
|
||||
except TelegramError as te:
|
||||
self.logger.error("Error while getting Updates: {0}".format(te))
|
||||
|
||||
@@ -262,7 +274,8 @@ class Updater(object):
|
||||
url_path = '/{0}'.format(url_path)
|
||||
|
||||
# Create and start server
|
||||
self.httpd = WebhookServer((listen, port), WebhookHandler, self.update_queue, url_path)
|
||||
self.httpd = WebhookServer((listen, port), WebhookHandler, self.update_queue, url_path,
|
||||
self.bot)
|
||||
|
||||
if use_ssl:
|
||||
self._check_ssl_cert(cert, key)
|
||||
@@ -271,10 +284,11 @@ class Updater(object):
|
||||
if not webhook_url:
|
||||
webhook_url = self._gen_webhook_url(listen, port, url_path)
|
||||
|
||||
self._bootstrap(max_retries=bootstrap_retries,
|
||||
clean=clean,
|
||||
webhook_url=webhook_url,
|
||||
cert=open(cert, 'rb'))
|
||||
self._bootstrap(
|
||||
max_retries=bootstrap_retries,
|
||||
clean=clean,
|
||||
webhook_url=webhook_url,
|
||||
cert=open(cert, 'rb'))
|
||||
elif clean:
|
||||
self.logger.warning("cleaning updates is not supported if "
|
||||
"SSL-termination happens elsewhere; skipping")
|
||||
@@ -292,10 +306,8 @@ class Updater(object):
|
||||
exit_code = 0
|
||||
if exit_code is 0:
|
||||
try:
|
||||
self.httpd.socket = ssl.wrap_socket(self.httpd.socket,
|
||||
certfile=cert,
|
||||
keyfile=key,
|
||||
server_side=True)
|
||||
self.httpd.socket = ssl.wrap_socket(
|
||||
self.httpd.socket, certfile=cert, keyfile=key, server_side=True)
|
||||
except ssl.SSLError as error:
|
||||
self.logger.exception('Failed to init SSL socket')
|
||||
raise TelegramError(str(error))
|
||||
@@ -345,7 +357,7 @@ class Updater(object):
|
||||
|
||||
self.job_queue.stop()
|
||||
with self.__lock:
|
||||
if self.running or dispatcher.ASYNC_THREADS:
|
||||
if self.running or self.dispatcher.has_running_threads:
|
||||
self.logger.debug('Stopping Updater and Dispatcher...')
|
||||
|
||||
self.running = False
|
||||
@@ -353,9 +365,10 @@ class Updater(object):
|
||||
self._stop_httpd()
|
||||
self._stop_dispatcher()
|
||||
self._join_threads()
|
||||
# async threads must be join()ed only after the dispatcher thread was joined,
|
||||
# otherwise we can still have new async threads dispatched
|
||||
self._join_async_threads()
|
||||
|
||||
# Stop the Request instance only if it was created by the Updater
|
||||
if self._request:
|
||||
self._request.stop()
|
||||
|
||||
def _stop_httpd(self):
|
||||
if self.httpd:
|
||||
@@ -369,21 +382,6 @@ class Updater(object):
|
||||
self.logger.debug('Requesting Dispatcher to stop...')
|
||||
self.dispatcher.stop()
|
||||
|
||||
def _join_async_threads(self):
|
||||
with dispatcher.ASYNC_LOCK:
|
||||
threads = list(dispatcher.ASYNC_THREADS)
|
||||
total = len(threads)
|
||||
|
||||
# Stop all threads in the thread pool by put()ting one non-tuple per thread
|
||||
for i in range(total):
|
||||
dispatcher.ASYNC_QUEUE.put(None)
|
||||
|
||||
for i, thr in enumerate(threads):
|
||||
self.logger.debug('Waiting for async thread {0}/{1} to end'.format(i + 1, total))
|
||||
thr.join()
|
||||
dispatcher.ASYNC_THREADS.remove(thr)
|
||||
self.logger.debug('async thread {0}/{1} has ended'.format(i + 1, total))
|
||||
|
||||
def _join_threads(self):
|
||||
for thr in self.__threads:
|
||||
self.logger.debug('Waiting for {0} thread to end'.format(thr.name))
|
||||
|
||||
+12
-6
@@ -21,7 +21,6 @@
|
||||
from os.path import basename
|
||||
|
||||
from telegram import TelegramObject
|
||||
from telegram.utils.request import download as _download
|
||||
|
||||
|
||||
class File(TelegramObject):
|
||||
@@ -34,25 +33,31 @@ class File(TelegramObject):
|
||||
|
||||
Args:
|
||||
file_id (str):
|
||||
bot (telegram.Bot):
|
||||
**kwargs: Arbitrary keyword arguments.
|
||||
|
||||
Keyword Args:
|
||||
file_size (Optional[int]):
|
||||
file_path (Optional[str]):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, file_id, **kwargs):
|
||||
def __init__(self, file_id, bot, **kwargs):
|
||||
# Required
|
||||
self.file_id = str(file_id)
|
||||
|
||||
# Optionals
|
||||
self.file_size = int(kwargs.get('file_size', 0))
|
||||
self.file_path = str(kwargs.get('file_path', ''))
|
||||
|
||||
self.bot = bot
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.File:
|
||||
@@ -60,12 +65,13 @@ class File(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return File(**data)
|
||||
return File(bot=bot, **data)
|
||||
|
||||
def download(self, custom_path=None):
|
||||
"""
|
||||
Args:
|
||||
custom_path (str):
|
||||
|
||||
"""
|
||||
url = self.file_path
|
||||
|
||||
@@ -74,4 +80,4 @@ class File(TelegramObject):
|
||||
else:
|
||||
filename = basename(url)
|
||||
|
||||
_download(url, filename)
|
||||
self.bot.request.download(url, filename)
|
||||
|
||||
@@ -43,10 +43,11 @@ class ForceReply(ReplyMarkup):
|
||||
self.selective = bool(kwargs.get('selective', False))
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.ForceReply:
|
||||
|
||||
@@ -52,8 +52,16 @@ class InlineKeyboardButton(TelegramObject):
|
||||
self.switch_inline_query = kwargs.get('switch_inline_query')
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineKeyboardButton, InlineKeyboardButton).de_json(data)
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.InlineKeyboardButton:
|
||||
"""
|
||||
data = super(InlineKeyboardButton, InlineKeyboardButton).de_json(data, bot)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
@@ -61,12 +69,12 @@ class InlineKeyboardButton(TelegramObject):
|
||||
return InlineKeyboardButton(**data)
|
||||
|
||||
@staticmethod
|
||||
def de_list(data):
|
||||
def de_list(data, bot):
|
||||
if not data:
|
||||
return []
|
||||
|
||||
inline_keyboards = list()
|
||||
for inline_keyboard in data:
|
||||
inline_keyboards.append(InlineKeyboardButton.de_json(inline_keyboard))
|
||||
inline_keyboards.append(InlineKeyboardButton.de_json(inline_keyboard, bot))
|
||||
|
||||
return inline_keyboards
|
||||
|
||||
@@ -38,13 +38,21 @@ class InlineKeyboardMarkup(ReplyMarkup):
|
||||
self.inline_keyboard = inline_keyboard
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineKeyboardMarkup, InlineKeyboardMarkup).de_json(data)
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.InlineKeyboardMarkup:
|
||||
"""
|
||||
data = super(InlineKeyboardMarkup, InlineKeyboardMarkup).de_json(data, bot)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['inline_keyboard'] = [InlineKeyboardButton.de_list(inline_keyboard)
|
||||
data['inline_keyboard'] = [InlineKeyboardButton.de_list(inline_keyboard, bot)
|
||||
for inline_keyboard in data['inline_keyboard']]
|
||||
|
||||
return InlineKeyboardMarkup(**data)
|
||||
|
||||
+14
-6
@@ -42,9 +42,10 @@ class InlineQuery(TelegramObject):
|
||||
|
||||
Keyword Args:
|
||||
location (optional[:class:`telegram.Location`]):
|
||||
bot (Optional[Bot]): The Bot to use for instance methods
|
||||
"""
|
||||
|
||||
def __init__(self, id, from_user, query, offset, **kwargs):
|
||||
def __init__(self, id, from_user, query, offset, bot=None, **kwargs):
|
||||
# Required
|
||||
self.id = id
|
||||
self.from_user = from_user
|
||||
@@ -54,24 +55,27 @@ class InlineQuery(TelegramObject):
|
||||
# Optional
|
||||
self.location = kwargs.get('location')
|
||||
|
||||
self.bot = bot
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.InlineQuery:
|
||||
"""
|
||||
data = super(InlineQuery, InlineQuery).de_json(data)
|
||||
data = super(InlineQuery, InlineQuery).de_json(data, bot)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['from_user'] = User.de_json(data.get('from'))
|
||||
data['location'] = Location.de_json(data.get('location'))
|
||||
data['from_user'] = User.de_json(data.get('from'), bot)
|
||||
data['location'] = Location.de_json(data.get('location'), bot)
|
||||
|
||||
return InlineQuery(**data)
|
||||
return InlineQuery(bot=bot, **data)
|
||||
|
||||
def to_dict(self):
|
||||
"""
|
||||
@@ -84,3 +88,7 @@ class InlineQuery(TelegramObject):
|
||||
data['from'] = data.pop('from_user', None)
|
||||
|
||||
return data
|
||||
|
||||
def answer(self, *args, **kwargs):
|
||||
"""Shortcut for ``bot.answerInlineQuery(update.inline_query.id, *args, **kwargs)``"""
|
||||
return self.bot.answerInlineQuery(self.id, *args, **kwargs)
|
||||
|
||||
@@ -35,11 +35,11 @@ class InlineQueryResult(TelegramObject):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, type, id):
|
||||
def __init__(self, type, id, **kwargs):
|
||||
# Required
|
||||
self.type = str(type)
|
||||
self.id = str(id)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
return super(InlineQueryResult, InlineQueryResult).de_json(data)
|
||||
def de_json(data, bot):
|
||||
return super(InlineQueryResult, InlineQueryResult).de_json(data, bot)
|
||||
|
||||
@@ -94,11 +94,11 @@ class InlineQueryResultArticle(InlineQueryResult):
|
||||
self.thumb_height = thumb_height
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultArticle, InlineQueryResultArticle).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultArticle, InlineQueryResultArticle).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultArticle(**data)
|
||||
|
||||
@@ -84,11 +84,11 @@ class InlineQueryResultAudio(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultAudio, InlineQueryResultAudio).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultAudio, InlineQueryResultAudio).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultAudio(**data)
|
||||
|
||||
@@ -65,11 +65,11 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultCachedAudio, InlineQueryResultCachedAudio).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultCachedAudio, InlineQueryResultCachedAudio).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultCachedAudio(**data)
|
||||
|
||||
@@ -49,12 +49,12 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultCachedDocument,
|
||||
InlineQueryResultCachedDocument).de_json(data)
|
||||
InlineQueryResultCachedDocument).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultCachedDocument(**data)
|
||||
|
||||
@@ -47,11 +47,11 @@ class InlineQueryResultCachedGif(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultCachedGif, InlineQueryResultCachedGif).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultCachedGif, InlineQueryResultCachedGif).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultCachedGif(**data)
|
||||
|
||||
@@ -47,12 +47,12 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultCachedMpeg4Gif,
|
||||
InlineQueryResultCachedMpeg4Gif).de_json(data)
|
||||
InlineQueryResultCachedMpeg4Gif).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultCachedMpeg4Gif(**data)
|
||||
|
||||
@@ -50,11 +50,11 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultCachedPhoto, InlineQueryResultCachedPhoto).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultCachedPhoto, InlineQueryResultCachedPhoto).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultCachedPhoto(**data)
|
||||
|
||||
@@ -41,11 +41,12 @@ class InlineQueryResultCachedSticker(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultCachedSticker, InlineQueryResultCachedSticker).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultCachedSticker,
|
||||
InlineQueryResultCachedSticker).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultCachedSticker(**data)
|
||||
|
||||
@@ -49,11 +49,11 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultCachedVideo, InlineQueryResultCachedVideo).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultCachedVideo, InlineQueryResultCachedVideo).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultCachedVideo(**data)
|
||||
|
||||
@@ -46,11 +46,11 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultCachedVoice, InlineQueryResultCachedVoice).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultCachedVoice, InlineQueryResultCachedVoice).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultCachedVoice(**data)
|
||||
|
||||
@@ -55,11 +55,11 @@ class InlineQueryResultContact(InlineQueryResult):
|
||||
self.thumb_height = thumb_height
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultContact, InlineQueryResultContact).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultContact, InlineQueryResultContact).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultContact(**data)
|
||||
|
||||
@@ -60,11 +60,11 @@ class InlineQueryResultDocument(InlineQueryResult):
|
||||
self.thumb_height = thumb_height
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultDocument, InlineQueryResultDocument).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultDocument, InlineQueryResultDocument).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultDocument(**data)
|
||||
|
||||
@@ -56,11 +56,11 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultGif, InlineQueryResultGif).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultGif, InlineQueryResultGif).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultGif(**data)
|
||||
|
||||
@@ -54,11 +54,11 @@ class InlineQueryResultLocation(InlineQueryResult):
|
||||
self.thumb_height = thumb_height
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultLocation, InlineQueryResultLocation).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultLocation, InlineQueryResultLocation).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultLocation(**data)
|
||||
|
||||
@@ -56,11 +56,11 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultMpeg4Gif, InlineQueryResultMpeg4Gif).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultMpeg4Gif, InlineQueryResultMpeg4Gif).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultMpeg4Gif(**data)
|
||||
|
||||
@@ -58,11 +58,11 @@ class InlineQueryResultPhoto(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultPhoto, InlineQueryResultPhoto).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultPhoto, InlineQueryResultPhoto).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultPhoto(**data)
|
||||
|
||||
@@ -60,11 +60,11 @@ class InlineQueryResultVenue(InlineQueryResult):
|
||||
self.thumb_height = thumb_height
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultVenue, InlineQueryResultVenue).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultVenue, InlineQueryResultVenue).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultVenue(**data)
|
||||
|
||||
@@ -63,11 +63,11 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultVideo, InlineQueryResultVideo).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultVideo, InlineQueryResultVideo).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultVideo(**data)
|
||||
|
||||
@@ -47,11 +47,11 @@ class InlineQueryResultVoice(InlineQueryResult):
|
||||
self.input_message_content = input_message_content
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InlineQueryResultVoice, InlineQueryResultVoice).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InlineQueryResultVoice, InlineQueryResultVoice).de_json(data, bot)
|
||||
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'))
|
||||
data['input_message_content'] = InputMessageContent.de_json(data.get(
|
||||
'input_message_content'))
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['input_message_content'] = InputMessageContent.de_json(
|
||||
data.get('input_message_content'), bot)
|
||||
|
||||
return InlineQueryResultVoice(**data)
|
||||
|
||||
@@ -33,5 +33,5 @@ class InputContactMessageContent(InputMessageContent):
|
||||
self.last_name = last_name
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
return InputContactMessageContent(**data)
|
||||
|
||||
@@ -125,9 +125,9 @@ class InputFile(object):
|
||||
|
||||
# Add input_file to upload
|
||||
form.extend([
|
||||
form_boundary, 'Content-Disposition: form-data; name="%s"; filename="%s"' % (
|
||||
self.input_name, self.filename
|
||||
), 'Content-Type: %s' % self.mimetype, '', self.input_file_content
|
||||
form_boundary, 'Content-Disposition: form-data; name="%s"; filename="%s"' %
|
||||
(self.input_name,
|
||||
self.filename), 'Content-Type: %s' % self.mimetype, '', self.input_file_content
|
||||
])
|
||||
|
||||
form.append('--' + self.boundary + '--')
|
||||
|
||||
@@ -31,5 +31,5 @@ class InputLocationMessageContent(InputMessageContent):
|
||||
self.longitude = longitude
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
return InputLocationMessageContent(**data)
|
||||
|
||||
@@ -26,33 +26,33 @@ class InputMessageContent(TelegramObject):
|
||||
"""Base class for Telegram InputMessageContent Objects"""
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(InputMessageContent, InputMessageContent).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(InputMessageContent, InputMessageContent).de_json(data, bot)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
try:
|
||||
from telegram import InputTextMessageContent
|
||||
return InputTextMessageContent.de_json(data)
|
||||
return InputTextMessageContent.de_json(data, bot)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
from telegram import InputVenueMessageContent
|
||||
return InputVenueMessageContent.de_json(data)
|
||||
return InputVenueMessageContent.de_json(data, bot)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
from telegram import InputLocationMessageContent
|
||||
return InputLocationMessageContent.de_json(data)
|
||||
return InputLocationMessageContent.de_json(data, bot)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
from telegram import InputContactMessageContent
|
||||
return InputContactMessageContent.de_json(data)
|
||||
return InputContactMessageContent.de_json(data, bot)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
|
||||
@@ -33,5 +33,5 @@ class InputTextMessageContent(InputMessageContent):
|
||||
self.disable_web_page_preview = disable_web_page_preview
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
return InputTextMessageContent(**data)
|
||||
|
||||
@@ -35,5 +35,5 @@ class InputVenueMessageContent(InputMessageContent):
|
||||
self.foursquare_id = foursquare_id
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
return InputVenueMessageContent(**data)
|
||||
|
||||
@@ -43,19 +43,19 @@ class KeyboardButton(TelegramObject):
|
||||
self.request_location = request_location
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return KeyboardButton(**data)
|
||||
|
||||
@staticmethod
|
||||
def de_list(data):
|
||||
def de_list(data, bot):
|
||||
if not data:
|
||||
return []
|
||||
|
||||
keyboards = list()
|
||||
for keyboard in data:
|
||||
keyboards.append(KeyboardButton.de_json(keyboard))
|
||||
keyboards.append(KeyboardButton.de_json(keyboard, bot))
|
||||
|
||||
return keyboards
|
||||
|
||||
@@ -39,10 +39,11 @@ class Location(TelegramObject):
|
||||
self.latitude = float(latitude)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.Location:
|
||||
|
||||
+241
-22
@@ -19,6 +19,7 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains a object that represents a Telegram Message."""
|
||||
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from time import mktime
|
||||
|
||||
@@ -102,9 +103,10 @@ class Message(TelegramObject):
|
||||
migrate_to_chat_id (Optional[int]):
|
||||
migrate_from_chat_id (Optional[int]):
|
||||
channel_chat_created (Optional[bool]):
|
||||
bot (Optional[Bot]): The Bot to use for instance methods
|
||||
"""
|
||||
|
||||
def __init__(self, message_id, from_user, date, chat, **kwargs):
|
||||
def __init__(self, message_id, from_user, date, chat, bot=None, **kwargs):
|
||||
# Required
|
||||
self.message_id = int(message_id)
|
||||
self.from_user = from_user
|
||||
@@ -140,16 +142,19 @@ class Message(TelegramObject):
|
||||
self.channel_chat_created = bool(kwargs.get('channel_chat_created', False))
|
||||
self.pinned_message = kwargs.get('pinned_message')
|
||||
|
||||
self.bot = bot
|
||||
|
||||
@property
|
||||
def chat_id(self):
|
||||
"""int: Short for :attr:`Message.chat.id`"""
|
||||
return self.chat.id
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.Message:
|
||||
@@ -157,30 +162,30 @@ class Message(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['from_user'] = User.de_json(data.get('from'))
|
||||
data['from_user'] = User.de_json(data.get('from'), bot)
|
||||
data['date'] = datetime.fromtimestamp(data['date'])
|
||||
data['chat'] = Chat.de_json(data.get('chat'))
|
||||
data['entities'] = MessageEntity.de_list(data.get('entities'))
|
||||
data['forward_from'] = User.de_json(data.get('forward_from'))
|
||||
data['forward_from_chat'] = Chat.de_json(data.get('forward_from_chat'))
|
||||
data['chat'] = Chat.de_json(data.get('chat'), bot)
|
||||
data['entities'] = MessageEntity.de_list(data.get('entities'), bot)
|
||||
data['forward_from'] = User.de_json(data.get('forward_from'), bot)
|
||||
data['forward_from_chat'] = Chat.de_json(data.get('forward_from_chat'), bot)
|
||||
data['forward_date'] = Message._fromtimestamp(data.get('forward_date'))
|
||||
data['reply_to_message'] = Message.de_json(data.get('reply_to_message'))
|
||||
data['reply_to_message'] = Message.de_json(data.get('reply_to_message'), bot)
|
||||
data['edit_date'] = Message._fromtimestamp(data.get('edit_date'))
|
||||
data['audio'] = Audio.de_json(data.get('audio'))
|
||||
data['document'] = Document.de_json(data.get('document'))
|
||||
data['photo'] = PhotoSize.de_list(data.get('photo'))
|
||||
data['sticker'] = Sticker.de_json(data.get('sticker'))
|
||||
data['video'] = Video.de_json(data.get('video'))
|
||||
data['voice'] = Voice.de_json(data.get('voice'))
|
||||
data['contact'] = Contact.de_json(data.get('contact'))
|
||||
data['location'] = Location.de_json(data.get('location'))
|
||||
data['venue'] = Venue.de_json(data.get('venue'))
|
||||
data['new_chat_member'] = User.de_json(data.get('new_chat_member'))
|
||||
data['left_chat_member'] = User.de_json(data.get('left_chat_member'))
|
||||
data['new_chat_photo'] = PhotoSize.de_list(data.get('new_chat_photo'))
|
||||
data['pinned_message'] = Message.de_json(data.get('pinned_message'))
|
||||
data['audio'] = Audio.de_json(data.get('audio'), bot)
|
||||
data['document'] = Document.de_json(data.get('document'), bot)
|
||||
data['photo'] = PhotoSize.de_list(data.get('photo'), bot)
|
||||
data['sticker'] = Sticker.de_json(data.get('sticker'), bot)
|
||||
data['video'] = Video.de_json(data.get('video'), bot)
|
||||
data['voice'] = Voice.de_json(data.get('voice'), bot)
|
||||
data['contact'] = Contact.de_json(data.get('contact'), bot)
|
||||
data['location'] = Location.de_json(data.get('location'), bot)
|
||||
data['venue'] = Venue.de_json(data.get('venue'), bot)
|
||||
data['new_chat_member'] = User.de_json(data.get('new_chat_member'), bot)
|
||||
data['left_chat_member'] = User.de_json(data.get('left_chat_member'), bot)
|
||||
data['new_chat_photo'] = PhotoSize.de_list(data.get('new_chat_photo'), bot)
|
||||
data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot)
|
||||
|
||||
return Message(**data)
|
||||
return Message(bot=bot, **data)
|
||||
|
||||
def __getitem__(self, item):
|
||||
if item in self.__dict__.keys():
|
||||
@@ -244,3 +249,217 @@ class Message(TelegramObject):
|
||||
except AttributeError:
|
||||
# Python 3 (< 3.3) and Python 2
|
||||
return int(mktime(dt_obj.timetuple()))
|
||||
|
||||
def _quote(self, kwargs):
|
||||
"""Modify kwargs for replying with or without quoting"""
|
||||
|
||||
if 'reply_to_message_id' in kwargs:
|
||||
if 'quote' in kwargs:
|
||||
del kwargs['quote']
|
||||
|
||||
elif 'quote' in kwargs:
|
||||
if kwargs['quote']:
|
||||
kwargs['reply_to_message_id'] = self.message_id
|
||||
|
||||
del kwargs['quote']
|
||||
|
||||
else:
|
||||
if self.chat.type != Chat.PRIVATE:
|
||||
kwargs['reply_to_message_id'] = self.message_id
|
||||
|
||||
def reply_text(self, *args, **kwargs):
|
||||
"""
|
||||
Shortcut for ``bot.sendMessage(update.message.chat_id, *args, **kwargs)``
|
||||
|
||||
Keyword Args:
|
||||
quote (Optional[bool]): If set to ``True``, the message is sent as an actual reply to
|
||||
this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
|
||||
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
|
||||
"""
|
||||
|
||||
self._quote(kwargs)
|
||||
return self.bot.sendMessage(self.chat_id, *args, **kwargs)
|
||||
|
||||
def reply_photo(self, *args, **kwargs):
|
||||
"""
|
||||
Shortcut for ``bot.sendPhoto(update.message.chat_id, *args, **kwargs)``
|
||||
|
||||
Keyword Args:
|
||||
quote (Optional[bool]): If set to ``True``, the photo is sent as an actual reply to
|
||||
this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
|
||||
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
|
||||
"""
|
||||
|
||||
self._quote(kwargs)
|
||||
return self.bot.sendPhoto(self.chat_id, *args, **kwargs)
|
||||
|
||||
def reply_audio(self, *args, **kwargs):
|
||||
"""
|
||||
Shortcut for ``bot.sendAudio(update.message.chat_id, *args, **kwargs)``
|
||||
|
||||
Keyword Args:
|
||||
quote (Optional[bool]): If set to ``True``, the audio is sent as an actual reply to
|
||||
this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
|
||||
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
|
||||
"""
|
||||
|
||||
self._quote(kwargs)
|
||||
return self.bot.sendAudio(self.chat_id, *args, **kwargs)
|
||||
|
||||
def reply_document(self, *args, **kwargs):
|
||||
"""
|
||||
Shortcut for ``bot.sendDocument(update.message.chat_id, *args, **kwargs)``
|
||||
|
||||
Keyword Args:
|
||||
quote (Optional[bool]): If set to ``True``, the document is sent as an actual reply to
|
||||
this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
|
||||
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
|
||||
"""
|
||||
|
||||
self._quote(kwargs)
|
||||
return self.bot.sendDocument(self.chat_id, *args, **kwargs)
|
||||
|
||||
def reply_sticker(self, *args, **kwargs):
|
||||
"""
|
||||
Shortcut for ``bot.sendSticker(update.message.chat_id, *args, **kwargs)``
|
||||
|
||||
Keyword Args:
|
||||
quote (Optional[bool]): If set to ``True``, the sticker is sent as an actual reply to
|
||||
this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
|
||||
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
|
||||
"""
|
||||
|
||||
self._quote(kwargs)
|
||||
return self.bot.sendSticker(self.chat_id, *args, **kwargs)
|
||||
|
||||
def reply_video(self, *args, **kwargs):
|
||||
"""
|
||||
Shortcut for ``bot.sendVideo(update.message.chat_id, *args, **kwargs)``
|
||||
|
||||
Keyword Args:
|
||||
quote (Optional[bool]): If set to ``True``, the video is sent as an actual reply to
|
||||
this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
|
||||
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
|
||||
"""
|
||||
|
||||
self._quote(kwargs)
|
||||
return self.bot.sendVideo(self.chat_id, *args, **kwargs)
|
||||
|
||||
def reply_voice(self, *args, **kwargs):
|
||||
"""
|
||||
Shortcut for ``bot.sendVoice(update.message.chat_id, *args, **kwargs)``
|
||||
|
||||
Keyword Args:
|
||||
quote (Optional[bool]): If set to ``True``, the voice is sent as an actual reply to
|
||||
this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
|
||||
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
|
||||
"""
|
||||
|
||||
self._quote(kwargs)
|
||||
return self.bot.sendVoice(self.chat_id, *args, **kwargs)
|
||||
|
||||
def reply_location(self, *args, **kwargs):
|
||||
"""
|
||||
Shortcut for ``bot.sendLocation(update.message.chat_id, *args, **kwargs)``
|
||||
|
||||
Keyword Args:
|
||||
quote (Optional[bool]): If set to ``True``, the location is sent as an actual reply to
|
||||
this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
|
||||
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
|
||||
"""
|
||||
|
||||
self._quote(kwargs)
|
||||
return self.bot.sendLocation(self.chat_id, *args, **kwargs)
|
||||
|
||||
def reply_venue(self, *args, **kwargs):
|
||||
"""
|
||||
Shortcut for ``bot.sendVenue(update.message.chat_id, *args, **kwargs)``
|
||||
|
||||
Keyword Args:
|
||||
quote (Optional[bool]): If set to ``True``, the venue is sent as an actual reply to
|
||||
this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
|
||||
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
|
||||
"""
|
||||
|
||||
self._quote(kwargs)
|
||||
return self.bot.sendVenue(self.chat_id, *args, **kwargs)
|
||||
|
||||
def reply_contact(self, *args, **kwargs):
|
||||
"""
|
||||
Shortcut for ``bot.sendContact(update.message.chat_id, *args, **kwargs)``
|
||||
|
||||
Keyword Args:
|
||||
quote (Optional[bool]): If set to ``True``, the contact is sent as an actual reply to
|
||||
this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
|
||||
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
|
||||
"""
|
||||
|
||||
self._quote(kwargs)
|
||||
return self.bot.sendContact(self.chat_id, *args, **kwargs)
|
||||
|
||||
def forward(self, chat_id, disable_notification=False):
|
||||
"""Shortcut for
|
||||
|
||||
bot.forwardMessage(chat_id=chat_id,
|
||||
from_chat_id=update.message.chat_id,
|
||||
disable_notification=disable_notification,
|
||||
message_id=update.message.message_id)
|
||||
|
||||
"""
|
||||
return self.bot.forwardMessage(
|
||||
chat_id=chat_id,
|
||||
from_chat_id=self.chat_id,
|
||||
disable_notification=disable_notification,
|
||||
message_id=self.message_id)
|
||||
|
||||
def parse_entity(self, entity):
|
||||
"""
|
||||
Returns the text from a given :class:`telegram.MessageEntity`.
|
||||
|
||||
Note:
|
||||
This method is present because Telegram calculates the offset and length in
|
||||
UTF-16 codepoint pairs, which some versions of Python don't handle automatically.
|
||||
(That is, you can't just slice ``Message.text`` with the offset and length.)
|
||||
|
||||
Args:
|
||||
entity (MessageEntity): The entity to extract the text from. It must be an entity that
|
||||
belongs to this message.
|
||||
|
||||
Returns:
|
||||
str: The text of the given entity
|
||||
"""
|
||||
# Is it a narrow build, if so we don't need to convert
|
||||
if sys.maxunicode == 0xffff:
|
||||
return self.text[entity.offset:entity.offset + entity.length]
|
||||
else:
|
||||
entity_text = self.text.encode('utf-16-le')
|
||||
entity_text = entity_text[entity.offset * 2:(entity.offset + entity.length) * 2]
|
||||
|
||||
return entity_text.decode('utf-16-le')
|
||||
|
||||
def parse_entities(self, types=None):
|
||||
"""
|
||||
Returns a ``dict`` that maps :class:`telegram.MessageEntity` to ``str``.
|
||||
It contains entities from this message filtered by their ``type`` attribute as the key, and
|
||||
the text that each entity belongs to as the value of the ``dict``.
|
||||
|
||||
Note:
|
||||
This method should always be used instead of the ``entities`` attribute, since it
|
||||
calculates the correct substring from the message text based on UTF-16 codepoints.
|
||||
See ``get_entity_text`` for more info.
|
||||
|
||||
Args:
|
||||
types (Optional[list]): List of ``MessageEntity`` types as strings. If the ``type``
|
||||
attribute of an entity is contained in this list, it will be returned.
|
||||
Defaults to a list of all types. All types can be found as constants in
|
||||
:class:`telegram.MessageEntity`.
|
||||
|
||||
Returns:
|
||||
dict[:class:`telegram.MessageEntity`, ``str``]: A dictionary of entities mapped to the
|
||||
text that belongs to them, calculated based on UTF-16 codepoints.
|
||||
"""
|
||||
if types is None:
|
||||
types = MessageEntity.ALL_TYPES
|
||||
|
||||
return {entity: self.parse_entity(entity)
|
||||
for entity in self.entities if entity.type in types}
|
||||
|
||||
@@ -44,15 +44,15 @@ class MessageEntity(TelegramObject):
|
||||
self.user = kwargs.get('user')
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(MessageEntity, MessageEntity).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(MessageEntity, MessageEntity).de_json(data, bot)
|
||||
|
||||
data['user'] = User.de_json(data.get('user'))
|
||||
data['user'] = User.de_json(data.get('user'), bot)
|
||||
|
||||
return MessageEntity(**data)
|
||||
|
||||
@staticmethod
|
||||
def de_list(data):
|
||||
def de_list(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (list):
|
||||
@@ -65,6 +65,20 @@ class MessageEntity(TelegramObject):
|
||||
|
||||
entities = list()
|
||||
for entity in data:
|
||||
entities.append(MessageEntity.de_json(entity))
|
||||
entities.append(MessageEntity.de_json(entity, bot))
|
||||
|
||||
return entities
|
||||
|
||||
MENTION = 'mention'
|
||||
HASHTAG = 'hashtag'
|
||||
BOT_COMMAND = 'bot_command'
|
||||
URL = 'url'
|
||||
EMAIL = 'email'
|
||||
BOLD = 'bold'
|
||||
ITALIC = 'italic'
|
||||
CODE = 'code'
|
||||
PRE = 'pre'
|
||||
TEXT_LINK = 'text_link'
|
||||
TEXT_MENTION = 'text_mention'
|
||||
ALL_TYPES = [MENTION, HASHTAG, BOT_COMMAND, URL, EMAIL, BOLD, ITALIC, CODE, PRE, TEXT_LINK,
|
||||
TEXT_MENTION]
|
||||
|
||||
@@ -55,10 +55,11 @@ class PhotoSize(TelegramObject):
|
||||
and self.height == other.height and self.file_size == other.file_size)
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.PhotoSize:
|
||||
@@ -69,10 +70,11 @@ class PhotoSize(TelegramObject):
|
||||
return PhotoSize(**data)
|
||||
|
||||
@staticmethod
|
||||
def de_list(data):
|
||||
def de_list(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (list):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
List<telegram.PhotoSize>:
|
||||
@@ -82,6 +84,6 @@ class PhotoSize(TelegramObject):
|
||||
|
||||
photos = list()
|
||||
for photo in data:
|
||||
photos.append(PhotoSize.de_json(photo))
|
||||
photos.append(PhotoSize.de_json(photo, bot))
|
||||
|
||||
return photos
|
||||
|
||||
@@ -44,10 +44,11 @@ class ReplyKeyboardHide(ReplyMarkup):
|
||||
self.selective = bool(kwargs.get('selective', False))
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot(telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.ReplyKeyboardHide:
|
||||
|
||||
@@ -50,10 +50,11 @@ class ReplyKeyboardMarkup(ReplyMarkup):
|
||||
self.selective = bool(kwargs.get('selective', False))
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.ReplyKeyboardMarkup:
|
||||
@@ -61,7 +62,7 @@ class ReplyKeyboardMarkup(ReplyMarkup):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['keyboard'] = [KeyboardButton.de_list(keyboard) for keyboard in data['keyboard']]
|
||||
data['keyboard'] = [KeyboardButton.de_list(keyboard, bot) for keyboard in data['keyboard']]
|
||||
|
||||
return ReplyKeyboardMarkup(**data)
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ class ReplyMarkup(TelegramObject):
|
||||
"""Base class for Telegram ReplyMarkup Objects"""
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(ReplyMarkup, ReplyMarkup).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(ReplyMarkup, ReplyMarkup).de_json(data, bot)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
+4
-3
@@ -55,10 +55,11 @@ class Sticker(TelegramObject):
|
||||
self.file_size = int(kwargs.get('file_size', 0))
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.Sticker:
|
||||
@@ -66,6 +67,6 @@ class Sticker(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'))
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
|
||||
return Sticker(**data)
|
||||
|
||||
+8
-6
@@ -55,10 +55,11 @@ class Update(TelegramObject):
|
||||
self.callback_query = kwargs.get('callback_query')
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.Update:
|
||||
@@ -66,10 +67,11 @@ class Update(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['message'] = Message.de_json(data.get('message'))
|
||||
data['edited_message'] = Message.de_json(data.get('edited_message'))
|
||||
data['inline_query'] = InlineQuery.de_json(data.get('inline_query'))
|
||||
data['chosen_inline_result'] = ChosenInlineResult.de_json(data.get('chosen_inline_result'))
|
||||
data['callback_query'] = CallbackQuery.de_json(data.get('callback_query'))
|
||||
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)
|
||||
data['chosen_inline_result'] = ChosenInlineResult.de_json(
|
||||
data.get('chosen_inline_result'), bot)
|
||||
data['callback_query'] = CallbackQuery.de_json(data.get('callback_query'), bot)
|
||||
|
||||
return Update(**data)
|
||||
|
||||
+14
-4
@@ -41,9 +41,10 @@ class User(TelegramObject):
|
||||
type (Optional[str]):
|
||||
last_name (Optional[str]):
|
||||
username (Optional[str]):
|
||||
bot (Optional[Bot]): The Bot to use for instance methods
|
||||
"""
|
||||
|
||||
def __init__(self, id, first_name, **kwargs):
|
||||
def __init__(self, id, first_name, bot=None, **kwargs):
|
||||
# Required
|
||||
self.id = int(id)
|
||||
self.first_name = first_name
|
||||
@@ -52,6 +53,8 @@ class User(TelegramObject):
|
||||
self.last_name = kwargs.get('last_name', '')
|
||||
self.username = kwargs.get('username', '')
|
||||
|
||||
self.bot = bot
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""str: """
|
||||
@@ -62,10 +65,11 @@ class User(TelegramObject):
|
||||
return self.first_name
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.User:
|
||||
@@ -73,4 +77,10 @@ class User(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return User(**data)
|
||||
return User(bot=bot, **data)
|
||||
|
||||
def get_profile_photos(self, *args, **kwargs):
|
||||
"""
|
||||
Shortcut for ``bot.getUserProfilePhotos(update.message.from_user.id, *args, **kwargs)``
|
||||
"""
|
||||
return self.bot.getUserProfilePhotos(self.id, *args, **kwargs)
|
||||
|
||||
@@ -40,10 +40,11 @@ class UserProfilePhotos(TelegramObject):
|
||||
self.photos = photos
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.UserProfilePhotos:
|
||||
@@ -51,7 +52,7 @@ class UserProfilePhotos(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['photos'] = [PhotoSize.de_list(photo) for photo in data['photos']]
|
||||
data['photos'] = [PhotoSize.de_list(photo, bot) for photo in data['photos']]
|
||||
|
||||
return UserProfilePhotos(**data)
|
||||
|
||||
|
||||
+159
-194
@@ -18,7 +18,10 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains methods to make POST and GET requests"""
|
||||
|
||||
import json
|
||||
try:
|
||||
import ujson as json
|
||||
except ImportError:
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import logging
|
||||
@@ -28,225 +31,187 @@ import urllib3
|
||||
from urllib3.connection import HTTPConnection
|
||||
|
||||
from telegram import (InputFile, TelegramError)
|
||||
from telegram.error import Unauthorized, NetworkError, TimedOut, BadRequest
|
||||
|
||||
_CON_POOL = None
|
||||
""":type: urllib3.PoolManager"""
|
||||
_CON_POOL_PROXY = None
|
||||
_CON_POOL_PROXY_KWARGS = {}
|
||||
CON_POOL_SIZE = 1
|
||||
from telegram.error import Unauthorized, NetworkError, TimedOut, BadRequest, ChatMigrated
|
||||
|
||||
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
||||
|
||||
|
||||
def _get_con_pool():
|
||||
if _CON_POOL is not None:
|
||||
return _CON_POOL
|
||||
|
||||
_init_con_pool()
|
||||
return _CON_POOL
|
||||
|
||||
|
||||
def _init_con_pool():
|
||||
global _CON_POOL
|
||||
kwargs = dict(maxsize=CON_POOL_SIZE,
|
||||
cert_reqs='CERT_REQUIRED',
|
||||
ca_certs=certifi.where(),
|
||||
socket_options=HTTPConnection.default_socket_options + [
|
||||
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
|
||||
])
|
||||
proxy_url = _get_con_pool_proxy()
|
||||
if not proxy_url:
|
||||
mgr = urllib3.PoolManager(**kwargs)
|
||||
else:
|
||||
if _CON_POOL_PROXY_KWARGS:
|
||||
kwargs.update(_CON_POOL_PROXY_KWARGS)
|
||||
mgr = urllib3.proxy_from_url(proxy_url, **kwargs)
|
||||
if mgr.proxy.auth:
|
||||
# TODO: what about other auth types?
|
||||
auth_hdrs = urllib3.make_headers(proxy_basic_auth=mgr.proxy.auth)
|
||||
mgr.proxy_headers.update(auth_hdrs)
|
||||
|
||||
_CON_POOL = mgr
|
||||
|
||||
|
||||
def is_con_pool_initialized():
|
||||
return _CON_POOL is not None
|
||||
|
||||
|
||||
def stop_con_pool():
|
||||
global _CON_POOL
|
||||
if _CON_POOL is not None:
|
||||
_CON_POOL.clear()
|
||||
_CON_POOL = None
|
||||
|
||||
|
||||
def set_con_pool_proxy(url, **urllib3_kwargs):
|
||||
"""Setup connection pool behind a proxy
|
||||
class Request(object):
|
||||
"""
|
||||
Helper class for python-telegram-bot which provides methods to perform POST & GET towards
|
||||
telegram servers.
|
||||
|
||||
Args:
|
||||
url (str): The URL to the proxy server. For example: `http://127.0.0.1:3128`
|
||||
urllib3_kwargs (dict): Arbitrary arguments passed as-is to `urllib3.ProxyManager`
|
||||
|
||||
"""
|
||||
global _CON_POOL_PROXY
|
||||
global _CON_POOL_PROXY_KWARGS
|
||||
|
||||
if is_con_pool_initialized():
|
||||
raise TelegramError('conpool already initialized')
|
||||
|
||||
_CON_POOL_PROXY = url
|
||||
_CON_POOL_PROXY_KWARGS = urllib3_kwargs
|
||||
|
||||
|
||||
def _get_con_pool_proxy():
|
||||
"""Return the user configured proxy according to the following order:
|
||||
|
||||
* proxy configured using `set_con_pool_proxy()`.
|
||||
* proxy set in `HTTPS_PROXY` env. var.
|
||||
* proxy set in `https_proxy` env. var.
|
||||
* None (if no proxy is configured)
|
||||
|
||||
Returns:
|
||||
str | None
|
||||
|
||||
"""
|
||||
if _CON_POOL_PROXY:
|
||||
return _CON_POOL_PROXY
|
||||
from_env = os.environ.get('HTTPS_PROXY')
|
||||
if from_env:
|
||||
return from_env
|
||||
from_env = os.environ.get('https_proxy')
|
||||
if from_env:
|
||||
return from_env
|
||||
return None
|
||||
|
||||
|
||||
def _parse(json_data):
|
||||
"""Try and parse the JSON returned from Telegram.
|
||||
|
||||
Returns:
|
||||
dict: A JSON parsed as Python dict with results - on error this dict will be empty.
|
||||
|
||||
"""
|
||||
decoded_s = json_data.decode('utf-8')
|
||||
try:
|
||||
data = json.loads(decoded_s)
|
||||
except ValueError:
|
||||
raise TelegramError('Invalid server response')
|
||||
|
||||
if not data.get('ok') and data.get('description'):
|
||||
return data['description']
|
||||
|
||||
return data['result']
|
||||
|
||||
|
||||
def _request_wrapper(*args, **kwargs):
|
||||
"""Wraps urllib3 request for handling known exceptions.
|
||||
|
||||
Args:
|
||||
args: unnamed arguments, passed to urllib3 request.
|
||||
kwargs: keyword arguments, passed tp urllib3 request.
|
||||
|
||||
Returns:
|
||||
str: A non-parsed JSON text.
|
||||
|
||||
Raises:
|
||||
TelegramError
|
||||
proxy_url (str): The URL to the proxy server. For example: `http://127.0.0.1:3128`.
|
||||
urllib3_proxy_kwargs (dict): Arbitrary arguments passed as-is to `urllib3.ProxyManager`.
|
||||
This value will be ignored if proxy_url is not set.
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
resp = _get_con_pool().request(*args, **kwargs)
|
||||
except urllib3.exceptions.TimeoutError as error:
|
||||
raise TimedOut()
|
||||
except urllib3.exceptions.HTTPError as error:
|
||||
# HTTPError must come last as its the base urllib3 exception class
|
||||
# TODO: do something smart here; for now just raise NetworkError
|
||||
raise NetworkError('urllib3 HTTPError {0}'.format(error))
|
||||
def __init__(self, con_pool_size=1, proxy_url=None, urllib3_proxy_kwargs=None):
|
||||
if urllib3_proxy_kwargs is None:
|
||||
urllib3_proxy_kwargs = dict()
|
||||
|
||||
if 200 <= resp.status <= 299:
|
||||
# 200-299 range are HTTP success statuses
|
||||
return resp.data
|
||||
kwargs = dict(
|
||||
maxsize=con_pool_size,
|
||||
cert_reqs='CERT_REQUIRED',
|
||||
ca_certs=certifi.where(),
|
||||
socket_options=HTTPConnection.default_socket_options + [
|
||||
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
|
||||
])
|
||||
|
||||
try:
|
||||
message = _parse(resp.data)
|
||||
except ValueError:
|
||||
raise NetworkError('Unknown HTTPError {0}'.format(resp.status))
|
||||
# Set a proxy according to the following order:
|
||||
# * proxy defined in proxy_url (+ urllib3_proxy_kwargs)
|
||||
# * proxy set in `HTTPS_PROXY` env. var.
|
||||
# * proxy set in `https_proxy` env. var.
|
||||
# * None (if no proxy is configured)
|
||||
|
||||
if resp.status in (401, 403):
|
||||
raise Unauthorized()
|
||||
elif resp.status == 400:
|
||||
raise BadRequest(repr(message))
|
||||
elif resp.status == 502:
|
||||
raise NetworkError('Bad Gateway')
|
||||
else:
|
||||
raise NetworkError('{0} ({1})'.format(message, resp.status))
|
||||
if not proxy_url:
|
||||
proxy_url = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy')
|
||||
|
||||
if not proxy_url:
|
||||
mgr = urllib3.PoolManager(**kwargs)
|
||||
else:
|
||||
kwargs.update(urllib3_proxy_kwargs)
|
||||
mgr = urllib3.proxy_from_url(proxy_url, **kwargs)
|
||||
if mgr.proxy.auth:
|
||||
# TODO: what about other auth types?
|
||||
auth_hdrs = urllib3.make_headers(proxy_basic_auth=mgr.proxy.auth)
|
||||
mgr.proxy_headers.update(auth_hdrs)
|
||||
|
||||
def get(url):
|
||||
"""Request an URL.
|
||||
Args:
|
||||
url:
|
||||
The web location we want to retrieve.
|
||||
self._con_pool = mgr
|
||||
|
||||
Returns:
|
||||
A JSON object.
|
||||
def stop(self):
|
||||
self._con_pool.clear()
|
||||
|
||||
"""
|
||||
result = _request_wrapper('GET', url)
|
||||
@staticmethod
|
||||
def _parse(json_data):
|
||||
"""Try and parse the JSON returned from Telegram.
|
||||
|
||||
return _parse(result)
|
||||
Returns:
|
||||
dict: A JSON parsed as Python dict with results - on error this dict will be empty.
|
||||
|
||||
"""
|
||||
decoded_s = json_data.decode('utf-8')
|
||||
try:
|
||||
data = json.loads(decoded_s)
|
||||
except ValueError:
|
||||
raise TelegramError('Invalid server response')
|
||||
|
||||
def post(url, data, timeout=None):
|
||||
"""Request an URL.
|
||||
Args:
|
||||
url:
|
||||
The web location we want to retrieve.
|
||||
data:
|
||||
A dict of (str, unicode) key/value pairs.
|
||||
timeout:
|
||||
float. If this value is specified, use it as the definitive timeout (in
|
||||
seconds) for urlopen() operations. [Optional]
|
||||
if not data.get('ok'):
|
||||
description = data.get('description')
|
||||
parameters = data.get('parameters')
|
||||
if parameters:
|
||||
migrate_to_chat_id = parameters.get('migrate_to_chat_id')
|
||||
if migrate_to_chat_id:
|
||||
raise ChatMigrated(migrate_to_chat_id)
|
||||
if description:
|
||||
return description
|
||||
|
||||
Notes:
|
||||
If neither `timeout` nor `data['timeout']` is specified. The underlying
|
||||
defaults are used.
|
||||
return data['result']
|
||||
|
||||
Returns:
|
||||
A JSON object.
|
||||
def _request_wrapper(self, *args, **kwargs):
|
||||
"""Wraps urllib3 request for handling known exceptions.
|
||||
|
||||
"""
|
||||
urlopen_kwargs = {}
|
||||
Args:
|
||||
args: unnamed arguments, passed to urllib3 request.
|
||||
kwargs: keyword arguments, passed tp urllib3 request.
|
||||
|
||||
if timeout is not None:
|
||||
urlopen_kwargs['timeout'] = timeout
|
||||
Returns:
|
||||
str: A non-parsed JSON text.
|
||||
|
||||
if InputFile.is_inputfile(data):
|
||||
data = InputFile(data)
|
||||
result = _request_wrapper('POST', url, body=data.to_form(), headers=data.headers)
|
||||
else:
|
||||
data = json.dumps(data)
|
||||
result = _request_wrapper('POST',
|
||||
url,
|
||||
body=data.encode(),
|
||||
headers={'Content-Type': 'application/json'},
|
||||
**urlopen_kwargs)
|
||||
Raises:
|
||||
TelegramError
|
||||
|
||||
return _parse(result)
|
||||
"""
|
||||
try:
|
||||
resp = self._con_pool.request(*args, **kwargs)
|
||||
except urllib3.exceptions.TimeoutError:
|
||||
raise TimedOut()
|
||||
except urllib3.exceptions.HTTPError as error:
|
||||
# HTTPError must come last as its the base urllib3 exception class
|
||||
# TODO: do something smart here; for now just raise NetworkError
|
||||
raise NetworkError('urllib3 HTTPError {0}'.format(error))
|
||||
|
||||
if 200 <= resp.status <= 299:
|
||||
# 200-299 range are HTTP success statuses
|
||||
return resp.data
|
||||
|
||||
def download(url, filename):
|
||||
"""Download a file by its URL.
|
||||
Args:
|
||||
url:
|
||||
The web location we want to retrieve.
|
||||
try:
|
||||
message = self._parse(resp.data)
|
||||
except ValueError:
|
||||
raise NetworkError('Unknown HTTPError {0}'.format(resp.status))
|
||||
|
||||
filename:
|
||||
The filename within the path to download the file.
|
||||
if resp.status in (401, 403):
|
||||
raise Unauthorized()
|
||||
elif resp.status == 400:
|
||||
raise BadRequest(repr(message))
|
||||
elif resp.status == 502:
|
||||
raise NetworkError('Bad Gateway')
|
||||
else:
|
||||
raise NetworkError('{0} ({1})'.format(message, resp.status))
|
||||
|
||||
"""
|
||||
buf = _request_wrapper('GET', url)
|
||||
with open(filename, 'wb') as fobj:
|
||||
fobj.write(buf)
|
||||
def get(self, url):
|
||||
"""Request an URL.
|
||||
Args:
|
||||
url:
|
||||
The web location we want to retrieve.
|
||||
|
||||
Returns:
|
||||
A JSON object.
|
||||
|
||||
"""
|
||||
result = self._request_wrapper('GET', url)
|
||||
return self._parse(result)
|
||||
|
||||
def post(self, url, data, timeout=None):
|
||||
"""Request an URL.
|
||||
Args:
|
||||
url:
|
||||
The web location we want to retrieve.
|
||||
data:
|
||||
A dict of (str, unicode) key/value pairs.
|
||||
timeout:
|
||||
float. If this value is specified, use it as the definitive timeout (in
|
||||
seconds) for urlopen() operations. [Optional]
|
||||
|
||||
Notes:
|
||||
If neither `timeout` nor `data['timeout']` is specified. The underlying
|
||||
defaults are used.
|
||||
|
||||
Returns:
|
||||
A JSON object.
|
||||
|
||||
"""
|
||||
urlopen_kwargs = {}
|
||||
|
||||
if timeout is not None:
|
||||
urlopen_kwargs['timeout'] = timeout
|
||||
|
||||
if InputFile.is_inputfile(data):
|
||||
data = InputFile(data)
|
||||
result = self._request_wrapper('POST', url, body=data.to_form(), headers=data.headers)
|
||||
else:
|
||||
data = json.dumps(data)
|
||||
result = self._request_wrapper(
|
||||
'POST',
|
||||
url,
|
||||
body=data.encode(),
|
||||
headers={'Content-Type': 'application/json'},
|
||||
**urlopen_kwargs)
|
||||
|
||||
return self._parse(result)
|
||||
|
||||
def download(self, url, filename):
|
||||
"""Download a file by its URL.
|
||||
Args:
|
||||
url:
|
||||
The web location we want to retrieve.
|
||||
|
||||
filename:
|
||||
The filename within the path to download the file.
|
||||
|
||||
"""
|
||||
buf = self._request_wrapper('GET', url)
|
||||
with open(filename, 'wb') as fobj:
|
||||
fobj.write(buf)
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import logging
|
||||
|
||||
from telegram import Update, NullHandler
|
||||
from telegram import Update
|
||||
from future.utils import bytes_to_native_str
|
||||
from threading import Lock
|
||||
import json
|
||||
try:
|
||||
import ujson as json
|
||||
except ImportError:
|
||||
import json
|
||||
try:
|
||||
import BaseHTTPServer
|
||||
except ImportError:
|
||||
import http.server as BaseHTTPServer
|
||||
|
||||
logging.getLogger(__name__).addHandler(NullHandler())
|
||||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
class _InvalidPost(Exception):
|
||||
@@ -21,11 +24,12 @@ class _InvalidPost(Exception):
|
||||
|
||||
class WebhookServer(BaseHTTPServer.HTTPServer, object):
|
||||
|
||||
def __init__(self, server_address, RequestHandlerClass, update_queue, webhook_path):
|
||||
def __init__(self, server_address, RequestHandlerClass, update_queue, webhook_path, bot):
|
||||
super(WebhookServer, self).__init__(server_address, RequestHandlerClass)
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.update_queue = update_queue
|
||||
self.webhook_path = webhook_path
|
||||
self.bot = bot
|
||||
self.is_running = False
|
||||
self.server_lock = Lock()
|
||||
self.shutdown_lock = Lock()
|
||||
@@ -82,7 +86,8 @@ class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler, object):
|
||||
|
||||
self.logger.debug('Webhook received data: ' + json_string)
|
||||
|
||||
update = Update.de_json(json.loads(json_string))
|
||||
update = Update.de_json(json.loads(json_string), self.server.bot)
|
||||
|
||||
self.logger.debug('Received Update with ID %d on Webhook' % update.update_id)
|
||||
self.server.update_queue.put(update)
|
||||
|
||||
|
||||
+3
-3
@@ -41,12 +41,12 @@ class Venue(TelegramObject):
|
||||
self.foursquare_id = foursquare_id
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
data = super(Venue, Venue).de_json(data)
|
||||
def de_json(data, bot):
|
||||
data = super(Venue, Venue).de_json(data, bot)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['location'] = Location.de_json(data.get('location'))
|
||||
data['location'] = Location.de_json(data.get('location'), bot)
|
||||
|
||||
return Venue(**data)
|
||||
|
||||
@@ -16,17 +16,5 @@
|
||||
#
|
||||
# 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 a object that represents a logging NullHandler."""
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
class NullHandler(logging.Handler):
|
||||
"""This object represents a logging NullHandler."""
|
||||
|
||||
def emit(self, record):
|
||||
"""
|
||||
Args:
|
||||
record (str):
|
||||
"""
|
||||
pass
|
||||
__version__ = '5.1.0'
|
||||
+4
-3
@@ -58,10 +58,11 @@ class Video(TelegramObject):
|
||||
self.file_size = int(kwargs.get('file_size', 0))
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot):
|
||||
|
||||
Returns:
|
||||
telegram.Video:
|
||||
@@ -69,6 +70,6 @@ class Video(TelegramObject):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'))
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
|
||||
return Video(**data)
|
||||
|
||||
+3
-2
@@ -49,10 +49,11 @@ class Voice(TelegramObject):
|
||||
self.file_size = int(kwargs.get('file_size', 0))
|
||||
|
||||
@staticmethod
|
||||
def de_json(data):
|
||||
def de_json(data, bot):
|
||||
"""
|
||||
Args:
|
||||
data (str):
|
||||
data (dict):
|
||||
bot (telegram.Bot)
|
||||
|
||||
Returns:
|
||||
telegram.Voice:
|
||||
|
||||
+2
-2
@@ -36,8 +36,8 @@ class BaseTest(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BaseTest, self).__init__(*args, **kwargs)
|
||||
|
||||
bot = telegram.Bot(os.environ.get('TOKEN',
|
||||
'133505823:AAHZFMHno3mzVLErU5b5jJvaeG--qUyLyG0'))
|
||||
bot = telegram.Bot(
|
||||
os.environ.get('TOKEN', '133505823:AAHZFMHno3mzVLErU5b5jJvaeG--qUyLyG0'))
|
||||
chat_id = os.environ.get('CHAT_ID', '12173560')
|
||||
|
||||
self._group_id = os.environ.get('GROUP_ID', '-49740850')
|
||||
|
||||
+45
-31
@@ -70,13 +70,14 @@ class AudioTest(BaseTest, unittest.TestCase):
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def test_send_audio_all_args(self):
|
||||
message = self._bot.sendAudio(self._chat_id,
|
||||
self.audio_file,
|
||||
duration=self.duration,
|
||||
performer=self.performer,
|
||||
title=self.title,
|
||||
mime_type=self.mime_type,
|
||||
file_size=self.file_size)
|
||||
message = self._bot.sendAudio(
|
||||
self._chat_id,
|
||||
self.audio_file,
|
||||
duration=self.duration,
|
||||
performer=self.performer,
|
||||
title=self.title,
|
||||
mime_type=self.mime_type,
|
||||
file_size=self.file_size)
|
||||
|
||||
audio = message.audio
|
||||
|
||||
@@ -91,11 +92,12 @@ class AudioTest(BaseTest, unittest.TestCase):
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def test_send_audio_mp3_file(self):
|
||||
message = self._bot.sendAudio(chat_id=self._chat_id,
|
||||
audio=self.audio_file,
|
||||
duration=self.duration,
|
||||
performer=self.performer,
|
||||
title=self.title)
|
||||
message = self._bot.sendAudio(
|
||||
chat_id=self._chat_id,
|
||||
audio=self.audio_file,
|
||||
duration=self.duration,
|
||||
performer=self.performer,
|
||||
title=self.title)
|
||||
|
||||
audio = message.audio
|
||||
|
||||
@@ -110,12 +112,13 @@ class AudioTest(BaseTest, unittest.TestCase):
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def test_send_audio_mp3_file_custom_filename(self):
|
||||
message = self._bot.sendAudio(chat_id=self._chat_id,
|
||||
audio=self.audio_file,
|
||||
duration=self.duration,
|
||||
performer=self.performer,
|
||||
title=self.title,
|
||||
filename='telegram_custom.mp3')
|
||||
message = self._bot.sendAudio(
|
||||
chat_id=self._chat_id,
|
||||
audio=self.audio_file,
|
||||
duration=self.duration,
|
||||
performer=self.performer,
|
||||
title=self.title,
|
||||
filename='telegram_custom.mp3')
|
||||
|
||||
audio = message.audio
|
||||
|
||||
@@ -130,11 +133,12 @@ class AudioTest(BaseTest, unittest.TestCase):
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def test_send_audio_mp3_url_file(self):
|
||||
message = self._bot.sendAudio(chat_id=self._chat_id,
|
||||
audio=self.audio_file_url,
|
||||
duration=self.duration,
|
||||
performer=self.performer,
|
||||
title=self.title)
|
||||
message = self._bot.sendAudio(
|
||||
chat_id=self._chat_id,
|
||||
audio=self.audio_file_url,
|
||||
duration=self.duration,
|
||||
performer=self.performer,
|
||||
title=self.title)
|
||||
|
||||
audio = message.audio
|
||||
|
||||
@@ -149,11 +153,12 @@ class AudioTest(BaseTest, unittest.TestCase):
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def test_send_audio_resend(self):
|
||||
message = self._bot.sendAudio(chat_id=self._chat_id,
|
||||
audio=self.audio_file_id,
|
||||
duration=self.duration,
|
||||
performer=self.performer,
|
||||
title=self.title)
|
||||
message = self._bot.sendAudio(
|
||||
chat_id=self._chat_id,
|
||||
audio=self.audio_file_id,
|
||||
duration=self.duration,
|
||||
performer=self.performer,
|
||||
title=self.title)
|
||||
|
||||
audio = message.audio
|
||||
|
||||
@@ -164,7 +169,7 @@ class AudioTest(BaseTest, unittest.TestCase):
|
||||
self.assertEqual(audio.mime_type, self.mime_type)
|
||||
|
||||
def test_audio_de_json(self):
|
||||
audio = telegram.Audio.de_json(self.json_dict)
|
||||
audio = telegram.Audio.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertEqual(audio.file_id, self.audio_file_id)
|
||||
self.assertEqual(audio.duration, self.duration)
|
||||
@@ -174,12 +179,12 @@ class AudioTest(BaseTest, unittest.TestCase):
|
||||
self.assertEqual(audio.file_size, self.file_size)
|
||||
|
||||
def test_audio_to_json(self):
|
||||
audio = telegram.Audio.de_json(self.json_dict)
|
||||
audio = telegram.Audio.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertTrue(self.is_json(audio.to_json()))
|
||||
|
||||
def test_audio_to_dict(self):
|
||||
audio = telegram.Audio.de_json(self.json_dict)
|
||||
audio = telegram.Audio.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertTrue(self.is_dict(audio.to_dict()))
|
||||
self.assertEqual(audio['file_id'], self.audio_file_id)
|
||||
@@ -225,6 +230,15 @@ class AudioTest(BaseTest, unittest.TestCase):
|
||||
TypeError,
|
||||
lambda: self._bot.sendAudio(chat_id=self._chat_id, **json_dict))
|
||||
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def test_reply_audio(self):
|
||||
"""Test for Message.reply_audio"""
|
||||
message = self._bot.sendMessage(self._chat_id, '.')
|
||||
message = message.reply_audio(self.audio_file)
|
||||
|
||||
self.assertNotEqual(message.audio.file_id, '')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
+28
-32
@@ -23,14 +23,10 @@ import io
|
||||
import re
|
||||
from datetime import datetime
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from flaky import flaky
|
||||
|
||||
if sys.version_info[0:2] == (2, 6):
|
||||
import unittest2 as unittest
|
||||
else:
|
||||
import unittest
|
||||
|
||||
sys.path.append('.')
|
||||
|
||||
import telegram
|
||||
@@ -51,8 +47,8 @@ class BotTest(BaseTest, unittest.TestCase):
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def testSendMessage(self):
|
||||
message = self._bot.sendMessage(chat_id=self._chat_id,
|
||||
text='Моё судно на воздушной подушке полно угрей')
|
||||
message = self._bot.sendMessage(
|
||||
chat_id=self._chat_id, text='Моё судно на воздушной подушке полно угрей')
|
||||
|
||||
self.assertTrue(self.is_json(message.to_json()))
|
||||
self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей')
|
||||
@@ -61,9 +57,10 @@ class BotTest(BaseTest, unittest.TestCase):
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def testSilentSendMessage(self):
|
||||
message = self._bot.sendMessage(chat_id=self._chat_id,
|
||||
text='Моё судно на воздушной подушке полно угрей',
|
||||
disable_notification=True)
|
||||
message = self._bot.sendMessage(
|
||||
chat_id=self._chat_id,
|
||||
text='Моё судно на воздушной подушке полно угрей',
|
||||
disable_notification=True)
|
||||
|
||||
self.assertTrue(self.is_json(message.to_json()))
|
||||
self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей')
|
||||
@@ -81,9 +78,8 @@ class BotTest(BaseTest, unittest.TestCase):
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def testForwardMessage(self):
|
||||
message = self._bot.forwardMessage(chat_id=self._chat_id,
|
||||
from_chat_id=self._chat_id,
|
||||
message_id=2398)
|
||||
message = self._bot.forwardMessage(
|
||||
chat_id=self._chat_id, from_chat_id=self._chat_id, message_id=2398)
|
||||
|
||||
self.assertTrue(self.is_json(message.to_json()))
|
||||
self.assertEqual(message.text, 'teste')
|
||||
@@ -93,9 +89,10 @@ class BotTest(BaseTest, unittest.TestCase):
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def testSendPhoto(self):
|
||||
message = self._bot.sendPhoto(photo=open('tests/data/telegram.png', 'rb'),
|
||||
caption='testSendPhoto',
|
||||
chat_id=self._chat_id)
|
||||
message = self._bot.sendPhoto(
|
||||
photo=open('tests/data/telegram.png', 'rb'),
|
||||
caption='testSendPhoto',
|
||||
chat_id=self._chat_id)
|
||||
|
||||
self.assertTrue(self.is_json(message.to_json()))
|
||||
self.assertEqual(message.photo[0].file_size, 1451)
|
||||
@@ -104,10 +101,11 @@ class BotTest(BaseTest, unittest.TestCase):
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def testSilentSendPhoto(self):
|
||||
message = self._bot.sendPhoto(photo=open('tests/data/telegram.png', 'rb'),
|
||||
caption='testSendPhoto',
|
||||
chat_id=self._chat_id,
|
||||
disable_notification=True)
|
||||
message = self._bot.sendPhoto(
|
||||
photo=open('tests/data/telegram.png', 'rb'),
|
||||
caption='testSendPhoto',
|
||||
chat_id=self._chat_id,
|
||||
disable_notification=True)
|
||||
|
||||
self.assertTrue(self.is_json(message.to_json()))
|
||||
self.assertEqual(message.photo[0].file_size, 1451)
|
||||
@@ -117,8 +115,7 @@ class BotTest(BaseTest, unittest.TestCase):
|
||||
@timeout(10)
|
||||
def testResendPhoto(self):
|
||||
message = self._bot.sendPhoto(
|
||||
photo='AgADAQAD1y0yGx8j9Qf8f_m3CKeS6Iy95y8ABI1ggfVJ4-UvwJcAAgI',
|
||||
chat_id=self._chat_id)
|
||||
photo='AgADAQAD1y0yGx8j9Qf8f_m3CKeS6Iy95y8ABI1ggfVJ4-UvwJcAAgI', chat_id=self._chat_id)
|
||||
|
||||
self.assertTrue(self.is_json(message.to_json()))
|
||||
self.assertEqual(message.photo[0].file_id,
|
||||
@@ -128,31 +125,28 @@ class BotTest(BaseTest, unittest.TestCase):
|
||||
@timeout(10)
|
||||
def testSendJPGURLPhoto(self):
|
||||
message = self._bot.sendPhoto(
|
||||
photo='http://dummyimage.com/600x400/000/fff.jpg&text=telegram',
|
||||
chat_id=self._chat_id)
|
||||
photo='http://dummyimage.com/600x400/000/fff.jpg&text=telegram', chat_id=self._chat_id)
|
||||
|
||||
self.assertTrue(self.is_json(message.to_json()))
|
||||
self.assertEqual(message.photo[0].file_size, 822)
|
||||
self.assertEqual(message.photo[0].file_size, 813)
|
||||
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def testSendPNGURLPhoto(self):
|
||||
message = self._bot.sendPhoto(
|
||||
photo='http://dummyimage.com/600x400/000/fff.png&text=telegram',
|
||||
chat_id=self._chat_id)
|
||||
photo='http://dummyimage.com/600x400/000/fff.png&text=telegram', chat_id=self._chat_id)
|
||||
|
||||
self.assertTrue(self.is_json(message.to_json()))
|
||||
self.assertEqual(message.photo[0].file_size, 685)
|
||||
self.assertEqual(message.photo[0].file_size, 670)
|
||||
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def testSendGIFURLPhoto(self):
|
||||
message = self._bot.sendPhoto(
|
||||
photo='http://dummyimage.com/600x400/000/fff.gif&text=telegram',
|
||||
chat_id=self._chat_id)
|
||||
photo='http://dummyimage.com/600x400/000/fff.gif&text=telegram', chat_id=self._chat_id)
|
||||
|
||||
self.assertTrue(self.is_json(message.to_json()))
|
||||
self.assertEqual(message.photo[0].file_size, 685)
|
||||
self.assertEqual(message.photo[0].file_size, 670)
|
||||
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
@@ -204,7 +198,9 @@ class BotTest(BaseTest, unittest.TestCase):
|
||||
def testInvalidSrvResp(self):
|
||||
with self.assertRaisesRegexp(telegram.TelegramError, 'Invalid server response'):
|
||||
# bypass the valid token check
|
||||
bot = telegram.Bot.__new__(telegram.Bot)
|
||||
newbot_cls = type(
|
||||
'NoTokenValidateBot', (telegram.Bot,), dict(_validate_token=lambda x, y: None))
|
||||
bot = newbot_cls('0xdeadbeef')
|
||||
bot.base_url = 'https://api.telegram.org/bot{0}'.format('12')
|
||||
|
||||
bot.getMe()
|
||||
|
||||
+18
-4
@@ -20,6 +20,9 @@
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
|
||||
from flaky import flaky
|
||||
|
||||
sys.path.append('.')
|
||||
|
||||
import telegram
|
||||
@@ -37,30 +40,41 @@ class ChatTest(BaseTest, unittest.TestCase):
|
||||
self.json_dict = {'id': self.id, 'title': self.title, 'type': self.type}
|
||||
|
||||
def test_group_chat_de_json_empty_json(self):
|
||||
group_chat = telegram.Chat.de_json({})
|
||||
group_chat = telegram.Chat.de_json({}, self._bot)
|
||||
|
||||
self.assertEqual(group_chat, None)
|
||||
|
||||
def test_group_chat_de_json(self):
|
||||
group_chat = telegram.Chat.de_json(self.json_dict)
|
||||
group_chat = telegram.Chat.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertEqual(group_chat.id, self.id)
|
||||
self.assertEqual(group_chat.title, self.title)
|
||||
self.assertEqual(group_chat.type, self.type)
|
||||
|
||||
def test_group_chat_to_json(self):
|
||||
group_chat = telegram.Chat.de_json(self.json_dict)
|
||||
group_chat = telegram.Chat.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertTrue(self.is_json(group_chat.to_json()))
|
||||
|
||||
def test_group_chat_to_dict(self):
|
||||
group_chat = telegram.Chat.de_json(self.json_dict)
|
||||
group_chat = telegram.Chat.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertTrue(self.is_dict(group_chat.to_dict()))
|
||||
self.assertEqual(group_chat['id'], self.id)
|
||||
self.assertEqual(group_chat['title'], self.title)
|
||||
self.assertEqual(group_chat['type'], self.type)
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_send_action(self):
|
||||
"""Test for Chat.send_action"""
|
||||
self.json_dict['id'] = self._chat_id
|
||||
group_chat = telegram.Chat.de_json(self.json_dict, self._bot)
|
||||
group_chat.bot = self._bot
|
||||
|
||||
result = group_chat.send_action(telegram.ChatAction.TYPING)
|
||||
|
||||
self.assertTrue(result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -20,11 +20,7 @@
|
||||
ChosenInlineResult"""
|
||||
|
||||
import sys
|
||||
|
||||
if sys.version_info[0:2] == (2, 6):
|
||||
import unittest2 as unittest
|
||||
else:
|
||||
import unittest
|
||||
import unittest
|
||||
|
||||
sys.path.append('.')
|
||||
|
||||
@@ -49,19 +45,19 @@ class ChosenInlineResultTest(BaseTest, unittest.TestCase):
|
||||
}
|
||||
|
||||
def test_choseninlineresult_de_json(self):
|
||||
result = telegram.ChosenInlineResult.de_json(self.json_dict)
|
||||
result = telegram.ChosenInlineResult.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertEqual(result.result_id, self.result_id)
|
||||
self.assertDictEqual(result.from_user.to_dict(), self.from_user.to_dict())
|
||||
self.assertEqual(result.query, self.query)
|
||||
|
||||
def test_choseninlineresult_to_json(self):
|
||||
result = telegram.ChosenInlineResult.de_json(self.json_dict)
|
||||
result = telegram.ChosenInlineResult.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertTrue(self.is_json(result.to_json()))
|
||||
|
||||
def test_choseninlineresult_to_dict(self):
|
||||
result = telegram.ChosenInlineResult.de_json(self.json_dict).to_dict()
|
||||
result = telegram.ChosenInlineResult.de_json(self.json_dict, self._bot).to_dict()
|
||||
|
||||
self.assertTrue(self.is_dict(result))
|
||||
self.assertEqual(result['result_id'], self.result_id)
|
||||
|
||||
+13
-15
@@ -17,14 +17,10 @@
|
||||
"""Test the Telegram constants."""
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from flaky import flaky
|
||||
|
||||
if sys.version_info[0:2] == (2, 6):
|
||||
import unittest2 as unittest
|
||||
else:
|
||||
import unittest
|
||||
|
||||
sys.path.append('.')
|
||||
|
||||
import telegram
|
||||
@@ -37,12 +33,12 @@ class ConstantsTest(BaseTest, unittest.TestCase):
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def testMaxMessageLength(self):
|
||||
self._bot.sendMessage(chat_id=self._chat_id,
|
||||
text='a' * telegram.constants.MAX_MESSAGE_LENGTH)
|
||||
self._bot.sendMessage(
|
||||
chat_id=self._chat_id, text='a' * telegram.constants.MAX_MESSAGE_LENGTH)
|
||||
|
||||
try:
|
||||
self._bot.sendMessage(chat_id=self._chat_id,
|
||||
text='a' * (telegram.constants.MAX_MESSAGE_LENGTH + 1))
|
||||
self._bot.sendMessage(
|
||||
chat_id=self._chat_id, text='a' * (telegram.constants.MAX_MESSAGE_LENGTH + 1))
|
||||
except BadRequest as e:
|
||||
err = str(e)
|
||||
|
||||
@@ -51,14 +47,16 @@ class ConstantsTest(BaseTest, unittest.TestCase):
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def testMaxCaptionLength(self):
|
||||
self._bot.sendPhoto(photo=open('tests/data/telegram.png', 'rb'),
|
||||
caption='a' * telegram.constants.MAX_CAPTION_LENGTH,
|
||||
chat_id=self._chat_id)
|
||||
self._bot.sendPhoto(
|
||||
photo=open('tests/data/telegram.png', 'rb'),
|
||||
caption='a' * telegram.constants.MAX_CAPTION_LENGTH,
|
||||
chat_id=self._chat_id)
|
||||
|
||||
try:
|
||||
self._bot.sendPhoto(photo=open('tests/data/telegram.png', 'rb'),
|
||||
caption='a' * (telegram.constants.MAX_CAPTION_LENGTH + 1),
|
||||
chat_id=self._chat_id)
|
||||
self._bot.sendPhoto(
|
||||
photo=open('tests/data/telegram.png', 'rb'),
|
||||
caption='a' * (telegram.constants.MAX_CAPTION_LENGTH + 1),
|
||||
chat_id=self._chat_id)
|
||||
except BadRequest as e:
|
||||
err = str(e)
|
||||
|
||||
|
||||
+17
-3
@@ -20,6 +20,9 @@
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
|
||||
from flaky import flaky
|
||||
|
||||
sys.path.append('.')
|
||||
|
||||
import telegram
|
||||
@@ -43,7 +46,7 @@ class ContactTest(BaseTest, unittest.TestCase):
|
||||
}
|
||||
|
||||
def test_contact_de_json(self):
|
||||
contact = telegram.Contact.de_json(self.json_dict)
|
||||
contact = telegram.Contact.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertEqual(contact.phone_number, self.phone_number)
|
||||
self.assertEqual(contact.first_name, self.first_name)
|
||||
@@ -51,12 +54,12 @@ class ContactTest(BaseTest, unittest.TestCase):
|
||||
self.assertEqual(contact.user_id, self.user_id)
|
||||
|
||||
def test_contact_to_json(self):
|
||||
contact = telegram.Contact.de_json(self.json_dict)
|
||||
contact = telegram.Contact.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertTrue(self.is_json(contact.to_json()))
|
||||
|
||||
def test_contact_to_dict(self):
|
||||
contact = telegram.Contact.de_json(self.json_dict)
|
||||
contact = telegram.Contact.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertTrue(self.is_dict(contact.to_dict()))
|
||||
self.assertEqual(contact['phone_number'], self.phone_number)
|
||||
@@ -65,5 +68,16 @@ class ContactTest(BaseTest, unittest.TestCase):
|
||||
self.assertEqual(contact['user_id'], self.user_id)
|
||||
|
||||
|
||||
''' Commented out, because it would cause "Too Many Requests (429)" errors.
|
||||
@flaky(3, 1)
|
||||
def test_reply_contact(self):
|
||||
"""Test for Message.reply_contact"""
|
||||
message = self._bot.sendMessage(self._chat_id, '.')
|
||||
message = message.reply_contact(self.phone_number, self.first_name)
|
||||
|
||||
self.assertEqual(message.contact.phone_number, self.phone_number)
|
||||
self.assertEqual(message.contact.first_name, self.first_name)
|
||||
'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -22,13 +22,9 @@ This module contains a object that represents Tests for ConversationHandler
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
import unittest
|
||||
from time import sleep
|
||||
|
||||
if sys.version_info[0:2] == (2, 6):
|
||||
import unittest2 as unittest
|
||||
else:
|
||||
import unittest
|
||||
|
||||
try:
|
||||
# python2
|
||||
from urllib2 import urlopen, Request, HTTPError
|
||||
@@ -40,8 +36,7 @@ except ImportError:
|
||||
sys.path.append('.')
|
||||
|
||||
from telegram import Update, Message, TelegramError, User, Chat, Bot
|
||||
from telegram.utils.request import stop_con_pool
|
||||
from telegram.ext import *
|
||||
from telegram.ext import Updater, ConversationHandler, CommandHandler
|
||||
from tests.base import BaseTest
|
||||
from tests.test_updater import MockBot
|
||||
|
||||
@@ -65,10 +60,10 @@ class ConversationHandlerTest(BaseTest, unittest.TestCase):
|
||||
# At first we're thirsty. Then we brew coffee, we drink it
|
||||
# and then we can start coding!
|
||||
END, THIRSTY, BREWING, DRINKING, CODING = range(-1, 4)
|
||||
_updater = None
|
||||
|
||||
# Test related
|
||||
def setUp(self):
|
||||
self.updater = None
|
||||
self.current_state = dict()
|
||||
self.entry_points = [CommandHandler('start', self.start)]
|
||||
self.states = {self.THIRSTY: [CommandHandler('brew', self.brew),
|
||||
@@ -82,14 +77,22 @@ class ConversationHandlerTest(BaseTest, unittest.TestCase):
|
||||
self.fallbacks = [CommandHandler('eat', self.start)]
|
||||
|
||||
def _setup_updater(self, *args, **kwargs):
|
||||
stop_con_pool()
|
||||
bot = MockBot(*args, **kwargs)
|
||||
self.updater = Updater(workers=2, bot=bot)
|
||||
|
||||
def tearDown(self):
|
||||
if self.updater is not None:
|
||||
self.updater.stop()
|
||||
stop_con_pool()
|
||||
|
||||
@property
|
||||
def updater(self):
|
||||
return self._updater
|
||||
|
||||
@updater.setter
|
||||
def updater(self, val):
|
||||
if self._updater:
|
||||
self._updater.stop()
|
||||
self._updater = val
|
||||
|
||||
def reset(self):
|
||||
self.current_state = dict()
|
||||
@@ -106,6 +109,9 @@ class ConversationHandlerTest(BaseTest, unittest.TestCase):
|
||||
def start(self, bot, update):
|
||||
return self._set_state(update, self.THIRSTY)
|
||||
|
||||
def start_end(self, bot, update):
|
||||
return self._set_state(update, self.END)
|
||||
|
||||
def brew(self, bot, update):
|
||||
return self._set_state(update, self.BREWING)
|
||||
|
||||
@@ -122,9 +128,8 @@ class ConversationHandlerTest(BaseTest, unittest.TestCase):
|
||||
user = User(first_name="Misses Test", id=123)
|
||||
second_user = User(first_name="Mister Test", id=124)
|
||||
|
||||
handler = ConversationHandler(entry_points=self.entry_points,
|
||||
states=self.states,
|
||||
fallbacks=self.fallbacks)
|
||||
handler = ConversationHandler(
|
||||
entry_points=self.entry_points, states=self.states, fallbacks=self.fallbacks)
|
||||
d.add_handler(handler)
|
||||
queue = self.updater.start_polling(0.01)
|
||||
|
||||
@@ -159,6 +164,48 @@ class ConversationHandlerTest(BaseTest, unittest.TestCase):
|
||||
sleep(.1)
|
||||
self.assertRaises(KeyError, self._get_state, user_id=second_user.id)
|
||||
|
||||
def test_endOnFirstMessage(self):
|
||||
self._setup_updater('', messages=0)
|
||||
d = self.updater.dispatcher
|
||||
user = User(first_name="Misses Test", id=123)
|
||||
|
||||
handler = ConversationHandler(
|
||||
entry_points=[CommandHandler('start', self.start_end)], states={}, fallbacks=[])
|
||||
d.add_handler(handler)
|
||||
queue = self.updater.start_polling(0.01)
|
||||
|
||||
# User starts the state machine and immediately ends it.
|
||||
message = Message(0, user, None, None, text="/start")
|
||||
queue.put(Update(update_id=0, message=message))
|
||||
sleep(.1)
|
||||
self.assertEquals(len(handler.conversations), 0)
|
||||
|
||||
def test_endOnFirstMessageAsync(self):
|
||||
self._setup_updater('', messages=0)
|
||||
d = self.updater.dispatcher
|
||||
user = User(first_name="Misses Test", id=123)
|
||||
|
||||
start_end_async = (lambda bot, update: d.run_async(self.start_end, bot, update))
|
||||
|
||||
handler = ConversationHandler(
|
||||
entry_points=[CommandHandler('start', start_end_async)], states={}, fallbacks=[])
|
||||
d.add_handler(handler)
|
||||
queue = self.updater.start_polling(0.01)
|
||||
|
||||
# User starts the state machine with an async function that immediately ends the
|
||||
# conversation. Async results are resolved when the users state is queried next time.
|
||||
message = Message(0, user, None, None, text="/start")
|
||||
queue.put(Update(update_id=0, message=message))
|
||||
sleep(.1)
|
||||
# Assert that the Promise has been accepted as the new state
|
||||
self.assertEquals(len(handler.conversations), 1)
|
||||
|
||||
message = Message(0, user, None, None, text="resolve promise pls")
|
||||
queue.put(Update(update_id=0, message=message))
|
||||
sleep(.1)
|
||||
# Assert that the Promise has been resolved and the conversation ended.
|
||||
self.assertEquals(len(handler.conversations), 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
+14
-6
@@ -70,9 +70,8 @@ class DocumentTest(BaseTest, unittest.TestCase):
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def test_send_document_png_file_with_custom_file_name(self):
|
||||
message = self._bot.sendDocument(self._chat_id,
|
||||
self.document_file,
|
||||
filename='telegram_custom.png')
|
||||
message = self._bot.sendDocument(
|
||||
self._chat_id, self.document_file, filename='telegram_custom.png')
|
||||
|
||||
document = message.document
|
||||
|
||||
@@ -110,7 +109,7 @@ class DocumentTest(BaseTest, unittest.TestCase):
|
||||
self.assertEqual(document.mime_type, self.mime_type)
|
||||
|
||||
def test_document_de_json(self):
|
||||
document = telegram.Document.de_json(self.json_dict)
|
||||
document = telegram.Document.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertEqual(document.file_id, self.document_file_id)
|
||||
self.assertTrue(isinstance(document.thumb, telegram.PhotoSize))
|
||||
@@ -119,12 +118,12 @@ class DocumentTest(BaseTest, unittest.TestCase):
|
||||
self.assertEqual(document.file_size, self.file_size)
|
||||
|
||||
def test_document_to_json(self):
|
||||
document = telegram.Document.de_json(self.json_dict)
|
||||
document = telegram.Document.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertTrue(self.is_json(document.to_json()))
|
||||
|
||||
def test_document_to_dict(self):
|
||||
document = telegram.Document.de_json(self.json_dict)
|
||||
document = telegram.Document.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertTrue(self.is_dict(document.to_dict()))
|
||||
self.assertEqual(document['file_id'], self.document_file_id)
|
||||
@@ -168,6 +167,15 @@ class DocumentTest(BaseTest, unittest.TestCase):
|
||||
lambda: self._bot.sendDocument(chat_id=self._chat_id,
|
||||
**json_dict))
|
||||
|
||||
@flaky(3, 1)
|
||||
@timeout(10)
|
||||
def test_reply_document(self):
|
||||
"""Test for Message.reply_document"""
|
||||
message = self._bot.sendMessage(self._chat_id, '.')
|
||||
message = message.reply_document(self.document_file)
|
||||
|
||||
self.assertNotEqual(message.document.file_id, '')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains a object that represents Tests for Telegram Emoji"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
sys.path.append('.')
|
||||
|
||||
import telegram
|
||||
from telegram.emoji import Emoji
|
||||
from tests.base import BaseTest
|
||||
|
||||
|
||||
class EmojiTest(BaseTest, unittest.TestCase):
|
||||
"""This object represents Tests for Telegram Emoji."""
|
||||
|
||||
def test_emoji(self):
|
||||
for attr in dir(Emoji):
|
||||
if attr[0] != '_': # TODO: dirty way to filter out functions
|
||||
self.assertTrue(type(getattr(Emoji, attr)) is str)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
+3
-3
@@ -101,19 +101,19 @@ class FileTest(BaseTest, unittest.TestCase):
|
||||
self.assertTrue(os.path.isfile('telegram.ogg'))
|
||||
|
||||
def test_file_de_json(self):
|
||||
newFile = telegram.File.de_json(self.json_dict)
|
||||
newFile = telegram.File.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertEqual(newFile.file_id, self.json_dict['file_id'])
|
||||
self.assertEqual(newFile.file_path, self.json_dict['file_path'])
|
||||
self.assertEqual(newFile.file_size, self.json_dict['file_size'])
|
||||
|
||||
def test_file_to_json(self):
|
||||
newFile = telegram.File.de_json(self.json_dict)
|
||||
newFile = telegram.File.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertTrue(self.is_json(newFile.to_json()))
|
||||
|
||||
def test_file_to_dict(self):
|
||||
newFile = telegram.File.de_json(self.json_dict)
|
||||
newFile = telegram.File.de_json(self.json_dict, self._bot)
|
||||
|
||||
self.assertTrue(self.is_dict(newFile.to_dict()))
|
||||
self.assertEqual(newFile['file_id'], self.json_dict['file_id'])
|
||||
|
||||
+17
-1
@@ -23,10 +23,11 @@ This module contains a object that represents Tests for MessageHandler.Filters
|
||||
import sys
|
||||
import unittest
|
||||
from datetime import datetime
|
||||
import functools
|
||||
|
||||
sys.path.append('.')
|
||||
|
||||
from telegram import Message, User, Chat
|
||||
from telegram import Message, User, Chat, MessageEntity
|
||||
from telegram.ext import Filters
|
||||
from tests.base import BaseTest
|
||||
|
||||
@@ -150,6 +151,21 @@ class FiltersTest(BaseTest, unittest.TestCase):
|
||||
self.assertTrue(Filters.status_update(self.message))
|
||||
self.message.pinned_message = None
|
||||
|
||||
def test_entities_filter(self):
|
||||
e = functools.partial(MessageEntity, offset=0, length=0)
|
||||
|
||||
self.message.entities = [e(MessageEntity.MENTION)]
|
||||
self.assertTrue(Filters.entity(MessageEntity.MENTION)(self.message))
|
||||
|
||||
self.message.entities = []
|
||||
self.assertFalse(Filters.entity(MessageEntity.MENTION)(self.message))
|
||||
|
||||
self.message.entities = [e(MessageEntity.BOLD)]
|
||||
self.assertFalse(Filters.entity(MessageEntity.MENTION)(self.message))
|
||||
|
||||
self.message.entities = [e(MessageEntity.BOLD), e(MessageEntity.MENTION)]
|
||||
self.assertTrue(Filters.entity(MessageEntity.MENTION)(self.message))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user