Compare commits

..

115 Commits

Author SHA1 Message Date
Jannes Höke d5583190b8 Bump version to v6.1.0 2017-06-18 12:35:16 +02:00
Jacob Bom faddb92395 Clean up Bot code a bit (#673)
* Clean up Bot code a bit

- Move decorators to module. It really wasn't clear how decorators inside classes work, and why they didn't have a self parameter, but still wasn't static. This also makes them effectively private without having to underscore them, which I think we should have done long time ago atm. Note that this might break backwards compatibility slightly (only if people are daft enough to have used the decorators themselves)
- Don't call _message_wrapper directly. Ever. Instead always use the message decorator, since it's what it's there for. Closes #627
- Don't use the message decorator if the method isn't supposed to return a message. The decorator could handle values like True (which is often the return value), but to someone reading the code, it seems like it's a message returning method even when it wasn't.
- Always document timeout and **kwargs
- Log all methods

* Add test to make sure timeout propagates properly despite decorators
2017-06-18 12:14:24 +02:00
saschalalala 9b5e014a0a Simplification of boolean checks (#662)
* Simplification of boolean checks

* Cast ok to bool for Telegram API json encoding
2017-06-18 12:09:32 +02:00
Jannes Höke 845312da59 Merge branch 'master' of github.com:python-telegram-bot/python-telegram-bot 2017-06-16 04:16:45 +02:00
saschalalala 3863b4f371 Rename shortcut functions to snake_case (#661)
* Rename shortcut functions to snake_case

* More function renaming

* Example function rewrite

* Add myself to authors.rst

* More function renaming

* Rename mockbot test functions

* Break comment line for flake max line length
2017-06-14 00:07:03 +02:00
Eldinnie 25912dca6e add --recursive in contributing guide (#664) 2017-06-14 00:02:16 +02:00
alateas 6877886f7d Change hard-coded 20. to timeout parameter (#663)
* Change hard-coded 20. to timeout paramter

Passing timeout parameter to _message_wrapper in send_audio instead of hard-coded 20 seconds

* add myself to contributors
2017-06-14 00:01:38 +02:00
Noam Meltzer d16d38530c Merge pull request #658 from jeffffc/paymentbot_example_and_fixes
Add PaymentBot Example, Bugfixes on payment methods
2017-06-11 00:18:20 +03:00
Jeff c7ed87d475 add telegram doc link comment 2017-06-11 04:30:21 +08:00
Jeff a68e0ad044 bugfixes on error in answer_shipping_query and answer_precheckout_query 2017-06-11 03:45:48 +08:00
Jeff ad603bd2f9 add new payment example 2017-06-11 03:43:38 +08:00
Jannes Höke 179fc141bb Merge branch 'master' of github.com:python-telegram-bot/python-telegram-bot 2017-06-10 21:24:28 +02:00
Noam Meltzer da8a3cee44 Merge pull request #631 from jeffffc/paymenthandlers
Add both handlers for queries from new Payment API
2017-06-09 18:23:29 +03:00
Eldin bc3669fa4b make commandhandler case insensitive 2017-06-09 17:45:15 +03:00
Eldin 7def2c53e1 fix tests for pycharm windows
Refractored self.id in new tests to self._id (as I did before) so testrunner in pycharm can run under Windows.
2017-06-09 17:44:43 +03:00
Noam Meltzer 09230e6e84 Merge pull request #649 from python-telegram-bot/fix-docs
Fix some documentation stuff
[ci skip]
2017-06-09 13:14:03 +03:00
Eldin 011e321583 Update gitignore with new video file 2017-06-09 12:27:10 +03:00
Eldin 0afb38f45c Fix video_note tests
Added a video_note to send.
2017-06-09 12:27:10 +03:00
Jeff 4247dc0e21 tiny changes upon PR review 2017-06-08 09:47:19 +08:00
Jacob Bom 896392d01e Merge branch 'master' into fix-docs 2017-06-07 13:12:32 +02:00
Jacob Bom 61c5d6ff08 Remove more warnings 2017-06-07 13:07:45 +02:00
Jacob Bom aa1c4e0e02 Fix most of "more than one target found for cross-reference" warnings 2017-06-07 12:53:02 +02:00
Jacob Bom 92183a5bbc Some objects don't have a module 2017-06-07 12:52:36 +02:00
Jacob Bom 87b096c7f9 Only document functions/methods that are 100% lowercase + use favicon
In bot.py we did stuff like sendAudio = send_audio, which made sphinx document both, which led to clutter in our docs.
2017-06-07 12:40:09 +02:00
Jacob Bom 3329d15691 Add orange favicon
Do we need a blue one too?
2017-06-07 12:37:29 +02:00
Noam Meltzer 0fb00c4c8b When Promise running throws an uncaught exception - log it 2017-06-03 16:34:03 +03:00
Noam Meltzer 2680740316 Merge pull request #641 from azogue/fixes
fix setting default mimetype of inputfile
2017-06-03 00:22:32 +03:00
Jeff b6ba66ba8e Fix typo in filter test 2017-06-03 02:05:26 +08:00
Jeff eaf765dcbc Remove duplicated/useless codes, added Filters.invoice test 2017-06-03 01:48:30 +08:00
Jeff f735a37828 Merge from master and resolve conflicts
Merge from master and resolve conflicts
2017-06-03 01:19:29 +08:00
Jeff e9d08c6f7a Merge branch 'master' into paymenthandlers-new
# Conflicts:
#	telegram/bot.py
#	telegram/message.py
#	telegram/precheckoutquery.py
#	telegram/shippingquery.py
2017-06-03 01:09:17 +08:00
Noam Meltzer c0e3453a62 Travis: add test to make sure setup.py doesn't break
Refs #635
2017-06-02 11:51:31 +03:00
Noam Meltzer 90e9e32632 Merge pull request #622 from python-telegram-bot/payment
Payment stuff

refs #617
2017-06-02 11:14:18 +03:00
azogue 1e3e8973ba added myself to authors 2017-06-02 08:25:39 +02:00
Noam Meltzer 0ed997cc53 Merge remote-tracking branch 'origin/master' into payment 2017-06-01 22:07:14 +03:00
Jacob Bom 760737345d Fix tests for video_note
It would seem that length is no longer incorrectly required so that's good
However they stopped sending the file_size when you send via a file_id which is a bit odd
2017-06-01 22:06:34 +03:00
Noam Meltzer 137ffe2e73 Merge remote-tracking branch 'origin/master' into payment 2017-06-01 22:01:04 +03:00
Noam Meltzer c4b78673a3 payment: cr fixes 2017-06-01 21:52:47 +03:00
Noam Meltzer ee1cf6b9b0 test_set_webhook_get_webhook_info: Retry more
Unitests fail too much on this one. Lets retry more, maybe we'll stop
seeing this.
[ci skip]
2017-06-01 21:25:00 +03:00
Noam Meltzer c15371e8c0 Merge pull request #642 from evgfilim1/new-filters
New filters
2017-06-01 21:21:22 +03:00
evgfilim1 f66076b06c Returned old tests 2017-06-01 16:37:39 +05:00
evgfilim1 5b1252ac2d Perfomance optimizations
Using `or` instead of `|`
2017-06-01 16:36:32 +05:00
Rahiel Kasim 647059735c CHANGES: document introduction of telegram.constants (#643) 2017-06-01 11:33:36 +02:00
evgfilim1 d0f6860bac Fixed tests to use new filters 2017-06-01 07:10:26 +05:00
evgfilim1 d98b18d1e3 Added new status_update filters 2017-06-01 07:09:30 +05:00
Jannes Höke 9d6c394b02 Bump version to v6.0.3 2017-05-29 22:55:15 +02:00
Jannes Höke 1951d6fdcd Bump version to v6.0.2 2017-05-29 21:24:12 +02:00
azogue 84b59c8cb2 unittest sending non standard jpg photo with bytesio stream 2017-05-29 09:38:02 +02:00
azogue b46bdf5304 fix setting default mimetype of inputfile 2017-05-29 08:30:29 +02:00
Noam Meltzer cd24bb4ba5 payment: Small fixes
- Semantic fixes bot.py (if XXX is not None: ...).
 - Documentation fixes (arguments which are optional).
2017-05-26 20:02:18 +03:00
Noam Meltzer ae33d33580 Merge pull request #625 from python-telegram-bot/videonote
Add VideoNote
2017-05-26 19:34:13 +03:00
Noam Meltzer 706f79fa99 Fix setup.py with urllib3 vendoring
fixes #635
2017-05-26 19:24:54 +03:00
Noam Meltzer a5bfc52326 videonote: Semantic fix according to CR 2017-05-26 18:36:21 +03:00
Noam Meltzer 858684ab64 Merge pull request #628 from python-telegram-bot/may18minor
May 18 minor changes
2017-05-26 18:23:31 +03:00
Jacob Bom f4c6197c5f Update filters to new_chat_members instead of new_chat_member 2017-05-25 14:45:47 +02:00
Jacob Bom a7044562af Fix stuff as per @tsnoam's comments 2017-05-25 14:16:53 +02:00
Jacob Bom 27a4fa8dba Move *_duration attributes as to keep backwardscompat 2017-05-25 14:05:08 +02:00
Jacob Bom a41ee3b040 Actually deprecate new_chat_member to help users switch 2017-05-25 14:02:41 +02:00
Jacob Bom 8499dcc33c Better deprecation warnings
Actually shows where in the users code the error happened, not just where the warning came from in our internal code
2017-05-25 13:53:35 +02:00
Noam Meltzer ff897ce9ef Avoid confusion with user's urllib3 by renaming vendored urllib3 2017-05-24 13:14:33 +03:00
Jeff f314915954 fix typo again 2017-05-23 16:26:04 +08:00
Jeff 1bf0078573 add missing "need_email" in sendInvoice, fix pep8/flake 2017-05-23 16:21:45 +08:00
Jeff 1210e4ef04 Finalize Payment API
bugfixes
added payment-related handlers
2017-05-23 02:19:23 +08:00
Jeff 05ed693fb6 fix authors typo 2017-05-22 18:11:19 +00:00
Jeff 18822d1396 Merge branch 'fix-paymenthandlers' into paymenthandlers-final 2017-05-22 17:47:43 +00:00
Jeff 73ac259721 fix typo... 2017-05-23 00:13:30 +08:00
Jeff 96171112bf fix crucial typo in filters 2017-05-22 23:42:09 +08:00
Jeff 27238c52e2 add successful_payment filter under message 2017-05-22 23:39:28 +08:00
Jeff 43f4128b31 fix answer* methods not sending api requests 2017-05-22 22:55:19 +08:00
Jeff 1142953108 add checks for answer* methods 2017-05-22 22:47:35 +08:00
Jeff 1e250f2415 add kwargs to answer* methods 2017-05-22 21:37:15 +08:00
Jeff 8f2f29c7d4 fix typo, add bot and kwargs to class init 2017-05-22 21:32:42 +08:00
Jeff 3767d26fc8 Add both handlers for queries from new Payment API (#630)
* add handlers for new payment API

* fix typo

* fix docstring mistakes

* added missing 'from_user'
2017-05-22 14:07:53 +02:00
Jeff a78f72c086 added missing 'from_user' 2017-05-22 20:04:03 +08:00
Jeff cc03649f1c fix docstring mistakes 2017-05-22 19:59:13 +08:00
Jeff ee057e76ef fix typo 2017-05-22 19:42:48 +08:00
Jeff 2c05b03fba add handlers for new payment API 2017-05-22 19:20:26 +08:00
Jacob Bom 630b63ec10 Add payment filters 2017-05-22 12:13:00 +02:00
Jannes Höke 23b37c9ea4 mention beta version in README 2017-05-21 22:10:08 +02:00
Jacob Bom 01430a24a5 Import order fix 2017-05-21 15:51:01 +02:00
Jacob Bom acda19b7e7 Merge branch 'may18minor' into beta 2017-05-21 15:45:40 +02:00
Jacob Bom 48fa3d975b Merge branch 'videonote' into beta
# Conflicts:
#	telegram/__init__.py
#	telegram/message.py
2017-05-21 15:45:20 +02:00
Jacob Bom d220ff4e38 Merge branch 'payment' into beta 2017-05-21 15:42:24 +02:00
Jacob Bom 5a15d1b5d6 Add mpeg4_duration and gif_duration to inline gif and mpeg4gif 2017-05-21 14:38:12 +02:00
Jacob Bom ad347b5c02 new_chat_member -> new_chat_members
Keep old for now... we can remove it in the future when telegram stops parsing it along.
Also: TODO: write proper Message tests
2017-05-21 14:32:36 +02:00
Jannes Höke 017fdeaae8 run yapf with --diff 2017-05-21 14:29:24 +02:00
Jannes Höke ba6c4fd517 Bump version to v6.0.1 2017-05-21 14:25:40 +02:00
Jacob Bom 99ecac5649 assertRaisesRegex doesn't exist on py2 (also fuck yapf)
This deprecation is totally gonna come back and bite us when the regexp
gets removed completely in never version´
2017-05-21 14:10:55 +02:00
Jannes Höke 1c4c228cf1 add support for User.language_code (#624)
* add support for User.language_code

* Add language filter

Useful is you wanna do something like restrict your shop to a single or a few locales or something like that.
2017-05-21 14:00:53 +02:00
Jacob Bom 2e89e21261 Fix text_markdown and text_html (#623)
* Fix text_markdown and text_html

* Missed a few narrow build checks

* Added tests for emoji-first strings and emojis in url
2017-05-21 14:00:07 +02:00
Jacob Bom 7583fa9d65 Tests for video notes 2017-05-21 13:51:58 +02:00
Jacob Bom 2dd5290ec2 Add reply_video_note in line with other reply_* 2017-05-21 13:51:27 +02:00
Jacob Bom 41299244b7 Allow InputFile to handle video notes 2017-05-21 13:50:52 +02:00
Jacob Bom 0e2bcf28a6 Export VideoNote 2017-05-21 13:50:15 +02:00
Jacob Bom 2746ab77e5 Add sendVideoNote to Bot 2017-05-20 19:35:55 +02:00
Jacob Bom ae39c902ed Add video_note to Message 2017-05-20 19:30:07 +02:00
Jacob Bom 65929a0813 VideoNote chatactions 2017-05-20 19:26:26 +02:00
Jacob Bom 4426eb0c61 Add VideoNote 2017-05-20 19:25:24 +02:00
Jacob Bom d4f2f2e077 Getting instead of popping
I really think we should be popping and I'm unable to see why it makes our tests fail, but for now.. rolling back.
2017-05-20 19:02:01 +02:00
Jacob Bom 73d75b964a Remove extra space from payment provider token 2017-05-20 18:55:21 +02:00
Jacob Bom 45897048aa Add invoice test and there's no EUD currency, whoops 2017-05-20 18:51:31 +02:00
Jacob Bom 76db279624 We have to send and array... that's new... 2017-05-20 18:46:39 +02:00
Jacob Bom a97da5cb20 Sooo apparently python 2 doesn't like ‘ (\xe2) 2017-05-20 17:36:10 +02:00
Jacob Bom 9f6ec125b9 Basic tests for payment stuff 2017-05-20 17:31:46 +02:00
Jacob Bom 5f96c507b9 Payment ids should be unique... probably 2017-05-20 17:31:46 +02:00
Jacob Bom 402e1381bb OrderInfo can be completely empty
In which case it should still be an OrderInfo not None
2017-05-20 17:31:46 +02:00
Jacob Bom b276b06b31 Pop instead of get if our name doesn't match telegrams (user/from_user) 2017-05-20 17:31:46 +02:00
Jacob Bom b69ecc3015 Fix import order 2017-05-20 17:31:46 +02:00
Jacob Bom 77f06888cb MessageEntity != Sticker :P 2017-05-20 17:31:46 +02:00
Jacob Bom 834c1ab3c5 Merge branch 'master' into payment 2017-05-19 20:25:46 +02:00
Jacob Bom c78d697b0e Add invoice and successful_payment to Message 2017-05-19 20:06:58 +02:00
Jacob Bom 2929c76248 Fix docstring for Message 2017-05-19 20:04:24 +02:00
Jacob Bom 034bcf0895 Add pay inlinekeyboardbutton type 2017-05-19 19:49:31 +02:00
Jacob Bom 8be57198b4 New payment bot methods
sendInvoice, answerShippingQuery, and answerPreCheckoutQuery
2017-05-19 19:46:42 +02:00
Jacob Bom 8e62b02ff6 Add new payment classes
Invoice, LabeledPrice, OrderInfo, PreCheckoutQuery, ShippingAddress, ShippingOption, ShippingQuery and SuccessfulPayment
2017-05-19 19:41:06 +02:00
70 changed files with 2927 additions and 342 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ Setting things up
.. code-block:: bash
$ git clone https://github.com/<your username>/python-telegram-bot
$ git clone https://github.com/<your username>/python-telegram-bot --recursive
$ cd python-telegram-bot
3. Add a track to the original repository:
+1
View File
@@ -65,6 +65,7 @@ target/
# unitests files
telegram.mp3
telegram.mp4
telegram2.mp4
telegram.ogg
telegram.png
telegram.webp
+1 -1
View File
@@ -1,4 +1,4 @@
[submodule "telegram/vendor/urllib3"]
path = telegram/vendor/urllib3
path = telegram/vendor/ptb_urllib3
url = https://github.com/python-telegram-bot/urllib3.git
branch = ptb
+2
View File
@@ -3,6 +3,8 @@
hooks:
- id: yapf
files: ^(telegram|tests)/.*\.py$
args:
- --diff
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: 18d7035de5388cc7775be57f529c154bf541aab9
hooks:
+1
View File
@@ -18,5 +18,6 @@ install:
script:
- nosetests -v --with-flaky --no-flaky-report --with-coverage --cover-package=telegram/ tests
- if [[ $TRAVIS_PYTHON_VERSION == 3.5 ]]; then pre-commit run --all-files; fi
- python ./setup.py bdist_dumb
after_success:
coveralls
+4
View File
@@ -15,6 +15,7 @@ Contributors
The following wonderful people contributed directly or indirectly to this project:
- `Alateas <https://github.com/alateas>`_
- `Avanatiker <https://github.com/Avanatiker>`_
- `Anton Tagunov <https://github.com/anton-tagunov>`_
- `Balduro <https://github.com/Balduro>`_
@@ -24,11 +25,13 @@ The following wonderful people contributed directly or indirectly to this projec
- `Eli Gao <https://github.com/eligao>`_
- `ErgoZ Riftbit Vaper <https://github.com/ergoz>`_
- `Eugene Lisitsky <https://github.com/lisitsky>`_
- `Eugenio Panadero <https://github.com/azogue>`_
- `evgfilim1 <https://github.com/evgfilim1>`_
- `franciscod <https://github.com/franciscod>`_
- `Hugo Damer <https://github.com/HakimusGIT>`_
- `Jacob Bom <https://github.com/bomjacob>`_
- `JASON0916 <https://github.com/JASON0916>`_
- `jeffffc <https://github.com/jeffffc>`_
- `jh0ker <https://github.com/jh0ker>`_
- `John Yong <https://github.com/whipermr5>`_
- `jossalgon <https://github.com/jossalgon>`_
@@ -48,6 +51,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `Pieter Schutz <https://github.com/eldinnie>`_
- `Rahiel Kasim <https://github.com/rahiel>`_
- `Joscha Götzer <https://github.com/Rostgnom>`_
- `Sascha <https://github.com/saschalalala>`_
- `Shelomentsev D <https://github.com/shelomentsevd>`_
- `sooyhwang <https://github.com/sooyhwang>`_
- `thodnev <https://github.com/thodnev>`_
+28
View File
@@ -2,6 +2,33 @@
Changes
=======
**2017-06-18**
*Released 6.1.0*
- Fully support Bot API 3.0
- Add more fine-grained filters for status updates
- Bug fixes and other improvements
**2017-05-29**
*Released 6.0.3*
- Faulty PyPI release
**2017-05-29**
*Released 6.0.2*
- Avoid confusion with user's ``urllib3`` by renaming vendored ``urllib3`` to ``ptb_urllib3``
**2017-05-19**
*Released 6.0.1*
- Add support for ``User.language_code``
- Fix ``Message.text_html`` and ``Message.text_markdown`` for messages with emoji
**2017-05-19**
*Released 6.0.0*
@@ -73,6 +100,7 @@ Changes
- Rework ``JobQueue``
- Introduce ``ConversationHandler``
- Introduce ``telegram.constants`` - https://github.com/python-telegram-bot/python-telegram-bot/pull/342
**2016-07-12**
+1 -1
View File
@@ -84,7 +84,7 @@ make the development of bots easy and straightforward. These classes are contain
Telegram API support
====================
As of **4. Dec 2016**, all types and methods of the Telegram Bot API are supported.
As of **18. June 2017**, all types and methods of the Telegram Bot API 3.0 are supported.
==========
Installing
+22 -5
View File
@@ -11,7 +11,6 @@
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
import shlex
@@ -25,7 +24,7 @@ sys.path.insert(0, os.path.abspath('../..'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
needs_sphinx = '1.5.4' # fixes issues with autodoc-skip-member and napoleon
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@@ -59,9 +58,9 @@ author = u'Leandro Toledo'
# built documents.
#
# The short X.Y version.
version = '6.0' # telegram.__version__[:3]
version = '6.1' # telegram.__version__[:3]
# The full version, including alpha/beta/rc tags.
release = '6.0.0' # telegram.__version__
release = '6.1.0' # telegram.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -136,7 +135,7 @@ html_logo = 'ptb-logo-orange.png'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
html_favicon = 'ptb-logo-orange.ico'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
@@ -289,3 +288,21 @@ texinfo_documents = [
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
# -- script stuff --------------------------------------------------------
import inspect
def autodoc_skip_member(app, what, name, obj, skip, options):
try:
if inspect.getmodule(obj).__name__.startswith('telegram') and inspect.isfunction(obj):
if name.lower() != name:
return True
except AttributeError:
pass
# Return None so napoleon can handle it
def setup(app):
app.connect('autodoc-skip-member', autodoc_skip_member)
Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

+146
View File
@@ -0,0 +1,146 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Basic example for a bot that can receive payment from user.
# This program is dedicated to the public domain under the CC0 license.
from telegram import (LabeledPrice, ShippingOption)
from telegram.ext import (Updater, CommandHandler, MessageHandler,
Filters, PreCheckoutQueryHandler, ShippingQueryHandler)
import logging
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
def start_callback(bot, update):
msg = "Use /shipping to get an invoice for shipping-payment, "
msg += "or /noshipping for an invoice without shipping."
update.message.reply_text(msg)
def start_with_shipping_callback(bot, update):
chat_id = update.message.chat_id
title = "Payment Example"
description = "Payment Example using python-telegram-bot"
# select a payload just for you to recognize its the donation from your bot
payload = "Custom-Payload"
# get your provider_token at @botfather, see https://core.telegram.org/bots/payments#getting-a-token
provider_token = "PROVIDER_TOKEN"
start_parameter = "test-payment"
currency = "USD"
# price in dollars
price = 1
# price * 100 so as to include 2 d.p.
# check https://core.telegram.org/bots/payments#supported-currencies for more details
prices = [LabeledPrice("Test", price * 100)]
# optionally pass need_name=True, need_phone_number=True,
# need_email=True, need_shipping_address=True, is_flexible=True
bot.sendInvoice(chat_id, title, description, payload,
provider_token, start_parameter, currency, prices,
need_name=True, need_phone_number=True,
need_email=True, need_shipping_address=True, is_flexible=True)
def start_without_shipping_callback(bot, update):
chat_id = update.message.chat_id
title = "Payment Example"
description = "Payment Example using python-telegram-bot"
# select a payload just for you to recognize its the donation from your bot
payload = "Custom-Payload"
# get your provider_token at @botfather, see https://core.telegram.org/bots/payments#getting-a-token
provider_token = "PROVIDER_TOKEN"
start_parameter = "test-payment"
currency = "USD"
# price in dollars
price = 1
# price * 100 so as to include 2 d.p.
prices = [LabeledPrice("Test", price * 100)]
# optionally pass need_name=True, need_phone_number=True,
# need_email=True, need_shipping_address=True, is_flexible=True
bot.sendInvoice(chat_id, title, description, payload,
provider_token, start_parameter, currency, prices)
def shipping_callback(bot, update):
query = update.shipping_query
# check the payload, is this from your bot?
if query.invoice_payload != 'Custom-Payload':
# answer False pre_checkout_query
bot.answer_shipping_query(shipping_query_id=query.id, ok=False,
error_message="Something went wrong...")
return
else:
options = list()
# a single LabeledPrice
options.append(ShippingOption('1', 'Shipping Option A', [LabeledPrice('A', 100)]))
# an array of LabeledPrice objects
price_list = [LabeledPrice('B1', 150), LabeledPrice('B2', 200)]
options.append(ShippingOption('2', 'Shipping Option B', price_list))
bot.answer_shipping_query(shipping_query_id=query.id, ok=True,
shipping_options=options)
# after (optional) shipping, it's the pre-checkout
def precheckout_callback(bot, update):
query = update.pre_checkout_query
# check the payload, is this from your bot?
if query.invoice_payload != 'Custom-Payload':
# answer False pre_checkout_query
bot.answer_pre_checkout_query(pre_checkout_query_id=query.id, ok=False,
error_message="Something went wrong...")
else:
bot.answer_pre_checkout_query(pre_checkout_query_id=query.id, ok=True)
# finally, after contacting to the payment provider...
def successful_payment_callback(bot, update):
# do something after successful receive of payment?
update.message.reply_text("Thank you for your payment!")
def main():
# Create the EventHandler and pass it your bot's token.
updater = Updater(token="BOT_TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
# simple start function
dp.add_handler(CommandHandler("start", start_callback))
# Add command handler to start the payment invoice
dp.add_handler(CommandHandler("shipping", start_with_shipping_callback))
dp.add_handler(CommandHandler("noshipping", start_without_shipping_callback))
# Optional handler if your product requires shipping
dp.add_handler(ShippingQueryHandler(shipping_callback))
# Pre-checkout handler to final check
dp.add_handler(PreCheckoutQueryHandler(precheckout_callback))
# Success! Notify your user!
dp.add_handler(MessageHandler(Filters.successful_payment, successful_payment_callback))
# log all errors
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
-5
View File
@@ -18,11 +18,6 @@ def requirements():
packages = find_packages(exclude=['tests*'])
packages.extend(['telegram.vendor.urllib3.urllib3',
'telegram.vendor.urllib3.urllib3.packages', 'telegram.vendor.urllib3.urllib3.packages.ssl_match_hostname',
'telegram.vendor.urllib3.urllib3.packages.backports', 'telegram.vendor.urllib3.urllib3.contrib',
'telegram.vendor.urllib3.urllib3.util',
])
with codecs.open('README.rst', 'r', 'utf-8') as fd:
fn = os.path.join('telegram', 'version.py')
+11 -3
View File
@@ -22,8 +22,6 @@ from sys import version_info
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'vendor', 'urllib3'))
from .base import TelegramObject
from .user import User
from .chat import Chat
@@ -52,6 +50,10 @@ from .parsemode import ParseMode
from .messageentity import MessageEntity
from .animation import Animation
from .game import Game
from .shippingaddress import ShippingAddress
from .orderinfo import OrderInfo
from .successfulpayment import SuccessfulPayment
from .invoice import Invoice
from .message import Message
from .inputmessagecontent import InputMessageContent
from .callbackquery import CallbackQuery
@@ -84,8 +86,13 @@ from .inputtextmessagecontent import InputTextMessageContent
from .inputlocationmessagecontent import InputLocationMessageContent
from .inputvenuemessagecontent import InputVenueMessageContent
from .inputcontactmessagecontent import InputContactMessageContent
from .labeledprice import LabeledPrice
from .shippingoption import ShippingOption
from .precheckoutquery import PreCheckoutQuery
from .shippingquery import ShippingQuery
from .webhookinfo import WebhookInfo
from .gamehighscore import GameHighScore
from .videonote import VideoNote
from .update import Update
from .bot import Bot
from .constants import (MAX_MESSAGE_LENGTH, MAX_CAPTION_LENGTH, SUPPORTED_WEBHOOK_PORTS,
@@ -115,5 +122,6 @@ __all__ = [
'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'
'Game', 'GameHighScore', 'VideoNote', 'LabeledPrice', 'SuccessfulPayment', 'ShippingOption',
'ShippingAddress', 'PreCheckoutQuery', 'OrderInfo', 'Invoice', 'ShippingQuery'
]
+339 -108
View File
@@ -31,10 +31,45 @@ from telegram.utils.request import Request
logging.getLogger(__name__).addHandler(logging.NullHandler())
def info(func):
@functools.wraps(func)
def decorator(self, *args, **kwargs):
if not self.bot:
self.get_me()
result = func(self, *args, **kwargs)
return result
return decorator
def log(func):
logger = logging.getLogger(func.__module__)
@functools.wraps(func)
def decorator(self, *args, **kwargs):
logger.debug('Entering: %s', func.__name__)
result = func(self, *args, **kwargs)
logger.debug(result)
logger.debug('Exiting: %s', func.__name__)
return result
return decorator
def message(func):
@functools.wraps(func)
def decorator(self, *args, **kwargs):
url, data = func(self, *args, **kwargs)
return self._message_wrapper(url, data, *args, **kwargs)
return decorator
class Bot(TelegramObject):
"""This object represents a Telegram Bot.
Attributes:
Properties:
id (int): Unique identifier for this bot.
first_name (str): Bot's first name.
last_name (str): Bot's last name.
@@ -80,18 +115,6 @@ class Bot(TelegramObject):
return token
def info(func):
@functools.wraps(func)
def decorator(self, *args, **kwargs):
if not self.bot:
self.getMe()
result = func(self, *args, **kwargs)
return result
return decorator
@property
@info
def id(self):
@@ -116,19 +139,6 @@ class Bot(TelegramObject):
def name(self):
return '@{0}'.format(self.username)
def log(func):
logger = logging.getLogger(func.__module__)
@functools.wraps(func)
def decorator(self, *args, **kwargs):
logger.debug('Entering: %s', func.__name__)
result = func(self, *args, **kwargs)
logger.debug(result)
logger.debug('Exiting: %s', func.__name__)
return result
return decorator
def _message_wrapper(self, url, data, *args, **kwargs):
if kwargs.get('reply_to_message_id'):
data['reply_to_message_id'] = kwargs.get('reply_to_message_id')
@@ -150,15 +160,6 @@ class Bot(TelegramObject):
return Message.de_json(result, self)
def message(func):
@functools.wraps(func)
def decorator(self, *args, **kwargs):
url, data = func(self, *args, **kwargs)
return Bot._message_wrapper(self, url, data, *args, **kwargs)
return decorator
@log
def get_me(self, timeout=None, **kwargs):
"""A simple method for testing your bot's auth token.
@@ -242,8 +243,7 @@ class Bot(TelegramObject):
return url, data
@log
@message
def delete_message(self, chat_id, message_id):
def delete_message(self, chat_id, message_id, timeout=None, **kwargs):
"""Use this method to delete a message. A message can only be deleted if it was sent less
than 48 hours ago. Any such recently sent outgoing message may be deleted. Additionally,
if the bot is an administrator in a group chat, it can delete any message. If the bot is
@@ -257,6 +257,10 @@ class Bot(TelegramObject):
username of the target channel (in the format
@channelusername).
message_id (int): Unique message identifier.
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
from the server (instead of the one specified during creation of the connection
pool).
**kwargs (dict): Arbitrary keyword arguments.
Returns:
bool: On success, `True` is returned.
@@ -269,7 +273,9 @@ class Bot(TelegramObject):
data = {'chat_id': chat_id, 'message_id': message_id}
return url, data
result = self._request.post(url, data, timeout=timeout)
return result
@log
@message
@@ -315,6 +321,7 @@ class Bot(TelegramObject):
return url, data
@log
@message
def send_photo(self,
chat_id,
photo,
@@ -356,19 +363,10 @@ class Bot(TelegramObject):
if caption:
data['caption'] = caption
return self._message_wrapper(
url,
data,
chat_id=chat_id,
photo=photo,
caption=caption,
disable_notification=disable_notification,
reply_to_message_id=reply_to_message_id,
reply_markup=reply_markup,
timeout=timeout,
**kwargs)
return url, data
@log
@message
def send_audio(self,
chat_id,
audio,
@@ -431,22 +429,10 @@ class Bot(TelegramObject):
if caption:
data['caption'] = caption
return self._message_wrapper(
url,
data,
chat_id=chat_id,
audio=audio,
duration=duration,
performer=performer,
title=title,
caption=caption,
disable_notification=disable_notification,
reply_to_message_id=reply_to_message_id,
reply_markup=reply_markup,
timeout=20.,
**kwargs)
return url, data
@log
@message
def send_document(self,
chat_id,
document,
@@ -493,18 +479,7 @@ class Bot(TelegramObject):
if caption:
data['caption'] = caption
return self._message_wrapper(
url,
data,
chat_id=chat_id,
document=document,
filename=filename,
caption=caption,
disable_notification=disable_notification,
reply_to_message_id=reply_to_message_id,
reply_markup=reply_markup,
timeout=timeout,
**kwargs)
return url, data
@log
@message
@@ -549,6 +524,7 @@ class Bot(TelegramObject):
return url, data
@log
@message
def send_video(self,
chat_id,
video,
@@ -595,20 +571,10 @@ class Bot(TelegramObject):
if caption:
data['caption'] = caption
return self._message_wrapper(
url,
data,
chat_id=chat_id,
video=video,
duration=duration,
caption=caption,
disable_notification=disable_notification,
reply_to_message_id=reply_to_message_id,
reply_markup=reply_markup,
timeout=timeout,
**kwargs)
return url, data
@log
@message
def send_voice(self,
chat_id,
voice,
@@ -658,18 +624,57 @@ class Bot(TelegramObject):
if caption:
data['caption'] = caption
return self._message_wrapper(
url,
data,
chat_id=chat_id,
voice=voice,
duration=duration,
caption=caption,
disable_notification=disable_notification,
reply_to_message_id=reply_to_message_id,
reply_markup=reply_markup,
timeout=timeout,
**kwargs)
return url, data
@log
@message
def send_video_note(self,
chat_id,
video_note,
duration=None,
length=None,
disable_notification=False,
reply_to_message_id=None,
reply_markup=None,
timeout=20.,
**kwargs):
"""As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute
long. Use this method to send video messages
Args:
chat_id (int|str): Unique identifier for the message recipient - Chat id.
video_note (InputFile|str): Video note to send. Pass a file_id as String to send a
video note that exists on the Telegram servers (recommended) or upload a new video.
Sending video notes by a URL is currently unsupported
duration (Optional[int]): Duration of sent audio in seconds.
length (Optional[int]): Video width and height
disable_notification (Optional[bool]): Sends the message silently. iOS users will not
receive a notification, Android users will receive a notification with no sound.
reply_to_message_id (Optional[int]): If the message is a reply, ID of the original
message.
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options. A
JSON-serialized object for an inline keyboard, custom reply keyboard, instructions
to remove reply keyboard or to force a reply from the user.
timeout (Optional[int|float]): Send file timeout (default: 20 seconds).
**kwargs (dict): Arbitrary keyword arguments.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/sendVideoNote'.format(self.base_url)
data = {'chat_id': chat_id, 'video_note': video_note}
if duration is not None:
data['duration'] = duration
if length is not None:
data['length'] = length
return url, data
@log
@message
@@ -838,8 +843,6 @@ class Bot(TelegramObject):
channel (in the format @channelusername).
game_short_name (str): Short name of the game, serves as the unique identifier for the
game.
Keyword Args:
disable_notification (Optional[bool]): Sends the message silently. iOS users will not
receive a notification, Android users will receive a notification with no sound.
reply_to_message_id (Optional[int]): If the message is a reply,
@@ -850,6 +853,7 @@ class Bot(TelegramObject):
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
from the server (instead of the one specified during creation of the connection
pool).
**kwargs (dict): Arbitrary keyword arguments.
Returns:
:class:`telegram.Message`: On success, the sent message is returned.
@@ -865,7 +869,6 @@ class Bot(TelegramObject):
return url, data
@log
@message
def send_chat_action(self, chat_id, action, timeout=None, **kwargs):
"""Use this method when you need to tell the user that something is happening on the bot's
side. The status is set for 5 seconds or less (when a message arrives from your bot,
@@ -892,7 +895,9 @@ class Bot(TelegramObject):
data = {'chat_id': chat_id, 'action': action}
return url, data
result = self._request.post(url, data, timeout=timeout)
return result
@log
def answer_inline_query(self,
@@ -1638,6 +1643,7 @@ class Bot(TelegramObject):
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
from the server (instead of the one specified during creation of the connection
pool).
**kwargs (dict): Arbitrary keyword arguments.
Returns:
:class: `telegram.WebhookInfo`
@@ -1651,6 +1657,8 @@ class Bot(TelegramObject):
return WebhookInfo.de_json(result, self)
@log
@message
def set_game_score(self,
user_id,
score,
@@ -1683,6 +1691,7 @@ class Bot(TelegramObject):
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
from the server (instead of the one specified during creation of the connection
pool).
**kwargs (dict): Arbitrary keyword arguments.
Returns:
:class:`telegram.Message` or True: The edited message, or if the
@@ -1710,12 +1719,9 @@ class Bot(TelegramObject):
else:
warnings.warn('edit_message is ignored when disable_edit_message is used')
result = self._request.post(url, data, timeout=timeout)
if result is True:
return result
else:
return Message.de_json(result, self)
return url, data
@log
def get_game_high_scores(self,
user_id,
chat_id=None,
@@ -1737,6 +1743,7 @@ class Bot(TelegramObject):
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
from the server (instead of the one specified during creation of the connection
pool).
**kwargs (dict): Arbitrary keyword arguments.
Returns:
list[:class:`telegram.GameHighScore`]: Scores of the specified user and several of his
@@ -1758,6 +1765,226 @@ class Bot(TelegramObject):
return [GameHighScore.de_json(hs, self) for hs in result]
@log
@message
def send_invoice(self,
chat_id,
title,
description,
payload,
provider_token,
start_parameter,
currency,
prices,
photo_url=None,
photo_size=None,
photo_width=None,
photo_height=None,
need_name=None,
need_phone_number=None,
need_email=None,
need_shipping_address=None,
is_flexible=None,
disable_notification=False,
reply_to_message_id=None,
reply_markup=None,
timeout=None,
**kwargs):
"""
Use this method to send invoices.
Args:
chat_id (int|str): Unique identifier for the target private chat
title (str): Product name
description (str): Product description
payload (str): Bot-defined invoice payload, 1-128 bytes. This will not be displayed
to the user, use for your internal processes.
provider_token (str): Payments provider token, obtained via Botfather
start_parameter (str): Unique deep-linking parameter that can be used to generate
this invoice when used as a start parameter
currency (str): Three-letter ISO 4217 currency code
prices (List[:class:`telegram.LabeledPrice`]): Price breakdown, a list of components
(e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
photo_url (Optional[str]): URL of the product photo for the invoice. Can be a photo of
the goods or a marketing image for a service. People like it better when they
see what they are paying for.
photo_size (Optional[str]): Photo size
photo_width (Optional[int]): Photo width
photo_height (Optional[int]): Photo height
need_name (Optional[bool]): Pass True, if you require the user's full name to complete
the order
need_phone_number (Optional[bool]): Pass True, if you require the user's phone number
to complete the order
need_email (Optional[bool]): Pass True, if you require the user's email to
complete the order
need_shipping_address (Optional[bool]): Pass True, if you require the user's shipping
address to complete the order
is_flexible (Optional[bool]): Pass True, if the final price depends on the shipping
method
disable_notification (Optional[bool]): Sends the message silently. iOS users will not
receive a notification, Android users will receive a notification with no sound.
reply_to_message_id (Optional[int]): If the message is a reply, ID of the original
message.
reply_markup (Optional[:class:`telegram.ReplyMarkup`]): Additional interface options.
An inlinekeyboard. If empty, one 'Pay total price' button will be shown. If not
empty, the first button must be a Pay button.
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
from the server (instead of the one specified during creation of the connection
pool).
**kwargs (dict): Arbitrary keyword arguments.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
Raises:
:class:`telegram.TelegramError`
"""
url = '{0}/sendInvoice'.format(self.base_url)
data = {
'chat_id': chat_id,
'title': title,
'description': description,
'payload': payload,
'provider_token': provider_token,
'start_parameter': start_parameter,
'currency': currency,
'prices': [p.to_dict() for p in prices]
}
if photo_url is not None:
data['photo_url'] = photo_url
if photo_size is not None:
data['photo_size'] = photo_size
if photo_width is not None:
data['photo_width'] = photo_width
if photo_height is not None:
data['photo_height'] = photo_height
if need_name is not None:
data['need_name'] = need_name
if need_phone_number is not None:
data['need_phone_number'] = need_phone_number
if need_email is not None:
data['need_email'] = need_email
if need_shipping_address is not None:
data['need_shipping_address'] = need_shipping_address
if is_flexible is not None:
data['is_flexible'] = is_flexible
return url, data
@log
def answer_shipping_query(self,
shipping_query_id,
ok,
shipping_options=None,
error_message=None,
timeout=None,
**kwargs):
"""
If you sent an invoice requesting a shipping address and the parameter is_flexible was
specified, the Bot API will send an Update with a shipping_query field to the bot. Use
this method to reply to shipping queries.
Args:
shipping_query_id (str): Unique identifier for the query to be answered
ok (bool): Specify True if delivery to the specified address is possible and False if
there are any problems (for example, if delivery to the specified address
is not possible)
shipping_options (Optional[List[:class:`telegram.ShippingOption`]]): Required if ok is
True. A list of available shipping options.
error_message (Optional[str]): Required if ok is False. Error message in human readable
form that explains why it is impossible to complete the order (e.g. "Sorry,
delivery to your desired address is unavailable'). Telegram will display this
message to the user.
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
from the server (instead of the one specified during creation of the connection
pool).
**kwargs (dict): Arbitrary keyword arguments.
Returns:
bool: On success, `True` is returned.
Raises:
:class:`telegram.TelegramError`
"""
ok = bool(ok)
if ok and (shipping_options is None or error_message is not None):
raise TelegramError(
'answerShippingQuery: If ok is True, shipping_options '
'should not be empty and there should not be error_message')
if not ok and (shipping_options is not None or error_message is None):
raise TelegramError(
'answerShippingQuery: If ok is False, error_message '
'should not be empty and there should not be shipping_options')
url_ = '{0}/answerShippingQuery'.format(self.base_url)
data = {'shipping_query_id': shipping_query_id, 'ok': ok}
if ok:
data['shipping_options'] = [option.to_dict() for option in shipping_options]
if error_message is not None:
data['error_message'] = error_message
result = self._request.post(url_, data, timeout=timeout)
return result
@log
def answer_pre_checkout_query(self, pre_checkout_query_id, ok,
error_message=None, timeout=None, **kwargs):
"""
If you sent an invoice requesting a shipping address and the parameter is_flexible was
specified, the Bot API will send an Update with a shipping_query field to the bot.
Use this method to reply to shipping queries.
Args:
pre_checkout_query_id (str): Unique identifier for the query to be answered
ok (bool): Specify True if everything is alright (goods are available, etc.) and the
bot is ready to proceed with the order. Use False if there are any problems.
error_message (Optional[str]): Required if ok is False. Error message in human readable
form that explains the reason for failure to proceed with the checkout (e.g.
"Sorry, somebody just bought the last of our amazing black T-shirts while you were
busy filling out your payment details. Please choose a different color or
garment!"). Telegram will display this message to the user.
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
from the server (instead of the one specified during creation of the connection
pool).
**kwargs (dict): Arbitrary keyword arguments.
Returns:
bool: On success, `True` is returned.
Raises:
:class:`telegram.TelegramError`
"""
ok = bool(ok)
if not (ok ^ (error_message is not None)):
raise TelegramError(
'answerPreCheckoutQuery: If ok is True, there should '
'not be error_message; if ok is False, error_message '
'should not be empty')
url_ = '{0}/answerPreCheckoutQuery'.format(self.base_url)
data = {'pre_checkout_query_id': pre_checkout_query_id, 'ok': ok}
if error_message is not None:
data['error_message'] = error_message
result = self._request.post(url_, data, timeout=timeout)
return result
@staticmethod
def de_json(data, bot):
data = super(Bot, Bot).de_json(data, bot)
@@ -1787,6 +2014,7 @@ class Bot(TelegramObject):
sendSticker = send_sticker
sendVideo = send_video
sendVoice = send_voice
sendVideoNote = send_video_note
sendLocation = send_location
sendVenue = send_venue
sendContact = send_contact
@@ -1812,3 +2040,6 @@ class Bot(TelegramObject):
getWebhookInfo = get_webhook_info
setGameScore = set_game_score
getGameHighScores = get_game_high_scores
sendInvoice = send_invoice
answerShippingQuery = answer_shipping_query
answerPreCheckoutQuery = answer_pre_checkout_query
+15 -15
View File
@@ -41,7 +41,7 @@ class Chat(TelegramObject):
username(Optional[str]):
first_name(Optional[str]):
last_name(Optional[str]):
bot (Optional[Bot]): The Bot to use for instance methods
bot (Optional[telegram.Bot]): The Bot to use for instance methods
**kwargs (dict): Arbitrary keyword arguments.
"""
@@ -89,29 +89,29 @@ class Chat(TelegramObject):
return Chat(bot=bot, **data)
def send_action(self, *args, **kwargs):
"""Shortcut for ``bot.sendChatAction(update.message.chat.id, *args, **kwargs)``"""
return self.bot.sendChatAction(self.id, *args, **kwargs)
"""Shortcut for ``bot.send_chat_action(update.message.chat.id, *args, **kwargs)``"""
return self.bot.send_chat_action(self.id, *args, **kwargs)
def leave(self, *args, **kwargs):
"""Shortcut for ``bot.leaveChat(update.message.chat.id, *args, **kwargs)``"""
return self.bot.leaveChat(self.id, *args, **kwargs)
"""Shortcut for ``bot.leave_chat(update.message.chat.id, *args, **kwargs)``"""
return self.bot.leave_chat(self.id, *args, **kwargs)
def get_administrators(self, *args, **kwargs):
"""Shortcut for ``bot.getChatAdministrators(update.message.chat.id, *args, **kwargs)``"""
return self.bot.getChatAdministrators(self.id, *args, **kwargs)
"""Shortcut for ``bot.get_chat_administrators(update.message.chat.id, *args, **kwargs)``"""
return self.bot.get_chat_administrators(self.id, *args, **kwargs)
def get_members_count(self, *args, **kwargs):
"""Shortcut for ``bot.getChatMembersCount(update.message.chat.id, *args, **kwargs)``"""
return self.bot.getChatMembersCount(self.id, *args, **kwargs)
"""Shortcut for ``bot.get_chat_members_count(update.message.chat.id, *args, **kwargs)``"""
return self.bot.get_chat_members_count(self.id, *args, **kwargs)
def get_member(self, *args, **kwargs):
"""Shortcut for ``bot.getChatMember(update.message.chat.id, *args, **kwargs)``"""
return self.bot.getChatMember(self.id, *args, **kwargs)
"""Shortcut for ``bot.get_chat_member(update.message.chat.id, *args, **kwargs)``"""
return self.bot.get_chat_member(self.id, *args, **kwargs)
def kick_member(self, *args, **kwargs):
"""Shortcut for ``bot.kickChatMember(update.message.chat.id, *args, **kwargs)``"""
return self.bot.kickChatMember(self.id, *args, **kwargs)
"""Shortcut for ``bot.kick_chat_member(update.message.chat.id, *args, **kwargs)``"""
return self.bot.kick_chat_member(self.id, *args, **kwargs)
def unban_member(self, *args, **kwargs):
"""Shortcut for ``bot.unbanChatMember(update.message.chat.id, *args, **kwargs)``"""
return self.bot.unbanChatMember(self.id, *args, **kwargs)
"""Shortcut for ``bot.unban_chat_member(update.message.chat.id, *args, **kwargs)``"""
return self.bot.unban_chat_member(self.id, *args, **kwargs)
+2
View File
@@ -31,3 +31,5 @@ class ChatAction(object):
UPLOAD_AUDIO = 'upload_audio'
UPLOAD_DOCUMENT = 'upload_document'
FIND_LOCATION = 'find_location'
RECORD_VIDEO_NOTE = 'record_video_note'
UPLOAD_VIDEO_NOTE = 'upload_video_note'
+4 -1
View File
@@ -33,8 +33,11 @@ from .stringcommandhandler import StringCommandHandler
from .stringregexhandler import StringRegexHandler
from .typehandler import TypeHandler
from .conversationhandler import ConversationHandler
from .precheckoutqueryhandler import PreCheckoutQueryHandler
from .shippingqueryhandler import ShippingQueryHandler
__all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
'ChosenInlineResultHandler', 'CommandHandler', 'Handler', 'InlineQueryHandler',
'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler',
'StringRegexHandler', 'TypeHandler', 'ConversationHandler')
'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
'PreCheckoutQueryHandler', 'ShippingQueryHandler')
+3 -3
View File
@@ -86,9 +86,9 @@ class CommandHandler(Handler):
_str = str # Python 3
if isinstance(command, _str):
self.command = [command]
self.command = [command.lower()]
else:
self.command = command
self.command = [x.lower() for x in command]
self.filters = filters
self.allow_edited = allow_edited
self.pass_args = pass_args
@@ -117,7 +117,7 @@ class CommandHandler(Handler):
else:
res = self.filters(message)
return res and (message.text.startswith('/') and command[0] in self.command
return res and (message.text.startswith('/') and command[0].lower() in self.command
and command[1].lower() == message.bot.username.lower())
else:
return False
+101 -6
View File
@@ -19,6 +19,11 @@
""" This module contains the Filters for use with the MessageHandler class """
from telegram import Chat
try:
str_type = base_string
except NameError:
str_type = str
class BaseFilter(object):
"""Base class for all Message Filters
@@ -209,13 +214,68 @@ class Filters(object):
class _StatusUpdate(BaseFilter):
class _NewChatMembers(BaseFilter):
def filter(self, message):
return bool(message.new_chat_members)
new_chat_members = _NewChatMembers()
class _LeftChatMember(BaseFilter):
def filter(self, message):
return bool(message.left_chat_member)
left_chat_member = _LeftChatMember()
class _NewChatTitle(BaseFilter):
def filter(self, message):
return bool(message.new_chat_title)
new_chat_title = _NewChatTitle()
class _NewChatPhoto(BaseFilter):
def filter(self, message):
return bool(message.new_chat_photo)
new_chat_photo = _NewChatPhoto()
class _DeleteChatPhoto(BaseFilter):
def filter(self, message):
return bool(message.delete_chat_photo)
delete_chat_photo = _DeleteChatPhoto()
class _ChatCreated(BaseFilter):
def filter(self, message):
return bool(message.group_chat_created or message.supergroup_chat_created or
message.channel_chat_created)
chat_created = _ChatCreated()
class _Migrate(BaseFilter):
def filter(self, message):
return bool(message.migrate_from_chat_id or message.migrate_to_chat_id)
migrate = _Migrate()
class _PinnedMessage(BaseFilter):
def filter(self, message):
return bool(message.pinned_message)
pinned_message = _PinnedMessage()
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)
return bool(self.new_chat_members(message) or self.left_chat_member(message) or
self.new_chat_title(message) or self.new_chat_photo(message) or
self.delete_chat_photo(message) or self.chat_created(message) or
self.migrate(message) or self.pinned_message(message))
status_update = _StatusUpdate()
@@ -263,3 +323,38 @@ class Filters(object):
return message.chat.type in [Chat.GROUP, Chat.SUPERGROUP]
group = _Group()
class _Invoice(BaseFilter):
def filter(self, message):
return bool(message.invoice)
invoice = _Invoice()
class _SuccessfulPayment(BaseFilter):
def filter(self, message):
return bool(message.successful_payment)
successful_payment = _SuccessfulPayment()
class language(BaseFilter):
"""
Filters messages to only allow those which are from users with a certain language code.
Note that according to telegrams documentation, every single user does not have the
language_code attribute.
Args:
lang (str|list): Which language code(s) to allow through. This will be matched using
.startswith meaning that 'en' will match both 'en_US' and 'en_GB'
"""
def __init__(self, lang):
if isinstance(lang, str_type):
self.lang = [lang]
else:
self.lang = lang
def filter(self, message):
return message.from_user.language_code and any(
[message.from_user.language_code.startswith(x) for x in self.lang])
+7 -4
View File
@@ -166,7 +166,8 @@ class JobQueue(object):
name (Optional[str]): The name of the new job. Defaults to ``callback.__name__``
Returns:
Job: The new ``Job`` instance that has been added to the job queue.
telegram.ext.jobqueue.Job: The new ``Job`` instance that has been added to the
job queue.
"""
job = Job(callback, repeat=False, context=context, name=name, job_queue=self)
@@ -201,7 +202,8 @@ class JobQueue(object):
name (Optional[str]): The name of the new job. Defaults to ``callback.__name__``
Returns:
Job: The new ``Job`` instance that has been added to the job queue.
telegram.ext.jobqueue.Job: The new ``Job`` instance that has been added to the
job queue.
"""
job = Job(callback,
@@ -228,7 +230,8 @@ class JobQueue(object):
name (Optional[str]): The name of the new job. Defaults to ``callback.__name__``
Returns:
Job: The new ``Job`` instance that has been added to the job queue.
telegram.ext.jobqueue.Job: The new ``Job`` instance that has been added to the
job queue.
"""
job = Job(callback,
@@ -366,7 +369,7 @@ class Job(object):
job runs
repeat (bool): If the job runs periodically or only once
name (str): The name of this job
job_queue (JobQueue): The ``JobQueue`` this job belongs to
job_queue (telegram.ext.JobQueue): The ``JobQueue`` this job belongs to
enabled (bool): Boolean property that decides if this job is currently active
Args:
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the PreCheckoutQueryHandler class """
from telegram import Update
from .handler import Handler
class PreCheckoutQueryHandler(Handler):
"""
Handler class to handle Telegram PreCheckout callback queries.
Args:
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
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,
pass_user_data=False,
pass_chat_data=False):
super(PreCheckoutQueryHandler, self).__init__(
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
def check_update(self, update):
return isinstance(update, Update) and update.pre_checkout_query
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher, update)
return self.callback(dispatcher.bot, update, **optional_args)
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the ShippingQueryHandler class """
from telegram import Update
from .handler import Handler
class ShippingQueryHandler(Handler):
"""
Handler class to handle Telegram shipping callback queries.
Args:
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
pass_update_queue (optional[bool]): If set to ``True``, a keyword argument called
``update_queue`` will be passed to the callback function. It will be the ``Queue``
instance used by the ``Updater`` and ``Dispatcher`` that contains new updates which can
be used to insert updates. Default is ``False``.
pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
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,
pass_user_data=False,
pass_chat_data=False):
super(ShippingQueryHandler, self).__init__(
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
def check_update(self, update):
return isinstance(update, Update) and update.shipping_query
def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher, update)
return self.callback(dispatcher.bot, update, **optional_args)
+1 -1
View File
@@ -27,7 +27,7 @@ class TypeHandler(Handler):
Handler class to handle updates of custom types.
Args:
type (type): The ``type`` of updates this handler should process, as
type (class): The ``type`` of updates this handler should process, as
determined by ``isinstance``
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
+8 -8
View File
@@ -59,9 +59,9 @@ class Updater(object):
base_url (Optional[str]):
workers (Optional[int]): Amount of threads in the thread pool for
functions decorated with @run_async
bot (Optional[Bot]): A pre-initialized bot instance. If a pre-initizlied bot is used, it is
the user's responsibility to create it using a `Request` instance with a large enough
connection pool.
bot (Optional[telegram.Bot]): A pre-initialized bot instance. If a pre-initizlied bot is
used, it is the user's responsibility to create it using a `Request` instance with
a large enough connection pool.
user_sig_handler (Optional[function]): Takes ``signum, frame`` as positional arguments.
This will be called when a signal is received, defaults are (SIGINT, SIGTERM, SIGABRT)
setable with Updater.idle(stop_signals=(signals))
@@ -264,7 +264,7 @@ class Updater(object):
while self.running:
try:
updates = self.bot.getUpdates(
updates = self.bot.get_updates(
self.last_update_id,
timeout=timeout,
read_latency=read_latency,
@@ -367,11 +367,11 @@ class Updater(object):
try:
if clean:
# Disable webhook for cleaning
self.bot.deleteWebhook()
self.bot.delete_webhook()
self._clean_updates()
sleep(1)
self.bot.setWebhook(
self.bot.set_webhook(
url=webhook_url, certificate=cert, allowed_updates=allowed_updates)
except (Unauthorized, InvalidToken):
raise
@@ -390,9 +390,9 @@ class Updater(object):
def _clean_updates(self):
self.logger.debug('Cleaning updates from Telegram server')
updates = self.bot.getUpdates()
updates = self.bot.get_updates()
while updates:
updates = self.bot.getUpdates(updates[-1].update_id + 1)
updates = self.bot.get_updates(updates[-1].update_id + 1)
def stop(self):
"""
+6 -1
View File
@@ -46,7 +46,10 @@ class InlineKeyboardButton(TelegramObject):
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.
be launched when the user presses the button. NOTE: This type of button must always be
the first button in the first row.
pay (Optional[bool]): Specify True, to send a Pay button. NOTE: This type of button must
always be the first button in the first row.
**kwargs (dict): Arbitrary keyword arguments.
"""
@@ -58,6 +61,7 @@ class InlineKeyboardButton(TelegramObject):
switch_inline_query=None,
switch_inline_query_current_chat=None,
callback_game=None,
pay=None,
**kwargs):
# Required
self.text = text
@@ -68,6 +72,7 @@ class InlineKeyboardButton(TelegramObject):
self.switch_inline_query = switch_inline_query
self.switch_inline_query_current_chat = switch_inline_query_current_chat
self.callback_game = callback_game
self.pay = pay
@staticmethod
def de_json(data, bot):
+3 -3
View File
@@ -40,7 +40,7 @@ class InlineQuery(TelegramObject):
query (str):
offset (str):
location (optional[:class:`telegram.Location`]):
bot (Optional[Bot]): The Bot to use for instance methods
bot (Optional[telegram.Bot]): The Bot to use for instance methods
**kwargs (dict): Arbitrary keyword arguments.
"""
@@ -91,5 +91,5 @@ class InlineQuery(TelegramObject):
return data
def answer(self, *args, **kwargs):
"""Shortcut for ``bot.answerInlineQuery(update.inline_query.id, *args, **kwargs)``"""
return self.bot.answerInlineQuery(self.id, *args, **kwargs)
"""Shortcut for ``bot.answer_inline_query(update.inline_query.id, *args, **kwargs)``"""
return self.bot.answer_inline_query(self.id, *args, **kwargs)
+5
View File
@@ -32,6 +32,7 @@ class InlineQueryResultGif(InlineQueryResult):
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.
gif_duration (Optional[int]): Duration 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
@@ -45,6 +46,7 @@ class InlineQueryResultGif(InlineQueryResult):
thumb_url (str):
gif_width (Optional[int]):
gif_height (Optional[int]):
gif_duration (Optional[int]):
title (Optional[str]):
caption (Optional[str]):
reply_markup (Optional[:class:`telegram.InlineKeyboardMarkup`]):
@@ -63,6 +65,7 @@ class InlineQueryResultGif(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
gif_duration=None,
**kwargs):
# Required
@@ -75,6 +78,8 @@ class InlineQueryResultGif(InlineQueryResult):
self.gif_width = gif_width
if gif_height:
self.gif_height = gif_height
if gif_duration:
self.gif_duration = gif_duration
if title:
self.title = title
if caption:
+5
View File
@@ -32,6 +32,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
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.
mpeg4_duration (Optional[int]): Video duration
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
@@ -44,6 +45,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
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.
mpeg4_duration (Optional[int]): Video duration
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
@@ -64,6 +66,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
caption=None,
reply_markup=None,
input_message_content=None,
mpeg4_duration=None,
**kwargs):
# Required
@@ -76,6 +79,8 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
self.mpeg4_width = mpeg4_width
if mpeg4_height:
self.mpeg4_height = mpeg4_height
if mpeg4_duration:
self.mpeg4_duration = mpeg4_duration
if title:
self.title = title
if caption:
+16 -27
View File
@@ -35,7 +35,8 @@ 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')
FILE_TYPES = ('audio', 'document', 'photo', 'sticker', 'video', 'voice', 'certificate',
'video_note')
class InputFile(object):
@@ -45,27 +46,11 @@ class InputFile(object):
self.data = data
self.boundary = choose_boundary()
if 'audio' in data:
self.input_name = 'audio'
self.input_file = data.pop('audio')
elif 'document' in data:
self.input_name = 'document'
self.input_file = data.pop('document')
elif 'photo' in data:
self.input_name = 'photo'
self.input_file = data.pop('photo')
elif 'sticker' in data:
self.input_name = 'sticker'
self.input_file = data.pop('sticker')
elif 'video' in data:
self.input_name = 'video'
self.input_file = data.pop('video')
elif 'voice' in data:
self.input_name = 'voice'
self.input_file = data.pop('voice')
elif 'certificate' in data:
self.input_name = 'certificate'
self.input_file = data.pop('certificate')
for t in FILE_TYPES:
if t in data:
self.input_name = t
self.input_file = data.pop(t)
break
else:
raise TelegramError('Unknown inputfile type')
@@ -84,13 +69,17 @@ class InputFile(object):
if not self.filename or '.' not in self.filename:
self.filename = self.mimetype.replace('/', '.')
except TelegramError:
self.mimetype = mimetypes.guess_type(self.filename)[0] or DEFAULT_MIME_TYPE
if self.filename:
self.mimetype = mimetypes.guess_type(
self.filename)[0] or DEFAULT_MIME_TYPE
else:
self.mimetype = DEFAULT_MIME_TYPE
@property
def headers(self):
"""
Returns:
str:
str
"""
return {'User-agent': USER_AGENT, 'Content-type': self.content_type}
@@ -98,14 +87,14 @@ class InputFile(object):
def content_type(self):
"""
Returns:
str:
str
"""
return 'multipart/form-data; boundary=%s' % self.boundary
def to_form(self):
"""
Returns:
str:
str
"""
form = []
form_boundary = '--' + self.boundary
@@ -133,7 +122,7 @@ class InputFile(object):
def _parse(form):
"""
Returns:
str:
str
"""
if sys.version_info > (3,):
# on Python 3 form needs to be byte encoded
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Invoice."""
from telegram import TelegramObject
class Invoice(TelegramObject):
"""This object contains basic information about an invoice.
Attributes:
title (str): Product name
description (str): Product description
start_parameter (str): Unique bot deep-linking parameter that can
be used to generate this invoice
currency (str): Three-letter ISO 4217 currency code
total_amount (int): Total price in the smallest units of the currency (integer)
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, title, description, start_parameter, currency, total_amount, **kwargs):
self.title = title
self.description = description
self.start_parameter = start_parameter
self.currency = currency
self.total_amount = total_amount
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.Invoice:
"""
if not data:
return None
return Invoice(**data)
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram LabeledPrice."""
from telegram import TelegramObject
class LabeledPrice(TelegramObject):
"""This object represents a portion of the price for goods or services.
Attributes:
label (str): Portion label
amount (int): Price of the product in the smallest units of the currency (integer)
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, label, amount, **kwargs):
self.label = label
self.amount = amount
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.LabeledPrice:
"""
if not data:
return None
return LabeledPrice(**data)
@staticmethod
def de_list(data, bot):
"""
Args:
data (list):
bot (telegram.Bot):
Returns:
List<telegram.PhotoSize>:
"""
if not data:
return []
labeled_prices = list()
for labeled_price in data:
labeled_prices.append(LabeledPrice.de_json(labeled_price, bot))
return labeled_prices
+154 -95
View File
@@ -23,8 +23,10 @@ from datetime import datetime
from time import mktime
from telegram import (Audio, Contact, Document, Chat, Location, PhotoSize, Sticker, TelegramObject,
User, Video, Voice, Venue, MessageEntity, Game)
User, Video, Voice, Venue, MessageEntity, Game, Invoice, SuccessfulPayment)
from telegram.utils.deprecate import warn_deprecate_obj
from telegram.utils.helpers import escape_html, escape_markdown
from telegram.videonote import VideoNote
class Message(TelegramObject):
@@ -34,36 +36,69 @@ class Message(TelegramObject):
* In Python `from` is a reserved word, use `from_user` instead.
Attributes:
message_id (int):
from_user (:class:`telegram.User`):
date (:class:`datetime.datetime`):
forward_from (:class:`telegram.User`):
forward_from_chat (:class:`telegram.Chat`):
forward_from_message_id (int):
forward_date (:class:`datetime.datetime`):
reply_to_message (:class:`telegram.Message`):
edit_date (:class:`datetime.datetime`):
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`):
voice (:class:`telegram.Voice`):
caption (str):
contact (:class:`telegram.Contact`):
location (:class:`telegram.Location`):
new_chat_member (:class:`telegram.User`):
left_chat_member (:class:`telegram.User`):
new_chat_title (str):
new_chat_photo (List[:class:`telegram.PhotoSize`]):
delete_chat_photo (bool):
group_chat_created (bool):
supergroup_chat_created (bool):
migrate_to_chat_id (int):
migrate_from_chat_id (int):
channel_chat_created (bool):
message_id (int): Unique message identifier inside this chat
from_user (:class:`telegram.User`): Sender, can be empty for messages sent to channels
date (:class:`datetime.datetime`): Date the message was sent in Unix time
chat (:class:`telegram.Chat`): Conversation the message belongs to
forward_from (:class:`telegram.User`): For forwarded messages, sender of the original
message
forward_from_chat (:class:`telegram.Chat`): For messages forwarded from a channel,
information about the original channel
forward_from_message_id (int): For forwarded channel posts, identifier of the original
message in the channel
forward_date (:class:`datetime.datetime`): For forwarded messages, date the original
message was sent in Unix time
reply_to_message (:class:`telegram.Message`): For replies, the original message. Note
that the Message object in this field will not contain further reply_to_message
fields even if it itself is a reply.
edit_date (:class:`datetime.datetime`): Date the message was last edited in Unix time
text (str): For text messages, the actual UTF-8 text of the message, 0-4096 characters.
entities (List[:class:`telegram.MessageEntity`]): For text messages, special entities
like usernames, URLs, bot commands, etc. that appear in the text. See
parse_entity and parse_entities methods for how to use properly
video_note (:class:`telegram.VideoNote`): Message is a video note, information about the
video message
audio (:class:`telegram.Audio`): Message is an audio file, information about the file
document (:class:`telegram.Document`): Message is a general file, information about the
file
game (:class:`telegram.Game`):Message is a game, information about the game
photo (List[:class:`telegram.PhotoSize`]): Message is a photo, available sizes of the photo
sticker (:class:`telegram.Sticker`): Message is a sticker, information about the sticker
video (:class:`telegram.Video`): Message is a video, information about the video
voice (:class:`telegram.Voice`): Message is a voice message, information about the file
caption (str): Caption for the document, photo or video, 0-200 characters
contact (:class:`telegram.Contact`): Message is a shared contact, information about the
contact
location (:class:`telegram.Location`): Message is a shared location, information about the
location
new_chat_member (:class:`telegram.User`): A new member was added to the group,
information about them (this member may be the bot itself)
left_chat_member (:class:`telegram.User`): A member was removed from the group,
information about them (this member may be the bot itself)
new_chat_title (str): A chat title was changed to this value
new_chat_photo (List[:class:`telegram.PhotoSize`]): A chat photo was change to this value
delete_chat_photo (bool): Service message: the chat photo was deleted
group_chat_created (bool): Service message: the group has been created
supergroup_chat_created (bool): Service message: the supergroup has been created. This
field can't be received in a message coming through updates, because bot can't be a
member of a supergroup when it is created. It can only be found in reply_to_message
if someone replies to a very first message in a directly created supergroup.
migrate_to_chat_id (int): The group has been migrated to a supergroup with the specified
identifier.
migrate_from_chat_id (int): The supergroup has been migrated from a group with the
specified identifier.
channel_chat_created (bool): Service message: the channel has been created. This field
can't be received in a message coming through updates, because bot can't be a member
of a channel when it is created. It can only be found in reply_to_message if someone
replies to a very first message in a channel.
pinned_message (:class:`telegram.message`): Specified message was pinned. Note that the
Message object in this field will not contain further reply_to_message fields even if
it is itself a reply.
invoice (:class:`telegram.Invoice`): Message is an invoice for a payment, information
about the invoice.
successful_payment (:class:`telegram.SuccessfulPayment`): Message is a service message
about a successful payment, information about the payment.
bot (Optional[telegram.Bot]): The Bot to use for instance methods
Deprecated: 4.0
new_chat_participant (:class:`telegram.User`): Use `new_chat_member`
@@ -71,40 +106,6 @@ class Message(TelegramObject):
left_chat_participant (:class:`telegram.User`): Use `left_chat_member`
instead.
Args:
message_id (int):
from_user (:class:`telegram.User`):
date (:class:`datetime.datetime`):
chat (:class:`telegram.Chat`):
forward_from (Optional[:class:`telegram.User`]):
forward_from_chat (Optional[:class:`telegram.Chat`]):
forward_from_message_id (Optional[int]):
forward_date (Optional[:class:`datetime.datetime`]):
reply_to_message (Optional[:class:`telegram.Message`]):
edit_date (Optional[:class:`datetime.datetime`]):
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`]):
voice (Optional[:class:`telegram.Voice`]):
caption (Optional[str]):
contact (Optional[:class:`telegram.Contact`]):
location (Optional[:class:`telegram.Location`]):
new_chat_member (Optional[:class:`telegram.User`]):
left_chat_member (Optional[:class:`telegram.User`]):
new_chat_title (Optional[str]):
new_chat_photo (Optional[List[:class:`telegram.PhotoSize`]):
delete_chat_photo (Optional[bool]):
group_chat_created (Optional[bool]):
supergroup_chat_created (Optional[bool]):
migrate_to_chat_id (Optional[int]):
migrate_from_chat_id (Optional[int]):
channel_chat_created (Optional[bool]):
bot (Optional[Bot]): The Bot to use for instance methods
"""
def __init__(self,
@@ -130,6 +131,7 @@ class Message(TelegramObject):
location=None,
venue=None,
new_chat_member=None,
new_chat_members=None,
left_chat_member=None,
new_chat_title=None,
new_chat_photo=None,
@@ -141,7 +143,10 @@ class Message(TelegramObject):
channel_chat_created=False,
pinned_message=None,
forward_from_message_id=None,
invoice=None,
successful_payment=None,
bot=None,
video_note=None,
**kwargs):
# Required
self.message_id = int(message_id)
@@ -163,11 +168,13 @@ class Message(TelegramObject):
self.sticker = sticker
self.video = video
self.voice = voice
self.video_note = video_note
self.caption = caption
self.contact = contact
self.location = location
self.venue = venue
self.new_chat_member = new_chat_member
self._new_chat_member = new_chat_member
self.new_chat_members = new_chat_members
self.left_chat_member = left_chat_member
self.new_chat_title = new_chat_title
self.new_chat_photo = new_chat_photo
@@ -179,6 +186,8 @@ class Message(TelegramObject):
self.channel_chat_created = bool(channel_chat_created)
self.pinned_message = pinned_message
self.forward_from_message_id = forward_from_message_id
self.invoice = invoice
self.successful_payment = successful_payment
self.bot = bot
@@ -220,13 +229,17 @@ class Message(TelegramObject):
data['sticker'] = Sticker.de_json(data.get('sticker'), bot)
data['video'] = Video.de_json(data.get('video'), bot)
data['voice'] = Voice.de_json(data.get('voice'), bot)
data['video_note'] = VideoNote.de_json(data.get('video_note'), bot)
data['contact'] = Contact.de_json(data.get('contact'), bot)
data['location'] = Location.de_json(data.get('location'), bot)
data['venue'] = Venue.de_json(data.get('venue'), bot)
data['new_chat_member'] = User.de_json(data.get('new_chat_member'), bot)
data['new_chat_members'] = User.de_list(data.get('new_chat_members'), bot)
data['left_chat_member'] = User.de_json(data.get('left_chat_member'), bot)
data['new_chat_photo'] = PhotoSize.de_list(data.get('new_chat_photo'), bot)
data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot)
data['invoice'] = Invoice.de_json(data.get('invoice'), bot)
data['successful_payment'] = SuccessfulPayment.de_json(data.get('successful_payment'), bot)
return Message(bot=bot, **data)
@@ -257,6 +270,9 @@ class Message(TelegramObject):
data['entities'] = [e.to_dict() for e in self.entities]
if self.new_chat_photo:
data['new_chat_photo'] = [p.to_dict() for p in self.new_chat_photo]
data['new_chat_member'] = data.pop('_new_chat_member', None)
if self.new_chat_members:
data['new_chat_members'] = [u.to_dict() for u in self.new_chat_members]
return data
@@ -312,7 +328,7 @@ class Message(TelegramObject):
def reply_text(self, *args, **kwargs):
"""
Shortcut for ``bot.sendMessage(update.message.chat_id, *args, **kwargs)``
Shortcut for ``bot.send_message(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the message is sent as an actual reply to
@@ -321,11 +337,11 @@ class Message(TelegramObject):
"""
self._quote(kwargs)
return self.bot.sendMessage(self.chat_id, *args, **kwargs)
return self.bot.send_message(self.chat_id, *args, **kwargs)
def reply_photo(self, *args, **kwargs):
"""
Shortcut for ``bot.sendPhoto(update.message.chat_id, *args, **kwargs)``
Shortcut for ``bot.send_photo(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the photo is sent as an actual reply to
@@ -338,11 +354,11 @@ class Message(TelegramObject):
"""
self._quote(kwargs)
return self.bot.sendPhoto(self.chat_id, *args, **kwargs)
return self.bot.send_photo(self.chat_id, *args, **kwargs)
def reply_audio(self, *args, **kwargs):
"""
Shortcut for ``bot.sendAudio(update.message.chat_id, *args, **kwargs)``
Shortcut for ``bot.send_audio(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the audio is sent as an actual reply to
@@ -355,11 +371,11 @@ class Message(TelegramObject):
"""
self._quote(kwargs)
return self.bot.sendAudio(self.chat_id, *args, **kwargs)
return self.bot.send_audio(self.chat_id, *args, **kwargs)
def reply_document(self, *args, **kwargs):
"""
Shortcut for ``bot.sendDocument(update.message.chat_id, *args, **kwargs)``
Shortcut for ``bot.send_document(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the document is sent as an actual reply to
@@ -372,11 +388,11 @@ class Message(TelegramObject):
"""
self._quote(kwargs)
return self.bot.sendDocument(self.chat_id, *args, **kwargs)
return self.bot.send_document(self.chat_id, *args, **kwargs)
def reply_sticker(self, *args, **kwargs):
"""
Shortcut for ``bot.sendSticker(update.message.chat_id, *args, **kwargs)``
Shortcut for ``bot.send_sticker(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the sticker is sent as an actual reply to
@@ -389,11 +405,11 @@ class Message(TelegramObject):
"""
self._quote(kwargs)
return self.bot.sendSticker(self.chat_id, *args, **kwargs)
return self.bot.send_sticker(self.chat_id, *args, **kwargs)
def reply_video(self, *args, **kwargs):
"""
Shortcut for ``bot.sendVideo(update.message.chat_id, *args, **kwargs)``
Shortcut for ``bot.send_video(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the video is sent as an actual reply to
@@ -406,11 +422,28 @@ class Message(TelegramObject):
"""
self._quote(kwargs)
return self.bot.sendVideo(self.chat_id, *args, **kwargs)
return self.bot.send_video(self.chat_id, *args, **kwargs)
def reply_video_note(self, *args, **kwargs):
"""
Shortcut for ``bot.send_video_note(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the video is sent as an actual reply to
this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
Returns:
:class:`telegram.Message`: On success, instance representing the message posted.
"""
self._quote(kwargs)
return self.bot.send_video_note(self.chat_id, *args, **kwargs)
def reply_voice(self, *args, **kwargs):
"""
Shortcut for ``bot.sendVoice(update.message.chat_id, *args, **kwargs)``
Shortcut for ``bot.send_voice(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the voice is sent as an actual reply to
@@ -423,11 +456,11 @@ class Message(TelegramObject):
"""
self._quote(kwargs)
return self.bot.sendVoice(self.chat_id, *args, **kwargs)
return self.bot.send_voice(self.chat_id, *args, **kwargs)
def reply_location(self, *args, **kwargs):
"""
Shortcut for ``bot.sendLocation(update.message.chat_id, *args, **kwargs)``
Shortcut for ``bot.send_location(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the location is sent as an actual reply to
@@ -440,11 +473,11 @@ class Message(TelegramObject):
"""
self._quote(kwargs)
return self.bot.sendLocation(self.chat_id, *args, **kwargs)
return self.bot.send_location(self.chat_id, *args, **kwargs)
def reply_venue(self, *args, **kwargs):
"""
Shortcut for ``bot.sendVenue(update.message.chat_id, *args, **kwargs)``
Shortcut for ``bot.send_venue(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the venue is sent as an actual reply to
@@ -457,11 +490,11 @@ class Message(TelegramObject):
"""
self._quote(kwargs)
return self.bot.sendVenue(self.chat_id, *args, **kwargs)
return self.bot.send_venue(self.chat_id, *args, **kwargs)
def reply_contact(self, *args, **kwargs):
"""
Shortcut for ``bot.sendContact(update.message.chat_id, *args, **kwargs)``
Shortcut for ``bot.send_contact(update.message.chat_id, *args, **kwargs)``
Keyword Args:
quote (Optional[bool]): If set to ``True``, the contact is sent as an actual reply to
@@ -474,12 +507,12 @@ class Message(TelegramObject):
"""
self._quote(kwargs)
return self.bot.sendContact(self.chat_id, *args, **kwargs)
return self.bot.send_contact(self.chat_id, *args, **kwargs)
def forward(self, chat_id, disable_notification=False):
"""Shortcut for
>>> bot.forwardMessage(chat_id=chat_id,
>>> bot.forward_message(chat_id=chat_id,
... from_chat_id=update.message.chat_id,
... disable_notification=disable_notification,
... message_id=update.message.message_id)
@@ -488,7 +521,7 @@ class Message(TelegramObject):
:class:`telegram.Message`: On success, instance representing the message forwarded.
"""
return self.bot.forwardMessage(
return self.bot.forward_message(
chat_id=chat_id,
from_chat_id=self.chat_id,
disable_notification=disable_notification,
@@ -498,7 +531,7 @@ class Message(TelegramObject):
"""
Shortcut for
>>> bot.editMessageText(chat_id=message.chat_id,
>>> bot.edit_message_text(chat_id=message.chat_id,
... message_id=message.message_id,
... *args, **kwargs)
@@ -515,7 +548,7 @@ class Message(TelegramObject):
"""
Shortcut for
>>> bot.editMessageCaption(chat_id=message.chat_id,
>>> bot.edit_message_caption(chat_id=message.chat_id,
... message_id=message.message_id,
... *args, **kwargs)
@@ -531,7 +564,7 @@ class Message(TelegramObject):
"""
Shortcut for
>>> bot.editReplyMarkup(chat_id=message.chat_id,
>>> bot.edit_message_reply_markup(chat_id=message.chat_id,
... message_id=message.message_id,
... *args, **kwargs)
@@ -628,6 +661,9 @@ class Message(TelegramObject):
"""
entities = self.parse_entities()
message_text = self.text
if not sys.maxunicode == 0xffff:
message_text = message_text.encode('utf-16-le')
markdown_text = ''
last_offset = 0
@@ -647,10 +683,18 @@ class Message(TelegramObject):
else:
insert = text
markdown_text += escape_html(message_text[last_offset:entity.offset]) + insert
if sys.maxunicode == 0xffff:
markdown_text += escape_html(message_text[last_offset:entity.offset]) + insert
else:
markdown_text += escape_html(message_text[last_offset * 2:entity.offset * 2]
.decode('utf-16-le')) + insert
last_offset = entity.offset + entity.length
markdown_text += escape_html(message_text[last_offset:])
if sys.maxunicode == 0xffff:
markdown_text += escape_html(message_text[last_offset:])
else:
markdown_text += escape_html(message_text[last_offset * 2:].decode('utf-16-le'))
return markdown_text
@property
@@ -667,6 +711,9 @@ class Message(TelegramObject):
"""
entities = self.parse_entities()
message_text = self.text
if not sys.maxunicode == 0xffff:
message_text = message_text.encode('utf-16-le')
markdown_text = ''
last_offset = 0
@@ -685,9 +732,21 @@ class Message(TelegramObject):
insert = '```' + text + '```'
else:
insert = text
if sys.maxunicode == 0xffff:
markdown_text += escape_markdown(message_text[last_offset:entity.offset]) + insert
else:
markdown_text += escape_markdown(message_text[last_offset * 2:entity.offset * 2]
.decode('utf-16-le')) + insert
markdown_text += escape_markdown(message_text[last_offset:entity.offset]) + insert
last_offset = entity.offset + entity.length
markdown_text += escape_markdown(message_text[last_offset:])
if sys.maxunicode == 0xffff:
markdown_text += escape_markdown(message_text[last_offset:])
else:
markdown_text += escape_markdown(message_text[last_offset * 2:].decode('utf-16-le'))
return markdown_text
@property
def new_chat_member(self):
warn_deprecate_obj('new_chat_member', 'new_chat_members')
return self._new_chat_member
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram OrderInfo."""
from telegram import TelegramObject, ShippingAddress
class OrderInfo(TelegramObject):
"""This object represents information about an order.
Attributes:
name (Optional[str]): User name
phone_number (Optional[str]): User's phone number
email (Optional[str]): User email
shipping_address (Optional[:class:`telegram.ShippingAddress`]): User shipping address
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, name=None, phone_number=None, email=None, shipping_address=None, **kwargs):
self.name = name
self.phone_number = phone_number
self.email = email
self.shipping_address = shipping_address
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.OrderInfo:
"""
if not data:
return OrderInfo()
data = super(OrderInfo, OrderInfo).de_json(data, bot)
data['shipping_address'] = ShippingAddress.de_json(data.get('shipping_address'), bot)
return OrderInfo(**data)
+101
View File
@@ -0,0 +1,101 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram PreCheckoutQuery."""
from telegram import TelegramObject, User, OrderInfo
class PreCheckoutQuery(TelegramObject):
"""This object contains information about an incoming pre-checkout query.
Note:
* In Python `from` is a reserved word, use `from_user` instead.
Attributes:
id (str): Unique query identifier
from_user (:class:`telegram.User`): User who sent the query
currency (str): Three-letter ISO 4217 currency code
total_amount (int): Total price in the smallest units of the currency (integer)
invoice_payload (str): Bot specified invoice payload
shipping_option_id (Optional[str]): Identifier of the shipping option chosen by the user
order_info (Optional[:class:`telegram.OrderInfo`]): Order info provided by the user
bot (Optional[Bot]): The Bot to use for instance methods
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
id,
from_user,
currency,
total_amount,
invoice_payload,
shipping_option_id=None,
order_info=None,
bot=None,
**kwargs):
self.id = id
self.from_user = from_user
self.currency = currency
self.total_amount = total_amount
self.invoice_payload = invoice_payload
self.shipping_option_id = shipping_option_id
self.order_info = order_info
self.bot = bot
self._id_attrs = (self.id,)
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.PreCheckoutQuery:
"""
if not data:
return None
data = super(PreCheckoutQuery, PreCheckoutQuery).de_json(data, bot)
data['from_user'] = User.de_json(data.pop('from'), bot)
data['order_info'] = OrderInfo.de_json(data.get('order_info'), bot)
return PreCheckoutQuery(**data)
def to_dict(self):
"""
Returns:
dict:
"""
data = super(PreCheckoutQuery, self).to_dict()
data['from'] = data.pop('from_user', None)
return data
def answer(self, *args, **kwargs):
"""
Shortcut for
``bot.answer_pre_checkout_query(update.pre_checkout_query.id, *args, **kwargs)``
"""
return self.bot.answer_pre_checkout_query(self.id, *args, **kwargs)
+5 -3
View File
@@ -31,9 +31,11 @@ class ReplyKeyboardRemove(ReplyMarkup):
Args:
selective (Optional[bool]): Use this parameter if you want to remove the keyboard for
specific users only. Targets:
1) users that are @mentioned in the text of the Message object;
2) if the bot's message is a reply (has reply_to_message_id), sender of the
original message.
- users that are @mentioned in the text of the Message object
- if the bot's message is a reply (has reply_to_message_id), sender of the
original message.
**kwargs: Arbitrary keyword arguments.
"""
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ShippingAddress."""
from telegram import TelegramObject
class ShippingAddress(TelegramObject):
"""This object represents a Telegram ShippingAddress.
Attributes:
country_code (str): ISO 3166-1 alpha-2 country code
state (str): State, if applicable
city (str): City
street_line1 (str): First line for the address
street_line2 (str): Second line for the address
post_code (str): Address post code
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, country_code, state, city, street_line1, street_line2, post_code, **kwargs):
self.country_code = country_code
self.state = state
self.city = city
self.street_line1 = street_line1
self.street_line2 = street_line2
self.post_code = post_code
self._id_attrs = (self.country_code, self.state, self.city, self.street_line1,
self.street_line2, self.post_code)
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.ShippingAddress:
"""
if not data:
return None
return ShippingAddress(**data)
+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ShippingOption."""
from telegram import TelegramObject, LabeledPrice
class ShippingOption(TelegramObject):
"""This object represents one shipping option.
Note:
* In Python `from` is a reserved word, use `from_user` instead.
Attributes:
id (str): Shipping option identifier
title (str): Option title
prices (List[:class:`telegram.LabeledPrice`]): List of price portions
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, id, title, prices, **kwargs):
self.id = id
self.title = title
self.prices = prices
self._id_attrs = (self.id,)
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.ShippingOption:
"""
if not data:
return None
data = super(ShippingOption, ShippingOption).de_json(data, bot)
data['prices'] = LabeledPrice.de_list(data.get('prices'), bot)
return ShippingOption(**data)
def to_dict(self):
"""
Returns:
dict:
"""
data = super(ShippingOption, self).to_dict()
data['prices'] = [p.to_dict() for p in self.prices]
return data
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram ShippingQuery."""
from telegram import TelegramObject, User, ShippingAddress
class ShippingQuery(TelegramObject):
"""This object contains information about an incoming shipping query.
Note:
* In Python `from` is a reserved word, use `from_user` instead.
Attributes:
id (str): Unique query identifier
from_user (:class:`telegram.User`): User who sent the query
invoice_payload (str): Bot specified invoice payload
shipping_address (:class:`telegram.ShippingQuery`): User specified shipping address
bot (Optional[Bot]): The Bot to use for instance methods
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self, id, from_user, invoice_payload, shipping_address, bot=None, **kwargs):
self.id = id
self.from_user = from_user
self.invoice_payload = invoice_payload
self.shipping_address = shipping_address
self.bot = bot
self._id_attrs = (self.id,)
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.ShippingQuery:
"""
if not data:
return None
data = super(ShippingQuery, ShippingQuery).de_json(data, bot)
data['from_user'] = User.de_json(data.pop('from'), bot)
data['shipping_address'] = ShippingAddress.de_json(data.get('shipping_address'), bot)
return ShippingQuery(**data)
def to_dict(self):
"""
Returns:
dict:
"""
data = super(ShippingQuery, self).to_dict()
data['from'] = data.pop('from_user', None)
return data
def answer(self, *args, **kwargs):
"""Shortcut for ``bot.answer_shipping_query(update.shipping_query.id, *args, **kwargs)``"""
return self.bot.answer_shipping_query(self.id, *args, **kwargs)
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram SuccessfulPayment."""
from telegram import TelegramObject, OrderInfo
class SuccessfulPayment(TelegramObject):
"""This object contains basic information about a successful payment.
Note:
* In Python `from` is a reserved word, use `from_user` instead.
Attributes:
currency (str): Three-letter ISO 4217 currency code
total_amount (int): Total price in the smallest units of the currency (integer)
invoice_payload (str): Bot specified invoice payload
telegram_payment_charge_id (str): Telegram payment identifier
provider_payment_charge_id (str): Provider payment identifier
shipping_option_id (Optional[str]): Identifier of the shipping option chosen by the user
order_info (Optional[:class:`telegram.OrderInfo`]): Order info provided by the user
**kwargs (dict): Arbitrary keyword arguments.
"""
def __init__(self,
currency,
total_amount,
invoice_payload,
telegram_payment_charge_id,
provider_payment_charge_id,
shipping_option_id=None,
order_info=None,
**kwargs):
self.currency = currency
self.total_amount = total_amount
self.invoice_payload = invoice_payload
self.shipping_option_id = shipping_option_id
self.order_info = order_info
self.telegram_payment_charge_id = telegram_payment_charge_id
self.provider_payment_charge_id = provider_payment_charge_id
self._id_attrs = (self.telegram_payment_charge_id, self.provider_payment_charge_id)
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.SuccessfulPayment:
"""
if not data:
return None
data = super(SuccessfulPayment, SuccessfulPayment).de_json(data, bot)
data['order_info'] = OrderInfo.de_json(data.get('order_info'), bot)
return SuccessfulPayment(**data)
+19 -1
View File
@@ -18,7 +18,8 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram Update."""
from telegram import (Message, TelegramObject, InlineQuery, ChosenInlineResult, CallbackQuery)
from telegram import (Message, TelegramObject, InlineQuery, ChosenInlineResult,
CallbackQuery, ShippingQuery, PreCheckoutQuery)
class Update(TelegramObject):
@@ -38,6 +39,9 @@ class Update(TelegramObject):
text, photo, sticker, etc.
edited_channel_post (Optional[:class:`telegram.Message`]): New version of a channel post
that is known to the bot and was edited.
shipping_query (:class:`telegram.ShippingQuery`): New incoming shipping query.
pre_checkout_query (:class:`telegram.PreCheckoutQuery`): New incoming pre-checkout query.
Args:
update_id (int):
@@ -48,6 +52,8 @@ class Update(TelegramObject):
callback_query (Optional[:class:`telegram.CallbackQuery`]):
channel_post (Optional[:class:`telegram.Message`]):
edited_channel_post (Optional[:class:`telegram.Message`]):
shipping_query (Optional[:class:`telegram.ShippingQuery`]):
pre_checkout_query (Optional[:class:`telegram.PreCheckoutQuery`]):
**kwargs: Arbitrary keyword arguments.
"""
@@ -61,6 +67,8 @@ class Update(TelegramObject):
callback_query=None,
channel_post=None,
edited_channel_post=None,
shipping_query=None,
pre_checkout_query=None,
**kwargs):
# Required
self.update_id = int(update_id)
@@ -70,6 +78,8 @@ class Update(TelegramObject):
self.inline_query = inline_query
self.chosen_inline_result = chosen_inline_result
self.callback_query = callback_query
self.shipping_query = shipping_query
self.pre_checkout_query = pre_checkout_query
self.channel_post = channel_post
self.edited_channel_post = edited_channel_post
@@ -100,6 +110,8 @@ class Update(TelegramObject):
data['chosen_inline_result'] = ChosenInlineResult.de_json(
data.get('chosen_inline_result'), bot)
data['callback_query'] = CallbackQuery.de_json(data.get('callback_query'), bot)
data['shipping_query'] = ShippingQuery.de_json(data.get('shipping_query'), bot)
data['pre_checkout_query'] = PreCheckoutQuery.de_json(data.get('pre_checkout_query'), bot)
data['channel_post'] = Message.de_json(data.get('channel_post'), bot)
data['edited_channel_post'] = Message.de_json(data.get('edited_channel_post'), bot)
@@ -132,6 +144,12 @@ class Update(TelegramObject):
elif self.callback_query:
user = self.callback_query.from_user
elif self.shipping_query:
user = self.shipping_query.from_user
elif self.pre_checkout_query:
user = self.pre_checkout_query.from_user
self._effective_user = user
return user
+36 -13
View File
@@ -26,22 +26,24 @@ class User(TelegramObject):
"""This object represents a Telegram User.
Attributes:
id (int):
first_name (str):
last_name (str):
username (str):
type (str):
id (int): Unique identifier for this user or bot
first_name (str): User's or bot's first name
last_name (str): User's or bot's last name
username (str): User's or bot's username
language_code (str): IETF language tag of the user's language
type (str): Deprecated
Args:
id (int):
first_name (str):
id (int): Unique identifier for this user or bot
first_name (str): User's or bot's first name
**kwargs: Arbitrary keyword arguments.
Keyword Args:
type (Optional[str]):
last_name (Optional[str]):
username (Optional[str]):
bot (Optional[Bot]): The Bot to use for instance methods
type (Optional[str]): Deprecated
last_name (Optional[str]): User's or bot's last name
username (Optional[str]): User's or bot's username
language_code (Optional[str]): IETF language tag of the user's language
bot (Optional[telegram.Bot]): The Bot to use for instance methods
"""
def __init__(self,
@@ -50,6 +52,7 @@ class User(TelegramObject):
type=None,
last_name=None,
username=None,
language_code=None,
bot=None,
**kwargs):
# Required
@@ -59,6 +62,7 @@ class User(TelegramObject):
self.type = type
self.last_name = last_name
self.username = username
self.language_code = language_code
self.bot = bot
@@ -92,6 +96,25 @@ class User(TelegramObject):
def get_profile_photos(self, *args, **kwargs):
"""
Shortcut for ``bot.getUserProfilePhotos(update.message.from_user.id, *args, **kwargs)``
Shortcut for ``bot.get_user_profile_photos(update.message.from_user.id, *args, **kwargs)``
"""
return self.bot.getUserProfilePhotos(self.id, *args, **kwargs)
return self.bot.get_user_profile_photos(self.id, *args, **kwargs)
@staticmethod
def de_list(data, bot):
"""
Args:
data (list):
bot (telegram.Bot):
Returns:
List<telegram.User>:
"""
if not data:
return []
users = list()
for user in data:
users.append(User.de_json(user, bot))
return users
+12 -2
View File
@@ -21,8 +21,18 @@
import warnings
def warn_deprecate_obj(old, new):
warnings.warn('{0} is being deprecated, please use {1} from now on'.format(old, new))
# We use our own DeprecationWarning since they are muted by default and "UserWarning" makes it
# seem like it's the user that issued the warning
# We name it something else so that you don't get confused when you attempt to suppress it
class TelegramDeprecationWarning(Warning):
pass
def warn_deprecate_obj(old, new, stacklevel=3):
warnings.warn(
'{0} is being deprecated, please use {1} from now on.'.format(old, new),
category=TelegramDeprecationWarning,
stacklevel=stacklevel)
def deprecate(func, old, new):
+6
View File
@@ -18,9 +18,14 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the Promise class """
import logging
from threading import Event
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
class Promise(object):
"""A simple Promise implementation for the run_async decorator"""
@@ -37,6 +42,7 @@ class Promise(object):
self._result = self.pooled_function(*self.args, **self.kwargs)
except Exception as exc:
logger.exception('An uncaught error was raised while running the promise')
self._exception = exc
finally:
+14 -8
View File
@@ -20,6 +20,7 @@
import os
import socket
import logging
import warnings
try:
import ujson as json
@@ -27,10 +28,15 @@ except ImportError:
import json
import certifi
import urllib3
import urllib3.contrib.appengine
from urllib3.connection import HTTPConnection
from urllib3.util.timeout import Timeout
try:
import telegram.vendor.ptb_urllib3.urllib3 as urllib3
import telegram.vendor.ptb_urllib3.urllib3.contrib.appengine as appengine
from telegram.vendor.ptb_urllib3.urllib3.connection import HTTPConnection
from telegram.vendor.ptb_urllib3.urllib3.util.timeout import Timeout
except ImportError:
warnings.warn("python-telegram-bot wasn't properly installed. Please refer to README.rst on "
"how to properly install.")
raise
from telegram import (InputFile, TelegramError)
from telegram.error import (Unauthorized, NetworkError, TimedOut, BadRequest, ChatMigrated,
@@ -90,16 +96,16 @@ class Request(object):
proxy_url = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy')
if not proxy_url:
if urllib3.contrib.appengine.is_appengine_sandbox():
if appengine.is_appengine_sandbox():
# Use URLFetch service if running in App Engine
mgr = urllib3.contrib.appengine.AppEngineManager()
mgr = appengine.AppEngineManager()
else:
mgr = urllib3.PoolManager(**kwargs)
else:
kwargs.update(urllib3_proxy_kwargs)
if proxy_url.startswith('socks'):
try:
from urllib3.contrib.socks import SOCKSProxyManager
from telegram.vendor.ptb_urllib3.urllib3.contrib.socks import SOCKSProxyManager
except ImportError:
raise RuntimeError('PySocks is missing')
mgr = SOCKSProxyManager(proxy_url, **kwargs)
@@ -218,7 +224,7 @@ class Request(object):
"""Request an URL.
Args:
url (str): The web location we want to retrieve.
data (dict[str, str]): A dict of key/value pairs. Note: On py2.7 value is unicode.
data (dict[str, str|int]): A dict of key/value pairs. Note: On py2.7 value is unicode.
timeout (Optional[int|float]): If this value is specified, use it as the read timeout
from the server (instead of the one specified during creation of the connection
pool).
View File
+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__ = '6.0.0'
__version__ = '6.1.0'
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents a Telegram VideoNote."""
from telegram import PhotoSize, TelegramObject
class VideoNote(TelegramObject):
"""This object represents a Telegram VideoNote.
Attributes:
file_id (str): Unique identifier for this file
length (int): Video width and height as defined by sender
duration (int): Duration of the video in seconds as defined by sender
thumb (Optional[:class:`telegram.PhotoSize`]): Video thumbnail
file_size (Optional[int]): File size
"""
def __init__(self, file_id, length, duration, thumb=None, file_size=None, **kwargs):
# Required
self.file_id = str(file_id)
self.length = int(length)
self.duration = int(duration)
# Optionals
self.thumb = thumb
self.file_size = file_size
self._id_attrs = (self.file_id,)
@staticmethod
def de_json(data, bot):
"""
Args:
data (dict):
bot (telegram.Bot):
Returns:
telegram.VideoNote:
"""
if not data:
return None
data = super(VideoNote, VideoNote).de_json(data, bot)
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
return VideoNote(**data)
+2
View File
@@ -44,6 +44,8 @@ class BaseTest(object):
self._channel_id = os.environ.get('CHANNEL_ID', '@pythontelegrambottests')
self._bot = bot
self._chat_id = chat_id
self._payment_provider_token = os.environ.get('PAYMENT_PROVIDER_TOKEN',
'284685063:TEST:ZGJlMmQxZDI3ZTc3')
@staticmethod
def is_json(string):
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

+20 -1
View File
@@ -31,6 +31,7 @@ from flaky import flaky
sys.path.append('.')
import telegram
from telegram.utils.request import Request
from telegram.error import BadRequest
from tests.base import BaseTest, timeout
@@ -317,7 +318,7 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertEqual(text, fwdmsg.text)
self.assertEqual(fwdmsg.forward_from_message_id, msg.message_id)
@flaky(3, 1)
@flaky(20, 1)
@timeout(10)
def test_set_webhook_get_webhook_info(self):
url = 'https://python-telegram-bot.org/test/webhook'
@@ -467,6 +468,24 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertEqual(name, message.contact.first_name)
self.assertEqual(last, message.contact.last_name)
def test_timeout_propagation(self):
class OkException(Exception):
pass
class MockRequest(Request):
def post(self, url, data, timeout=None):
raise OkException(timeout)
_request = self._bot._request
self._bot._request = MockRequest()
timeout = 500
with self.assertRaises(OkException) as ok:
self._bot.send_photo(self._chat_id, open('tests/data/telegram.jpg'), timeout=timeout)
self.assertEqual(ok.exception.args[0], timeout)
self._bot._request = _request
if __name__ == '__main__':
unittest.main()
+51 -2
View File
@@ -118,51 +118,74 @@ class FiltersTest(BaseTest, unittest.TestCase):
self.message.game = None
self.assertFalse(Filters.game(self.message))
def test_filters_successful_payment(self):
self.message.successful_payment = 'test'
self.assertTrue(Filters.successful_payment(self.message))
self.message.successful_payment = None
self.assertFalse(Filters.successful_payment(self.message))
def test_filters_invoice(self):
self.message.invoice = 'test'
self.assertTrue(Filters.invoice(self.message))
self.message.invoice = None
self.assertFalse(Filters.invoice(self.message))
def test_filters_status_update(self):
self.assertFalse(Filters.status_update(self.message))
self.message.new_chat_member = 'test'
self.message.new_chat_members = ['test']
self.assertTrue(Filters.status_update(self.message))
self.message.new_chat_member = None
self.assertTrue(Filters.status_update.new_chat_members(self.message))
self.message.new_chat_members = None
self.message.left_chat_member = 'test'
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.left_chat_member(self.message))
self.message.left_chat_member = None
self.message.new_chat_title = 'test'
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.new_chat_title(self.message))
self.message.new_chat_title = ''
self.message.new_chat_photo = 'test'
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.new_chat_photo(self.message))
self.message.new_chat_photo = None
self.message.delete_chat_photo = True
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.delete_chat_photo(self.message))
self.message.delete_chat_photo = False
self.message.group_chat_created = True
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.chat_created(self.message))
self.message.group_chat_created = False
self.message.supergroup_chat_created = True
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.chat_created(self.message))
self.message.supergroup_chat_created = False
self.message.migrate_to_chat_id = 100
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.migrate(self.message))
self.message.migrate_to_chat_id = 0
self.message.migrate_from_chat_id = 100
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.migrate(self.message))
self.message.migrate_from_chat_id = 0
self.message.channel_chat_created = True
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.chat_created(self.message))
self.message.channel_chat_created = False
self.message.pinned_message = 'test'
self.assertTrue(Filters.status_update(self.message))
self.assertTrue(Filters.status_update.pinned_message(self.message))
self.message.pinned_message = None
def test_entities_filter(self):
@@ -274,6 +297,32 @@ class FiltersTest(BaseTest, unittest.TestCase):
with self.assertRaises(NotImplementedError):
(custom & Filters.text)(self.message)
def test_language_filter_single(self):
self.message.from_user.language_code = 'en_US'
self.assertTrue((Filters.language('en_US'))(self.message))
self.assertTrue((Filters.language('en'))(self.message))
self.assertFalse((Filters.language('en_GB'))(self.message))
self.assertFalse((Filters.language('da'))(self.message))
self.message.from_user.language_code = 'en_GB'
self.assertFalse((Filters.language('en_US'))(self.message))
self.assertTrue((Filters.language('en'))(self.message))
self.assertTrue((Filters.language('en_GB'))(self.message))
self.assertFalse((Filters.language('da'))(self.message))
self.message.from_user.language_code = 'da'
self.assertFalse((Filters.language('en_US'))(self.message))
self.assertFalse((Filters.language('en'))(self.message))
self.assertFalse((Filters.language('en_GB'))(self.message))
self.assertTrue((Filters.language('da'))(self.message))
def test_language_filter_multiple(self):
f = Filters.language(['en_US', 'da'])
self.message.from_user.language_code = 'en_US'
self.assertTrue(f(self.message))
self.message.from_user.language_code = 'en_GB'
self.assertFalse(f(self.message))
self.message.from_user.language_code = 'da'
self.assertTrue(f(self.message))
if __name__ == '__main__':
unittest.main()
+3
View File
@@ -37,6 +37,7 @@ class InlineQueryResultGifTest(BaseTest, unittest.TestCase):
self.gif_url = 'gif url'
self.gif_width = 10
self.gif_height = 15
self.gif_duration = 1
self.thumb_url = 'thumb url'
self.title = 'title'
self.caption = 'caption'
@@ -50,6 +51,7 @@ class InlineQueryResultGifTest(BaseTest, unittest.TestCase):
'gif_url': self.gif_url,
'gif_width': self.gif_width,
'gif_height': self.gif_height,
'gif_duration': self.gif_duration,
'thumb_url': self.thumb_url,
'title': self.title,
'caption': self.caption,
@@ -65,6 +67,7 @@ class InlineQueryResultGifTest(BaseTest, unittest.TestCase):
self.assertEqual(gif.gif_url, self.gif_url)
self.assertEqual(gif.gif_width, self.gif_width)
self.assertEqual(gif.gif_height, self.gif_height)
self.assertEqual(gif.gif_duration, self.gif_duration)
self.assertEqual(gif.thumb_url, self.thumb_url)
self.assertEqual(gif.title, self.title)
self.assertEqual(gif.caption, self.caption)
+3
View File
@@ -37,6 +37,7 @@ class InlineQueryResultMpeg4GifTest(BaseTest, unittest.TestCase):
self.mpeg4_url = 'mpeg4 url'
self.mpeg4_width = 10
self.mpeg4_height = 15
self.mpeg4_duration = 1
self.thumb_url = 'thumb url'
self.title = 'title'
self.caption = 'caption'
@@ -50,6 +51,7 @@ class InlineQueryResultMpeg4GifTest(BaseTest, unittest.TestCase):
'mpeg4_url': self.mpeg4_url,
'mpeg4_width': self.mpeg4_width,
'mpeg4_height': self.mpeg4_height,
'mpeg4_duration': self.mpeg4_duration,
'thumb_url': self.thumb_url,
'title': self.title,
'caption': self.caption,
@@ -65,6 +67,7 @@ class InlineQueryResultMpeg4GifTest(BaseTest, unittest.TestCase):
self.assertEqual(mpeg4.mpeg4_url, self.mpeg4_url)
self.assertEqual(mpeg4.mpeg4_width, self.mpeg4_width)
self.assertEqual(mpeg4.mpeg4_height, self.mpeg4_height)
self.assertEqual(mpeg4.mpeg4_duration, self.mpeg4_duration)
self.assertEqual(mpeg4.thumb_url, self.thumb_url)
self.assertEqual(mpeg4.title, self.title)
self.assertEqual(mpeg4.caption, self.caption)
+121
View File
@@ -0,0 +1,121 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents Tests for Telegram
Invoice"""
import sys
import unittest
from flaky import flaky
sys.path.append('.')
import telegram
from tests.base import BaseTest, timeout
class InvoiceTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram Invoice."""
def setUp(self):
self.payload = 'payload'
self.provider_token = self._payment_provider_token
self.prices = [telegram.LabeledPrice('Fish', 100), telegram.LabeledPrice('Fish Tax', 1000)]
self.title = 'title'
self.description = 'description'
self.start_parameter = 'start_parameter'
self.currency = 'EUR'
self.total_amount = sum([p.amount for p in self.prices])
self.json_dict = {
'title': self.title,
'description': self.description,
'start_parameter': self.start_parameter,
'currency': self.currency,
'total_amount': self.total_amount
}
def test_invoice_de_json(self):
invoice = telegram.Invoice.de_json(self.json_dict, self._bot)
self.assertEqual(invoice.title, self.title)
self.assertEqual(invoice.description, self.description)
self.assertEqual(invoice.start_parameter, self.start_parameter)
self.assertEqual(invoice.currency, self.currency)
self.assertEqual(invoice.total_amount, self.total_amount)
def test_invoice_to_json(self):
invoice = telegram.Invoice.de_json(self.json_dict, self._bot)
self.assertTrue(self.is_json(invoice.to_json()))
def test_invoice_to_dict(self):
invoice = telegram.Invoice.de_json(self.json_dict, self._bot).to_dict()
self.assertTrue(self.is_dict(invoice))
self.assertDictEqual(self.json_dict, invoice)
@flaky(3, 1)
@timeout(10)
def test_send_invoice_required_args_only(self):
message = self._bot.send_invoice(self._chat_id, self.title, self.description, self.payload,
self.provider_token, self.start_parameter, self.currency,
self.prices)
invoice = message.invoice
self.assertEqual(invoice.currency, self.currency)
self.assertEqual(invoice.start_parameter, self.start_parameter)
self.assertEqual(invoice.description, self.description)
self.assertEqual(invoice.title, self.title)
self.assertEqual(invoice.total_amount, self.total_amount)
@flaky(3, 1)
@timeout(10)
def test_send_invoice_all_args(self):
message = self._bot.send_invoice(
self._chat_id,
self.title,
self.description,
self.payload,
self.provider_token,
self.start_parameter,
self.currency,
self.prices,
photo_url='https://raw.githubusercontent.com/'
'python-telegram-bot/logos/master/'
'logo/png/ptb-logo_240.png',
photo_size=240,
photo_width=240,
photo_height=240,
need_name=True,
need_phone_number=True,
need_shipping_address=True,
is_flexible=True)
invoice = message.invoice
self.assertEqual(invoice.currency, self.currency)
self.assertEqual(invoice.start_parameter, self.start_parameter)
self.assertEqual(invoice.description, self.description)
self.assertEqual(invoice.title, self.title)
self.assertEqual(invoice.total_amount, self.total_amount)
if __name__ == '__main__':
unittest.main()
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents Tests for Telegram
LabeledPrice"""
import sys
import unittest
sys.path.append('.')
import telegram
from tests.base import BaseTest
class LabeledPriceTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram LabeledPrice."""
def setUp(self):
self.label = 'label'
self.amount = 100
self.json_dict = {'label': self.label, 'amount': self.amount}
def test_labeledprice_de_json(self):
labeledprice = telegram.LabeledPrice.de_json(self.json_dict, self._bot)
self.assertEqual(labeledprice.label, self.label)
self.assertEqual(labeledprice.amount, self.amount)
def test_labeledprice_to_json(self):
labeledprice = telegram.LabeledPrice.de_json(self.json_dict, self._bot)
self.assertTrue(self.is_json(labeledprice.to_json()))
def test_labeledprice_to_dict(self):
labeledprice = telegram.LabeledPrice.de_json(self.json_dict, self._bot).to_dict()
self.assertTrue(self.is_dict(labeledprice))
self.assertDictEqual(self.json_dict, labeledprice)
if __name__ == '__main__':
unittest.main()
+32 -2
View File
@@ -98,16 +98,46 @@ class MessageTest(BaseTest, unittest.TestCase):
{entity: 'http://google.com',
entity_2: 'h'})
def test_text_html(self):
def test_text_html_simple(self):
test_html_string = 'Test for &lt;<b>bold</b>, <i>ita_lic</i>, <code>code</code>, <a href="http://github.com/">links</a> and <pre>pre</pre>.'
text_html = self.test_message.text_html
self.assertEquals(test_html_string, text_html)
def test_text_markdown(self):
def test_text_markdown_simple(self):
test_md_string = 'Test for <*bold*, _ita\_lic_, `code`, [links](http://github.com/) and ```pre```.'
text_markdown = self.test_message.text_markdown
self.assertEquals(test_md_string, text_markdown)
def test_text_html_emoji(self):
text = (b'\\U0001f469\\u200d\\U0001f469\\u200d ABC').decode('unicode-escape')
expected = (b'\\U0001f469\\u200d\\U0001f469\\u200d <b>ABC</b>').decode('unicode-escape')
bold_entity = telegram.MessageEntity(type=telegram.MessageEntity.BOLD, offset=7, length=3)
message = telegram.Message(
message_id=1, from_user=None, date=None, chat=None, text=text, entities=[bold_entity])
self.assertEquals(expected, message.text_html)
def test_text_markdown_emoji(self):
text = (b'\\U0001f469\\u200d\\U0001f469\\u200d ABC').decode('unicode-escape')
expected = (b'\\U0001f469\\u200d\\U0001f469\\u200d *ABC*').decode('unicode-escape')
bold_entity = telegram.MessageEntity(type=telegram.MessageEntity.BOLD, offset=7, length=3)
message = telegram.Message(
message_id=1, from_user=None, date=None, chat=None, text=text, entities=[bold_entity])
self.assertEquals(expected, message.text_markdown)
def test_parse_entities_url_emoji(self):
url = b'http://github.com/?unicode=\\u2713\\U0001f469'.decode('unicode-escape')
text = 'some url'
link_entity = telegram.MessageEntity(type=telegram.MessageEntity.URL, offset=0, length=8, url=url)
message = telegram.Message(
message_id=1,
from_user=None,
date=None,
chat=None,
text=text,
entities=[link_entity])
self.assertDictEqual(message.parse_entities(), {link_entity: text})
self.assertEqual(next(iter(message.parse_entities())).url, url)
@flaky(3, 1)
def test_reply_text(self):
"""Test for Message.reply_text"""
+13 -13
View File
@@ -44,24 +44,24 @@ class MessageEntityTest(BaseTest, unittest.TestCase):
'url': self.url
}
def test_sticker_de_json(self):
sticker = telegram.MessageEntity.de_json(self.json_dict, self._bot)
def test_messageentity_de_json(self):
entity = telegram.MessageEntity.de_json(self.json_dict, self._bot)
self.assertEqual(sticker.type, self.type)
self.assertEqual(sticker.offset, self.offset)
self.assertEqual(sticker.length, self.length)
self.assertEqual(sticker.url, self.url)
self.assertEqual(entity.type, self.type)
self.assertEqual(entity.offset, self.offset)
self.assertEqual(entity.length, self.length)
self.assertEqual(entity.url, self.url)
def test_sticker_to_json(self):
sticker = telegram.MessageEntity.de_json(self.json_dict, self._bot)
def test_messageentity_to_json(self):
entity = telegram.MessageEntity.de_json(self.json_dict, self._bot)
self.assertTrue(self.is_json(sticker.to_json()))
self.assertTrue(self.is_json(entity.to_json()))
def test_sticker_to_dict(self):
sticker = telegram.MessageEntity.de_json(self.json_dict, self._bot).to_dict()
def test_messageentity_to_dict(self):
entity = telegram.MessageEntity.de_json(self.json_dict, self._bot).to_dict()
self.assertTrue(self.is_dict(sticker))
self.assertDictEqual(self.json_dict, sticker)
self.assertTrue(self.is_dict(entity))
self.assertDictEqual(self.json_dict, entity)
if __name__ == '__main__':
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents Tests for Telegram
OrderInfo"""
import sys
import unittest
sys.path.append('.')
import telegram
from tests.base import BaseTest
class OrderInfoTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram OrderInfo."""
def setUp(self):
self.name = 'name'
self.phone_number = 'phone_number'
self.email = 'email'
self.shipping_address = telegram.ShippingAddress('GB', '', 'London', '12 Grimmauld Place',
'', 'WC1')
self.json_dict = {
'name': self.name,
'phone_number': self.phone_number,
'email': self.email,
'shipping_address': self.shipping_address.to_dict()
}
def test_orderinfo_de_json(self):
orderinfo = telegram.OrderInfo.de_json(self.json_dict, self._bot)
self.assertEqual(orderinfo.name, self.name)
self.assertEqual(orderinfo.phone_number, self.phone_number)
self.assertEqual(orderinfo.email, self.email)
self.assertEqual(orderinfo.shipping_address, self.shipping_address)
def test_orderinfo_to_json(self):
orderinfo = telegram.OrderInfo.de_json(self.json_dict, self._bot)
self.assertTrue(self.is_json(orderinfo.to_json()))
def test_orderinfo_to_dict(self):
orderinfo = telegram.OrderInfo.de_json(self.json_dict, self._bot).to_dict()
self.assertTrue(self.is_dict(orderinfo))
self.assertDictEqual(self.json_dict, orderinfo)
if __name__ == '__main__':
unittest.main()
+28
View File
@@ -18,6 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents Tests for Telegram Photo"""
from io import BytesIO
import sys
import unittest
import os
@@ -37,6 +38,7 @@ class PhotoTest(BaseTest, unittest.TestCase):
self.photo_file = open('tests/data/telegram.jpg', 'rb')
self.photo_file_id = 'AgADAQADgEsyGx8j9QfmDMmwkPBrFcKRzy8ABHW8ul9nW7FoNHYBAAEC'
self.photo_file_url = 'https://raw.githubusercontent.com/python-telegram-bot/python-telegram-bot/master/tests/data/telegram.jpg'
self.photo_bytes_jpg_no_standard = 'tests/data/telegram_no_standard_header.jpg'
self.width = 300
self.height = 300
self.thumb = {
@@ -122,6 +124,32 @@ class PhotoTest(BaseTest, unittest.TestCase):
self.assertEqual(photo.height, self.height)
self.assertEqual(photo.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_photo_bytesio_jpg_file(self):
from telegram.inputfile import InputFile
# raw image bytes
raw_bytes = BytesIO(open(self.photo_bytes_jpg_no_standard, 'rb').read())
inputfile = InputFile({"photo": raw_bytes})
self.assertEqual(inputfile.mimetype, 'application/octet-stream')
# raw image bytes with name info
raw_bytes = BytesIO(open(self.photo_bytes_jpg_no_standard, 'rb').read())
raw_bytes.name = self.photo_bytes_jpg_no_standard
inputfile = InputFile({"photo": raw_bytes})
self.assertEqual(inputfile.mimetype, 'image/jpeg')
# send raw photo
raw_bytes = BytesIO(open(self.photo_bytes_jpg_no_standard, 'rb').read())
message = self._bot.sendPhoto(self._chat_id, photo=raw_bytes)
photo = message.photo[-1]
self.assertTrue(isinstance(photo.file_id, str))
self.assertNotEqual(photo.file_id, '')
self.assertTrue(isinstance(photo, telegram.PhotoSize))
self.assertEqual(photo.width, 1920)
self.assertEqual(photo.height, 1080)
self.assertEqual(photo.file_size, 30907)
@flaky(3, 1)
@timeout(10)
def test_send_photo_resend(self):
+99
View File
@@ -0,0 +1,99 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents Tests for Telegram
PreCheckoutQuery"""
import sys
import unittest
sys.path.append('.')
import telegram
from tests.base import BaseTest
class PreCheckoutQueryTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram PreCheckoutQuery."""
def setUp(self):
self._id = 5
self.invoice_payload = 'invoice_payload'
self.shipping_option_id = 'shipping_option_id'
self.currency = 'EUR'
self.total_amount = 100
self.from_user = telegram.User(0, '')
self.order_info = telegram.OrderInfo()
self.json_dict = {
'id': self._id,
'invoice_payload': self.invoice_payload,
'shipping_option_id': self.shipping_option_id,
'currency': self.currency,
'total_amount': self.total_amount,
'from': self.from_user.to_dict(),
'order_info': self.order_info.to_dict()
}
def test_precheckoutquery_de_json(self):
precheckoutquery = telegram.PreCheckoutQuery.de_json(self.json_dict, self._bot)
self.assertEqual(precheckoutquery.id, self._id)
self.assertEqual(precheckoutquery.invoice_payload, self.invoice_payload)
self.assertEqual(precheckoutquery.shipping_option_id, self.shipping_option_id)
self.assertEqual(precheckoutquery.currency, self.currency)
self.assertEqual(precheckoutquery.from_user, self.from_user)
self.assertEqual(precheckoutquery.order_info, self.order_info)
def test_precheckoutquery_to_json(self):
precheckoutquery = telegram.PreCheckoutQuery.de_json(self.json_dict, self._bot)
self.assertTrue(self.is_json(precheckoutquery.to_json()))
def test_precheckoutquery_to_dict(self):
precheckoutquery = telegram.PreCheckoutQuery.de_json(self.json_dict, self._bot).to_dict()
self.assertTrue(self.is_dict(precheckoutquery))
self.assertDictEqual(self.json_dict, precheckoutquery)
def test_equality(self):
a = telegram.PreCheckoutQuery(self._id, self.from_user, self.currency, self.total_amount,
self.invoice_payload)
b = telegram.PreCheckoutQuery(self._id, self.from_user, self.currency, self.total_amount,
self.invoice_payload)
c = telegram.PreCheckoutQuery(self._id, None, '', 0, '')
d = telegram.PreCheckoutQuery(0, self.from_user, self.currency, self.total_amount,
self.invoice_payload)
e = telegram.Update(self._id)
self.assertEqual(a, b)
self.assertEqual(hash(a), hash(b))
self.assertIsNot(a, b)
self.assertEqual(a, c)
self.assertEqual(hash(a), hash(c))
self.assertNotEqual(a, d)
self.assertNotEqual(hash(a), hash(d))
self.assertNotEqual(a, e)
self.assertNotEqual(hash(a), hash(e))
if __name__ == '__main__':
unittest.main()
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents Tests for Telegram
ShippingAddress"""
import sys
import unittest
sys.path.append('.')
import telegram
from tests.base import BaseTest
class ShippingAddressTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram ShippingAddress."""
def setUp(self):
self.country_code = 'GB'
self.state = 'state'
self.city = 'London'
self.street_line1 = '12 Grimmauld Place'
self.street_line2 = 'street_line2'
self.post_code = 'WC1'
self.json_dict = {
'country_code': self.country_code,
'state': self.state,
'city': self.city,
'street_line1': self.street_line1,
'street_line2': self.street_line2,
'post_code': self.post_code
}
def test_shippingaddress_de_json(self):
shippingaddress = telegram.ShippingAddress.de_json(self.json_dict, self._bot)
self.assertEqual(shippingaddress.country_code, self.country_code)
self.assertEqual(shippingaddress.state, self.state)
self.assertEqual(shippingaddress.city, self.city)
self.assertEqual(shippingaddress.street_line1, self.street_line1)
self.assertEqual(shippingaddress.street_line2, self.street_line2)
self.assertEqual(shippingaddress.post_code, self.post_code)
def test_shippingaddress_to_json(self):
shippingaddress = telegram.ShippingAddress.de_json(self.json_dict, self._bot)
self.assertTrue(self.is_json(shippingaddress.to_json()))
def test_shippingaddress_to_dict(self):
shippingaddress = telegram.ShippingAddress.de_json(self.json_dict, self._bot).to_dict()
self.assertTrue(self.is_dict(shippingaddress))
self.assertDictEqual(self.json_dict, shippingaddress)
def test_equality(self):
a = telegram.ShippingAddress(self.country_code, self.state, self.city, self.street_line1,
self.street_line2, self.post_code)
b = telegram.ShippingAddress(self.country_code, self.state, self.city, self.street_line1,
self.street_line2, self.post_code)
d = telegram.ShippingAddress('', self.state, self.city, self.street_line1,
self.street_line2, self.post_code)
d2 = telegram.ShippingAddress(self.country_code, '', self.city, self.street_line1,
self.street_line2, self.post_code)
d3 = telegram.ShippingAddress(self.country_code, self.state, '', self.street_line1,
self.street_line2, self.post_code)
d4 = telegram.ShippingAddress(self.country_code, self.state, self.city, '',
self.street_line2, self.post_code)
d5 = telegram.ShippingAddress(self.country_code, self.state, self.city, self.street_line1,
'', self.post_code)
d6 = telegram.ShippingAddress(self.country_code, self.state, self.city, self.street_line1,
self.street_line2, '')
self.assertEqual(a, b)
self.assertEqual(hash(a), hash(b))
self.assertIsNot(a, b)
self.assertNotEqual(a, d)
self.assertNotEqual(hash(a), hash(d))
self.assertNotEqual(a, d2)
self.assertNotEqual(hash(a), hash(d2))
self.assertNotEqual(a, d3)
self.assertNotEqual(hash(a), hash(d3))
self.assertNotEqual(a, d4)
self.assertNotEqual(hash(a), hash(d4))
self.assertNotEqual(a, d5)
self.assertNotEqual(hash(a), hash(d5))
self.assertNotEqual(a, d6)
self.assertNotEqual(hash(6), hash(d6))
if __name__ == '__main__':
unittest.main()
+88
View File
@@ -0,0 +1,88 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents Tests for Telegram
ShippingOption"""
import sys
import unittest
sys.path.append('.')
import telegram
from tests.base import BaseTest
class ShippingOptionTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram ShippingOption."""
def setUp(self):
self._id = 'id'
self.title = 'title'
self.prices = [
telegram.LabeledPrice('Fish Container', 100),
telegram.LabeledPrice('Premium Fish Container', 1000)
]
self.json_dict = {
'id': self._id,
'title': self.title,
'prices': [x.to_dict() for x in self.prices]
}
def test_shippingoption_de_json(self):
shippingoption = telegram.ShippingOption.de_json(self.json_dict, self._bot)
self.assertEqual(shippingoption.id, self._id)
self.assertEqual(shippingoption.title, self.title)
self.assertEqual(shippingoption.prices, self.prices)
def test_shippingoption_to_json(self):
shippingoption = telegram.ShippingOption.de_json(self.json_dict, self._bot)
self.assertTrue(self.is_json(shippingoption.to_json()))
def test_shippingoption_to_dict(self):
shippingoption = telegram.ShippingOption.de_json(self.json_dict, self._bot).to_dict()
self.assertTrue(self.is_dict(shippingoption))
self.assertDictEqual(self.json_dict, shippingoption)
def test_equality(self):
a = telegram.ShippingOption(self._id, self.title, self.prices)
b = telegram.ShippingOption(self._id, self.title, self.prices)
c = telegram.ShippingOption(self._id, '', [])
d = telegram.ShippingOption(0, self.title, self.prices)
e = telegram.Voice(self._id, 0)
self.assertEqual(a, b)
self.assertEqual(hash(a), hash(b))
self.assertIsNot(a, b)
self.assertEqual(a, c)
self.assertEqual(hash(a), hash(c))
self.assertNotEqual(a, d)
self.assertNotEqual(hash(a), hash(d))
self.assertNotEqual(a, e)
self.assertNotEqual(hash(a), hash(e))
if __name__ == '__main__':
unittest.main()
+91
View File
@@ -0,0 +1,91 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents Tests for Telegram
ShippingQuery"""
import sys
import unittest
sys.path.append('.')
import telegram
from tests.base import BaseTest
class ShippingQueryTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram ShippingQuery."""
def setUp(self):
self._id = 5
self.invoice_payload = 'invoice_payload'
self.from_user = telegram.User(0, '')
self.shipping_address = telegram.ShippingAddress('GB', '', 'London', '12 Grimmauld Place',
'', 'WC1')
self.json_dict = {
'id': self._id,
'invoice_payload': self.invoice_payload,
'from': self.from_user.to_dict(),
'shipping_address': self.shipping_address.to_dict()
}
def test_shippingquery_de_json(self):
shippingquery = telegram.ShippingQuery.de_json(self.json_dict, self._bot)
self.assertEqual(shippingquery.id, self._id)
self.assertEqual(shippingquery.invoice_payload, self.invoice_payload)
self.assertEqual(shippingquery.from_user, self.from_user)
self.assertEqual(shippingquery.shipping_address, self.shipping_address)
def test_shippingquery_to_json(self):
shippingquery = telegram.ShippingQuery.de_json(self.json_dict, self._bot)
self.assertTrue(self.is_json(shippingquery.to_json()))
def test_shippingquery_to_dict(self):
shippingquery = telegram.ShippingQuery.de_json(self.json_dict, self._bot).to_dict()
self.assertTrue(self.is_dict(shippingquery))
self.assertDictEqual(self.json_dict, shippingquery)
def test_equality(self):
a = telegram.ShippingQuery(self._id, self.from_user, self.invoice_payload,
self.shipping_address)
b = telegram.ShippingQuery(self._id, self.from_user, self.invoice_payload,
self.shipping_address)
c = telegram.ShippingQuery(self._id, None, '', None)
d = telegram.ShippingQuery(0, self.from_user, self.invoice_payload, self.shipping_address)
e = telegram.Update(self._id)
self.assertEqual(a, b)
self.assertEqual(hash(a), hash(b))
self.assertIsNot(a, b)
self.assertEqual(a, c)
self.assertEqual(hash(a), hash(c))
self.assertNotEqual(a, d)
self.assertNotEqual(hash(a), hash(d))
self.assertNotEqual(a, e)
self.assertNotEqual(hash(a), hash(e))
if __name__ == '__main__':
unittest.main()
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents Tests for Telegram
SuccessfulPayment"""
import sys
import unittest
sys.path.append('.')
import telegram
from tests.base import BaseTest
class SuccessfulPaymentTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram SuccessfulPayment."""
def setUp(self):
self.invoice_payload = 'invoice_payload'
self.shipping_option_id = 'shipping_option_id'
self.currency = 'EUR'
self.total_amount = 100
self.order_info = telegram.OrderInfo()
self.telegram_payment_charge_id = 'telegram_payment_charge_id'
self.provider_payment_charge_id = 'provider_payment_charge_id'
self.json_dict = {
'invoice_payload': self.invoice_payload,
'shipping_option_id': self.shipping_option_id,
'currency': self.currency,
'total_amount': self.total_amount,
'order_info': self.order_info.to_dict(),
'telegram_payment_charge_id': self.telegram_payment_charge_id,
'provider_payment_charge_id': self.provider_payment_charge_id
}
def test_successfulpayment_de_json(self):
successfulpayment = telegram.SuccessfulPayment.de_json(self.json_dict, self._bot)
self.assertEqual(successfulpayment.invoice_payload, self.invoice_payload)
self.assertEqual(successfulpayment.shipping_option_id, self.shipping_option_id)
self.assertEqual(successfulpayment.currency, self.currency)
self.assertEqual(successfulpayment.order_info, self.order_info)
self.assertEqual(successfulpayment.telegram_payment_charge_id,
self.telegram_payment_charge_id)
self.assertEqual(successfulpayment.provider_payment_charge_id,
self.provider_payment_charge_id)
def test_successfulpayment_to_json(self):
successfulpayment = telegram.SuccessfulPayment.de_json(self.json_dict, self._bot)
self.assertTrue(self.is_json(successfulpayment.to_json()))
def test_successfulpayment_to_dict(self):
successfulpayment = telegram.SuccessfulPayment.de_json(self.json_dict, self._bot).to_dict()
self.assertTrue(self.is_dict(successfulpayment))
self.assertDictEqual(self.json_dict, successfulpayment)
def test_equality(self):
a = telegram.SuccessfulPayment(self.currency, self.total_amount, self.invoice_payload,
self.telegram_payment_charge_id,
self.provider_payment_charge_id)
b = telegram.SuccessfulPayment(self.currency, self.total_amount, self.invoice_payload,
self.telegram_payment_charge_id,
self.provider_payment_charge_id)
c = telegram.SuccessfulPayment('', 0, '', self.telegram_payment_charge_id,
self.provider_payment_charge_id)
d = telegram.SuccessfulPayment(self.currency, self.total_amount, self.invoice_payload,
self.telegram_payment_charge_id, '')
self.assertEqual(a, b)
self.assertEqual(hash(a), hash(b))
self.assertIsNot(a, b)
self.assertEqual(a, c)
self.assertEqual(hash(a), hash(c))
self.assertNotEqual(a, d)
self.assertNotEqual(hash(a), hash(d))
if __name__ == '__main__':
unittest.main()
+67 -7
View File
@@ -46,7 +46,8 @@ except ImportError:
sys.path.append('.')
from telegram import Update, Message, TelegramError, User, Chat, Bot, InlineQuery, CallbackQuery
from telegram import (Update, Message, TelegramError, User, Chat, Bot,
InlineQuery, CallbackQuery, ShippingQuery, PreCheckoutQuery)
from telegram.ext import *
from telegram.ext.dispatcher import run_async
from telegram.error import Unauthorized, InvalidToken
@@ -119,6 +120,14 @@ class UpdaterTest(BaseTest, unittest.TestCase):
self.received_message = update.callback_query
self.message_count += 1
def telegramShippingHandlerTest(self, bot, update):
self.received_message = update.shipping_query
self.message_count += 1
def telegramPreCheckoutHandlerTest(self, bot, update):
self.received_message = update.pre_checkout_query
self.message_count += 1
@run_async
def asyncHandlerTest(self, bot, update):
sleep(1)
@@ -271,6 +280,19 @@ class UpdaterTest(BaseTest, unittest.TestCase):
sleep(.1)
self.assertTrue(None is self.received_message)
# case insensitivity
self.reset()
message = Message(0, user, None, None, text="/Test", bot=bot)
queue.put(Update(update_id=0, message=message))
sleep(.1)
self.assertTrue(self.received_message, '/Test')
handler = CommandHandler('Test', self.telegramHandlerTest)
self.updater.dispatcher.add_handler(handler)
message = Message(0, user, None, None, text="/test", bot=bot)
queue.put(Update(update_id=0, message=message))
sleep(.1)
self.assertTrue(self.received_message, '/test')
# Remove handler
d.remove_handler(handler)
handler = CommandHandler('test', self.telegramHandlerEditedTest, allow_edited=False)
@@ -488,6 +510,44 @@ class UpdaterTest(BaseTest, unittest.TestCase):
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addRemoveShippingQueryHandler(self):
self._setup_updater('', messages=0)
d = self.updater.dispatcher
handler = ShippingQueryHandler(self.telegramShippingHandlerTest)
d.add_handler(handler)
queue = self.updater.start_polling(0.01)
update = Update(update_id=0, shipping_query="testshipping")
queue.put(update)
sleep(.1)
self.assertEqual(self.received_message, "testshipping")
# Remove handler
d.remove_handler(handler)
self.reset()
queue.put(update)
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addRemovePreCheckoutQueryHandler(self):
self._setup_updater('', messages=0)
d = self.updater.dispatcher
handler = PreCheckoutQueryHandler(self.telegramPreCheckoutHandlerTest)
d.add_handler(handler)
queue = self.updater.start_polling(0.01)
update = Update(update_id=0, pre_checkout_query="testprecheckout")
queue.put(update)
sleep(.1)
self.assertEqual(self.received_message, "testprecheckout")
# Remove handler
d.remove_handler(handler)
self.reset()
queue.put(update)
sleep(.1)
self.assertTrue(None is self.received_message)
def test_runAsync(self):
self._setup_updater('Test5', messages=2)
d = self.updater.dispatcher
@@ -884,7 +944,7 @@ class MockBot(object):
self.edited = edited
self.username = "MockBot"
def mockUpdate(self, text):
def mock_update(self, text):
message = Message(0, User(0, 'Testuser'), None, Chat(0, Chat.GROUP), bot=self)
message.text = text
update = Update(0)
@@ -896,7 +956,7 @@ class MockBot(object):
return update
def setWebhook(self, url=None, certificate=None, allowed_updates=None):
def set_webhook(self, url=None, certificate=None, allowed_updates=None):
if self.bootstrap_retries is None:
return
@@ -904,7 +964,7 @@ class MockBot(object):
self.bootstrap_attempts += 1
raise self.bootstrap_err
def deleteWebhook(self):
def delete_webhook(self):
if self.bootstrap_retries is None:
return
@@ -912,7 +972,7 @@ class MockBot(object):
self.bootstrap_attempts += 1
raise self.bootstrap_err
def getUpdates(self,
def get_updates(self,
offset=None,
limit=100,
timeout=0,
@@ -924,10 +984,10 @@ class MockBot(object):
raise TelegramError('Test Error 2')
elif self.send_messages >= 2:
self.send_messages -= 2
return self.mockUpdate(self.text), self.mockUpdate(self.text)
return self.mock_update(self.text), self.mock_update(self.text)
elif self.send_messages == 1:
self.send_messages -= 1
return self.mockUpdate(self.text),
return self.mock_update(self.text),
else:
return []
+4
View File
@@ -37,6 +37,7 @@ class UserTest(BaseTest, unittest.TestCase):
self.first_name = "Leandro"
self.last_name = "S."
self.username = "leandrotoledo"
self.language_code = "pt-BR"
self.type = "private"
self.json_dict = {
@@ -44,6 +45,7 @@ class UserTest(BaseTest, unittest.TestCase):
'first_name': self.first_name,
'last_name': self.last_name,
'username': self.username,
'language_code': self.language_code,
'type': self.type
}
@@ -54,6 +56,7 @@ class UserTest(BaseTest, unittest.TestCase):
self.assertEqual(user.first_name, self.first_name)
self.assertEqual(user.last_name, self.last_name)
self.assertEqual(user.username, self.username)
self.assertEqual(user.language_code, self.language_code)
self.assertEqual(user.type, self.type)
self.assertEqual(user.name, '@leandrotoledo')
@@ -98,6 +101,7 @@ class UserTest(BaseTest, unittest.TestCase):
self.assertEqual(user['first_name'], self.first_name)
self.assertEqual(user['last_name'], self.last_name)
self.assertEqual(user['username'], self.username)
self.assertEqual(user['language_code'], self.language_code)
self.assertEqual(user['type'], self.type)
@flaky(3, 1)
+198
View File
@@ -0,0 +1,198 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2017
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains an object that represents Tests for Telegram VideoNote"""
import numbers
import sys
import unittest
import os
from flaky import flaky
sys.path.append('.')
import telegram
from tests.base import BaseTest, timeout
class VideoNoteTest(BaseTest, unittest.TestCase):
"""This object represents Tests for Telegram VideoNote."""
def setUp(self):
self.videonote_file = open('tests/data/telegram2.mp4', 'rb')
self.videonote_file_id = 'DQADAQADBwAD5VIIRYemhHpbPmIQAg'
self.duration = 3
self.length = 1 # No bloody clue what this does
self.thumb = telegram.PhotoSize.de_json({
'file_id': 'AAQBABMsDPcvAAQX7NUVpGq-s34OAAIC',
'width': 90,
'file_size': 3043,
'height': 90
}, self._bot)
self.thumb_resend = telegram.PhotoSize.de_json({
'file_id': 'AAQBABOMsecvAAQqqoY1Pee_MqcyAAIC',
'width': 51,
'file_size': 645,
'height': 90
}, self._bot)
self.file_size = 132084
self.json_dict = {
'file_id': self.videonote_file_id,
'duration': self.duration,
'length': self.length,
'thumb': self.thumb.to_dict(),
'file_size': self.file_size
}
@flaky(3, 1)
@timeout(10)
def test_send_videonote_required_args_only(self):
message = self._bot.sendVideoNote(self._chat_id, self.videonote_file, timeout=10)
videonote = message.video_note
self.assertTrue(isinstance(videonote.file_id, str))
self.assertNotEqual(videonote.file_id, None)
self.assertEqual(videonote.duration, self.duration)
# self.assertEqual(videonote.length, self.length)
self.assertIsInstance(videonote.length, numbers.Number)
self.assertEqual(videonote.thumb, self.thumb)
self.assertEqual(videonote.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_videonote_all_args(self):
message = self._bot.sendVideoNote(
self._chat_id,
self.videonote_file,
timeout=10,
duration=self.duration,
length=self.length)
videonote = message.video_note
self.assertTrue(isinstance(videonote.file_id, str))
self.assertNotEqual(videonote.file_id, None)
# self.assertEqual(videonote.length, self.length)
self.assertIsInstance(videonote.length, numbers.Number)
self.assertEqual(videonote.duration, self.duration)
self.assertEqual(videonote.thumb, self.thumb)
self.assertEqual(videonote.file_size, self.file_size)
@flaky(3, 1)
@timeout(10)
def test_send_videonote_resend(self):
message = self._bot.sendVideoNote(
chat_id=self._chat_id,
video_note=self.videonote_file_id,
timeout=10
)
videonote = message.video_note
self.assertEqual(videonote.file_id, self.videonote_file_id)
# self.assertEqual(videonote.length, self.length)
self.assertIsInstance(videonote.length, numbers.Number)
self.assertEqual(videonote.duration, 5)
self.assertEqual(videonote.thumb, self.thumb_resend)
# Telegram doesn't send file_size for resends?
# self.assertEqual(videonote.file_size, self.file_size)
def test_videonote_de_json(self):
videonote = telegram.VideoNote.de_json(self.json_dict, self._bot)
self.assertEqual(videonote.file_id, self.videonote_file_id)
self.assertEqual(videonote.duration, self.duration)
self.assertEqual(videonote.thumb, self.thumb)
self.assertEqual(videonote.length, self.length)
self.assertEqual(videonote.file_size, self.file_size)
def test_videonote_to_json(self):
videonote = telegram.VideoNote.de_json(self.json_dict, self._bot)
self.assertTrue(self.is_json(videonote.to_json()))
def test_videonote_to_dict(self):
videonote = telegram.VideoNote.de_json(self.json_dict, self._bot)
self.assertTrue(self.is_dict(videonote.to_dict()))
self.assertEqual(videonote['file_id'], self.videonote_file_id)
self.assertEqual(videonote['duration'], self.duration)
self.assertEqual(videonote['length'], self.length)
self.assertEqual(videonote['file_size'], self.file_size)
@flaky(3, 1)
@timeout(10)
def test_error_send_videonote_empty_file(self):
json_dict = self.json_dict
del (json_dict['file_id'])
json_dict['video_note'] = open(os.devnull, 'rb')
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendVideoNote(chat_id=self._chat_id,
timeout=10,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_error_send_videonote_empty_file_id(self):
json_dict = self.json_dict
del (json_dict['file_id'])
json_dict['video_note'] = ''
self.assertRaises(telegram.TelegramError,
lambda: self._bot.sendVideoNote(chat_id=self._chat_id,
timeout=10,
**json_dict))
@flaky(3, 1)
@timeout(10)
def test_reply_videonote(self):
"""Test for Message.reply_videonote"""
message = self._bot.sendMessage(self._chat_id, '.')
# Length is needed... see first test
message = message.reply_video_note(self.videonote_file, length=self.length)
self.assertNotEqual(message.video_note.file_id, None)
def test_equality(self):
a = telegram.VideoNote(self.videonote_file_id, self.length, self.duration)
b = telegram.VideoNote(self.videonote_file_id, self.length, self.duration)
c = telegram.VideoNote(self.videonote_file_id, 0, 0, 0)
d = telegram.VideoNote("", self.length, self.duration)
e = telegram.Voice(self.videonote_file_id, self.duration)
self.assertEqual(a, b)
self.assertEqual(hash(a), hash(b))
self.assertIsNot(a, b)
self.assertEqual(a, c)
self.assertEqual(hash(a), hash(c))
self.assertNotEqual(a, d)
self.assertNotEqual(hash(a), hash(d))
self.assertNotEqual(a, e)
self.assertNotEqual(hash(a), hash(e))
if __name__ == '__main__':
unittest.main()