diff --git a/aiogram/bot/api.py b/aiogram/bot/api.py index 29edff20..2ed3f0d4 100644 --- a/aiogram/bot/api.py +++ b/aiogram/bot/api.py @@ -100,6 +100,12 @@ class Methods: SEND_AUDIO = 'sendAudio' SEND_DOCUMENT = 'sendDocument' SEND_STICKER = 'sendSticker' + GET_STICKER_SET = 'getStickerSet' + UPLOAD_STICKER_FILE = 'uploadStickerFile' + CREATE_NEW_STICKER_SET = 'createNewStickerSet' + ADD_STICKER_TO_SET = 'addStickerToSet' + SET_STICKER_POSITION_IN_SET = 'setStickerPositionInSet' + DELETE_STICKER_FROM_SET = 'deleteStickerFromSet' SEND_VIDEO = 'sendVideo' SEND_VOICE = 'sendVoice' SEND_VIDEO_NOTE = 'sendVideoNote' diff --git a/aiogram/bot/base.py b/aiogram/bot/base.py index c957bf44..bb95eac3 100644 --- a/aiogram/bot/base.py +++ b/aiogram/bot/base.py @@ -111,30 +111,16 @@ class BaseBot: finally: self.destroy_temp_session(session) - async def _send_file(self, file_type, file, payload): - methods = { - 'photo': api.Methods.SEND_PHOTO, - 'audio': api.Methods.SEND_AUDIO, - 'document': api.Methods.SEND_DOCUMENT, - 'sticker': api.Methods.SEND_STICKER, - 'video': api.Methods.SEND_VIDEO, - 'voice': api.Methods.SEND_VOICE, - 'video_note': api.Methods.SEND_VIDEO_NOTE - } - - method = methods[file_type] + async def _send_file(self, file_type, method, file, payload): if isinstance(file, str): payload[file_type] = file req = self.request(method, payload) elif isinstance(file, io.IOBase): data = {file_type: file.read()} - req = self.request(api.Methods.SEND_PHOTO, payload, data) - elif isinstance(file, bytes): - data = {file_type: file} - req = self.request(api.Methods.SEND_PHOTO, payload, data) + req = self.request(method, payload, data) else: data = {file_type: file} - req = self.request(api.Methods.SEND_PHOTO, payload, data) + req = self.request(method, payload, data) return await req @@ -181,8 +167,8 @@ class BaseBot: if reply_markup and isinstance(reply_markup, dict): reply_markup = json.dumps(reply_markup) - payload = generate_payload(**locals()) - return await self._send_file(_message_type, photo, payload) + payload = generate_payload(**locals(), exclude=[_message_type]) + return await self._send_file(_message_type, api.Methods.SEND_PHOTO, photo, payload) async def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None, title=None, disable_notification=None, reply_to_message_id=None, reply_markup=None) -> dict: @@ -190,8 +176,8 @@ class BaseBot: if reply_markup and isinstance(reply_markup, dict): reply_markup = json.dumps(reply_markup) - payload = generate_payload(**locals()) - return await self._send_file(_message_type, audio, payload) + payload = generate_payload(**locals(), exclude=[_message_type]) + return await self._send_file(_message_type, api.Methods.SEND_AUDIO, audio, payload) async def send_document(self, chat_id, document, caption=None, disable_notification=None, reply_to_message_id=None, reply_markup=None) -> dict: @@ -199,8 +185,8 @@ class BaseBot: if reply_markup and isinstance(reply_markup, dict): reply_markup = json.dumps(reply_markup) - payload = generate_payload(**locals()) - return await self._send_file(_message_type, document, payload) + payload = generate_payload(**locals(), exclude=[_message_type]) + return await self._send_file(_message_type, api.Methods.SEND_DOCUMENT, document, payload) async def send_sticker(self, chat_id, sticker, disable_notification=None, reply_to_message_id=None, reply_markup=None) -> dict: @@ -208,8 +194,43 @@ class BaseBot: if reply_markup and isinstance(reply_markup, dict): reply_markup = json.dumps(reply_markup) + payload = generate_payload(**locals(), exclude=[_message_type]) + return await self._send_file(_message_type, api.Methods.SEND_STICKER, sticker, payload) + + async def get_sticker_set(self, name: str) -> dict: payload = generate_payload(**locals()) - return await self._send_file(_message_type, sticker, payload) + return await self.request(api.Methods.GET_STICKER_SET, payload) + + async def upload_sticker_file(self, user_id, png_sticker) -> dict: + _message_type = 'png_sticker' + payload = generate_payload(**locals(), exclude=['png_sticker']) + return await self._send_file(_message_type, api.Methods.UPLOAD_STICKER_FILE, png_sticker, payload) + + async def create_new_sticker_set(self, name: str, title: str, png_sticker, emojis: str, is_mask: bool = None, + mask_position: dict or str = None) -> bool: + _message_type = 'png_sticker' + if mask_position and isinstance(mask_position, dict): + mask_position = json.dumps(mask_position) + + payload = generate_payload(**locals(), exclude=[_message_type]) + return await self._send_file(_message_type, api.Methods.CREATE_NEW_STICKER_SET, png_sticker, payload) + + async def add_sticker_to_set(self, user_id: int, name: str, png_sticker, emojis: str, + mask_position: str or dict) -> bool: + _message_type = 'png_sticker' + if mask_position and isinstance(mask_position, dict): + mask_position = json.dumps(mask_position) + payload = generate_payload(**locals(), exclude=[_message_type]) + + return await self._send_file(_message_type, api.Methods.ADD_STICKER_TO_SET, png_sticker, payload) + + async def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: + payload = generate_payload(**locals()) + return await self.request(api.Methods.SET_STICKER_POSITION_IN_SET, payload) + + async def delete_sticker_from_set(self, sticker: str) -> bool: + payload = generate_payload(**locals()) + return await self.request(api.Methods.DELETE_STICKER_FROM_SET, payload) async def send_video(self, chat_id, video, duration=None, width=None, height=None, caption=None, disable_notification=None, reply_to_message_id=None, reply_markup=None) -> dict: @@ -217,8 +238,8 @@ class BaseBot: if reply_markup and isinstance(reply_markup, dict): reply_markup = json.dumps(reply_markup) - payload = generate_payload(**locals()) - return await self._send_file(_message_type, video, payload) + payload = generate_payload(**locals(), exclude=[_message_type]) + return await self._send_file(_message_type, api.Methods.SEND_VIDEO, video, payload) async def send_voice(self, chat_id, voice, caption=None, duration=None, disable_notification=None, reply_to_message_id=None, reply_markup=None) -> dict: @@ -226,8 +247,8 @@ class BaseBot: if reply_markup and isinstance(reply_markup, dict): reply_markup = json.dumps(reply_markup) - payload = generate_payload(**locals()) - return await self._send_file(_message_type, voice, payload) + payload = generate_payload(**locals(), exclude=[_message_type]) + return await self._send_file(_message_type, api.Methods.SEND_VOICE, voice, payload) async def send_video_note(self, chat_id, video_note, duration=None, length=None, disable_notification=None, reply_to_message_id=None, reply_markup=None) -> dict: @@ -235,8 +256,8 @@ class BaseBot: if reply_markup and isinstance(reply_markup, dict): reply_markup = json.dumps(reply_markup) - payload = generate_payload(**locals()) - return await self._send_file(_message_type, video_note, payload) + payload = generate_payload(**locals(), exclude=[_message_type]) + return await self._send_file(_message_type, api.Methods.SEND_VIDEO_NOTE, video_note, payload) async def send_location(self, chat_id, latitude, longitude, disable_notification=None, reply_to_message_id=None, reply_markup=None) -> dict: @@ -407,4 +428,4 @@ class BaseBot: inline_message_id: str = None) -> dict: payload = generate_payload(**locals()) - return await self.request(api.Methods.GET_GAME_HIGH_SCORES, payload) \ No newline at end of file + return await self.request(api.Methods.GET_GAME_HIGH_SCORES, payload) diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index fe4b84ae..6ea9cc1f 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -1,7 +1,7 @@ import json -from .. import types from .base import BaseBot +from .. import types class Bot(BaseBot): @@ -246,6 +246,80 @@ class Bot(BaseBot): reply_markup) return self.prepare_object(types.Message.de_json(message)) + async def get_sticker_set(self, name: str) -> types.StickerSet: + """ + Use this method to get a sticker set. + + :param name: str + :return: :class:`aiogram.types.StickerSet` + """ + return self.prepare_object(types.StickerSet.deserialize(await super(Bot, self).get_sticker_set(name))) + + async def upload_sticker_file(self, user_id: int, png_sticker) -> types.File: + """ + Use this method to upload a .png file with a sticker for later use in + createNewStickerSet and addStickerToSet methods (can be used multiple times). + + :param user_id: int + :param png_sticker: InputFile + :return: :class:`aiogram.types.File` + """ + file = super(Bot, self).upload_sticker_file(user_id, png_sticker) + return self.prepare_object(types.File.deserialize(await file)) + + async def create_new_sticker_set(self, name: str, title: str, png_sticker, emojis: str, is_mask: bool = None, + mask_position: types.MaskPosition or dict or str = 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. + + :param name: + :param title: + :param png_sticker: + :param emojis: + :param is_mask: + :param mask_position: + :return: + """ + if isinstance(mask_position, types.MaskPosition): + mask_position = json.dumps(mask_position.to_json()) + + return await super(Bot, self).create_new_sticker_set(name, title, png_sticker, emojis, is_mask, mask_position) + + async def add_sticker_to_set(self, user_id: int, name: str, png_sticker, emojis: str, + mask_position: str or dict = None) -> bool: + """ + Use this method to add a new sticker to a set created by the bot. + + :param user_id: + :param name: + :param png_sticker: + :param emojis: + :param mask_position: + :return: + """ + if isinstance(mask_position, types.MaskPosition): + mask_position = json.dumps(mask_position.to_json()) + return await super(Bot, self).add_sticker_to_set(user_id, name, png_sticker, emojis, mask_position) + + async def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: + """ + Use this method to move a sticker in a set created by the bot to a specific position. + + :param sticker: str + :param position: int + :return: True on success. + """ + return await super(Bot, self).set_sticker_position_in_set(sticker, position) + + async def delete_sticker_from_set(self, sticker: str) -> bool: + """ + Use this method to delete a sticker from a set created by the bot. + + :param sticker: str + :return: True on success + """ + return await super(Bot, self).delete_sticker_from_set(sticker) + async def send_video(self, chat_id, video, duration=None, width=None, height=None, caption=None, disable_notification=None, reply_to_message_id=None, reply_markup=None) -> types.Message: """ diff --git a/aiogram/types/__init__.py b/aiogram/types/__init__.py index 52ad44fe..c43b3bc8 100644 --- a/aiogram/types/__init__.py +++ b/aiogram/types/__init__.py @@ -24,6 +24,7 @@ from .inline_query_result import InlineQueryResult, InlineQueryResultArticle, In from .invoice import Invoice from .labeled_price import LabeledPrice from .location import Location +from .mask_position import MaskPosition from .message import Message, ContentType, ParseMode from .message_entity import MessageEntity from .order_info import OrderInfo @@ -34,6 +35,7 @@ from .shipping_address import ShippingAddress from .shipping_option import ShippingOption from .shipping_query import ShippingQuery from .sticker import Sticker +from .sticker_set import StickerSet from .successful_payment import SuccessfulPayment from .update import Update from .user import User @@ -99,6 +101,7 @@ __all__ = [ 'KeyboardButton', 'LabeledPrice', 'Location', + 'MaskPosition' 'Message', 'MessageEntity', 'OrderInfo', @@ -111,6 +114,7 @@ __all__ = [ 'ShippingOption', 'ShippingQuery', 'Sticker', + 'StickerSet' 'SuccessfulPayment', 'Update', 'User', diff --git a/aiogram/types/mask_position.py b/aiogram/types/mask_position.py new file mode 100644 index 00000000..10d65487 --- /dev/null +++ b/aiogram/types/mask_position.py @@ -0,0 +1,15 @@ +from .base import Serializable + + +class MaskPosition(Serializable): + """ + This object describes the position on faces where a mask should be placed by default. + + https://core.telegram.org/bots/api#maskposition + """ + + def __init__(self, point, x_shift, y_shift, zoom): + self.point: str = point + self.x_shift: float = x_shift + self.y_shift: float = y_shift + self.zoom: float = zoom diff --git a/aiogram/types/sticker.py b/aiogram/types/sticker.py index 62eb26be..e8af70f0 100644 --- a/aiogram/types/sticker.py +++ b/aiogram/types/sticker.py @@ -1,5 +1,6 @@ from .base import Deserializable from .photo_size import PhotoSize +from .mask_position import MaskPosition class Sticker(Deserializable): @@ -8,12 +9,15 @@ class Sticker(Deserializable): https://core.telegram.org/bots/api#sticker """ - def __init__(self, file_id, width, height, thumb, emoji, file_size): + + def __init__(self, file_id, width, height, thumb, emoji, set_name, mask_position, file_size): self.file_id: str = file_id self.width: int = width self.height: int = height self.thumb: PhotoSize = thumb self.emoji: str = emoji + self.set_name: str = set_name + self.mask_position: MaskPosition = mask_position self.file_size: int = file_size @classmethod @@ -25,6 +29,8 @@ class Sticker(Deserializable): height = raw_data.get('height') thumb = PhotoSize.deserialize(raw_data.get('thumb')) emoji = raw_data.get('emoji') + set_name = raw_data.get('set_name') + mask_position = MaskPosition.deserialize(raw_data.get('mask_position')) file_size = raw_data.get('file_size') - return Sticker(file_id, width, height, thumb, emoji, file_size) + return Sticker(file_id, width, height, thumb, emoji, set_name, mask_position, file_size) diff --git a/aiogram/types/sticker_set.py b/aiogram/types/sticker_set.py new file mode 100644 index 00000000..c0e89b84 --- /dev/null +++ b/aiogram/types/sticker_set.py @@ -0,0 +1,27 @@ +from .base import Deserializable +from .sticker import Sticker + + +class StickerSet(Deserializable): + """ + This object represents a sticker set. + + https://core.telegram.org/bots/api#stickerset + """ + + def __init__(self, name, title, is_mask, stickers): + self.name: str = name + self.title: str = title + self.is_mask: bool = is_mask + self.stickers: [Sticker] = stickers + + @classmethod + def de_json(cls, raw_data): + raw_data = cls.check_json(raw_data) + + name = raw_data.get('name') + title = raw_data.get('title') + is_mask = raw_data.get('is_mask') + stickers = Sticker.deserialize(raw_data.get('stickers')) + + return StickerSet(name, title, is_mask, stickers)