1
0
mirror of https://github.com/eternnoir/pyTelegramBotAPI.git synced 2023-08-10 21:12:57 +03:00

Merge remote-tracking branch 'refs/remotes/eternnoir/master'

This commit is contained in:
Kylmakalle 2017-05-25 20:46:10 +03:00
commit 5ffd9c5755
5 changed files with 371 additions and 8 deletions

View File

@ -123,7 +123,7 @@ To start the bot, simply open up a terminal and enter `python echo_bot.py` to ru
All types are defined in types.py. They are all completely in line with the [Telegram API's definition of the types](https://core.telegram.org/bots/api#available-types), except for the Message's `from` field, which is renamed to `from_user` (because `from` is a Python reserved token). Thus, attributes such as `message_id` can be accessed directly with `message.message_id`. Note that `message.chat` can be either an instance of `User` or `GroupChat` (see [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)).
The Message object also has a `content_type`attribute, which defines the type of the Message. `content_type` can be one of the following strings:
`text`, `audio`, `document`, `photo`, `sticker`, `video`, `voice`, `location`, `contact`, `new_chat_member`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`.
`text`, `audio`, `document`, `photo`, `sticker`, `video`, `video_note`, `voice`, `location`, `contact`, `new_chat_member`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`.
### Methods
@ -284,6 +284,11 @@ video = open('/tmp/video.mp4', 'rb')
tb.send_video(chat_id, video)
tb.send_video(chat_id, "FILEID")
# sendVideoNote
videonote = open('/tmp/videonote.mp4', 'rb')
tb.send_video(chat_id, videonote)
tb.send_video(chat_id, "FILEID")
# sendLocation
tb.send_location(chat_id, lat, lon)

View File

@ -39,6 +39,7 @@ class TeleBot:
sendDocument
sendSticker
sendVideo
sendVideoNote
sendLocation
sendChatAction
getUserProfilePhotos
@ -510,6 +511,22 @@ class TeleBot:
apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup,
disable_notification, timeout))
def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
"""
Use this method to send video files, Telegram clients support mp4 videos.
:param chat_id: Integer : Unique identifier for the message recipient User or GroupChat id
:param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend a video that is already on the Telegram server
:param duration: Integer : Duration of sent video in seconds
:param length: Integer : Video width and height, Can't be None and should be in range of (0, 640)
:param reply_to_message_id:
:param reply_markup:
:return:
"""
return types.Message.de_json(
apihelper.send_video_note(self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup,
disable_notification, timeout))
def send_location(self, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None,
disable_notification=None):
"""
@ -559,7 +576,7 @@ class TeleBot:
its typing status).
:param chat_id:
:param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video',
'record_audio', 'upload_audio', 'upload_document', 'find_location'.
'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', 'upload_video_note'.
:return: API reply. :type: boolean
"""
return apihelper.send_chat_action(self.token, chat_id, action)
@ -953,6 +970,10 @@ class AsyncTeleBot(TeleBot):
def send_video(self, *args, **kwargs):
return TeleBot.send_video(self, *args, **kwargs)
@util.async()
def send_video_note(self, *args, **kwargs):
return TeleBot.send_video_note(self, *args, **kwargs)
@util.async()
def send_location(self, *args, **kwargs):
return TeleBot.send_location(self, *args, **kwargs)

View File

@ -328,6 +328,32 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
method_url = r'sendVideoNote'
payload = {'chat_id': chat_id}
files = None
if not util.is_string(data):
files = {'video_note': data}
else:
payload['video_note'] = data
if duration:
payload['duration'] = duration
if length:
payload['length'] = length
else:
payload['length'] = 639 # seems like it is MAX length size
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if disable_notification:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None,
reply_markup=None, disable_notification=None, timeout=None):
method_url = r'sendAudio'
@ -446,7 +472,7 @@ def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_messa
return _make_request(token, method_url, params=payload)
def delete_message(token, chat_id=None, message_id=None):
def delete_message(token, chat_id, message_id):
method_url = r'deleteMessage'
payload = {'chat_id': chat_id, 'message_id': message_id}
return _make_request(token, method_url, params=payload)
@ -518,6 +544,102 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m
payload['inline_message_id'] = inline_message_id
return _make_request(token, method_url, params=payload)
# Payments (https://core.telegram.org/bots/api#payments)
def send_invoice(token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter=None,
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=None, reply_to_message_id=None, reply_markup=None):
"""
Use this method to send invoices. On success, the sent Message is returned.
:param token: Bot's token (you don't need to fill this)
:param chat_id: Unique identifier for the target private chat
:param title: Product name
:param description: Product description
:param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.
:param provider_token: Payments provider token, obtained via @Botfather
:param currency: Three-letter ISO 4217 currency code, see https://core.telegram.org/bots/payments#supported-currencies
:param prices: Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
:param start_parameter: Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter
:param photo_url: 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.
:param photo_size: Photo size
:param photo_width: Photo width
:param photo_height: Photo height
:param need_name: Pass True, if you require the user's full name to complete the order
:param need_phone_number: Pass True, if you require the user's phone number to complete the order
:param need_email: Pass True, if you require the user's email to complete the order
:param need_shipping_address: Pass True, if you require the user's shipping address to complete the order
:param is_flexible: Pass True, if the final price depends on the shipping method
:param disable_notification: Sends the message silently. Users will receive a notification with no sound.
:param reply_to_message_id: If the message is a reply, ID of the original message
:param reply_markup: A JSON-serialized object for an inline keyboard. If empty, one 'Pay total price' button will be shown. If not empty, the first button must be a Pay button
:return:
"""
method_url = r'sendInvoice'
payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload, 'provider_token': provider_token, 'currency': currency, 'prices': prices}
if start_parameter:
payload['start_parameter'] = start_parameter
if photo_url:
payload['photo_url'] = photo_url
if photo_size:
payload['photo_size'] = photo_size
if photo_width:
payload['photo_width'] = photo_width
if photo_height:
payload['photo_height'] = photo_height
if need_name:
payload['need_name'] = need_name
if need_phone_number:
payload['need_phone_number'] = need_phone_number
if need_email:
payload['need_email'] = need_email
if need_shipping_address:
payload['need_shipping_address'] = need_shipping_address
if is_flexible:
payload['is_flexible'] = is_flexible
if disable_notification:
payload['disable_notification'] = disable_notification
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if reply_markup:
payload['reply_markup'] = reply_markup
return _make_request(token, method_url, params=payload)
def answer_shippingQuery(token, shipping_query_id, ok, shipping_options=None, error_message=None):
"""
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. On success, True is returned.
:param token: Bot's token (you don't need to fill this)
:param shipping_query_id: Unique identifier for the query to be answered
:param ok: 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)
:param shipping_options: Required if ok is True. A JSON-serialized array of available shipping options.
:param error_message: 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.
:return:
"""
method_url = 'answerShippingQuery'
payload = {'shipping_query_id': shipping_query_id, 'ok': ok}
if shipping_options:
payload['reply_markup'] = shipping_options
if error_message:
payload['reply_markup'] = error_message
return _make_request(token, method_url, params=payload)
def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_message=None):
"""
Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the field pre_checkout_query. Use this method to respond to such pre-checkout queries. On success, True is returned. Note: The Bot API must receive an answer within 10 seconds after the pre-checkout query was sent.
:param token: Bot's token (you don't need to fill this)
:param pre_checkout_query_id: Unique identifier for the query to be answered
:param ok: 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.
:param error_message: 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.
:return:
"""
method_url = 'answerPreCheckoutQuery'
payload = {'pre_checkout_query_id': pre_checkout_query_id, 'ok': ok}
if error_message:
payload['error_message'] = error_message
return _make_request(token, method_url, params=payload)
# InlineQuery
@ -525,7 +647,6 @@ def answer_callback_query(token, callback_query_id, text=None, show_alert=None,
"""
Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. On success, True is returned.
Alternatively, the user can be redirected to the specified Game URL. For this option to work, you must first create a game for your bot via BotFather and accept the terms. Otherwise, you may use links like telegram.me/your_bot?start=XXXX that open your bot with a parameter.
:param token: Bot's token (you don't need to fill this)
:param callback_query_id: Unique identifier for the query to be answered
:param text: (Optional) Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters

View File

@ -101,6 +101,8 @@ class Update(JsonDeserializable):
inline_query = None
chosen_inline_result = None
callback_query = None
shipping_query = None
pre_checkout_query = None
if 'message' in obj:
message = Message.de_json(obj['message'])
if 'edited_message' in obj:
@ -115,11 +117,15 @@ class Update(JsonDeserializable):
chosen_inline_result = ChosenInlineResult.de_json(obj['chosen_inline_result'])
if 'callback_query' in obj:
callback_query = CallbackQuery.de_json(obj['callback_query'])
if 'shipping_query' in obj:
shipping_query = ShippingQuery.de_json(obj['shipping_query'])
if 'pre_checkout_query' in obj:
pre_checkout_query = PreCheckoutQuery.de_json(obj['pre_checkout_query'])
return cls(update_id, message, edited_message, channel_post, edited_channel_post, inline_query,
chosen_inline_result, callback_query)
chosen_inline_result, callback_query, shipping_query, pre_checkout_query)
def __init__(self, update_id, message, edited_message, channel_post, edited_channel_post, inline_query,
chosen_inline_result, callback_query):
chosen_inline_result, callback_query, shipping_query, pre_checkout_query):
self.update_id = update_id
self.edited_message = edited_message
self.message = message
@ -129,6 +135,8 @@ class Update(JsonDeserializable):
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
class WebhookInfo(JsonDeserializable):
@ -148,9 +156,11 @@ class WebhookInfo(JsonDeserializable):
max_connections = obj['max_connections']
if 'allowed_updates' in obj:
allowed_updates = obj['allowed_updates']
return cls(url, has_custom_certificate, pending_update_count, last_error_date, last_error_message, max_connections, allowed_updates)
return cls(url, has_custom_certificate, pending_update_count, last_error_date, last_error_message,
max_connections, allowed_updates)
def __init__(self, url, has_custom_certificate, pending_update_count, last_error_date, last_error_message, max_connections, allowed_updates):
def __init__(self, url, has_custom_certificate, pending_update_count, last_error_date, last_error_message,
max_connections, allowed_updates):
self.url = url
self.has_custom_certificate = has_custom_certificate
self.pending_update_count = pending_update_count
@ -263,6 +273,9 @@ class Message(JsonDeserializable):
if 'video' in obj:
opts['video'] = Video.de_json(obj['video'])
content_type = 'video'
if 'video_note' in obj:
opts['video_note'] = VideoNote.de_json(obj['video_note'])
content_type = 'video_note'
if 'voice' in obj:
opts['voice'] = Audio.de_json(obj['voice'])
content_type = 'voice'
@ -280,6 +293,9 @@ class Message(JsonDeserializable):
if 'new_chat_member' in obj:
opts['new_chat_member'] = User.de_json(obj['new_chat_member'])
content_type = 'new_chat_member'
if 'new_chat_members' in obj:
opts['new_chat_members'] = obj['new_chat_members']
content_type = 'new_chat_members'
if 'left_chat_member' in obj:
opts['left_chat_member'] = User.de_json(obj['left_chat_member'])
content_type = 'left_chat_member'
@ -301,6 +317,12 @@ class Message(JsonDeserializable):
opts['migrate_from_chat_id'] = obj['migrate_from_chat_id']
if 'pinned_message' in obj:
opts['pinned_message'] = Message.de_json(obj['pinned_message'])
if 'invoice' in obj:
opts['invoice'] = Invoice.de_json(obj['invoice'])
content_type = 'invoice'
if 'successful_payment' in obj:
opts['successful_payment'] = SuccessfulPayment.de_json(obj['successful_payment'])
content_type = 'successful_payment'
return cls(message_id, from_user, date, chat, content_type, opts)
@classmethod
@ -342,6 +364,7 @@ class Message(JsonDeserializable):
self.photo = None
self.sticker = None
self.video = None
self.video_note = None
self.voice = None
self.caption = None
self.contact = None
@ -358,6 +381,8 @@ class Message(JsonDeserializable):
self.migrate_to_chat_id = None
self.migrate_from_chat_id = None
self.pinned_message = None
self.invoice = None
self.successful_payment = None
for key in options:
setattr(self, key, options[key])
@ -507,6 +532,27 @@ class Video(JsonDeserializable):
self.file_size = file_size
class VideoNote(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
file_id = obj['file_id']
length = obj['length']
duration = obj['duration']
thumb = None
if 'thumb' in obj:
thumb = PhotoSize.de_json(obj['thumb'])
file_size = obj.get('file_size')
return cls(file_id, length, duration, thumb, file_size)
def __init__(self, file_id, length, duration, thumb=None, file_size=None):
self.file_id = file_id
self.length = length
self.duration = duration
self.thumb = thumb
self.file_size = file_size
class Contact(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
@ -1580,3 +1626,167 @@ class GameHighScore(JsonDeserializable):
self.position = position
self.user = user
self.score = score
# Payments
class LabeledPrice(JsonSerializable):
def __init__(self, label, amount):
self.label = label
self.amount = amount
def to_json(self):
return json.dumps(self.to_dic())
def to_dic(self):
return {'label': self.label, 'amount': self.amount}
class Invoice(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
title = obj['title']
description = obj['description']
start_parameter = obj['start_parameter']
currency = obj['currency']
total_amount = obj['total_amount']
return cls(title, description, start_parameter, currency, total_amount)
def __init__(self, title, description, start_parameter, currency, total_amount):
self.title = title
self.description = description
self.start_parameter = start_parameter
self.currency = currency
self.total_amount = total_amount
class ShippingAddress(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
country_code = obj['country_code']
state = obj['state']
city = obj['city']
street_line1 = obj['street_line1']
street_line2 = obj['street_line2']
post_code = obj['post_code']
return cls(country_code, state, city, street_line1, street_line2, post_code)
def __init__(self, country_code, state, city, street_line1, street_line2, post_code):
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
class OrderInfo(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
name = obj.get('name')
phone_number = obj.get('phone_number')
email = obj.get('email')
shipping_address = None
if 'shipping_address' in obj:
shipping_address = ShippingAddress.de_json(obj['shipping_address'])
return cls(name, phone_number, email, shipping_address)
def __init__(self, name, phone_number, email, shipping_address):
self.name = name
self.phone_number = phone_number
self.email = email
self.shipping_address = shipping_address
class ShippingOption(JsonSerializable):
def __init__(self, id, title):
self.id = id
self.title = title
self.prices = []
def add_price(self, *args):
"""
Add LabeledPrice to ShippingOption
:param args: LabeledPrices
"""
for price in args:
self.prices.append(price)
def to_json(self):
price_list = []
for p in self.prices:
price_list.append(p.to_dic())
json_dict = {'id': self.id, 'title': self.title, 'prices': price_list}
return json_dict
class SuccessfulPayment(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
currency = obj['currency']
total_amount = obj['total_amount']
invoice_payload = obj['invoice_payload']
shipping_option_id = obj.get('shipping_option_id')
order_info = None
if 'order_info' in obj:
order_info = OrderInfo.de_json(obj['order_info'])
telegram_payment_charge_id = obj['telegram_payment_charge_id']
provider_payment_charge_id = obj['provider_payment_charge_id']
return cls(currency, total_amount, invoice_payload, shipping_option_id, order_info,
telegram_payment_charge_id, provider_payment_charge_id)
def __init__(self, currency, total_amount, invoice_payload, shipping_option_id, order_info,
telegram_payment_charge_id, provider_payment_charge_id):
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
class ShippingQuery(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
id = obj['id']
from_user = User.de_json(obj['from'])
invoice_payload = obj['invoice_payload']
shipping_address = ShippingAddress.de_json(obj['shipping_address'])
return cls(id, from_user, invoice_payload, shipping_address)
def __init__(self, id, from_user, invoice_payload, shipping_address):
self.id = id
self.from_user = from_user
self.invoice_payload = invoice_payload
self.shipping_address = shipping_address
class PreCheckoutQuery(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
obj = cls.check_json(json_string)
id = obj['id']
from_user = User.de_json(obj['from'])
currency = obj['currency']
total_amount = obj['total_amount']
invoice_payload = obj['invoice_payload']
shipping_option_id = obj.get('shipping_option_id')
order_info = None
if 'order_info' in obj:
order_info = OrderInfo.de_json(obj['order_info'])
return cls(id, from_user, currency, total_amount, invoice_payload, shipping_option_id, order_info)
def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipping_option_id, order_info):
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

View File

@ -403,3 +403,9 @@ class TestTeleBot:
def test_not_string(self):
i1 = 10
assert not util.is_string(i1)
def test_send_video_note(self):
file_data = open('./test_data/test_video.mp4', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_video_note(CHAT_ID, file_data)
assert ret_msg.message_id