diff --git a/telebot/__init__.py b/telebot/__init__.py index c8c4f84..a751be3 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2121,8 +2121,33 @@ class TeleBot: :param mask_position: :return: """ - return apihelper.create_new_sticker_set(self.token, user_id, name, title, png_sticker, emojis, - contains_masks, mask_position) + return apihelper.create_new_sticker_set( + self.token, user_id, name, title, png_sticker, emojis, + contains_masks, mask_position, animated=False) + + + def create_new_animated_sticker_set( + self, user_id: int, name: str, title: str, + tgs_sticker: Union[Any, str], + emojis: str, + contains_masks: Optional[bool]=None, + mask_position: Optional[types.MaskPosition]=None) -> bool: + """ + Use this method to create new sticker set owned by a user. + The bot will be able to edit the created sticker set. + Returns True on success. + :param user_id: + :param name: + :param title: + :param tgs_sticker: + :param emojis: + :param contains_masks: + :param mask_position: + :return: + """ + return apihelper.create_new_sticker_set( + self.token, user_id, name, title, tgs_sticker, emojis, + contains_masks, mask_position, animated=True) def add_sticker_to_set( @@ -2137,7 +2162,25 @@ class TeleBot: :param mask_position: :return: """ - return apihelper.add_sticker_to_set(self.token, user_id, name, png_sticker, emojis, mask_position) + return apihelper.add_sticker_to_set( + self.token, user_id, name, png_sticker, emojis, mask_position, animated=False) + + + def add_sticker_to_animated_set( + self, user_id: int, name: str, tgs_sticker: Union[Any, str], + emojis: str, mask_position: Optional[types.MaskPosition]=None) -> bool: + """ + Use this method to add a new sticker to a set created by the bot. Returns True on success. + :param user_id: + :param name: + :param tgs_sticker: + :param emojis: + :param mask_position: + :return: + """ + return apihelper.add_sticker_to_set( + self.token, user_id, name, tgs_sticker, emojis, mask_position, animated=True) + def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 49a125a..106702e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -245,7 +245,7 @@ def send_message( payload['connect-timeout'] = timeout if entities: payload['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, method='post') @@ -422,7 +422,7 @@ def send_dice( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['connect-timeout'] = timeout - if 'allow_sending_without_reply': + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -455,7 +455,7 @@ def send_photo( payload['connect-timeout'] = timeout if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -473,7 +473,7 @@ def send_media_group( payload['reply_to_message_id'] = reply_to_message_id if timeout: payload['connect-timeout'] = timeout - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request( token, method_url, params=payload, @@ -499,7 +499,7 @@ def send_location( payload['proximity_alert_radius'] = proximity_alert_radius if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) @@ -573,7 +573,7 @@ def send_venue( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['connect-timeout'] = timeout - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply if google_place_id: payload['google_place_id'] = google_place_id @@ -600,7 +600,7 @@ def send_contact( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['connect-timeout'] = timeout - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -653,7 +653,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['height'] = height if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -693,7 +693,7 @@ def send_animation( payload['thumb'] = thumb if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -724,7 +724,7 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess payload['connect-timeout'] = timeout if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -760,7 +760,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m files = {'thumb': thumb} else: payload['thumb'] = thumb - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -803,7 +803,7 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['thumb'] = thumb if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -840,7 +840,7 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['thumb'] = thumb if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1157,7 +1157,7 @@ def send_game( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['connect-timeout'] = timeout - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -1290,7 +1290,7 @@ def send_invoice( payload['provider_data'] = provider_data if timeout: payload['connect-timeout'] = timeout - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -1388,15 +1388,16 @@ def upload_sticker_file(token, user_id, png_sticker): def create_new_sticker_set( - token, user_id, name, title, png_sticker, emojis, - contains_masks=None, mask_position=None): + token, user_id, name, title, sticker, emojis, + contains_masks=None, mask_position=None, animated=False): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} files = None - if not util.is_string(png_sticker): - files = {'png_sticker': png_sticker} + stype = 'tgs_sticker' if animated else 'png_sticker' + if not util.is_string(sticker): + files = {stype: sticker} else: - payload['png_sticker'] = png_sticker + payload[stype] = sticker if contains_masks is not None: payload['contains_masks'] = contains_masks if mask_position: @@ -1404,14 +1405,15 @@ def create_new_sticker_set( return _make_request(token, method_url, params=payload, files=files, method='post') -def add_sticker_to_set(token, user_id, name, png_sticker, emojis, mask_position): +def add_sticker_to_set(token, user_id, name, sticker, emojis, mask_position, animated=False): method_url = 'addStickerToSet' payload = {'user_id': user_id, 'name': name, 'emojis': emojis} files = None - if not util.is_string(png_sticker): - files = {'png_sticker': png_sticker} + stype = 'tgs_sticker' if animated else 'png_sticker' + if not util.is_string(sticker): + files = {stype: sticker} else: - payload['png_sticker'] = png_sticker + payload[stype] = sticker if mask_position: payload['mask_position'] = mask_position.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/types.py b/telebot/types.py index a134c5a..29e2b9d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import logging -from typing import Dict, List, Union +from typing import Dict, List, Optional, Union try: import ujson as json @@ -146,7 +146,7 @@ class ChatMemberUpdated(JsonDeserializable): self.date: int = date self.old_chat_member: ChatMember = old_chat_member self.new_chat_member: ChatMember = new_chat_member - self.invite_link: Union[ChatInviteLink, None] = invite_link + self.invite_link: Optional[ChatInviteLink] = invite_link class WebhookInfo(JsonDeserializable): @@ -461,49 +461,49 @@ class Message(JsonDeserializable): self.from_user: User = from_user self.date: int = date self.chat: Chat = chat - self.forward_from: Union[User, None] = None - self.forward_from_chat: Union[Chat, None] = None - self.forward_from_message_id: Union[int, None] = None - self.forward_signature: Union[str, None] = None - self.forward_sender_name: Union[str, None] = None - self.forward_date: Union[int, None] = None - self.reply_to_message: Union[Message, None] = None - self.via_bot: Union[User, None] = None - self.edit_date: Union[int, None] = None - self.media_group_id: Union[str, None] = None - self.author_signature: Union[str, None] = None - self.text: Union[str, None] = None - self.entities: Union[List[MessageEntity], None] = None - self.caption_entities: Union[List[MessageEntity], None] = None - self.audio: Union[Audio, None] = None - self.document: Union[Document, None] = None - self.photo: Union[List[PhotoSize], None] = None - self.sticker: Union[Sticker, None] = None - self.video: Union[Video, None] = None - self.video_note: Union[VideoNote, None] = None - self.voice: Union[Voice, None] = None - self.caption: Union[str, None] = None - self.contact: Union[Contact, None] = None - self.location: Union[Location, None] = None - self.venue: Union[Venue, None] = None - self.animation: Union[Animation, None] = None - self.dice: Union[Dice, None] = None - self.new_chat_member: Union[User, None] = None # Deprecated since Bot API 3.0. Not processed anymore - self.new_chat_members: Union[List[User], None] = None - self.left_chat_member: Union[User, None] = None - self.new_chat_title: Union[str, None] = None - self.new_chat_photo: Union[List[PhotoSize], None] = None - self.delete_chat_photo: Union[bool, None] = None - self.group_chat_created: Union[bool, None] = None - self.supergroup_chat_created: Union[bool, None] = None - self.channel_chat_created: Union[bool, None] = None - self.migrate_to_chat_id: Union[int, None] = None - self.migrate_from_chat_id: Union[int, None] = None - self.pinned_message: Union[Message, None] = None - self.invoice: Union[Invoice, None] = None - self.successful_payment: Union[SuccessfulPayment, None] = None - self.connected_website: Union[str, None] = None - self.reply_markup: Union[InlineKeyboardMarkup, None] = None + self.forward_from: Optional[User] = None + self.forward_from_chat: Optional[Chat] = None + self.forward_from_message_id: Optional[int] = None + self.forward_signature: Optional[str] = None + self.forward_sender_name: Optional[str] = None + self.forward_date: Optional[int] = None + self.reply_to_message: Optional[Message] = None + self.via_bot: Optional[User] = None + self.edit_date: Optional[int] = None + self.media_group_id: Optional[str] = None + self.author_signature: Optional[str] = None + self.text: Optional[str] = None + self.entities: Optional[List[MessageEntity]] = None + self.caption_entities: Optional[List[MessageEntity]] = None + self.audio: Optional[Audio] = None + self.document: Optional[Document] = None + self.photo: Optional[List[PhotoSize]] = None + self.sticker: Optional[Sticker] = None + self.video: Optional[Video] = None + self.video_note: Optional[VideoNote] = None + self.voice: Optional[Voice] = None + self.caption: Optional[str] = None + self.contact: Optional[Contact] = None + self.location: Optional[Location] = None + self.venue: Optional[Venue] = None + self.animation: Optional[Animation] = None + self.dice: Optional[Dice] = None + self.new_chat_member: Optional[User] = None # Deprecated since Bot API 3.0. Not processed anymore + self.new_chat_members: Optional[List[User]] = None + self.left_chat_member: Optional[User] = None + self.new_chat_title: Optional[str] = None + self.new_chat_photo: Optional[List[PhotoSize]] = None + self.delete_chat_photo: Optional[bool] = None + self.group_chat_created: Optional[bool] = None + self.supergroup_chat_created: Optional[bool] = None + self.channel_chat_created: Optional[bool] = None + self.migrate_to_chat_id: Optional[int] = None + self.migrate_from_chat_id: Optional[int] = None + self.pinned_message: Optional[Message] = None + self.invoice: Optional[Invoice] = None + self.successful_payment: Optional[SuccessfulPayment] = None + self.connected_website: Optional[str] = None + self.reply_markup: Optional[InlineKeyboardMarkup] = None for key in options: setattr(self, key, options[key]) self.json = json_string @@ -1314,7 +1314,7 @@ class InputTextMessageContent(Dictionaryable): json_dict['parse_mode'] = self.parse_mode if self.entities: json_dict['entities'] = MessageEntity.to_list_of_dicts(self.entities) - if self.disable_web_page_preview: + if self.disable_web_page_preview is not None: json_dict['disable_web_page_preview'] = self.disable_web_page_preview return json_dict @@ -1387,6 +1387,73 @@ class InputContactMessageContent(Dictionaryable): return json_dict +class InputInvoiceMessageContent(Dictionaryable): + def __init__(self, title, description, payload, provider_token, currency, prices, + max_tip_amount=None, suggested_tip_amounts=None, provider_data=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, + send_phone_number_to_provider=None, send_email_to_provider=None, + is_flexible=None): + self.title: str = title + self.description: str = description + self.payload: str = payload + self.provider_token: str = provider_token + self.currency: str = currency + self.prices: List[LabeledPrice] = prices + self.max_tip_amount: Optional[int] = max_tip_amount + self.suggested_tip_amounts: Optional[List[int]] = suggested_tip_amounts + self.provider_data: Optional[str] = provider_data + self.photo_url: Optional[str] = photo_url + self.photo_size: Optional[int] = photo_size + self.photo_width: Optional[int] = photo_width + self.photo_height: Optional[int] = photo_height + self.need_name: Optional[bool] = need_name + self.need_phone_number: Optional[bool] = need_phone_number + self.need_email: Optional[bool] = need_email + self.need_shipping_address: Optional[bool] = need_shipping_address + self.send_phone_number_to_provider: Optional[bool] = send_phone_number_to_provider + self.send_email_to_provider: Optional[bool] = send_email_to_provider + self.is_flexible: Optional[bool] = is_flexible + + def to_dict(self): + json_dict = { + 'title': self.title, + 'description': self.description, + 'payload': self.payload, + 'provider_token': self.provider_token, + 'currency': self.currency, + 'prices': [LabeledPrice.to_dict(lp) for lp in self.prices] + } + if self.max_tip_amount: + json_dict['max_tip_amount'] = self.max_tip_amount + if self.suggested_tip_amounts: + json_dict['suggested_tip_amounts'] = self.suggested_tip_amounts + if self.provider_data: + json_dict['provider_data'] = self.provider_data + if self.photo_url: + json_dict['photo_url'] = self.photo_url + if self.photo_size: + json_dict['photo_size'] = self.photo_size + if self.photo_width: + json_dict['photo_width'] = self.photo_width + if self.photo_height: + json_dict['photo_height'] = self.photo_height + if self.need_name is not None: + json_dict['need_name'] = self.need_name + if self.need_phone_number is not None: + json_dict['need_phone_number'] = self.need_phone_number + if self.need_email is not None: + json_dict['need_email'] = self.need_email + if self.need_shipping_address is not None: + json_dict['need_shipping_address'] = self.need_shipping_address + if self.send_phone_number_to_provider is not None: + json_dict['send_phone_number_to_provider'] = self.send_phone_number_to_provider + if self.send_email_to_provider is not None: + json_dict['send_email_to_provider'] = self.send_email_to_provider + if self.is_flexible is not None: + json_dict['is_flexible'] = self.is_flexible + + class ChosenInlineResult(JsonDeserializable): @classmethod def de_json(cls, json_string): @@ -2135,10 +2202,13 @@ class LabeledPrice(JsonSerializable): self.label: str = label self.amount: int = amount - def to_json(self): - return json.dumps({ + def to_dict(self): + return { 'label': self.label, 'amount': self.amount - }) + } + + def to_json(self): + return json.dumps(self.to_dict()) class Invoice(JsonDeserializable):