Compare commits

...

92 Commits

Author SHA1 Message Date
Jannes Höke 2786252a51 Bump version to v5.2.0 2016-10-25 20:10:36 +02:00
Jannes Höke 3f30f74024 update examples to use bitwise filters 2016-10-25 19:51:56 +02:00
Jannes Höke 79fc3be9cd Update README.md 2016-10-25 19:37:19 +02:00
Jannes Höke 71c73bdc74 Update README.md 2016-10-25 19:36:57 +02:00
neutronnnate 761547e71d Issue 422: Fixed start_polling with clean=True can cause 'Too Many Requests' error from Telegram. (#437) 2016-10-25 19:30:05 +02:00
Jannes Höke 10bdf8212c Add pass_user_data and pass_chat_data to Handler (#436)
* initial commit for user_data

* add chat_data and use defaultdict

* fix chat_data copy-paste error

* add test for user_data and chat_data

* fix case where chat is None

* remove braces from import line
2016-10-25 19:28:34 +02:00
Kristofer Kirss 45936c9982 Remove deprecated argument from Updater docstring (#444)
Argument (job_queue_tick_interval) was removed with commit 3aedd78 but the docstring in updater.py wasn't.
2016-10-23 00:31:44 +03:00
Jannes Höke fae1896232 Switch to readthedocs (#443)
* replace pythonhosted by readthedocs

* fix rtd links in examples/README
2016-10-19 18:00:33 +02:00
Jacob Bom 2518ddac22 Update comtributing guide with explicit kwargs 2016-10-19 13:07:20 +02:00
Jacob Bom 61fe438a8b Merge pull request #431 from python-telegram-bot/october3
Add api changes as of october 3
2016-10-19 12:59:00 +02:00
Jacob Bom 960862ccb1 Merge branch 'master' into october3
# Conflicts:
#	telegram/bot.py
#	telegram/callbackquery.py
#	telegram/chat.py
#	telegram/ext/messagehandler.py
#	telegram/inlinekeyboardbutton.py
#	telegram/inlinequeryresultcachedaudio.py
#	telegram/message.py
#	tests/test_filters.py
2016-10-19 12:35:50 +02:00
Jacob Bom 4e5f4582dd Merge pull request #411 from python-telegram-bot/bitwise-filters
Make filters and/or-able using bitwise operators.
2016-10-19 11:40:35 +02:00
Jacob Bom 225bc24c2a Merge pull request #442 from python-telegram-bot/explicit-kwargs
Use explicit kwargs and change/add a bunch of documentation.
2016-10-19 11:36:19 +02:00
Noam Meltzer a5f9aa3171 more documentation 2016-10-17 23:44:40 +03:00
Noam Meltzer 78088f4f6a Fix grammer: 'a object' -> 'an object' 2016-10-17 01:22:40 +03:00
Noam Meltzer 59fa717023 Documentation improvements + small style fixes 2016-10-17 01:11:20 +03:00
Noam Meltzer 31cab0d1b4 editMessageCaption & editMessageReplyMarkup: more validation on input 2016-10-17 01:09:44 +03:00
Jacob Bom 7fafaa1ea3 Update support date in readme. 2016-10-16 16:54:08 +02:00
Jacob Bom e367b8519d Use explicit kwargs for all class inits in pure api.
While not stickily necessary for most classes (since user isn't directly creating them) it still unifies our approach.
However for some like ReplyKeyboardHide where users are making the classes themselves it should improve IDE autocomplete support.
2016-10-16 16:24:13 +02:00
Jacob Bom b610316667 Use explicit kwargs for all bot methods.
This improves support for many IDEs' autocompletion.
2016-10-16 15:54:48 +02:00
Jacob Bom 39832d2f6b __str__ behaves differntly on py2 apparently. 2016-10-16 13:19:42 +02:00
Jacob Bom 5408c23e33 assertRegexMatches still exists for now. Use it. 2016-10-16 12:48:45 +02:00
Jacob Bom 62dd3a33e6 Better kwargs defaults. 2016-10-16 12:41:12 +02:00
Jacob Bom 3754cdafb2 send_game has differnt kwargs. 2016-10-16 12:41:12 +02:00
Jacob Bom 305ff93018 set_game_score might return True 2016-10-16 12:41:12 +02:00
Jacob Bom 29e0cc64e9 Fix py2 compat 2016-10-15 23:29:46 +02:00
Jacob Bom a5671a8fb1 Merge pull request #423 from python-telegram-bot/edit-class-method
Add edit_* class methods
2016-10-15 23:03:43 +02:00
Jacob Bom f99b2f8f3b Inprove coverage 2016-10-15 22:59:41 +02:00
Jacob Bom c626044a30 Add "all" filter
Since and empty list cannot (in the future, currently only deprecated) be used.
2016-10-15 22:58:55 +02:00
Noam Meltzer a68cf8d464 Respect RetryAfter with polling Updater 2016-10-15 23:39:50 +03:00
Jacob Bom de96cc87ea Only build master branch and PRs on travis. Yapf only on py3.5. 2016-10-14 11:24:00 +02:00
Jacob Bom ca5e3146c6 Fix docstring according to Jannes' commentns. 2016-10-14 10:32:12 +02:00
Noam Meltzer 53a574bbbb Improve unitests coverage 2016-10-13 22:52:53 +03:00
Noam Meltzer 5b8efe0c14 upgrade yapf to ver 0.12.2 2016-10-12 23:56:57 +03:00
Noam Meltzer d07a1c3f67 yapf: use our own mirror of yapf & update to version 0.12.2 2016-10-12 23:51:46 +03:00
Noam Meltzer 32a78722ae yapf fixes 2016-10-12 23:33:52 +03:00
Jacob Bom 04feeeff55 Update test cases
Needs proper animation file_id and size.
Also assertEqual can't test if two objects have the same data, so we just check the file_id instead.
2016-10-12 20:30:34 +02:00
Jannes Höke a1495956aa temporary fix for documentation builds on readthedocs 2016-10-10 12:51:12 +02:00
Patrick Hofmann 8dc10fc7b2 fixes broken test cases with PhotoSize, Game and Animation classes (#435)
* fixes broken test with PhotoSize, Game and Animation

However:
testSendGame and test_set_game_score both produces *BadRequest: u'Wrong file identifier/HTTP URL specified'*.

* fixes test_set_game_score

* adds to_dict method to Game to prevent extra collection type checks in base.TelegramObject
2016-10-10 11:44:40 +02:00
Jacob Bom 7ad92dcc65 Add test for set_game_score 2016-10-09 12:39:05 +02:00
Jacob Bom d1ddfaddf0 Add tests for Game, Animation and sendGame.
Still need setGameScore (I'm thinking we can set it to the ever increasing
envvar 'TRAVIS_BUILD_NUMBER') and getGameHighScores.
The tests currently don't work... Since I don't really understand how
PhotoSize works... Please halp :P
2016-10-07 23:57:10 +02:00
Jacob Bom b7c7612b3f Add game filter 2016-10-07 22:37:29 +02:00
Jacob Bom ade89772f4 Fix tests
Data is an optional kwargs in CallbackQuery according to Telegram
2016-10-07 22:32:54 +02:00
Jacob Bom 67b98e3190 Merge pull request #433 from PH89/october3
small fix for game related parts
2016-10-07 22:25:52 +02:00
Patrick Hofmann e672625a41 add myself to AUTHORS.rst 2016-10-06 18:17:42 +02:00
Patrick Hofmann 8cab735342 small fix for game related parts
* bot.py fix copy paste error in url
* callbackquery.py make data field optional
* message.py introduce optional game field
2016-10-06 17:52:53 +02:00
Noam Meltzer bfb4c63d60 add unitest for getWebhookInfo 2016-10-06 00:11:28 +03:00
Noam Meltzer 01a5a1c5b3 small documentation fix 2016-10-05 23:22:55 +03:00
Noam Meltzer 1db1b76a7c fix test_video unitest 2016-10-05 23:19:17 +03:00
Jacob Bom 976f34082f Add possibly good-enough test for all_members_are_admin
Should be good enough... If we /really/ wanted to we could get two different chats from telegram and check them, but it really shouldn't be necessary.
2016-10-05 17:55:00 +02:00
Jacob Bom 6b7788570c Add tests for captions in audio and voice 2016-10-05 17:50:48 +02:00
Noam Meltzer 1f9d3163dd Game: use explicit keyword args + added docmentation 2016-10-04 02:17:12 +03:00
Noam Meltzer 837e9d2964 Animation: use explicit keyword args + added documentation 2016-10-04 02:16:33 +03:00
Noam Meltzer fab97df58a GameHighScore: added attributes documentation 2016-10-04 02:06:22 +03:00
Noam Meltzer d70fc48e94 getGameHighScores(): added documentation + fixed copy-paste errors 2016-10-04 01:57:19 +03:00
Noam Meltzer 36192912c2 setGameScore: fixes
- fix copy/paste errors
 - return Message object
2016-10-04 01:56:05 +03:00
Noam Meltzer 34748ec228 callbackgame: small documentation fix 2016-10-04 01:50:34 +03:00
Noam Meltzer d5567cd9cd sendGame(): mall fixes 2016-10-04 01:37:44 +03:00
Noam Meltzer 2463b4b9c8 New exception: RetryAfter
Also, small fix to the description text of ChatMigrated
2016-10-04 01:27:45 +03:00
Noam Meltzer 7cf5009517 small documentation fixes 2016-10-04 01:10:30 +03:00
Noam Meltzer 9b74625d4a answerCallbackQuery: fix copy/paste: show_alert -> url 2016-10-04 00:55:29 +03:00
Noam Meltzer 4180c069b3 InputFile: use self instead of explicit class name 2016-10-04 00:25:32 +03:00
Noam Meltzer 3a0f219783 inputfile: define the file types as constant and use iterator 2016-10-04 00:25:05 +03:00
Noam Meltzer 3c889655c1 Allow http url as a file_id
N.B. test_send_video_mp4_file_url() is still failing, probably because
of telegram servers bug. Will contact telegram bot support about that.
2016-10-04 00:20:17 +03:00
Jacob Bom c2b8c4bf95 Fix regexGroupHandlerCallbackQuery test 2016-10-03 21:29:30 +02:00
Jacob Bom 8234321a20 Fix tests for Inline Voice Queries 2016-10-03 21:28:35 +02:00
Jacob Bom 5e2d96b47d Make tests actually run at all 2016-10-03 21:07:41 +02:00
Jacob Bom 551f6c556c Add game parameters in various places
Also chat_instance in CallbackQuery which will break a lot of tests probably
2016-10-03 21:01:38 +02:00
Jacob Bom ae17eb3272 Add InlineQueryResultGame 2016-10-03 20:55:21 +02:00
Jacob Bom 358dd795c7 Add the rest of game_ methods to bot. 2016-10-03 20:43:02 +02:00
Jacob Bom 151a441af7 Add send_game 2016-10-03 20:40:17 +02:00
Noam Meltzer 1f67623615 Fix import order 2016-10-03 21:34:08 +03:00
Noam Meltzer 8737b5de63 fix syntax error by some weird char 2016-10-03 21:17:04 +03:00
Jacob Bom f3b8a3a5e9 Merge remote-tracking branch 'origin/october3' into october3 2016-10-03 20:10:14 +02:00
Jacob Bom 9e9309eb90 Add all new Game related classes
Missing docstrings for now though, wanna add everything first
2016-10-03 20:09:57 +02:00
Noam Meltzer e8a34d8eef cosmetic fixes and documentation for getWebhookInfo 2016-10-03 20:43:10 +03:00
Jacob Bom 34c62a633b Add url to answerCallbackQuery 2016-10-03 15:25:07 +02:00
Jacob Bom 868d9217bc Add WebhookInfo and getWebhookInfo
Still needs tests though
2016-10-03 15:16:43 +02:00
Jacob Bom f7ede4baea Add caption fields to voice and audio
Or at least the methods/classes for sending.
2016-10-03 15:05:49 +02:00
Jacob Bom c3e07b1056 Add switch_inline_query_current_chat 2016-10-03 14:52:58 +02:00
Jacob Bom 79bdfe4c5d Allow filters to be passed without list.
Also deprecates actually using a list.
2016-09-29 19:10:22 +02:00
Michael Elovskikh 46657afa95 Start additional threads only when necessary (#415)
* Start all additional threads only when necessary.

* Deprecate prevent_autostart in the c'tor of JobQueue.
2016-09-27 10:21:35 +03:00
Jacob Bom 79e065a730 Add __str__ and __repr__ to MergedFilter. 2016-09-25 16:31:06 +02:00
Jacob Bom 9928a1eefc Add test for edit_text 2016-09-25 16:11:19 +02:00
Jacob Bom 0e2c3666c0 Add edit_* family of methods to CallbackQuery.
Either edits the message attribute, or the message associated with the inline_message_id.
2016-09-25 16:03:06 +02:00
Jacob Bom a996e8873f Add edit_* family of methods to Message. 2016-09-25 15:50:58 +02:00
Jacob Bom 3244417f61 Add docs for filters. 2016-09-25 00:30:04 +02:00
Jacob Bom 61596400e1 __call__ should return the result
Also add tests with both & and |.
2016-09-24 18:56:54 +02:00
Jacob Bom be0f5bc519 Merge branch 'master' into bitwise-filters
# Conflicts:
#	telegram/ext/messagehandler.py
#	tests/test_filters.py
2016-09-24 18:30:58 +02:00
Jacob Bom 921fbae2f3 Merge branch 'master' into bitwise-filters 2016-09-24 18:21:06 +02:00
Jacob Bom 2161681131 Use filter method instead of __call__
__call__ is scary looking for users wanted to create their own filters.
Also allows us to put additional logic in __call__ if we want in the future.
2016-09-24 18:20:32 +02:00
Jacob Bom 71e74da0a2 Make filters and/or-able using bitwise operators.
See associated PR for more info.
2016-09-14 19:29:15 +02:00
145 changed files with 3113 additions and 1218 deletions
+2 -2
View File
@@ -184,8 +184,8 @@ break the API classes. For example:
.. code-block:: python
# GOOD
def __init__(self, id, name, **kwargs):
self.last_name = kwargs.get('last_name', '')
def __init__(self, id, name, last_name='', **kwargs):
self.last_name = last_name
# BAD
def __init__(self, id, name, last_name=''):
+3
View File
@@ -68,3 +68,6 @@ telegram.mp4
telegram.ogg
telegram.png
telegram.webp
# original files from merges
*.orig
+2 -2
View File
@@ -1,5 +1,5 @@
- repo: git://github.com/pre-commit/mirrors-yapf
sha: v0.11.0
- repo: git://github.com/python-telegram-bot/mirrors-yapf
sha: v0.12.2
hooks:
- id: yapf
files: ^(telegram|tests)/.*\.py$
+4 -1
View File
@@ -6,6 +6,9 @@ python:
- "3.5"
- "pypy"
- "pypy3"
branches:
only:
- master
install:
- pip install coveralls
- pip install -r requirements.txt
@@ -13,6 +16,6 @@ install:
- if [[ $TRAVIS_PYTHON_VERSION != 'pypy'* ]]; then pip install ujson; fi
script:
- nosetests -v --with-flaky --no-flaky-report --with-coverage --cover-package=telegram/
- if [ $TRAVIS_PYTHON_VERSION != 3.3 ] && [ $TRAVIS_PYTHON_VERSION != pypy3 ]; then pre-commit run --all-files; fi
- if [[ $TRAVIS_PYTHON_VERSION == 3.5 ]]; then pre-commit run --all-files; fi
after_success:
coveralls
+2
View File
@@ -21,11 +21,13 @@ The following wonderful people contributed directly or indirectly to this projec
- `jlmadurga <https://github.com/jlmadurga>`_
- `Li-aung Yip <https://github.com/LiaungYip>`_
- `macrojames <https://github.com/macrojames>`_
- `Michael Elovskikh <https://github.com/wronglink>`_
- `naveenvhegde <https://github.com/naveenvhegde>`_
- `njittam <https://github.com/njittam>`_
- `Noam Meltzer <https://github.com/tsnoam>`_
- `Oleg Shlyazhko <https://github.com/ollmer>`_
- `overquota <https://github.com/overquota>`_
- `Patrick Hofmann <https://github.com/PH89>`_
- `Rahiel Kasim <https://github.com/rahiel>`_
- `Shelomentsev D <https://github.com/shelomentsevd>`_
- `sooyhwang <https://github.com/sooyhwang>`_
+10
View File
@@ -2,6 +2,16 @@
Changes
=======
**2016-10-25**
*Released 5.2*
- Implement API changes of October 3rd (games update)
- Add ``Message.edit_*`` methods
- Filters for the ``MessageHandler`` can now be combined using bitwise operators (``& and |``)
- Add a way to save user- and chat-related data temporarily
- Other bugfixes and improvements
**2016-09-24**
*Released 5.1*
+5 -5
View File
@@ -16,7 +16,7 @@ We have made you a wrapper you can't refuse
:alt: Supported python versions
.. image:: https://img.shields.io/badge/docs-latest-af1a97.svg
:target: https://pythonhosted.org/python-telegram-bot/
:target: https://python-telegram-bot.readthedocs.io/
:alt: Documentation Status
.. image:: https://img.shields.io/pypi/l/python-telegram-bot.svg
@@ -37,7 +37,7 @@ We have made you a wrapper you can't refuse
.. image:: http://isitmaintained.com/badge/resolution/python-telegram-bot/python-telegram-bot.svg
:target: http://isitmaintained.com/project/python-telegram-bot/python-telegram-bot
:alt: Average time to resolve an issue
:alt: Median time to resolve an issue
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg
:target: https://telegram.me/pythontelegrambotgroup
@@ -84,7 +84,7 @@ make the development of bots easy and straightforward. These classes are contain
Telegram API support
====================
As of **28. May 2016**, all types and methods of the Telegram Bot API are supported.
As of **3. Oct 2016**, all types and methods of the Telegram Bot API are supported.
==========
Installing
@@ -116,7 +116,7 @@ Our Wiki contains a lot of resources to get you started with ``python-telegram-b
Other references:
- `Telegram API documentation <https://core.telegram.org/bots/api>`_
- `python-telegram-bot documentation <https://pythonhosted.org/python-telegram-bot/>`_
- `python-telegram-bot documentation <https://python-telegram-bot.readthedocs.io/>`_
-------------------
Learning by example
@@ -162,7 +162,7 @@ If you want DEBUG logs instead:
Documentation
=============
``python-telegram-bot``'s documentation lives at `pythonhosted.org <https://pythonhosted.org/python-telegram-bot/>`_.
``python-telegram-bot``'s documentation lives at `readthedocs.io <https://python-telegram-bot.readthedocs.io/>`_.
============
Getting help
+3 -3
View File
@@ -15,7 +15,7 @@
import sys
import os
import shlex
import telegram
# import telegram
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -59,9 +59,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = telegram.__version__[:3]
version = '5.2' # telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = telegram.__version__
release = '5.2.0' # telegram.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
+7
View File
@@ -0,0 +1,7 @@
telegram.ext.filters module
===========================
.. automodule:: telegram.ext.filters
:members:
:undoc-members:
:show-inheritance:
+1
View File
@@ -15,6 +15,7 @@ Submodules
telegram.ext.commandhandler
telegram.ext.inlinequeryhandler
telegram.ext.messagehandler
telegram.ext.filters
telegram.ext.regexhandler
telegram.ext.stringcommandhandler
telegram.ext.stringregexhandler
+5 -2
View File
@@ -8,10 +8,13 @@ All examples are licensed under the [CC0 License](https://github.com/python-tele
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).
This bot uses the [`JobQueue`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.jobqueue.html) class to send timed messages. The user sets a timer by using `/set` command with a specific time, for example `/set 30`. The bot then sets up a job to send a message to that user after 30 seconds. The user can also cancel the timer by sending `/unset`. To learn more about the `JobQueue`, read [this wiki article](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue).
### [`conversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot.py)
A common task for a bot is to ask information from the user. In v5.0 of this library, we introduced the [`ConversationHandler`](https://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.
A common task for a bot is to ask information from the user. In v5.0 of this library, we introduced the [`ConversationHandler`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.conversationhandler.html) for that exact purpose. This example uses it to retrieve user-information in a conversation-like style.
### [`conversationbot2.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot2.py)
A more complex example of a bot that uses the `ConversationHandler`
### [`inlinekeyboard.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinekeyboard.py)
This example sheds some light on inline keyboards, callback queries and message editing.
+3 -3
View File
@@ -127,13 +127,13 @@ def main():
states={
GENDER: [RegexHandler('^(Boy|Girl|Other)$', gender)],
PHOTO: [MessageHandler([Filters.photo], photo),
PHOTO: [MessageHandler(Filters.photo, photo),
CommandHandler('skip', skip_photo)],
LOCATION: [MessageHandler([Filters.location], location),
LOCATION: [MessageHandler(Filters.location, location),
CommandHandler('skip', skip_location)],
BIO: [MessageHandler([Filters.text], bio)]
BIO: [MessageHandler(Filters.text, bio)]
},
fallbacks=[CommandHandler('cancel', cancel)]
+152
View File
@@ -0,0 +1,152 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Simple Bot to reply to Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot.
First, a few callback functions are defined. Then, those functions are passed to
the Dispatcher and registered at their respective places.
Then, the bot is started and runs until we press Ctrl-C on the command line.
Usage:
Example of a bot-user conversation using ConversationHandler.
Send /start to initiate the conversation.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram import ReplyKeyboardMarkup
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
ConversationHandler)
import logging
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3)
reply_keyboard = [['Age', 'Favourite colour'],
['Number of siblings', 'Something else...'],
['Done']]
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
def facts_to_str(user_data):
facts = list()
for key, value in user_data.items():
facts.append('%s - %s' % (key, value))
return "\n".join(facts).join(['\n', '\n'])
def start(bot, update):
update.message.reply_text(
"Hi! My name is Doctor Botter. I will hold a more complex conversation with you. "
"Why don't you tell me something about yourself?",
reply_markup=markup)
return CHOOSING
def regular_choice(bot, update, user_data):
text = update.message.text
user_data['choice'] = text
update.message.reply_text('Your %s? Yes, I would love to hear about that!' % text.lower())
return TYPING_REPLY
def custom_choice(bot, update):
update.message.reply_text('Alright, please send me the category first, '
'for example "Most impressive skill"')
return TYPING_CHOICE
def received_information(bot, update, user_data):
text = update.message.text
category = user_data['choice']
user_data[category] = text
del user_data['choice']
update.message.reply_text("Neat! Just so you know, this is what you already told me:"
"%s"
"You can tell me more, or change your opinion on something."
% facts_to_str(user_data),
reply_markup=markup)
return CHOOSING
def done(bot, update, user_data):
if 'choice' in user_data:
del user_data['choice']
update.message.reply_text("I learned these facts about you:"
"%s"
"Until next time!" % facts_to_str(user_data))
user_data.clear()
return ConversationHandler.END
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
def main():
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
# Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
CHOOSING: [RegexHandler('^(Age|Favourite colour|Number of siblings)$',
regular_choice,
pass_user_data=True),
RegexHandler('^Something else...$',
custom_choice),
],
TYPING_CHOICE: [MessageHandler(Filters.text,
regular_choice,
pass_user_data=True),
],
TYPING_REPLY: [MessageHandler(Filters.text,
received_information,
pass_user_data=True),
],
},
fallbacks=[RegexHandler('^Done$', done, pass_user_data=True)]
)
dp.add_handler(conv_handler)
# log all errors
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
# Run the bot until the you presses Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
+1 -1
View File
@@ -56,7 +56,7 @@ def main():
dp.add_handler(CommandHandler("help", help))
# on noncommand i.e message - echo the message on Telegram
dp.add_handler(MessageHandler([Filters.text], echo))
dp.add_handler(MessageHandler(Filters.text, echo))
# log all errors
dp.add_error_handler(error)
+26 -19
View File
@@ -46,6 +46,8 @@ from .file import File
from .emoji import Emoji
from .parsemode import ParseMode
from .messageentity import MessageEntity
from .animation import Animation
from .game import Game
from .message import Message
from .inputmessagecontent import InputMessageContent
from .callbackquery import CallbackQuery
@@ -73,10 +75,13 @@ from .inlinequeryresultphoto import InlineQueryResultPhoto
from .inlinequeryresultvenue import InlineQueryResultVenue
from .inlinequeryresultvideo import InlineQueryResultVideo
from .inlinequeryresultvoice import InlineQueryResultVoice
from .inlinequeryresultgame import InlineQueryResultGame
from .inputtextmessagecontent import InputTextMessageContent
from .inputlocationmessagecontent import InputLocationMessageContent
from .inputvenuemessagecontent import InputVenueMessageContent
from .inputcontactmessagecontent import InputContactMessageContent
from .webhookinfo import WebhookInfo
from .gamehighscore import GameHighScore
from .update import Update
from .bot import Bot
from .constants import (MAX_MESSAGE_LENGTH, MAX_CAPTION_LENGTH, SUPPORTED_WEBHOOK_PORTS,
@@ -87,22 +92,24 @@ from .version import __version__ # flake8: noqa
__author__ = 'devs@python-telegram-bot.org'
__all__ = ['Audio', 'Bot', 'Chat', 'ChatMember', 'ChatAction', 'ChosenInlineResult',
'CallbackQuery', 'Contact', 'Document', 'Emoji', 'File', 'ForceReply',
'InlineKeyboardButton', 'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult',
'InlineQueryResult', 'InlineQueryResultArticle', 'InlineQueryResultAudio',
'InlineQueryResultCachedAudio', 'InlineQueryResultCachedDocument',
'InlineQueryResultCachedGif', 'InlineQueryResultCachedMpeg4Gif',
'InlineQueryResultCachedPhoto', 'InlineQueryResultCachedSticker',
'InlineQueryResultCachedVideo', 'InlineQueryResultCachedVoice',
'InlineQueryResultContact', 'InlineQueryResultDocument', 'InlineQueryResultGif',
'InlineQueryResultLocation', 'InlineQueryResultMpeg4Gif', 'InlineQueryResultPhoto',
'InlineQueryResultVenue', 'InlineQueryResultVideo', 'InlineQueryResultVoice',
'InputContactMessageContent', 'InputFile', 'InputLocationMessageContent',
'InputMessageContent', 'InputTextMessageContent', 'InputVenueMessageContent',
'KeyboardButton', 'Location', 'Message', 'MessageEntity', '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']
__all__ = [
'Audio', 'Bot', 'Chat', 'ChatMember', 'ChatAction', 'ChosenInlineResult', 'CallbackQuery',
'Contact', 'Document', 'Emoji', 'File', 'ForceReply', 'InlineKeyboardButton',
'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult', 'InlineQueryResult',
'InlineQueryResultArticle', 'InlineQueryResultAudio', 'InlineQueryResultCachedAudio',
'InlineQueryResultCachedDocument', 'InlineQueryResultCachedGif',
'InlineQueryResultCachedMpeg4Gif', 'InlineQueryResultCachedPhoto',
'InlineQueryResultCachedSticker', 'InlineQueryResultCachedVideo',
'InlineQueryResultCachedVoice', 'InlineQueryResultContact', 'InlineQueryResultDocument',
'InlineQueryResultGif', 'InlineQueryResultLocation', 'InlineQueryResultMpeg4Gif',
'InlineQueryResultPhoto', 'InlineQueryResultVenue', 'InlineQueryResultVideo',
'InlineQueryResultVoice', 'InlineQueryResultGame', 'InputContactMessageContent', 'InputFile',
'InputLocationMessageContent', 'InputMessageContent', 'InputTextMessageContent',
'InputVenueMessageContent', 'KeyboardButton', 'Location', 'Message', 'MessageEntity',
'ParseMode', 'PhotoSize', 'ReplyKeyboardHide', 'ReplyKeyboardMarkup', 'ReplyMarkup', 'Sticker',
'TelegramError', 'TelegramObject', 'Update', 'User', 'UserProfilePhotos', 'Venue', 'Video',
'Voice', 'MAX_MESSAGE_LENGTH', 'MAX_CAPTION_LENGTH', 'SUPPORTED_WEBHOOK_PORTS',
'MAX_FILESIZE_DOWNLOAD', 'MAX_FILESIZE_UPLOAD', 'MAX_MESSAGES_PER_SECOND_PER_CHAT',
'MAX_MESSAGES_PER_SECOND', 'MAX_MESSAGES_PER_MINUTE_PER_GROUP', 'WebhookInfo', 'Animation',
'Game', 'GameHighScore'
]
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Animation."""
from telegram import PhotoSize
from telegram import TelegramObject
class Animation(TelegramObject):
"""This object represents a Telegram Animation.
Attributes:
file_id (str): Unique file identifier.
Keyword Args:
thumb (Optional[:class:`telegram.PhotoSize`]): Animation thumbnail as defined by sender.
file_name (Optional[str]): Original animation filename as defined by sender.
mime_type (Optional[str]): MIME type of the file as defined by sender.
file_size (Optional[int]): File size.
"""
def __init__(self, file_id, **kwargs):
self.file_id = file_id
self.thumb = kwargs.get('thumb')
self.file_name = kwargs.get('file_name')
self.mime_type = kwargs.get('mime_type')
self.file_size = kwargs.get('file_size')
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.Game:
"""
if not data:
return None
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
return Animation(**data)
+15 -9
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Audio."""
"""This module contains an object that represents a Telegram Audio."""
from telegram import TelegramObject
@@ -35,24 +35,30 @@ class Audio(TelegramObject):
Args:
file_id (str):
duration (int):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
performer (Optional[str]):
title (Optional[str]):
mime_type (Optional[str]):
file_size (Optional[int]):
**kwargs: Arbitrary keyword arguments.
"""
def __init__(self, file_id, duration, **kwargs):
def __init__(self,
file_id,
duration,
performer='',
title='',
mime_type='',
file_size=0,
**kwargs):
# Required
self.file_id = str(file_id)
self.duration = int(duration)
# Optionals
self.performer = kwargs.get('performer', '')
self.title = kwargs.get('title', '')
self.mime_type = str(kwargs.get('mime_type', ''))
self.file_size = int(kwargs.get('file_size', 0))
self.performer = performer
self.title = title
self.mime_type = str(mime_type)
self.file_size = int(file_size)
@staticmethod
def de_json(data, bot):
+650 -582
View File
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram CallbackGame."""
from telegram import TelegramObject
class CallbackGame(TelegramObject):
"""A placeholder, currently holds no information. Use BotFather to set up your game."""
+65 -6
View File
@@ -16,8 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
CallbackQuery"""
"""This module contains an object that represents a Telegram CallbackQuery"""
from telegram import TelegramObject, Message, User
@@ -25,14 +24,25 @@ from telegram import TelegramObject, Message, User
class CallbackQuery(TelegramObject):
"""This object represents a Telegram CallbackQuery."""
def __init__(self, id, from_user, data, bot=None, **kwargs):
def __init__(self,
id,
from_user,
chat_instance,
message=None,
data=None,
inline_message_id=None,
game_short_name=None,
bot=None,
**kwargs):
# Required
self.id = id
self.from_user = from_user
self.data = data
self.chat_instance = chat_instance
# Optionals
self.message = kwargs.get('message')
self.inline_message_id = kwargs.get('inline_message_id', '')
self.message = message
self.data = data
self.inline_message_id = inline_message_id
self.game_short_name = game_short_name
self.bot = bot
@@ -69,3 +79,52 @@ class CallbackQuery(TelegramObject):
def answer(self, *args, **kwargs):
"""Shortcut for ``bot.answerCallbackQuery(update.callback_query.id, *args, **kwargs)``"""
return self.bot.answerCallbackQuery(self.id, *args, **kwargs)
def edit_message_text(self, *args, **kwargs):
"""
Shortcut for either ``bot.editMessageText(chat_id=update.callback_query.message.chat_id, \
message_id=update.callback_query.message.message_id, \
*args, **kwargs)``
or ``bot.editMessageText(inline_message_id=update.callback_query.inline_message_id, \
*args, **kwargs)``
"""
if self.inline_message_id:
return self.bot.edit_message_text(
inline_message_id=self.inline_message_id, *args, **kwargs)
else:
return self.bot.edit_message_text(
chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs)
def edit_message_caption(self, *args, **kwargs):
"""
Shortcut for either
``bot.editMessageCaption(chat_id=update.callback_query.message.chat_id, \
message_id=update.callback_query.message.message_id, \
*args, **kwargs)``
or
``bot.editMessageCaption(inline_message_id=update.callback_query.inline_message_id, \
*args, **kwargs)``
"""
if self.inline_message_id:
return self.bot.edit_message_caption(
inline_message_id=self.inline_message_id, *args, **kwargs)
else:
return self.bot.edit_message_caption(
chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs)
def edit_message_reply_markup(self, *args, **kwargs):
"""
Shortcut for either
``bot.editMessageReplyMarkup(chat_id=update.callback_query.message.chat_id, \
message_id=update.callback_query.message.message_id, \
*args, **kwargs)``
or
``bot.editMessageReplyMarkup(inline_message_id=update.callback_query.inline_message_id, \
*args, **kwargs)``
"""
if self.inline_message_id:
return self.bot.edit_message_reply_markup(
inline_message_id=self.inline_message_id, *args, **kwargs)
else:
return self.bot.edit_message_reply_markup(
chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs)
+23 -11
View File
@@ -17,7 +17,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Chat."""
"""This module contains an object that represents a Telegram Chat."""
from telegram import TelegramObject
@@ -32,31 +32,43 @@ class Chat(TelegramObject):
username (str): Username, for private chats and channels if available
first_name (str): First name of the other party in a private chat
last_name (str): Last name of the other party in a private chat
all_members_are_admins (bool): True if a group has 'All Members Are Admins' enabled.
Args:
id (int):
type (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
type (Optional[str]):
title (Optional[str]):
username(Optional[str]):
first_name(Optional[str]):
last_name(Optional[str]):
bot (Optional[Bot]): The Bot to use for instance methods
"""
**kwargs (dict): Arbitrary keyword arguments.
"""
PRIVATE = 'private'
GROUP = 'group'
SUPERGROUP = 'supergroup'
CHANNEL = 'channel'
def __init__(self, id, type, bot=None, **kwargs):
def __init__(self,
id,
type,
title='',
username='',
first_name='',
last_name='',
all_members_are_admins=False,
bot=None,
**kwargs):
# Required
self.id = int(id)
self.type = type
# Optionals
self.title = kwargs.get('title', '')
self.username = kwargs.get('username', '')
self.first_name = kwargs.get('first_name', '')
self.last_name = kwargs.get('last_name', '')
self.title = title
self.username = username
self.first_name = first_name
self.last_name = last_name
self.all_members_are_admins = all_members_are_admins
self.bot = bot
+1 -1
View File
@@ -17,7 +17,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram ChatAction."""
"""This module contains an object that represents a Telegram ChatAction."""
class ChatAction(object):
+3 -2
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram ChatMember."""
"""This module contains an object that represents a Telegram ChatMember."""
from telegram import User, TelegramObject
@@ -32,8 +32,9 @@ class ChatMember(TelegramObject):
Args:
user (:class:`telegram.User`):
status (str):
"""
**kwargs (dict): Arbitrary keyword arguments.
"""
CREATOR = 'creator'
ADMINISTRATOR = 'administrator'
MEMBER = 'member'
+7 -2
View File
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""
This module contains a object that represents a Telegram ChosenInlineResult
This module contains an object that represents a Telegram ChosenInlineResult
"""
from telegram import TelegramObject, User, Location
@@ -33,11 +33,16 @@ class ChosenInlineResult(TelegramObject):
result_id (str):
from_user (:class:`telegram.User`):
query (str):
location (:class:`telegram.Location`):
inline_message_id (str):
Args:
result_id (str):
from_user (:class:`telegram.User`):
query (str):
location (Optional[:class:`telegram.Location`]):
inline_message_id (Optional[str]):
**kwargs (dict): Arbitrary keyword arguments.
"""
@@ -69,7 +74,7 @@ class ChosenInlineResult(TelegramObject):
if not data:
return None
# Required
# Required
data['from_user'] = User.de_json(data.pop('from'), bot)
# Optionals
data['location'] = Location.de_json(data.get('location'), bot)
+6 -7
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Contact."""
"""This module contains an object that represents a Telegram Contact."""
from telegram import TelegramObject
@@ -33,20 +33,19 @@ class Contact(TelegramObject):
Args:
phone_number (str):
first_name (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
last_name (Optional[str]):
user_id (Optional[int]):
**kwargs: Arbitrary keyword arguments.
"""
def __init__(self, phone_number, first_name, **kwargs):
def __init__(self, phone_number, first_name, last_name='', user_id=0, **kwargs):
# Required
self.phone_number = str(phone_number)
self.first_name = first_name
# Optionals
self.last_name = kwargs.get('last_name', '')
self.user_id = int(kwargs.get('user_id', 0))
self.last_name = last_name
self.user_id = int(user_id)
@staticmethod
def de_json(data, bot):
+8 -9
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Document."""
"""This module contains an object that represents a Telegram Document."""
from telegram import PhotoSize, TelegramObject
@@ -33,23 +33,22 @@ class Document(TelegramObject):
Args:
file_id (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
thumb (Optional[:class:`telegram.PhotoSize`]):
file_name (Optional[str]):
mime_type (Optional[str]):
file_size (Optional[int]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, file_id, **kwargs):
def __init__(self, file_id, thumb=None, file_name='', mime_type='', file_size=0, **kwargs):
# Required
self.file_id = str(file_id)
# Optionals
self.thumb = kwargs.get('thumb')
self.file_name = kwargs.get('file_name', '')
self.mime_type = str(kwargs.get('mime_type', ''))
self.file_size = int(kwargs.get('file_size', 0))
self.thumb = thumb
self.file_name = file_name
self.mime_type = str(mime_type)
self.file_size = int(file_size)
@staticmethod
def de_json(data, bot):
+1 -1
View File
@@ -18,7 +18,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents an Emoji.
"""This module contains an object that represents an Emoji.
This module will be removed in the future.
"""
+16 -4
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Error."""
"""This module contains an object that represents a Telegram Error."""
def _lstrip_str(in_s, lstr):
@@ -94,8 +94,20 @@ class ChatMigrated(TelegramError):
Args:
new_chat_id (int):
Returns:
"""
super(ChatMigrated,
self).__init__('Group migrated to supergroup. New chat id: {}'.format(new_chat_id))
self.new_chat_id = new_chat_id
class RetryAfter(TelegramError):
def __init__(self, retry_after):
"""
Args:
retry_after (int):
"""
super(ChatMigrated, self).__init__('Chat migrated')
self.new_chat_id = new_chat_id
super(RetryAfter,
self).__init__('Flood control exceeded. Retry in {} seconds'.format(retry_after))
self.retry_after = float(retry_after)
+3 -2
View File
@@ -26,7 +26,8 @@ from .choseninlineresulthandler import ChosenInlineResultHandler
from .commandhandler import CommandHandler
from .handler import Handler
from .inlinequeryhandler import InlineQueryHandler
from .messagehandler import MessageHandler, Filters
from .messagehandler import MessageHandler
from .filters import BaseFilter, Filters
from .regexhandler import RegexHandler
from .stringcommandhandler import StringCommandHandler
from .stringregexhandler import StringRegexHandler
@@ -35,5 +36,5 @@ from .conversationhandler import ConversationHandler
__all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
'ChosenInlineResultHandler', 'CommandHandler', 'Handler', 'InlineQueryHandler',
'MessageHandler', 'Filters', 'RegexHandler', 'StringCommandHandler',
'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler',
'StringRegexHandler', 'TypeHandler', 'ConversationHandler')
+17 -3
View File
@@ -52,6 +52,14 @@ class CallbackQueryHandler(Handler):
pass_groupdict (optional[bool]): If the callback should be passed the
result of ``re.match(pattern, data).groupdict()`` as a keyword
argument called ``groupdict``. Default is ``False``
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
"""
def __init__(self,
@@ -60,9 +68,15 @@ class CallbackQueryHandler(Handler):
pass_job_queue=False,
pattern=None,
pass_groups=False,
pass_groupdict=False):
pass_groupdict=False,
pass_user_data=False,
pass_chat_data=False):
super(CallbackQueryHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
if isinstance(pattern, string_types):
pattern = re.compile(pattern)
@@ -81,7 +95,7 @@ class CallbackQueryHandler(Handler):
return True
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
optional_args = self.collect_optional_args(dispatcher, update)
if self.pattern:
match = re.match(self.pattern, update.callback_query.data)
+20 -3
View File
@@ -40,17 +40,34 @@ class ChosenInlineResultHandler(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``.
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
"""
def __init__(self, callback, pass_update_queue=False, pass_job_queue=False):
def __init__(self,
callback,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(ChosenInlineResultHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
def check_update(self, update):
return isinstance(update, Update) and update.chosen_inline_result
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
optional_args = self.collect_optional_args(dispatcher, update)
return self.callback(dispatcher.bot, update, **optional_args)
+17 -3
View File
@@ -49,6 +49,14 @@ class CommandHandler(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``.
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
"""
def __init__(self,
@@ -57,9 +65,15 @@ class CommandHandler(Handler):
allow_edited=False,
pass_args=False,
pass_update_queue=False,
pass_job_queue=False):
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(CommandHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
self.command = command
self.allow_edited = allow_edited
self.pass_args = pass_args
@@ -76,7 +90,7 @@ class CommandHandler(Handler):
return False
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
optional_args = self.collect_optional_args(dispatcher, update)
message = update.message or update.edited_message
+2 -23
View File
@@ -22,6 +22,7 @@ import logging
from telegram import Update
from telegram.ext import Handler
from telegram.utils.helpers import extract_chat_and_user
from telegram.utils.promise import Promise
@@ -115,29 +116,7 @@ class ConversationHandler(Handler):
if not isinstance(update, Update):
return False
user = None
chat = None
if update.message:
user = update.message.from_user
chat = update.message.chat
elif update.edited_message:
user = update.edited_message.from_user
chat = update.edited_message.chat
elif update.inline_query:
user = update.inline_query.from_user
elif update.chosen_inline_result:
user = update.chosen_inline_result.from_user
elif update.callback_query:
user = update.callback_query.from_user
chat = update.callback_query.message.chat if update.callback_query.message else None
else:
return False
chat, user = extract_chat_and_user(update)
key = (chat.id, user.id) if chat else (None, user.id)
state = self.conversations.get(key)
+8 -2
View File
@@ -24,6 +24,7 @@ from functools import wraps
from threading import Thread, Lock, Event, current_thread, BoundedSemaphore
from time import sleep
from uuid import uuid4
from collections import defaultdict
from queue import Queue, Empty
@@ -84,6 +85,12 @@ class Dispatcher(object):
self.bot = bot
self.update_queue = update_queue
self.job_queue = job_queue
self.workers = workers
self.user_data = defaultdict(dict)
""":type: dict[int, dict]"""
self.chat_data = defaultdict(dict)
""":type: dict[int, dict]"""
self.handlers = {}
""":type: dict[int, list[Handler]"""
@@ -105,8 +112,6 @@ class Dispatcher(object):
else:
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
@@ -193,6 +198,7 @@ class Dispatcher(object):
self.logger.error(msg)
raise TelegramError(msg)
self._init_async_threads(uuid4(), self.workers)
self.running = True
self.logger.debug('Dispatcher started')
+216
View File
@@ -0,0 +1,216 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the Filters for use with the MessageHandler class """
class BaseFilter(object):
"""Base class for all Message Filters
Subclassing from this class filters to be combined using bitwise operators:
And:
>>> (Filters.text & Filters.entity(MENTION))
Or:
>>> (Filters.audio | Filters.video)
Also works with more than two filters:
>>> (Filters.text & (Filters.entity(URL) | Filters.entity(TEXT_LINK)))
If you want to create your own filters create a class inheriting from this class and implement
a `filter` method that returns a boolean: `True` if the message should be handled, `False`
otherwise. Note that the filters work only as class instances, not actual class objects
(so remember to initialize your filter classes).
"""
def __call__(self, message):
return self.filter(message)
def __and__(self, other):
return MergedFilter(self, and_filter=other)
def __or__(self, other):
return MergedFilter(self, or_filter=other)
def filter(self, message):
raise NotImplementedError
class MergedFilter(BaseFilter):
"""Represents a filter consisting of two other filters.
Args:
base_filter: Filter 1 of the merged filter
and_filter: Optional filter to "and" with base_filter. Mutually exclusive with or_filter.
or_filter: Optional filter to "or" with base_filter. Mutually exclusive with and_filter.
"""
def __init__(self, base_filter, and_filter=None, or_filter=None):
self.base_filter = base_filter
self.and_filter = and_filter
self.or_filter = or_filter
def filter(self, message):
if self.and_filter:
return self.base_filter(message) and self.and_filter(message)
elif self.or_filter:
return self.base_filter(message) or self.or_filter(message)
def __str__(self):
return ("<telegram.ext.filters.MergedFilter consisting of"
" {} {} {}>").format(self.base_filter, "and" if self.and_filter else "or",
self.and_filter or self.or_filter)
__repr__ = __str__
class Filters(object):
"""
Predefined filters for use with the `filter` argument of :class:`telegram.ext.MessageHandler`.
"""
class _All(BaseFilter):
def filter(self, message):
return True
all = _All()
class _Text(BaseFilter):
def filter(self, message):
return bool(message.text and not message.text.startswith('/'))
text = _Text()
class _Command(BaseFilter):
def filter(self, message):
return bool(message.text and message.text.startswith('/'))
command = _Command()
class _Audio(BaseFilter):
def filter(self, message):
return bool(message.audio)
audio = _Audio()
class _Document(BaseFilter):
def filter(self, message):
return bool(message.document)
document = _Document()
class _Photo(BaseFilter):
def filter(self, message):
return bool(message.photo)
photo = _Photo()
class _Sticker(BaseFilter):
def filter(self, message):
return bool(message.sticker)
sticker = _Sticker()
class _Video(BaseFilter):
def filter(self, message):
return bool(message.video)
video = _Video()
class _Voice(BaseFilter):
def filter(self, message):
return bool(message.voice)
voice = _Voice()
class _Contact(BaseFilter):
def filter(self, message):
return bool(message.contact)
contact = _Contact()
class _Location(BaseFilter):
def filter(self, message):
return bool(message.location)
location = _Location()
class _Venue(BaseFilter):
def filter(self, message):
return bool(message.venue)
venue = _Venue()
class _StatusUpdate(BaseFilter):
def filter(self, message):
return bool(message.new_chat_member or message.left_chat_member
or message.new_chat_title or message.new_chat_photo
or message.delete_chat_photo or message.group_chat_created
or message.supergroup_chat_created or message.channel_chat_created
or message.migrate_to_chat_id or message.migrate_from_chat_id
or message.pinned_message)
status_update = _StatusUpdate()
class _Forwarded(BaseFilter):
def filter(self, message):
return bool(message.forward_date)
forwarded = _Forwarded()
class _Game(BaseFilter):
def filter(self, message):
return bool(message.game)
game = _Game()
class entity(BaseFilter):
"""Filters messages to only allow those which have a :class:`telegram.MessageEntity`
where their `type` matches `entity_type`.
Args:
entity_type: Entity type to check for. All types can be found as constants
in :class:`telegram.MessageEntity`.
Returns: function to use as filter
"""
def __init__(self, entity_type):
self.entity_type = entity_type
def filter(self, message):
return any([entity.type == self.entity_type for entity in message.entities])
+27 -2
View File
@@ -20,6 +20,7 @@
Dispatcher """
from telegram.utils.deprecate import deprecate
from telegram.utils.helpers import extract_chat_and_user
class Handler(object):
@@ -39,12 +40,27 @@ class Handler(object):
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
"""
def __init__(self, callback, pass_update_queue=False, pass_job_queue=False):
def __init__(self,
callback,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
self.callback = callback
self.pass_update_queue = pass_update_queue
self.pass_job_queue = pass_job_queue
self.pass_user_data = pass_user_data
self.pass_chat_data = pass_chat_data
def check_update(self, update):
"""
@@ -74,7 +90,7 @@ class Handler(object):
"""
raise NotImplementedError
def collect_optional_args(self, dispatcher):
def collect_optional_args(self, dispatcher, update=None):
"""
Prepares the optional arguments that are the same for all types of
handlers
@@ -83,10 +99,19 @@ class Handler(object):
dispatcher (Dispatcher):
"""
optional_args = dict()
if self.pass_update_queue:
optional_args['update_queue'] = dispatcher.update_queue
if self.pass_job_queue:
optional_args['job_queue'] = dispatcher.job_queue
if self.pass_user_data or self.pass_chat_data:
chat, user = extract_chat_and_user(update)
if self.pass_user_data:
optional_args['user_data'] = dispatcher.user_data[user.id]
if self.pass_chat_data:
optional_args['chat_data'] = dispatcher.chat_data[chat.id if chat else None]
return optional_args
+17 -3
View File
@@ -51,6 +51,14 @@ class InlineQueryHandler(Handler):
pass_groupdict (optional[bool]): If the callback should be passed the
result of ``re.match(pattern, query).groupdict()`` as a keyword
argument called ``groupdict``. Default is ``False``
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
"""
def __init__(self,
@@ -59,9 +67,15 @@ class InlineQueryHandler(Handler):
pass_job_queue=False,
pattern=None,
pass_groups=False,
pass_groupdict=False):
pass_groupdict=False,
pass_user_data=False,
pass_chat_data=False):
super(InlineQueryHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
if isinstance(pattern, string_types):
pattern = re.compile(pattern)
@@ -80,7 +94,7 @@ class InlineQueryHandler(Handler):
return True
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
optional_args = self.collect_optional_args(dispatcher, update)
if self.pattern:
match = re.match(self.pattern, update.inline_query.query)
+9 -8
View File
@@ -20,6 +20,7 @@
import logging
import time
import warnings
from threading import Thread, Lock, Event
from queue import PriorityQueue, Empty
@@ -30,15 +31,19 @@ class JobQueue(object):
Attributes:
queue (PriorityQueue):
bot (Bot):
prevent_autostart (Optional[bool]): If ``True``, the job queue will not be started
automatically. Defaults to ``False``
Args:
bot (Bot): The bot instance that should be passed to the jobs
Deprecated: 5.2
prevent_autostart (Optional[bool]): Thread does not start during initialisation.
Use `start` method instead.
"""
def __init__(self, bot, prevent_autostart=False):
def __init__(self, bot, prevent_autostart=None):
if prevent_autostart is not None:
warnings.warn("prevent_autostart is being deprecated, use `start` method instead.")
self.queue = PriorityQueue()
self.bot = bot
self.logger = logging.getLogger(self.__class__.__name__)
@@ -51,12 +56,8 @@ class JobQueue(object):
""":type: float"""
self._running = False
if not prevent_autostart:
self.logger.debug('Auto-starting %s', self.__class__.__name__)
self.start()
def put(self, job, next_t=None):
"""Queue a new job. If the JobQueue is not running, it will be started.
"""Queue a new job.
Args:
job (Job): The ``Job`` instance representing the new job
+34 -92
View File
@@ -17,92 +17,13 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the MessageHandler class """
import warnings
from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate
class Filters(object):
"""
Convenient namespace (class) & methods for the filter funcs of the
MessageHandler class.
"""
@staticmethod
def text(message):
return message.text and not message.text.startswith('/')
@staticmethod
def command(message):
return message.text and message.text.startswith('/')
@staticmethod
def audio(message):
return bool(message.audio)
@staticmethod
def document(message):
return bool(message.document)
@staticmethod
def photo(message):
return bool(message.photo)
@staticmethod
def sticker(message):
return bool(message.sticker)
@staticmethod
def video(message):
return bool(message.video)
@staticmethod
def voice(message):
return bool(message.voice)
@staticmethod
def contact(message):
return bool(message.contact)
@staticmethod
def location(message):
return bool(message.location)
@staticmethod
def venue(message):
return bool(message.venue)
@staticmethod
def status_update(message):
return bool(message.new_chat_member or message.left_chat_member or message.new_chat_title
or message.new_chat_photo or message.delete_chat_photo
or message.group_chat_created or message.supergroup_chat_created
or message.channel_chat_created or message.migrate_to_chat_id
or message.migrate_from_chat_id or message.pinned_message)
@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):
"""
Handler class to handle telegram messages. Messages are Telegram Updates
@@ -110,12 +31,10 @@ class MessageHandler(Handler):
updates.
Args:
filters (list[function]): A list of filter functions. Standard filters
can be found in the Filters class above.
| Each `function` takes ``Update`` as arg and returns ``bool``.
| All messages that match at least one of those filters will be
accepted. If ``bool(filters)`` evaluates to ``False``, messages are
not filtered.
filters (telegram.ext.BaseFilter): A filter inheriting from
:class:`telegram.filters.BaseFilter`. Standard filters can be found in
:class:`telegram.filters.Filters`. Filters can be combined using bitwise
operators (& for and, | for or).
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
@@ -124,6 +43,14 @@ class MessageHandler(Handler):
pass_update_queue (optional[bool]): If the handler should be passed the
update queue as a keyword argument called ``update_queue``. It can
be used to insert updates. Default is ``False``
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
"""
def __init__(self,
@@ -131,12 +58,25 @@ class MessageHandler(Handler):
callback,
allow_edited=False,
pass_update_queue=False,
pass_job_queue=False):
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(MessageHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
self.filters = filters
self.allow_edited = allow_edited
# We put this up here instead of with the rest of checking code
# in check_update since we don't wanna spam a ton
if isinstance(self.filters, list):
warnings.warn('Using a list of filters in MessageHandler is getting '
'deprecated, please use bitwise operators (& and |) '
'instead. More info: https://git.io/vPTbc.')
def check_update(self, update):
if (isinstance(update, Update)
and (update.message or update.edited_message and self.allow_edited)):
@@ -146,7 +86,10 @@ class MessageHandler(Handler):
else:
message = update.message or update.edited_message
res = any(func(message) for func in self.filters)
if isinstance(self.filters, list):
res = any(func(message) for func in self.filters)
else:
res = self.filters(message)
else:
res = False
@@ -154,12 +97,11 @@ class MessageHandler(Handler):
return res
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
optional_args = self.collect_optional_args(dispatcher, update)
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")
+18 -4
View File
@@ -53,6 +53,14 @@ class RegexHandler(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``.
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
"""
def __init__(self,
@@ -61,9 +69,15 @@ class RegexHandler(Handler):
pass_groups=False,
pass_groupdict=False,
pass_update_queue=False,
pass_job_queue=False):
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(RegexHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
if isinstance(pattern, string_types):
pattern = re.compile(pattern)
@@ -73,14 +87,14 @@ class RegexHandler(Handler):
self.pass_groupdict = pass_groupdict
def check_update(self, update):
if (isinstance(update, Update) and update.message and update.message.text):
if isinstance(update, Update) and update.message and update.message.text:
match = re.match(self.pattern, update.message.text)
return bool(match)
else:
return False
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
optional_args = self.collect_optional_args(dispatcher, update)
match = re.match(self.pattern, update.message.text)
if self.pass_groups:
+7 -3
View File
@@ -30,7 +30,7 @@ from queue import Queue
from telegram import Bot, TelegramError
from telegram.ext import Dispatcher, JobQueue
from telegram.error import Unauthorized, InvalidToken
from telegram.error import Unauthorized, InvalidToken, RetryAfter
from telegram.utils.request import Request
from telegram.utils.webhookhandler import (WebhookServer, WebhookHandler)
@@ -61,8 +61,6 @@ class Updater(object):
bot (Optional[Bot]): A pre-initialized bot instance. If a pre-initizlied bot is used, it is
the user's responsibility to create it using a `Request` instance with a large enough
connection pool.
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.
@@ -157,6 +155,7 @@ class Updater(object):
self.running = True
# Create & start threads
self.job_queue.start()
self._init_thread(self.dispatcher.start, "dispatcher")
self._init_thread(self._start_polling, "updater", poll_interval, timeout,
network_delay, bootstrap_retries, clean)
@@ -208,6 +207,7 @@ class Updater(object):
self.running = True
# Create & start threads
self.job_queue.start()
self._init_thread(self.dispatcher.start, "dispatcher"),
self._init_thread(self._start_webhook, "updater", listen, port, url_path, cert,
key, bootstrap_retries, clean, webhook_url)
@@ -231,6 +231,9 @@ class Updater(object):
try:
updates = self.bot.getUpdates(
self.last_update_id, timeout=timeout, network_delay=network_delay)
except RetryAfter as e:
self.logger.info(str(e))
cur_interval = 0.5 + e.retry_after
except TelegramError as te:
self.logger.error("Error while getting Updates: {0}".format(te))
@@ -327,6 +330,7 @@ class Updater(object):
# Disable webhook for cleaning
self.bot.setWebhook(webhook_url='')
self._clean_updates()
sleep(1)
self.bot.setWebhook(webhook_url=webhook_url, certificate=cert)
except (Unauthorized, InvalidToken):
+5 -7
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram File."""
"""This module contains an object that represents a Telegram File."""
from os.path import basename
@@ -34,21 +34,19 @@ class File(TelegramObject):
Args:
file_id (str):
bot (telegram.Bot):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
file_size (Optional[int]):
file_path (Optional[str]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, file_id, bot, **kwargs):
def __init__(self, file_id, bot, file_size=0, file_path='', **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.file_size = int(file_size)
self.file_path = str(file_path)
self.bot = bot
+5 -6
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram ForceReply."""
"""This module contains an object that represents a Telegram ForceReply."""
from telegram import ReplyMarkup
@@ -30,17 +30,16 @@ class ForceReply(ReplyMarkup):
Args:
force_reply (bool):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
selective (Optional[bool]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, force_reply=True, **kwargs):
def __init__(self, force_reply=True, selective=False, **kwargs):
# Required
self.force_reply = bool(force_reply)
# Optionals
self.selective = bool(kwargs.get('selective', False))
self.selective = bool(selective)
@staticmethod
def de_json(data, bot):
+146
View File
@@ -0,0 +1,146 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Game."""
import sys
from telegram import MessageEntity, TelegramObject, Animation, PhotoSize
class Game(TelegramObject):
"""This object represents a Telegram Game.
Attributes:
title (str): Title of the game.
description (str): Description of the game.
photo (list[:class:`telegram.PhotoSize`]): List of photos that will be displayed in the
game message in chats.
Keyword Args:
text (Optional[str]): Brief description of the game or high scores included in the game
message. Can be automatically edited to include current high scores for the game when
the bot calls setGameScore, or manually edited using editMessageText.
0-4096 characters.
text_entities (Optional[list[:class:`telegram.MessageEntity`]]): Special entities that
appear in text, such as usernames, URLs, bot commands, etc.
animation (Optional[:class:`telegram.Animation`]): Animation that will be displayed in the
game message in chats. Upload via BotFather.
"""
def __init__(self,
title,
description,
photo,
text='',
text_entities=None,
animation=None,
**kwargs):
self.title = title
self.description = description
self.photo = photo
self.text = text
self.text_entities = text_entities
self.animation = animation
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.Game:
"""
if not data:
return None
data['photo'] = PhotoSize.de_list(data.get('photo'), bot)
data['text_entities'] = MessageEntity.de_list(data.get('text_entities'), bot)
data['animation'] = Animation.de_json(data.get('animation'), bot)
return Game(**data)
def to_dict(self):
"""
Returns:
dict:
"""
data = super(Game, self).to_dict()
data['photo'] = [p.to_dict() for p in self.photo]
data['text_entities'] = [x.to_dict() for x in self.text_entities]
return data
def parse_text_entity(self, entity):
"""
Returns the text from a given :class:`telegram.MessageEntity`.
Note:
This method is present because Telegram calculates the offset and length in
UTF-16 codepoint pairs, which some versions of Python don't handle automatically.
(That is, you can't just slice ``Message.text`` with the offset and length.)
Args:
entity (MessageEntity): The entity to extract the text from. It must be an entity that
belongs to this message.
Returns:
str: The text of the given entity
"""
# Is it a narrow build, if so we don't need to convert
if sys.maxunicode == 0xffff:
return self.text[entity.offset:entity.offset + entity.length]
else:
entity_text = self.text.encode('utf-16-le')
entity_text = entity_text[entity.offset * 2:(entity.offset + entity.length) * 2]
return entity_text.decode('utf-16-le')
def parse_text_entities(self, types=None):
"""
Returns a ``dict`` that maps :class:`telegram.MessageEntity` to ``str``.
It contains entities from this message filtered by their ``type`` attribute as the key, and
the text that each entity belongs to as the value of the ``dict``.
Note:
This method should always be used instead of the ``entities`` attribute, since it
calculates the correct substring from the message text based on UTF-16 codepoints.
See ``get_entity_text`` for more info.
Args:
types (Optional[list]): List of ``MessageEntity`` types as strings. If the ``type``
attribute of an entity is contained in this list, it will be returned.
Defaults to a list of all types. All types can be found as constants in
:class:`telegram.MessageEntity`.
Returns:
dict[:class:`telegram.MessageEntity`, ``str``]: A dictionary of entities mapped to the
text that belongs to them, calculated based on UTF-16 codepoints.
"""
if types is None:
types = MessageEntity.ALL_TYPES
return {
entity: self.parse_text_entity(entity)
for entity in self.text_entities if entity.type in types
}
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram GameHighScore."""
from telegram import TelegramObject, User
class GameHighScore(TelegramObject):
"""This object represents a Telegram GameHighScore.
Attributes:
position (int): Position in high score table for the game.
user (:class:`telegram.User`): User object.
score (int): Score.
"""
def __init__(self, position, user, score):
self.position = position
self.user = user
self.score = score
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.Game:
"""
if not data:
return None
data['user'] = User.de_json(data.get('user'), bot)
return GameHighScore(**data)
+23 -12
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
"""This module contains an object that represents a Telegram
InlineKeyboardButton"""
from telegram import TelegramObject
@@ -30,26 +30,37 @@ class InlineKeyboardButton(TelegramObject):
url (str):
callback_data (str):
switch_inline_query (str):
switch_inline_query_current_chat (str):
callback_game (:class:`telegram.CallbackGame`):
Args:
text (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
url (Optional[str]):
callback_data (Optional[str]):
switch_inline_query (Optional[str]):
text (str): Label text on the button.
url (Optional[str]): HTTP url to be opened when button is pressed.
callback_data (Optional[str]): Data to be sent in a callback query to the bot when button
is pressed, 1-64 bytes.
switch_inline_query (Optional[str]): If set, pressing the button will prompt the user to
select one of their chats, open that chat and insert the bot's username and the
specified inline query in the input field. Can be empty, in which case just the bot's
username will be inserted.
switch_inline_query_current_chat (Optional[str]): If set, pressing the button will insert
the bot's username and the specified inline query in the current chat's input field.
Can be empty, in which case only the bot's username will be inserted.
callback_game (Optional[:class:`telegram.CallbackGame`]): Description of the game that will
be launched when the user presses the button.
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, text, **kwargs):
def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, **kwargs):
# Required
self.text = text
# Optionals
self.url = kwargs.get('url')
self.callback_data = kwargs.get('callback_data')
self.switch_inline_query = kwargs.get('switch_inline_query')
self.url = url
self.callback_data = callback_data
self.switch_inline_query = switch_inline_query
self.switch_inline_query_current_chat = kwargs.get('switch_inline_query_current_chat')
self.callback_game = kwargs.get('callback_game')
@staticmethod
def de_json(data, bot):
+7 -3
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
"""This module contains an object that represents a Telegram
InlineKeyboardMarkup"""
from telegram import ReplyMarkup, InlineKeyboardButton
@@ -30,6 +30,7 @@ class InlineKeyboardMarkup(ReplyMarkup):
Args:
inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]):
**kwargs (dict): Arbitrary keyword arguments.
"""
@@ -46,14 +47,17 @@ class InlineKeyboardMarkup(ReplyMarkup):
Returns:
telegram.InlineKeyboardMarkup:
"""
data = super(InlineKeyboardMarkup, InlineKeyboardMarkup).de_json(data, bot)
if not data:
return None
data['inline_keyboard'] = [InlineKeyboardButton.de_list(inline_keyboard, bot)
for inline_keyboard in data['inline_keyboard']]
data['inline_keyboard'] = [
InlineKeyboardButton.de_list(inline_keyboard, bot)
for inline_keyboard in data['inline_keyboard']
]
return InlineKeyboardMarkup(**data)
+5 -6
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram InlineQuery"""
"""This module contains an object that represents a Telegram InlineQuery"""
from telegram import TelegramObject, User, Location
@@ -38,14 +38,13 @@ class InlineQuery(TelegramObject):
from_user (:class:`telegram.User`):
query (str):
offset (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
location (optional[:class:`telegram.Location`]):
bot (Optional[Bot]): The Bot to use for instance methods
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, id, from_user, query, offset, bot=None, **kwargs):
def __init__(self, id, from_user, query, offset, location=None, bot=None, **kwargs):
# Required
self.id = id
self.from_user = from_user
@@ -53,7 +52,7 @@ class InlineQuery(TelegramObject):
self.offset = offset
# Optional
self.location = kwargs.get('location')
self.location = location
self.bot = bot
+4 -3
View File
@@ -26,12 +26,13 @@ class InlineQueryResult(TelegramObject):
"""This object represents a Telegram InlineQueryResult.
Attributes:
type (str):
id (str):
type (str): Type of the result.
id (str): Unique identifier for this result, 1-64 Bytes
Args:
type (str):
type (str): Type of the result.
id (str): Unique identifier for this result, 1-64 Bytes
**kwargs (dict): Arbitrary keyword arguments.
"""
+3 -4
View File
@@ -42,21 +42,20 @@ class InlineQueryResultArticle(InlineQueryResult):
parse_mode (str): Use :class:`InputTextMessageContent` instead.
disable_web_page_preview (bool): Use :class:`InputTextMessageContent`
instead.
disable_web_page_preview (bool): Use :class:`InputTextMessageContent` instead.
Args:
id (str): Unique identifier for this result, 1-64 Bytes
title (str):
reply_markup (:class:`telegram.ReplyMarkup`):
Keyword Args:
url (Optional[str]):
hide_url (Optional[bool]):
description (Optional[str]):
thumb_url (Optional[str]):
thumb_width (Optional[int]):
thumb_height (Optional[int]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
+10 -9
View File
@@ -33,29 +33,27 @@ class InlineQueryResultAudio(InlineQueryResult):
title (str):
performer (Optional[str]):
audio_duration (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[
:class:`telegram.input_message_content`]):
input_message_content (Optional[:class:`telegram.input_message_content`]):
Deprecated: 4.0
message_text (str): Use :class:`InputTextMessageContent` instead.
parse_mode (str): Use :class:`InputTextMessageContent` instead.
disable_web_page_preview (bool): Use :class:`InputTextMessageContent`
instead.
disable_web_page_preview (bool): Use :class:`InputTextMessageContent` instead.
Args:
audio_url (str):
title (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
performer (Optional[str]):
audio_duration (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[
:class:`telegram.input_message_content`]):
input_message_content (Optional[:class:`telegram.input_message_content`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
@@ -64,6 +62,7 @@ class InlineQueryResultAudio(InlineQueryResult):
title,
performer=None,
audio_duration=None,
caption=None,
reply_markup=None,
input_message_content=None,
**kwargs):
@@ -78,6 +77,8 @@ class InlineQueryResultAudio(InlineQueryResult):
self.performer = performer
if audio_duration:
self.audio_duration = audio_duration
if caption:
self.caption = caption
if reply_markup:
self.reply_markup = reply_markup
if input_message_content:
+19 -14
View File
@@ -23,42 +23,47 @@ from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageConten
class InlineQueryResultCachedAudio(InlineQueryResult):
"""Represents a link to an mp3 audio file stored on the Telegram
servers. By default, this audio file will be sent by the user.
Alternatively, you can use input_message_content to send a message with
the specified content instead of the audio.
"""Represents a link to an mp3 audio file stored on the Telegram servers. By default, this
audio file will be sent by the user. Alternatively, you can use input_message_content to send a
message with the specified content instead of the audio.
Attributes:
id (str):
audio_file_id (str):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[
:class:`telegram.input_message_content`]):
input_message_content (Optional[:class:`telegram.input_message_content`]):
Deprecated: 4.0
message_text (str): Use :class:`InputTextMessageContent` instead.
parse_mode (str): Use :class:`InputTextMessageContent` instead.
disable_web_page_preview (bool): Use :class:`InputTextMessageContent`
instead.
disable_web_page_preview (bool): Use :class:`InputTextMessageContent` instead.
Args:
audio_file_id (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[
:class:`telegram.input_message_content`]):
input_message_content (Optional[:class:`telegram.input_message_content`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, id, audio_file_id, reply_markup=None, input_message_content=None, **kwargs):
def __init__(self,
id,
audio_file_id,
caption=None,
reply_markup=None,
input_message_content=None,
**kwargs):
# Required
super(InlineQueryResultCachedAudio, self).__init__('audio', id)
self.audio_file_id = audio_file_id
# Optionals
if caption:
self.caption = caption
if reply_markup:
self.reply_markup = reply_markup
if input_message_content:
+27 -2
View File
@@ -16,13 +16,38 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultCachedDocument"""
"""This module contains the classes that represent Telegram InlineQueryResultCachedDocument"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedDocument(InlineQueryResult):
"""Represents a link to a file stored on the Telegram servers. By default, this file will be
sent by the user with an optional caption. Alternatively, you can use input_message_content to
send a message with the specified content instead of the file. Currently, only pdf-files and
zip archives can be sent using this method.
Attributes:
title (str): Title for the result.
document_file_id (str): A valid file identifier for the file.
description (Optional[str]): Short description of the result.
caption (Optional[str]): Caption of the document to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the file.
Args:
id (str):
title (str):
document_file_id (str):
description (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
+23
View File
@@ -23,6 +23,29 @@ from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageConten
class InlineQueryResultCachedGif(InlineQueryResult):
"""Represents a link to an animated GIF file stored on the Telegram servers. By default, this
animated GIF file will be sent by the user with an optional caption. Alternatively, you can use
input_message_content to send a message with specified content instead of the animation.
Attributes:
gif_file_id (str): A valid file identifier for the GIF file.
title (Optional[str]): Title for the result.
caption (Optional[str]): Caption of the GIF file to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the GIF animation.
Args:
id (str):
gif_file_id (str):
title (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
@@ -23,6 +23,30 @@ from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageConten
class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
"""Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the
Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an
optional caption. Alternatively, you can use input_message_content to send a message with the
specified content instead of the animation.
Attributes:
mpeg4_file_id (str): A valid file identifier for the MP4 file.
title (Optional[str]): Title for the result.
caption (Optional[str]): Caption of the MPEG-4 file to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the video animation
Args:
id (str):
mpeg4_file_id (str):
title (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
+26 -2
View File
@@ -16,13 +16,37 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultPhoto"""
"""This module contains the classes that represent Telegram InlineQueryResultPhoto"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedPhoto(InlineQueryResult):
"""Represents a link to a photo stored on the Telegram servers. By default, this photo will be
sent by the user with an optional caption. Alternatively, you can use input_message_content to
send a message with the specified content instead of the photo.
Attributes:
photo_file_id (str): A valid file identifier of the photo.
title (Optional[str]): Title for the result.
description (Optional[str]): Short description of the result.
caption (Optional[str]): Caption of the photo to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the photo
Args:
id (str):
photo_file_id (str):
title (Optional[str]):
description (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
+20 -2
View File
@@ -16,13 +16,31 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultCachedSticker"""
"""This module contains the classes that represent Telegram InlineQueryResultCachedSticker"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedSticker(InlineQueryResult):
"""Represents a link to a sticker stored on the Telegram servers. By default, this sticker will
be sent by the user. Alternatively, you can use input_message_content to send a message with
the specified content instead of the sticker.
Attributes:
sticker_file_id (str): A valid file identifier of the sticker.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the sticker.
Args:
id (str):
sticker_file_id (str):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
+26 -2
View File
@@ -16,13 +16,37 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultCachedVideo"""
"""This module contains the classes that represent Telegram InlineQueryResultCachedVideo"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedVideo(InlineQueryResult):
"""Represents a link to a video file stored on the Telegram servers. By default, this video
file will be sent by the user with an optional caption. Alternatively, you can use
input_message_content to send a message with the specified content instead of the video.
Attributes:
video_file_id (str): A valid file identifier for the video file.
title (str): Title for the result.
description (Optional[str]): Short description of the result.
caption (Optional[str]): Caption of the video to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the video
Args:
id (str):
video_file_id (str):
title (str):
description (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
+27 -5
View File
@@ -16,19 +16,41 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultCachedVoice"""
"""This module contains the classes that represent Telegram InlineQueryResultCachedVoice"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultCachedVoice(InlineQueryResult):
"""Represents a link to a voice message stored on the Telegram servers. By default, this voice
message will be sent by the user. Alternatively, you can use input_message_content to send a
message with the specified content instead of the voice message.
Attributes:
voice_file_id (str): A valid file identifier for the voice message.
title (str): Voice message title.
caption (Optional[str]): Caption, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the voice message.
Args:
id (str):
voice_file_id (str):
title (str):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
voice_file_id,
title,
description=None,
caption=None,
reply_markup=None,
input_message_content=None,
**kwargs):
@@ -38,8 +60,8 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
self.title = title
# Optionals
if description:
self.description = description
if caption:
self.caption = caption
if reply_markup:
self.reply_markup = reply_markup
if input_message_content:
+30 -2
View File
@@ -16,13 +16,41 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultContact"""
"""This module contains the classes that represent Telegram InlineQueryResultContact"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultContact(InlineQueryResult):
"""Represents a contact with a phone number. By default, this contact will be sent by the user.
Alternatively, you can use input_message_content to send a message with the specified content
instead of the contact.
Attributes:
phone_number (str): Contact's phone number.
first_name (str): Contact's first name.
last_name (Optional[str]): Contact's last name.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the contact.
thumb_url (Optional[str]): Url of the thumbnail for the result.
thumb_width (Optional[int]): Thumbnail width.
thumb_height (Optional[int]): Thumbnail height.
Args:
id (str):
phone_number (str):
first_name (str):
last_name (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
thumb_url (Optional[str]): Url of the thumbnail for the result.
thumb_width (Optional[int]):
thumb_height (Optional[int]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
+35 -2
View File
@@ -16,13 +16,46 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultDocument"""
"""This module contains the classes that represent Telegram InlineQueryResultDocument"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultDocument(InlineQueryResult):
"""Represents a link to a file. By default, this file will be sent by the user with an optional
caption. Alternatively, you can use input_message_content to send a message with the specified
content instead of the file. Currently, only .PDF and .ZIP files can be sent using this method.
Attributes:
title (str): Title for the result.
caption (Optional[str]): Caption of the document to be sent, 0-200 characters.
document_url (Optional[str]): A valid URL for the file.
mime_type (Optional[str]): Mime type of the content of the file, either "application/pdf"
or "application/zip".
description (Optional[str]): Short description of the result.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the file.
thumb_url (Optional[str]): URL of the thumbnail (jpeg only) for the file.
thumb_width (Optional[int]): Thumbnail width.
thumb_height (Optional[int]): Thumbnail height.
Args:
id (str):
document_url (str):
title (str):
mime_type (str):
caption (Optional[str]):
description (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
thumb_url (Optional[str]):
thumb_width (Optional[int]):
thumb_height (Optional[int]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultGame"""
from telegram import InlineQueryResult, InlineKeyboardMarkup
class InlineQueryResultGame(InlineQueryResult):
def __init__(self, id, game_short_name, reply_markup=None, **kwargs):
# Required
super(InlineQueryResultGame, self).__init__('game', id)
self.id = id
self.game_short_name = game_short_name
if reply_markup:
self.reply_markup = reply_markup
@staticmethod
def de_json(data, bot):
data = super(InlineQueryResultGame, InlineQueryResultGame).de_json(data, bot)
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
return InlineQueryResultGame(**data)
+29
View File
@@ -23,6 +23,35 @@ from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageConten
class InlineQueryResultGif(InlineQueryResult):
"""Represents a link to an animated GIF file. By default, this animated GIF file will be sent
by the user with optional caption. Alternatively, you can use input_message_content to send a
message with the specified content instead of the animation.
Attributes:
gif_url (str): A valid URL for the GIF file. File size must not exceed 1MB.
thumb_url (str): URL of the static thumbnail for the result (jpeg or gif).
gif_width (Optional[int]): Width of the GIF.
gif_height (Optional[int]): Height of the GIF.
title (Optional[str]): Title for the result.
caption (Optional[str]): Caption of the GIF file to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the GIF animation.
Args:
id (str):
gif_url (str):
thumb_url (str):
gif_width (Optional[int]):
gif_height (Optional[int]):
title (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
input_message_content (Optional[:class:`telegram.InputMessageContent`]):
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
+31 -2
View File
@@ -16,13 +16,42 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultLocation"""
"""This module contains the classes that represent Telegram InlineQueryResultLocation"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultLocation(InlineQueryResult):
"""Represents a location on a map. By default, the location will be sent by the user.
Alternatively, you can use input_message_content to send a message with the specified content
instead of the location.
Attributes:
latitude (float): Location latitude in degrees.
longitude (float): Location longitude in degrees.
title (str): Location title.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the location.
thumb_url (Optional[str]): Url of the thumbnail for the result.
thumb_width (Optional[int]): Thumbnail width.
thumb_height (Optional[int]): Thumbnail height.
Args:
latitude (float): Location latitude in degrees.
longitude (float): Location longitude in degrees.
title (str): Location title.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the location.
thumb_url (Optional[str]): Url of the thumbnail for the result.
thumb_width (Optional[int]): Thumbnail width.
thumb_height (Optional[int]): Thumbnail height.
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
+32 -2
View File
@@ -16,13 +16,43 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultMpeg4Gif"""
"""This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultMpeg4Gif(InlineQueryResult):
"""Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default,
this animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you
can use input_message_content to send a message with the specified content instead of the
animation.
Attributes:
mpeg4_url (str): A valid URL for the MP4 file. File size must not exceed 1MB.
thumb_url (str): URL of the static thumbnail (jpeg or gif) for the result.
mpeg4_width (Optional[int]): Video width.
mpeg4_height (Optional[int]): Video height.
title (Optional[str]): Title for the result.
caption (Optional[str]): Caption of the MPEG-4 file to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the video animation.
Args:
mpeg4_url (str): A valid URL for the MP4 file. File size must not exceed 1MB.
thumb_url (str): URL of the static thumbnail (jpeg or gif) for the result.
mpeg4_width (Optional[int]): Video width.
mpeg4_height (Optional[int]): Video height.
title (Optional[str]): Title for the result.
caption (Optional[str]): Caption of the MPEG-4 file to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the video animation.
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
+20 -2
View File
@@ -16,13 +16,31 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the classes that represent Telegram
InlineQueryResultPhoto"""
"""This module contains the classes that represent Telegram InlineQueryResultPhoto"""
from telegram import InlineQueryResult, InlineKeyboardMarkup, InputMessageContent
class InlineQueryResultPhoto(InlineQueryResult):
"""Represents a link to a photo. By default, this photo will be sent by the user with optional
caption. Alternatively, you can use input_message_content to send a message with the specified
content instead of the photo.
Attributes:
photo_url (str): A valid URL of the photo. Photo must be in jpeg format. Photo size must
not exceed 5MB.
thumb_url (str): URL of the thumbnail for the photo.
photo_width (Optional[int]): Width of the photo.
photo_height (Optional[int]): Height of the photo.
title (Optional[str]): Title for the result.
description (Optional[str]): Short description of the result.
caption (Optional[str]): Caption of the photo to be sent, 0-200 characters.
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]): Inline keyboard attached
to the message.
input_message_content (Optional[:class:`telegram.InputMessageContent`]): Content of the
message to be sent instead of the photo.
"""
def __init__(self,
id,
+3
View File
@@ -29,6 +29,7 @@ class InlineQueryResultVoice(InlineQueryResult):
voice_url,
title,
voice_duration=None,
caption=None,
reply_markup=None,
input_message_content=None,
**kwargs):
@@ -41,6 +42,8 @@ class InlineQueryResultVoice(InlineQueryResult):
# Optional
if voice_duration:
self.voice_duration = voice_duration
if caption:
self.caption = caption
if reply_markup:
self.reply_markup = reply_markup
if input_message_content:
+17 -24
View File
@@ -17,7 +17,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram InputFile."""
"""This module contains an object that represents a Telegram InputFile."""
try:
# python 3
@@ -31,12 +31,11 @@ import mimetypes
import os
import sys
from future.moves.urllib.request import urlopen
from telegram import TelegramError
DEFAULT_MIME_TYPE = 'application/octet-stream'
USER_AGENT = 'Python Telegram Bot (https://github.com/python-telegram-bot/python-telegram-bot)'
FILE_TYPES = ('audio', 'document', 'photo', 'sticker', 'video', 'voice', 'certificate')
class InputFile(object):
@@ -49,32 +48,28 @@ class InputFile(object):
if 'audio' in data:
self.input_name = 'audio'
self.input_file = data.pop('audio')
if 'document' in data:
elif 'document' in data:
self.input_name = 'document'
self.input_file = data.pop('document')
if 'photo' in data:
elif 'photo' in data:
self.input_name = 'photo'
self.input_file = data.pop('photo')
if 'sticker' in data:
elif 'sticker' in data:
self.input_name = 'sticker'
self.input_file = data.pop('sticker')
if 'video' in data:
elif 'video' in data:
self.input_name = 'video'
self.input_file = data.pop('video')
if 'voice' in data:
elif 'voice' in data:
self.input_name = 'voice'
self.input_file = data.pop('voice')
if 'certificate' in data:
elif 'certificate' in data:
self.input_name = 'certificate'
self.input_file = data.pop('certificate')
if str(self.input_file).startswith('http'):
from_url = True
self.input_file = urlopen(self.input_file)
else:
from_url = False
raise TelegramError('Unknown inputfile type')
if hasattr(self.input_file, 'read') or from_url:
if hasattr(self.input_file, 'read'):
self.filename = None
self.input_file_content = self.input_file.read()
if 'filename' in data:
@@ -83,11 +78,9 @@ class InputFile(object):
# on py2.7, pylint fails to understand this properly
# pylint: disable=E1101
self.filename = os.path.basename(self.input_file.name)
elif from_url:
self.filename = os.path.basename(self.input_file.url).split('?')[0].split('&')[0]
try:
self.mimetype = InputFile.is_image(self.input_file_content)
self.mimetype = self.is_image(self.input_file_content)
if not self.filename or '.' not in self.filename:
self.filename = self.mimetype.replace('/', '.')
except TelegramError:
@@ -118,7 +111,8 @@ class InputFile(object):
form_boundary = '--' + self.boundary
# Add data fields
for name, value in self.data.items():
for name in iter(self.data):
value = self.data[name]
form.extend([
form_boundary, 'Content-Disposition: form-data; name="%s"' % name, '', str(value)
])
@@ -133,7 +127,7 @@ class InputFile(object):
form.append('--' + self.boundary + '--')
form.append('')
return InputFile._parse(form)
return self._parse(form)
@staticmethod
def _parse(form):
@@ -174,18 +168,17 @@ class InputFile(object):
"""Check if the request is a file request.
Args:
data (str): A dict of (str, unicode) key/value pairs
data (dict): A dict of (str, unicode) key/value pairs
Returns:
bool
"""
if data:
file_types = ['audio', 'document', 'photo', 'sticker', 'video', 'voice', 'certificate']
file_type = [i for i in list(data.keys()) if i in file_types]
file_type = [i for i in iter(data) if i in FILE_TYPES]
if file_type:
file_content = data[file_type[0]]
return hasattr(file_content, 'read') or str(file_content).startswith('http')
return hasattr(file_content, 'read')
return False
+1 -1
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram KeyboardButton."""
"""This module contains an object that represents a Telegram KeyboardButton."""
from telegram import TelegramObject
+1 -1
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Location."""
"""This module contains an object that represents a Telegram Location."""
from telegram import TelegramObject
+115 -33
View File
@@ -17,14 +17,14 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Message."""
"""This module contains an object that represents a Telegram Message."""
import sys
from datetime import datetime
from time import mktime
from telegram import (Audio, Contact, Document, Chat, Location, PhotoSize, Sticker, TelegramObject,
User, Video, Voice, Venue, MessageEntity)
User, Video, Voice, Venue, MessageEntity, Game)
class Message(TelegramObject):
@@ -45,6 +45,7 @@ class Message(TelegramObject):
text (str):
audio (:class:`telegram.Audio`):
document (:class:`telegram.Document`):
game (:class:`telegram.Game`):
photo (List[:class:`telegram.PhotoSize`]):
sticker (:class:`telegram.Sticker`):
video (:class:`telegram.Video`):
@@ -86,6 +87,7 @@ class Message(TelegramObject):
text (Optional[str]):
audio (Optional[:class:`telegram.Audio`]):
document (Optional[:class:`telegram.Document`]):
game (Optional[:class:`telegram.Game`]):
photo (Optional[List[:class:`telegram.PhotoSize`]]):
sticker (Optional[:class:`telegram.Sticker`]):
video (Optional[:class:`telegram.Video`]):
@@ -106,41 +108,76 @@ class Message(TelegramObject):
bot (Optional[Bot]): The Bot to use for instance methods
"""
def __init__(self, message_id, from_user, date, chat, bot=None, **kwargs):
def __init__(self,
message_id,
from_user,
date,
chat,
forward_from=None,
forward_from_chat=None,
forward_date=None,
reply_to_message=None,
edit_date=None,
text='',
entities=None,
audio=None,
document=None,
photo=None,
sticker=None,
video=None,
voice=None,
caption='',
contact=None,
location=None,
venue=None,
new_chat_member=None,
left_chat_member=None,
new_chat_title='',
new_chat_photo=None,
delete_chat_photo=False,
group_chat_created=False,
supergroup_chat_created=False,
migrate_to_chat_id=0,
migrate_from_chat_id=0,
channel_chat_created=False,
pinned_message=None,
bot=None,
**kwargs):
# Required
self.message_id = int(message_id)
self.from_user = from_user
self.date = date
self.chat = chat
# Optionals
self.forward_from = kwargs.get('forward_from')
self.forward_from_chat = kwargs.get('forward_from_chat')
self.forward_date = kwargs.get('forward_date')
self.reply_to_message = kwargs.get('reply_to_message')
self.edit_date = kwargs.get('edit_date')
self.text = kwargs.get('text', '')
self.entities = kwargs.get('entities', list())
self.audio = kwargs.get('audio')
self.document = kwargs.get('document')
self.photo = kwargs.get('photo')
self.sticker = kwargs.get('sticker')
self.video = kwargs.get('video')
self.voice = kwargs.get('voice')
self.caption = kwargs.get('caption', '')
self.contact = kwargs.get('contact')
self.location = kwargs.get('location')
self.venue = kwargs.get('venue')
self.new_chat_member = kwargs.get('new_chat_member')
self.left_chat_member = kwargs.get('left_chat_member')
self.new_chat_title = kwargs.get('new_chat_title', '')
self.new_chat_photo = kwargs.get('new_chat_photo')
self.delete_chat_photo = bool(kwargs.get('delete_chat_photo', False))
self.group_chat_created = bool(kwargs.get('group_chat_created', False))
self.supergroup_chat_created = bool(kwargs.get('supergroup_chat_created', False))
self.migrate_to_chat_id = int(kwargs.get('migrate_to_chat_id', 0))
self.migrate_from_chat_id = int(kwargs.get('migrate_from_chat_id', 0))
self.channel_chat_created = bool(kwargs.get('channel_chat_created', False))
self.pinned_message = kwargs.get('pinned_message')
self.forward_from = forward_from
self.forward_from_chat = forward_from_chat
self.forward_date = forward_date
self.reply_to_message = reply_to_message
self.edit_date = edit_date
self.text = text
self.entities = entities or list()
self.audio = audio
self.game = kwargs.get('game')
self.document = document
self.photo = photo
self.sticker = sticker
self.video = video
self.voice = voice
self.caption = caption
self.contact = contact
self.location = location
self.venue = venue
self.new_chat_member = new_chat_member
self.left_chat_member = left_chat_member
self.new_chat_title = new_chat_title
self.new_chat_photo = new_chat_photo
self.delete_chat_photo = bool(delete_chat_photo)
self.group_chat_created = bool(group_chat_created)
self.supergroup_chat_created = bool(supergroup_chat_created)
self.migrate_to_chat_id = int(migrate_to_chat_id)
self.migrate_from_chat_id = int(migrate_from_chat_id)
self.channel_chat_created = bool(channel_chat_created)
self.pinned_message = pinned_message
self.bot = bot
@@ -173,6 +210,7 @@ class Message(TelegramObject):
data['edit_date'] = Message._fromtimestamp(data.get('edit_date'))
data['audio'] = Audio.de_json(data.get('audio'), bot)
data['document'] = Document.de_json(data.get('document'), bot)
data['game'] = Game.de_json(data.get('game'), bot)
data['photo'] = PhotoSize.de_list(data.get('photo'), bot)
data['sticker'] = Sticker.de_json(data.get('sticker'), bot)
data['video'] = Video.de_json(data.get('video'), bot)
@@ -412,6 +450,48 @@ class Message(TelegramObject):
disable_notification=disable_notification,
message_id=self.message_id)
def edit_text(self, *args, **kwargs):
"""
Shortcut for ``bot.editMessageText(chat_id=message.chat_id,
message_id=message.message_id,
*args, **kwargs)``
Note:
You can only edit messages that the bot sent itself,
therefore this method can only be used on the
return value of the bot.send_* family of methods.
"""
return self.bot.edit_message_text(
chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs)
def edit_caption(self, *args, **kwargs):
"""
Shortcut for ``bot.editMessageCaption(chat_id=message.chat_id,
message_id=message.message_id,
*args, **kwargs)``
Note:
You can only edit messages that the bot sent itself,
therefore this method can only be used on the
return value of the bot.send_* family of methods.
"""
return self.bot.edit_message_caption(
chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs)
def edit_reply_markup(self, *args, **kwargs):
"""
Shortcut for ``bot.editReplyMarkup(chat_id=message.chat_id,
message_id=message.message_id,
*args, **kwargs)``
Note:
You can only edit messages that the bot sent itself,
therefore this method can only be used on the
return value of the bot.send_* family of methods.
"""
return self.bot.edit_message_caption(
chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs)
def parse_entity(self, entity):
"""
Returns the text from a given :class:`telegram.MessageEntity`.
@@ -461,5 +541,7 @@ class Message(TelegramObject):
if types is None:
types = MessageEntity.ALL_TYPES
return {entity: self.parse_entity(entity)
for entity in self.entities if entity.type in types}
return {
entity: self.parse_entity(entity)
for entity in self.entities if entity.type in types
}
+7 -6
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram MessageEntity."""
"""This module contains an object that represents a Telegram MessageEntity."""
from telegram import User, TelegramObject
@@ -34,14 +34,14 @@ class MessageEntity(TelegramObject):
user (Optional[:class:`telegram.User`]):
"""
def __init__(self, type, offset, length, **kwargs):
def __init__(self, type, offset, length, url=None, user=None, **kwargs):
# Required
self.type = type
self.offset = offset
self.length = length
# Optionals
self.url = kwargs.get('url')
self.user = kwargs.get('user')
self.url = url
self.user = user
@staticmethod
def de_json(data, bot):
@@ -80,5 +80,6 @@ class MessageEntity(TelegramObject):
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]
ALL_TYPES = [
MENTION, HASHTAG, BOT_COMMAND, URL, EMAIL, BOLD, ITALIC, CODE, PRE, TEXT_LINK, TEXT_MENTION
]
+1 -1
View File
@@ -17,7 +17,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
"""This module contains an object that represents a Telegram
Message Parse Modes."""
+3 -3
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram PhotoSize."""
"""This module contains an object that represents a Telegram PhotoSize."""
from telegram import TelegramObject
@@ -40,13 +40,13 @@ class PhotoSize(TelegramObject):
file_size (Optional[int]):
"""
def __init__(self, file_id, width, height, **kwargs):
def __init__(self, file_id, width, height, file_size=0, **kwargs):
# Required
self.file_id = str(file_id)
self.width = int(width)
self.height = int(height)
# Optionals
self.file_size = int(kwargs.get('file_size', 0))
self.file_size = int(file_size)
def __eq__(self, other):
if not isinstance(other, self.__class__):
+3 -3
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
"""This module contains an object that represents a Telegram
ReplyKeyboardHide."""
from telegram import ReplyMarkup
@@ -37,11 +37,11 @@ class ReplyKeyboardHide(ReplyMarkup):
selective (Optional[bool]):
"""
def __init__(self, hide_keyboard=True, **kwargs):
def __init__(self, hide_keyboard=True, selective=False, **kwargs):
# Required
self.hide_keyboard = bool(hide_keyboard)
# Optionals
self.selective = bool(kwargs.get('selective', False))
self.selective = bool(selective)
@staticmethod
def de_json(data, bot):
+10 -5
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
"""This module contains an object that represents a Telegram
ReplyKeyboardMarkup."""
from telegram import ReplyMarkup, KeyboardButton
@@ -41,13 +41,18 @@ class ReplyKeyboardMarkup(ReplyMarkup):
selective (Optional[bool]):
"""
def __init__(self, keyboard, **kwargs):
def __init__(self,
keyboard,
resize_keyboard=False,
one_time_keyboard=False,
selective=False,
**kwargs):
# Required
self.keyboard = keyboard
# Optionals
self.resize_keyboard = bool(kwargs.get('resize_keyboard', False))
self.one_time_keyboard = bool(kwargs.get('one_time_keyboard', False))
self.selective = bool(kwargs.get('selective', False))
self.resize_keyboard = bool(resize_keyboard)
self.one_time_keyboard = bool(one_time_keyboard)
self.selective = bool(selective)
@staticmethod
def de_json(data, bot):
+5 -5
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Sticker."""
"""This module contains an object that represents a Telegram Sticker."""
from telegram import PhotoSize, TelegramObject
@@ -44,15 +44,15 @@ class Sticker(TelegramObject):
file_size (Optional[int]):
"""
def __init__(self, file_id, width, height, **kwargs):
def __init__(self, file_id, width, height, thumb=None, emoji='', file_size=0, **kwargs):
# Required
self.file_id = str(file_id)
self.width = int(width)
self.height = int(height)
# Optionals
self.thumb = kwargs.get('thumb')
self.emoji = kwargs.get('emoji', '')
self.file_size = int(kwargs.get('file_size', 0))
self.thumb = thumb
self.emoji = emoji
self.file_size = int(file_size)
@staticmethod
def de_json(data, bot):
+14 -7
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Update."""
"""This module contains an object that represents a Telegram Update."""
from telegram import (Message, TelegramObject, InlineQuery, ChosenInlineResult, CallbackQuery)
@@ -44,15 +44,22 @@ class Update(TelegramObject):
callback_query (Optional[:class:`telegram.CallbackQuery`]):
"""
def __init__(self, update_id, **kwargs):
def __init__(self,
update_id,
message=None,
edited_message=None,
inline_query=None,
chosen_inline_result=None,
callback_query=None,
**kwargs):
# Required
self.update_id = int(update_id)
# Optionals
self.message = kwargs.get('message')
self.edited_message = kwargs.get('edited_message')
self.inline_query = kwargs.get('inline_query')
self.chosen_inline_result = kwargs.get('chosen_inline_result')
self.callback_query = kwargs.get('callback_query')
self.message = message
self.edited_message = edited_message
self.inline_query = inline_query
self.chosen_inline_result = chosen_inline_result
self.callback_query = callback_query
@staticmethod
def de_json(data, bot):
+5 -5
View File
@@ -17,7 +17,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram User."""
"""This module contains an object that represents a Telegram User."""
from telegram import TelegramObject
@@ -44,14 +44,14 @@ class User(TelegramObject):
bot (Optional[Bot]): The Bot to use for instance methods
"""
def __init__(self, id, first_name, bot=None, **kwargs):
def __init__(self, id, first_name, type='', last_name='', username='', bot=None, **kwargs):
# Required
self.id = int(id)
self.first_name = first_name
# Optionals
self.type = kwargs.get('type', '')
self.last_name = kwargs.get('last_name', '')
self.username = kwargs.get('username', '')
self.type = type
self.last_name = last_name
self.username = username
self.bot = bot
+1 -1
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
"""This module contains an object that represents a Telegram
UserProfilePhotos."""
from telegram import PhotoSize, TelegramObject
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains helper functions """
def extract_chat_and_user(update):
user = None
chat = None
if update.message:
user = update.message.from_user
chat = update.message.chat
elif update.edited_message:
user = update.edited_message.from_user
chat = update.edited_message.chat
elif update.inline_query:
user = update.inline_query.from_user
elif update.chosen_inline_result:
user = update.chosen_inline_result.from_user
elif update.callback_query:
user = update.callback_query.from_user
chat = update.callback_query.message.chat if update.callback_query.message else None
return chat, user
+5 -1
View File
@@ -31,7 +31,8 @@ import urllib3
from urllib3.connection import HTTPConnection
from telegram import (InputFile, TelegramError)
from telegram.error import Unauthorized, NetworkError, TimedOut, BadRequest, ChatMigrated
from telegram.error import Unauthorized, NetworkError, TimedOut, BadRequest, ChatMigrated, \
RetryAfter
logging.getLogger('urllib3').setLevel(logging.WARNING)
@@ -105,6 +106,9 @@ class Request(object):
migrate_to_chat_id = parameters.get('migrate_to_chat_id')
if migrate_to_chat_id:
raise ChatMigrated(migrate_to_chat_id)
retry_after = parameters.get('retry_after')
if retry_after:
raise RetryAfter(retry_after)
if description:
return description
+1 -1
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Venue."""
"""This module contains an object that represents a Telegram Venue."""
from telegram import TelegramObject, Location
+1 -1
View File
@@ -17,4 +17,4 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
__version__ = '5.1.0'
__version__ = '5.2.0'
+13 -5
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Video."""
"""This module contains an object that represents a Telegram Video."""
from telegram import PhotoSize, TelegramObject
@@ -46,16 +46,24 @@ class Video(TelegramObject):
file_size (Optional[int]):
"""
def __init__(self, file_id, width, height, duration, **kwargs):
def __init__(self,
file_id,
width,
height,
duration,
thumb=None,
mime_type='',
file_size=0,
**kwargs):
# Required
self.file_id = str(file_id)
self.width = int(width)
self.height = int(height)
self.duration = int(duration)
# Optionals
self.thumb = kwargs.get('thumb')
self.mime_type = str(kwargs.get('mime_type', ''))
self.file_size = int(kwargs.get('file_size', 0))
self.thumb = thumb
self.mime_type = str(mime_type)
self.file_size = int(file_size)
@staticmethod
def de_json(data, bot):
+6 -6
View File
@@ -16,7 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Voice."""
"""This module contains an object that represents a Telegram Voice."""
from telegram import TelegramObject
@@ -32,21 +32,21 @@ class Voice(TelegramObject):
Args:
file_id (str):
duration (Optional[int]):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
duration (Optional[int]):
mime_type (Optional[str]):
file_size (Optional[int]):
"""
def __init__(self, file_id, **kwargs):
def __init__(self, file_id, duration, mime_type='', file_size=0, **kwargs):
# Required
self.file_id = str(file_id)
self.duration = int(duration)
# Optionals
self.duration = int(kwargs.get('duration', 0))
self.mime_type = str(kwargs.get('mime_type', ''))
self.file_size = int(kwargs.get('file_size', 0))
self.mime_type = str(mime_type)
self.file_size = int(file_size)
@staticmethod
def de_json(data, bot):
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram WebhookInfo."""
from telegram import TelegramObject
class WebhookInfo(TelegramObject):
"""This object represents a Telegram WebhookInfo.
Attributes:
url (str): Webhook URL, may be empty if webhook is not set up.
has_custom_certificate (bool):
pending_update_count (int):
last_error_date (int):
last_error_message (str):
Args:
url (str): Webhook URL, may be empty if webhook is not set up.
has_custom_certificate (bool):
pending_update_count (int):
last_error_date (Optional[int]):
last_error_message (Optional[str]):
"""
def __init__(self, url, has_custom_certificate, pending_update_count, **kwargs):
# Required
self.url = url
self.has_custom_certificate = has_custom_certificate
self.pending_update_count = pending_update_count
self.last_error_date = kwargs.get('last_error_date', '')
self.last_error_message = kwargs.get('last_error_message', '')
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.WebhookInfo:
"""
if not data:
return None
return WebhookInfo(**data)
+1 -1
View File
@@ -16,7 +16,7 @@
#
# 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 a Base class for tests"""
"""This module contains an object that represents a Base class for tests"""
import os
import sys
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

+39 -10
View File
@@ -16,7 +16,7 @@
#
# 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 Audio"""
"""This module contains an object that represents Tests for Telegram Audio"""
import sys
import unittest
@@ -40,6 +40,7 @@ class AudioTest(BaseTest, unittest.TestCase):
self.duration = 4
self.performer = 'Leandro Toledo'
self.title = 'Teste'
self.caption = "Test audio"
self.mime_type = 'audio/mpeg'
self.file_size = 28232
@@ -48,6 +49,7 @@ class AudioTest(BaseTest, unittest.TestCase):
'duration': self.duration,
'performer': self.performer,
'title': self.title,
'caption': self.caption,
'mime_type': self.mime_type,
'file_size': self.file_size
}
@@ -57,6 +59,8 @@ class AudioTest(BaseTest, unittest.TestCase):
def test_send_audio_required_args_only(self):
message = self._bot.sendAudio(self._chat_id, self.audio_file)
self.assertEqual(message.caption, '')
audio = message.audio
self.assertTrue(isinstance(audio.file_id, str))
@@ -76,9 +80,12 @@ class AudioTest(BaseTest, unittest.TestCase):
duration=self.duration,
performer=self.performer,
title=self.title,
caption=self.caption,
mime_type=self.mime_type,
file_size=self.file_size)
self.assertEqual(message.caption, self.caption)
audio = message.audio
self.assertTrue(isinstance(audio.file_id, str))
@@ -97,7 +104,10 @@ class AudioTest(BaseTest, unittest.TestCase):
audio=self.audio_file,
duration=self.duration,
performer=self.performer,
title=self.title)
title=self.title,
caption=self.caption)
self.assertEqual(message.caption, self.caption)
audio = message.audio
@@ -118,8 +128,11 @@ class AudioTest(BaseTest, unittest.TestCase):
duration=self.duration,
performer=self.performer,
title=self.title,
caption=self.caption,
filename='telegram_custom.mp3')
self.assertEqual(message.caption, self.caption)
audio = message.audio
self.assertTrue(isinstance(audio.file_id, str))
@@ -134,19 +147,32 @@ class AudioTest(BaseTest, unittest.TestCase):
@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)
chat_id=self._chat_id, audio=self.audio_file_url, duration=self.duration)
audio = message.audio
self.assertTrue(isinstance(audio.file_id, str))
self.assertNotEqual(audio.file_id, '')
self.assertEqual(audio.duration, self.duration)
self.assertEqual(audio.mime_type, self.mime_type)
self.assertEqual(audio.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_audio_mp3_url_file_with_caption(self):
message = self._bot.sendAudio(
chat_id=self._chat_id,
audio=self.audio_file_url,
duration=self.duration,
caption=self.caption)
self.assertEqual(message.caption, self.caption)
audio = message.audio
self.assertTrue(isinstance(audio.file_id, str))
self.assertNotEqual(audio.file_id, '')
self.assertEqual(audio.duration, self.duration)
self.assertEqual(audio.performer, self.performer)
self.assertEqual(audio.title, self.title)
self.assertEqual(audio.mime_type, self.mime_type)
self.assertEqual(audio.file_size, self.file_size)
@@ -158,7 +184,10 @@ class AudioTest(BaseTest, unittest.TestCase):
audio=self.audio_file_id,
duration=self.duration,
performer=self.performer,
title=self.title)
title=self.title,
caption=self.caption)
self.assertEqual(message.caption, self.caption)
audio = message.audio
+93 -1
View File
@@ -17,11 +17,12 @@
#
# 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 Bot"""
"""This module contains an object that represents Tests for Telegram Bot"""
import io
import re
from datetime import datetime
import time
import sys
import unittest
@@ -30,6 +31,7 @@ from flaky import flaky
sys.path.append('.')
import telegram
from telegram.error import BadRequest
from tests.base import BaseTest, timeout
@@ -66,6 +68,17 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей')
self.assertTrue(isinstance(message.date, datetime))
@flaky(3, 1)
@timeout(10)
def test_sendMessage_no_web_page_preview(self):
message = self._bot.sendMessage(
chat_id=self._chat_id,
text='Моё судно на воздушной подушке полно угрей',
disable_web_page_preview=True)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.text, u'Моё судно на воздушной подушке полно угрей')
@flaky(3, 1)
@timeout(10)
def testGetUpdates(self):
@@ -158,6 +171,17 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.photo[0].file_size, 1451)
@flaky(3, 1)
@timeout(10)
def testSendGame(self):
game_short_name = 'python_telegram_bot_test_game'
message = self._bot.sendGame(game_short_name=game_short_name, chat_id=self._chat_id)
self.assertTrue(self.is_json(message.to_json()))
self.assertEqual(message.game.description, 'This is a test game for python-telegram-bot.')
self.assertEqual(message.game.animation.file_id, 'BQADAQADKwIAAvjAuQABozciVqhFDO0C')
self.assertEqual(message.game.photo[0].file_size, 851)
@flaky(3, 1)
@timeout(10)
def testSendChatAction(self):
@@ -171,6 +195,13 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertTrue(self.is_json(upf.to_json()))
self.assertEqual(upf.photos[0][0].file_size, 12421)
@flaky(3, 1)
@timeout(10)
def test_get_one_user_profile_photo(self):
upf = self._bot.getUserProfilePhotos(user_id=self._chat_id, offset=0)
self.assertTrue(self.is_json(upf.to_json()))
self.assertEqual(upf.photos[0][0].file_size, 12421)
def _test_invalid_token(self, token):
self.assertRaisesRegexp(telegram.error.InvalidToken, 'Invalid token', telegram.Bot, token)
@@ -255,6 +286,46 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertEqual(chat_member.status, "administrator")
self._testUserEqualsBot(bot)
@flaky(3, 1)
@timeout(10)
def test_get_webhook_info(self):
url = 'https://python-telegram-bot.org/test/webhook'
self._bot.set_webhook(url)
info = self._bot.getWebhookInfo()
self._bot.set_webhook('')
self.assertEqual(url, info.url)
@flaky(3, 1)
@timeout(10)
def test_set_game_score(self):
# We need a game to set the score for
game_short_name = 'python_telegram_bot_test_game'
game = self._bot.sendGame(game_short_name=game_short_name, chat_id=self._chat_id)
message = self._bot.set_game_score(
user_id=self._chat_id,
score=int(time.time() - 1450000000),
chat_id=game.chat_id,
message_id=game.message_id,
edit_message=True)
self.assertTrue(self.is_json(game.to_json()))
self.assertEqual(message.game.description, game.game.description)
self.assertEqual(message.game.animation.file_id, game.game.animation.file_id)
self.assertEqual(message.game.photo[0].file_size, game.game.photo[0].file_size)
self.assertNotEqual(message.game.text, game.game.text)
@flaky(3, 1)
@timeout(10)
def test_set_game_score_too_low_score(self):
# We need a game to set the score for
game_short_name = 'python_telegram_bot_test_game'
game = self._bot.sendGame(game_short_name=game_short_name, chat_id=self._chat_id)
with self.assertRaises(BadRequest):
self._bot.set_game_score(
user_id=self._chat_id, score=100, chat_id=game.chat_id, message_id=game.message_id)
def _testUserEqualsBot(self, user):
"""Tests if user is our trusty @PythonTelegramBot."""
self.assertEqual(user.id, 133505823)
@@ -263,6 +334,27 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertEqual(user.username, 'PythonTelegramBot')
self.assertEqual(user.name, '@PythonTelegramBot')
@flaky(3, 1)
@timeout(10)
def test_info(self):
# tests the Bot.info decorator and associated funcs
self.assertEqual(self._bot.id, 133505823)
self.assertEqual(self._bot.first_name, 'PythonTelegramBot')
self.assertEqual(self._bot.last_name, '')
self.assertEqual(self._bot.username, 'PythonTelegramBot')
self.assertEqual(self._bot.name, '@PythonTelegramBot')
@flaky(3, 1)
@timeout(10)
def test_send_contact(self):
phone = '+3-54-5445445'
name = 'name'
last = 'last'
message = self._bot.send_contact(self._chat_id, phone, name, last)
self.assertEqual(phone.replace('-', ''), message.contact.phone_number)
self.assertEqual(name, message.contact.first_name)
self.assertEqual(last, message.contact.last_name)
if __name__ == '__main__':
unittest.main()
+10 -2
View File
@@ -16,7 +16,7 @@
#
# 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 Chat"""
"""This module contains an object that represents Tests for Telegram Chat"""
import unittest
import sys
@@ -36,8 +36,14 @@ class ChatTest(BaseTest, unittest.TestCase):
self.id = -28767330
self.title = 'ToledosPalaceBot - Group'
self.type = 'group'
self.all_members_are_admins = False
self.json_dict = {'id': self.id, 'title': self.title, 'type': self.type}
self.json_dict = {
'id': self.id,
'title': self.title,
'type': self.type,
'all_members_are_admins': self.all_members_are_admins
}
def test_group_chat_de_json_empty_json(self):
group_chat = telegram.Chat.de_json({}, self._bot)
@@ -50,6 +56,7 @@ class ChatTest(BaseTest, unittest.TestCase):
self.assertEqual(group_chat.id, self.id)
self.assertEqual(group_chat.title, self.title)
self.assertEqual(group_chat.type, self.type)
self.assertEqual(group_chat.all_members_are_admins, self.all_members_are_admins)
def test_group_chat_to_json(self):
group_chat = telegram.Chat.de_json(self.json_dict, self._bot)
@@ -63,6 +70,7 @@ class ChatTest(BaseTest, unittest.TestCase):
self.assertEqual(group_chat['id'], self.id)
self.assertEqual(group_chat['title'], self.title)
self.assertEqual(group_chat['type'], self.type)
self.assertEqual(group_chat['all_members_are_admins'], self.all_members_are_admins)
@flaky(3, 1)
def test_send_action(self):
+1 -1
View File
@@ -16,7 +16,7 @@
#
# 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
"""This module contains an object that represents Tests for Telegram
ChosenInlineResult"""
import sys
+1 -1
View File
@@ -16,7 +16,7 @@
#
# 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 Contact"""
"""This module contains an object that represents Tests for Telegram Contact"""
import unittest
import sys
+12 -9
View File
@@ -18,7 +18,7 @@
# 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 ConversationHandler
This module contains an object that represents Tests for ConversationHandler
"""
import logging
import sys
@@ -66,14 +66,17 @@ class ConversationHandlerTest(BaseTest, unittest.TestCase):
def setUp(self):
self.current_state = dict()
self.entry_points = [CommandHandler('start', self.start)]
self.states = {self.THIRSTY: [CommandHandler('brew', self.brew),
CommandHandler('wait', self.start)],
self.BREWING: [CommandHandler('pourCoffee', self.drink)],
self.DRINKING: [CommandHandler('startCoding', self.code),
CommandHandler('drinkMore', self.drink)],
self.CODING: [CommandHandler('keepCoding', self.code),
CommandHandler('gettingThirsty', self.start),
CommandHandler('drinkMore', self.drink)],}
self.states = {
self.THIRSTY: [CommandHandler('brew', self.brew), CommandHandler('wait', self.start)],
self.BREWING: [CommandHandler('pourCoffee', self.drink)],
self.DRINKING:
[CommandHandler('startCoding', self.code), CommandHandler('drinkMore', self.drink)],
self.CODING: [
CommandHandler('keepCoding', self.code),
CommandHandler('gettingThirsty', self.start),
CommandHandler('drinkMore', self.drink)
],
}
self.fallbacks = [CommandHandler('eat', self.start)]
def _setup_updater(self, *args, **kwargs):
+8 -6
View File
@@ -16,7 +16,7 @@
#
# 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 Document"""
"""This module contains an object that represents Tests for Telegram Document"""
import sys
import unittest
@@ -37,10 +37,12 @@ class DocumentTest(BaseTest, unittest.TestCase):
self.document_file = open('tests/data/telegram.png', 'rb')
self.document_file_id = 'BQADAQADpAADHyP1B04ipZxJTe2BAg'
self.document_file_url = 'https://raw.githubusercontent.com/python-telegram-bot/python-telegram-bot/master/tests/data/telegram.gif'
self.thumb = {'width': 90,
'height': 90,
'file_id': 'BQADAQADoQADHyP1B0mzJMVyzcB0Ag',
'file_size': 2364}
self.thumb = {
'width': 90,
'height': 90,
'file_id': 'BQADAQADoQADHyP1B0mzJMVyzcB0Ag',
'file_size': 2364
}
self.file_name = 'telegram.png'
self.mime_type = 'image/png'
self.file_size = 12948
@@ -56,7 +58,7 @@ class DocumentTest(BaseTest, unittest.TestCase):
@flaky(3, 1)
@timeout(10)
def test_send_document_png_file(self):
message = self._bot.sendDocument(self._chat_id, self.document_file)
message = self._bot.sendDocument(self._chat_id, self.document_file, caption='caption text')
document = message.document
+1 -1
View File
@@ -16,7 +16,7 @@
#
# 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 File"""
"""This module contains an object that represents Tests for Telegram File"""
import sys
import unittest
+70 -7
View File
@@ -17,7 +17,7 @@
# 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 MessageHandler.Filters
This module contains an object that represents Tests for Filters for use with MessageHandler.
"""
import sys
@@ -28,7 +28,7 @@ import functools
sys.path.append('.')
from telegram import Message, User, Chat, MessageEntity
from telegram.ext import Filters
from telegram.ext import Filters, BaseFilter
from tests.base import BaseTest
@@ -37,6 +37,7 @@ class FiltersTest(BaseTest, unittest.TestCase):
def setUp(self):
self.message = Message(0, User(0, "Testuser"), datetime.now(), Chat(0, 'private'))
self.e = functools.partial(MessageEntity, offset=0, length=0)
def test_filters_text(self):
self.message.text = 'test'
@@ -104,6 +105,12 @@ class FiltersTest(BaseTest, unittest.TestCase):
self.message.venue = None
self.assertFalse(Filters.venue(self.message))
def test_filters_game(self):
self.message.game = 'test'
self.assertTrue(Filters.game(self.message))
self.message.game = None
self.assertFalse(Filters.game(self.message))
def test_filters_status_update(self):
self.assertFalse(Filters.status_update(self.message))
@@ -152,20 +159,76 @@ class FiltersTest(BaseTest, unittest.TestCase):
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.message.entities = [self.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.message.entities = [self.e(MessageEntity.BOLD)]
self.assertFalse(Filters.entity(MessageEntity.MENTION)(self.message))
self.message.entities = [e(MessageEntity.BOLD), e(MessageEntity.MENTION)]
self.message.entities = [self.e(MessageEntity.BOLD), self.e(MessageEntity.MENTION)]
self.assertTrue(Filters.entity(MessageEntity.MENTION)(self.message))
def test_and_filters(self):
self.message.text = 'test'
self.message.forward_date = True
self.assertTrue((Filters.text & Filters.forwarded)(self.message))
self.message.text = '/test'
self.assertFalse((Filters.text & Filters.forwarded)(self.message))
self.message.text = 'test'
self.message.forward_date = None
self.assertFalse((Filters.text & Filters.forwarded)(self.message))
self.message.text = 'test'
self.message.forward_date = True
self.message.entities = [self.e(MessageEntity.MENTION)]
self.assertTrue((Filters.text & Filters.forwarded & Filters.entity(MessageEntity.MENTION))(
self.message))
self.message.entities = [self.e(MessageEntity.BOLD)]
self.assertFalse((Filters.text & Filters.forwarded & Filters.entity(MessageEntity.MENTION)
)(self.message))
def test_or_filters(self):
self.message.text = 'test'
self.assertTrue((Filters.text | Filters.status_update)(self.message))
self.message.group_chat_created = True
self.assertTrue((Filters.text | Filters.status_update)(self.message))
self.message.text = None
self.assertTrue((Filters.text | Filters.status_update)(self.message))
self.message.group_chat_created = False
self.assertFalse((Filters.text | Filters.status_update)(self.message))
def test_and_or_filters(self):
self.message.text = 'test'
self.message.forward_date = True
self.assertTrue((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION))
)(self.message))
self.message.forward_date = False
self.assertFalse((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)
))(self.message))
self.message.entities = [self.e(MessageEntity.MENTION)]
self.assertTrue((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION))
)(self.message))
self.assertRegexpMatches(
str((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)))),
r"<telegram.ext.filters.MergedFilter consisting of <telegram.ext.filters.(Filters.)?_"
r"Text object at .*?> and <telegram.ext.filters.MergedFilter consisting of "
r"<telegram.ext.filters.(Filters.)?_Forwarded object at .*?> or "
r"<telegram.ext.filters.(Filters.)?entity object at .*?>>>")
def test_faulty_custom_filter(self):
class _CustomFilter(BaseFilter):
pass
custom = _CustomFilter()
with self.assertRaises(NotImplementedError):
(custom & Filters.text)(self.message)
if __name__ == '__main__':
unittest.main()
+5 -3
View File
@@ -17,7 +17,7 @@
#
# 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 ForceReply"""
"""This module contains an object that represents Tests for Telegram ForceReply"""
import sys
import unittest
@@ -35,8 +35,10 @@ class ForceReplyTest(BaseTest, unittest.TestCase):
self.force_reply = True
self.selective = True
self.json_dict = {'force_reply': self.force_reply,
'selective': self.selective,}
self.json_dict = {
'force_reply': self.force_reply,
'selective': self.selective,
}
def test_send_message_with_force_reply(self):
message = self._bot.sendMessage(

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