diff --git a/include/tgbotxx/Api.hpp b/include/tgbotxx/Api.hpp index 94b25fce7..3999e0887 100644 --- a/include/tgbotxx/Api.hpp +++ b/include/tgbotxx/Api.hpp @@ -44,6 +44,8 @@ namespace tgbotxx { struct SentWebAppMessage; struct ReplyParameters; struct StickerSet; + struct InputSticker; + struct MaskPosition; /// @brief Api Methods https://core.telegram.org/bots/api#available-methods /// @note All methods in the Bot API are case-insensitive. @@ -243,7 +245,7 @@ namespace tgbotxx { std::int32_t duration = 0, const std::string& performer = "", const std::string& title = "", - std::optional> thumbnail = std::nullopt, + const std::optional>& thumbnail = std::nullopt, bool disableNotification = false, bool protectContent = false, std::int32_t replyToMessageId = 0, @@ -280,7 +282,7 @@ namespace tgbotxx { Ptr sendDocument(const std::variant& chatId, const std::variant& document, std::int32_t messageThreadId = 0, - std::optional> thumbnail = std::nullopt, + const std::optional>& thumbnail = std::nullopt, const std::string& caption = "", const std::string& parseMode = "", const std::vector>& captionEntities = std::vector>(), @@ -328,7 +330,7 @@ namespace tgbotxx { std::int32_t duration = 0, std::int32_t width = 0, std::int32_t height = 0, - std::optional> thumbnail = std::nullopt, + const std::optional>& thumbnail = std::nullopt, const std::string& caption = "", const std::string& parseMode = "", const std::vector>& captionEntities = std::vector>(), @@ -377,7 +379,7 @@ namespace tgbotxx { std::int32_t duration = 0, std::int32_t width = 0, std::int32_t height = 0, - std::optional> thumbnail = std::nullopt, + const std::optional>& thumbnail = std::nullopt, const std::string& caption = "", const std::string& parseMode = "", const std::vector>& captionEntities = std::vector>(), @@ -457,7 +459,7 @@ namespace tgbotxx { std::int32_t messageThreadId = 0, std::int32_t duration = 0, std::int32_t length = 0, - std::optional> thumbnail = std::nullopt, + const std::optional>& thumbnail = std::nullopt, bool disableNotification = false, bool protectContent = false, std::int32_t replyToMessageId = 0, @@ -1784,6 +1786,133 @@ namespace tgbotxx { /// @ref https://core.telegram.org/bots/api#getstickerset Ptr getStickerSet(const std::string& name) const; + /// @brief Use this method to get information about custom emoji stickers by their identifiers. + /// @param customEmojiIds List of custom emoji identifiers. At most 200 custom emoji identifiers can be specified. + /// @returns an Array of Sticker objects on success. + /// @throws Exception on failure + /// @ref https://core.telegram.org/bots/api#getcustomemojistickers + std::vector> getCustomEmojiStickers(const std::vector& customEmojiIds) const; + + /// @brief Use this method to upload a file with a sticker for later use in the createNewStickerSet and + /// addStickerToSet methods (the file can be used multiple times). + /// @param userId User identifier of sticker file owner + /// @param sticker A file with the sticker in .WEBP, .PNG, .TGS, or .WEBM format. + /// See https://core.telegram.org/stickers for technical requirements. [More information on Sending Files »](https://core.telegram.org/bots/api#sending-files) + /// @param stickerFormat Format of the sticker, must be one of “static”, “animated”, “video” + /// @returns the uploaded File on success. + /// @throws Exception on failure + /// @ref https://core.telegram.org/bots/api#uploadstickerfile + Ptr uploadStickerFile(std::int64_t userId, + const cpr::File& sticker, + const std::string& stickerFormat) const; + + /// @brief Use this method to create a new sticker set owned by a user. The bot will be able to edit the sticker set thus created. + /// @param userId User identifier of created sticker set owner + /// @param name Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., animals). + /// Can contain only English letters, digits and underscores. + /// Must begin with a letter, can't contain consecutive underscores and must end in "_by_". + /// is case insensitive. 1-64 characters. + /// @param title Sticker set title, 1-64 characters + /// @param stickers A JSON-serialized list of 1-50 initial stickers to be added to the sticker set + /// @param stickerFormat Format of stickers in the set, must be one of “static”, “animated”, “video” + /// @param stickerType Optional. Type of stickers in the set, pass “regular”, “mask”, or “custom_emoji”. By default, a regular sticker set is created. + /// @param needsRepainting Optional. Pass True if stickers in the sticker set must be repainted to the color of text when used in messages, + /// the accent color if used as emoji status, white on chat photos, or another appropriate color based on context; for custom emoji sticker sets only + /// @returns True on success. + /// @throws Exception on failure + /// @ref https://core.telegram.org/bots/api#createnewstickerset + bool createNewStickerSet(std::int64_t userId, + const std::string& name, + const std::string& title, + const std::vector>& stickers, + const std::string& stickerFormat, + const std::string& stickerType = "regular", + bool needsRepainting = false) const; + + /// @brief Use this method to add a new sticker to a set created by the bot. + /// The format of the added sticker must match the format of the other stickers in the set. + /// Emoji sticker sets can have up to 200 stickers. Animated and video sticker sets can have up to 50 stickers. + /// Static sticker sets can have up to 120 stickers. + /// @param userId User identifier of created sticker set owner + /// @param name Sticker set name + /// @param sticker A JSON-serialized object with information about the added sticker. If exactly the same sticker had already been added to the set, then the set isn't changed. + /// @returns True on success. + /// @throws Exception on failure + /// @ref https://core.telegram.org/bots/api#addstickertoset + bool addStickerToSet(std::int64_t userId, + const std::string& name, + const Ptr& sticker) const; + + /// @brief Use this method to move a sticker in a set created by the bot to a specific position. + /// @param sticker File identifier of the sticker + /// @param position New sticker position in the set, zero-based + /// @returns True on success. + /// @throws Exception on failure + /// @ref https://core.telegram.org/bots/api#setstickerpositioninset + bool setStickerPositionInSet(const std::string& sticker, std::int32_t position) const; + + /// @brief Use this method to delete a sticker from a set created by the bot. + /// @param sticker File identifier of the sticker + /// @returns True on success. + /// @throws Exception on failure + /// @ref https://core.telegram.org/bots/api#deletestickerfromset + bool deleteStickerFromSet(const std::string& sticker) const; + + /// @brief Use this method to change the list of emoji assigned to a regular or custom emoji sticker. + /// The sticker must belong to a sticker set created by the bot. + /// @param sticker File identifier of the sticker + /// @param emojiList A JSON-serialized list of 1-20 emoji associated with the sticker + /// @returns True on success. + /// @throws Exception on failure + /// @ref https://core.telegram.org/bots/api#setstickeremojilist + bool setStickerEmojiList(const std::string& sticker, const std::vector& emojiList) const; + + /// @brief Use this method to change search keywords assigned to a regular or custom emoji sticker. + /// The sticker must belong to a sticker set created by the bot. + /// @param sticker File identifier of the sticker + /// @param keywords Optional. A JSON-serialized list of 0-20 search keywords for the sticker with total length of up to 64 characters + /// @returns True on success. + /// @throws Exception on failure + /// @ref https://core.telegram.org/bots/api#setstickerkeywords + bool setStickerKeywords(const std::string& sticker, const std::vector& keywords = std::vector()) const; + + /// @brief Use this method to change the mask position of a mask sticker. + /// The sticker must belong to a sticker set that was created by the bot. + /// @param sticker File identifier of the sticker + /// @param maskPosition Optional. A JSON-serialized object with the position where the mask should be placed on faces. Omit the parameter to remove the mask position. + /// @returns True on success. + /// @throws Exception on failure + /// @ref https://core.telegram.org/bots/api#setstickermaskposition + bool setStickerMaskPosition(const std::string& sticker, + const Ptr& maskPosition = nullptr) const; + + /// @brief Use this method to set the title of a created sticker set. + /// @param name Sticker set name + /// @param title Sticker set title, 1-64 characters + /// @returns True on success. + /// @throws Exception on failure + /// @ref https://core.telegram.org/bots/api#setstickersettitle + bool setStickerSetTitle(const std::string& name, const std::string& title) const; + + /// @brief Use this method to set the thumbnail of a regular or mask sticker set. + /// The format of the thumbnail file must match the format of the stickers in the set. + /// @param name Sticker set name + /// @param userId User identifier of created sticker set owner + /// @param thumbnail Optional. A .WEBP or .PNG image with the thumbnail, must be up to 128 kilobytes in size and have a width and height + /// of exactly 100px, or a .TGS animation with a thumbnail up to 32 kilobytes in size + /// (see https://core.telegram.org/stickers#animated-sticker-requirements for animated sticker technical requirements), + /// or a WEBM video with the thumbnail up to 32 kilobytes in size; see https://core.telegram.org/stickers#video-sticker-requirements + /// for video sticker technical requirements. Pass a file_id as a String to send a file that already exists on the Telegram servers, + /// pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. + /// More information on Sending Files ». Animated and video sticker set thumbnails can't be uploaded via HTTP URL. + /// If omitted, then the thumbnail is dropped and the first sticker is used as the thumbnail. + /// @returns True on success. + /// @throws Exception on failure + /// @ref https://core.telegram.org/bots/api#setstickersetthumbnail + bool setStickerSetThumbnail(const std::string& name, + const std::string& title, + const std::optional>& thumbnail = std::nullopt) const; + public: /// Inline mode methods. Methods and objects used in the inline mode are described in the Inline mode section. https://core.telegram.org/bots/api#inline-mode /// @brief Use this method to send answers to an inline query. diff --git a/src/Api.cpp b/src/Api.cpp index 91858e8eb..9f400d00f 100644 --- a/src/Api.cpp +++ b/src/Api.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -45,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +77,7 @@ #include #include #include +#include #include #include #include @@ -319,7 +320,7 @@ Ptr Api::sendAudio(const std::variant& chatI std::int32_t duration, const std::string& performer, const std::string& title, - std::optional> thumbnail, + const std::optional>& thumbnail, bool disableNotification, bool protectContent, std::int32_t replyToMessageId, @@ -380,7 +381,7 @@ Ptr Api::sendAudio(const std::variant& chatI Ptr Api::sendDocument(const std::variant& chatId, const std::variant& document, std::int32_t messageThreadId, - std::optional> thumbnail, + const std::optional>& thumbnail, const std::string& caption, const std::string& parseMode, const std::vector>& captionEntities, @@ -478,7 +479,7 @@ Ptr Api::sendVideo(const std::variant& chatI std::int32_t duration, std::int32_t width, std::int32_t height, - std::optional> thumbnail, + const std::optional>& thumbnail, const std::string& caption, const std::string& parseMode, const std::vector>& captionEntities, @@ -552,7 +553,7 @@ Ptr Api::sendAnimation(const std::variant& c std::int32_t duration, std::int32_t width, std::int32_t height, - std::optional> thumbnail, + const std::optional>& thumbnail, const std::string& caption, const std::string& parseMode, const std::vector>& captionEntities, @@ -674,7 +675,7 @@ Ptr Api::sendVideoNote(const std::variant& c std::int32_t messageThreadId, std::int32_t duration, std::int32_t length, - std::optional> thumbnail, + const std::optional>& thumbnail, bool disableNotification, bool protectContent, std::int32_t replyToMessageId, @@ -2129,7 +2130,6 @@ Ptr Api::sendSticker(const std::variant& cha return message; } - Ptr Api::getStickerSet(const std::string& name) const { cpr::Multipart data{}; data.parts.reserve(1); @@ -2139,6 +2139,141 @@ Ptr Api::getStickerSet(const std::string& name) const { Ptr stickerSet(new StickerSet(stickerSetObj)); return stickerSet; } + +std::vector> Api::getCustomEmojiStickers(const std::vector& customEmojiIds) const { + cpr::Multipart data{}; + data.parts.reserve(1); + data.parts.emplace_back("custom_emoji_ids", nl::json(customEmojiIds).dump()); + nl::json stickersArray = sendRequest("getCustomEmojiStickers", data); + + std::vector> result; + result.reserve(stickersArray.size()); + for (const nl::json& stickerObj: stickersArray) { + Ptr sticker = makePtr(stickerObj); + result.push_back(std::move(sticker)); + } + return result; +} + +Ptr Api::uploadStickerFile(std::int64_t userId, + const cpr::File& sticker, + const std::string& stickerFormat) const { + cpr::Multipart data{}; + data.parts.reserve(3); + data.parts.emplace_back("user_id", std::to_string(userId)); + data.parts.emplace_back("sticker", cpr::Files{sticker}); + data.parts.emplace_back("sticker_format", stickerFormat); + + nl::json fileObj = sendRequest("uploadStickerFile", data); + Ptr file(new File(fileObj)); + return file; +} + +bool Api::createNewStickerSet(std::int64_t userId, + const std::string& name, + const std::string& title, + const std::vector>& stickers, + const std::string& stickerFormat, + const std::string& stickerType, + bool needsRepainting) const { + cpr::Multipart data{}; + data.parts.reserve(7); + data.parts.emplace_back("user_id", std::to_string(userId)); + data.parts.emplace_back("name", name); + data.parts.emplace_back("title", title); + nl::json stickersJson = nl::json::array(); + for (const Ptr& inputSticker: stickers) + stickersJson.push_back(inputSticker->toJson()); + data.parts.emplace_back("stickers", stickersJson.dump()); + data.parts.emplace_back("sticker_format", stickerFormat); + data.parts.emplace_back("sticker_type", stickerType); + if (needsRepainting) + data.parts.emplace_back("needs_repainting", needsRepainting); + + return sendRequest("createNewStickerSet", data); +} + +bool Api::addStickerToSet(std::int64_t userId, + const std::string& name, + const Ptr& sticker) const { + cpr::Multipart data{}; + data.parts.reserve(3); + data.parts.emplace_back("user_id", std::to_string(userId)); + data.parts.emplace_back("name", name); + data.parts.emplace_back("sticker", sticker->toJson().dump()); + return sendRequest("addStickerToSet", data); +} + +bool Api::setStickerPositionInSet(const std::string& sticker, std::int32_t position) const { + cpr::Multipart data{}; + data.parts.reserve(2); + data.parts.emplace_back("sticker", sticker); + data.parts.emplace_back("position", position); + return sendRequest("setStickerPositionInSet", data); +} + +bool Api::deleteStickerFromSet(const std::string& sticker) const { + cpr::Multipart data{}; + data.parts.reserve(1); + data.parts.emplace_back("sticker", sticker); + return sendRequest("deleteStickerFromSet", data); +} + +bool Api::setStickerEmojiList(const std::string& sticker, const std::vector& emojiList) const { + cpr::Multipart data{}; + data.parts.reserve(2); + data.parts.emplace_back("sticker", sticker); + data.parts.emplace_back("emoji_list", nl::json(emojiList).dump()); + return sendRequest("setStickerEmojiList", data); +} + +bool Api::setStickerKeywords(const std::string& sticker, const std::vector& keywords) const { + cpr::Multipart data{}; + data.parts.reserve(2); + data.parts.emplace_back("sticker", sticker); + if (not keywords.empty()) { + data.parts.emplace_back("keywords", nl::json(keywords).dump()); + } + return sendRequest("setStickerKeywords", data); +} + +bool Api::setStickerMaskPosition(const std::string& sticker, + const Ptr& maskPosition) const { + cpr::Multipart data{}; + data.parts.reserve(2); + data.parts.emplace_back("sticker", sticker); + if (maskPosition) { + data.parts.emplace_back("mask_position", maskPosition->toJson().dump()); + } + return sendRequest("setStickerMaskPosition", data); +} + +bool Api::setStickerSetTitle(const std::string& name, const std::string& title) const { + cpr::Multipart data{}; + data.parts.reserve(2); + data.parts.emplace_back("name", name); + data.parts.emplace_back("title", title); + return sendRequest("setStickerSetTitle", data); +} + +bool Api::setStickerSetThumbnail(const std::string& name, + const std::string& title, + const std::optional>& thumbnail) const { + cpr::Multipart data{}; + data.parts.reserve(3); + data.parts.emplace_back("name", name); + data.parts.emplace_back("title", title); + if (thumbnail.has_value()) { + if (thumbnail->index() == 0) /* cpr::File */ { + const cpr::File& file = std::get(*thumbnail); + data.parts.emplace_back("thumbnail", cpr::Files{file}); + } else /* std::string (fileId or Url) */ { + const std::string& fileIdOrUrl = std::get(*thumbnail); + data.parts.emplace_back("thumbnail", fileIdOrUrl); + } + } + return sendRequest("setStickerSetTitle", data); +} ///////////////////////////////////////////////////////////////////////////////////////////////// void Api::setUrl(const std::string& url) noexcept { m_apiUrl = url;