diff --git a/include/tgbotxx/Api.hpp b/include/tgbotxx/Api.hpp index f893d0269..94b25fce7 100644 --- a/include/tgbotxx/Api.hpp +++ b/include/tgbotxx/Api.hpp @@ -42,6 +42,8 @@ namespace tgbotxx { struct LabeledPrice; struct ShippingOption; struct SentWebAppMessage; + struct ReplyParameters; + struct StickerSet; /// @brief Api Methods https://core.telegram.org/bots/api#available-methods /// @note All methods in the Bot API are case-insensitive. @@ -63,6 +65,7 @@ namespace tgbotxx { cpr::Timeout m_downloadFilesTimeout = DEFAULT_DOWNLOAD_FILES_TIMEOUT; /// Api files download timeout friend class Bot; + public: /// @brief Constructs Api object. /// @param token Bot Token from FatherBot. @@ -232,7 +235,7 @@ namespace tgbotxx { /// @note For sending voice messages, use the sendVoice method instead. /// @ref https://core.telegram.org/bots/api#sendaudio Ptr sendAudio(const std::variant& chatId, - std::variant audio, + const std::variant& audio, std::int32_t messageThreadId = 0, const std::string& caption = "", const std::string& parseMode = "", @@ -1748,6 +1751,40 @@ namespace tgbotxx { /// @ref https://core.telegram.org/bots/api#deletemessage bool deleteMessage(const std::variant& chatId, std::int32_t messageId) const; + public: /// Stickers + /// @brief Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. + /// @param chatId Unique identifier for the target chat or username of the target channel (in the format @channelusername) + /// @param sticker Sticker to send. + /// - Pass a file_id std::string to send a file that exists on the Telegram servers (recommended), + /// - Pass an HTTP URL as an std::string for Telegram to get a .WEBP sticker from the Internet, or + /// - Pass a cpr::File to upload a new .WEBP or .TGS sticker + /// @param messageThreadId Optional. Unique identifier for the target message thread (topic) of the forum; for forum supergroups only + /// @param emoji Optional. Emoji associated with the sticker; only for just uploaded stickers + /// @param disableNotification Optional. Sends the message silently. Users will receive a notification with no sound. + /// @param protectContent Optional. Protects the contents of the sent message from forwarding and saving + /// @param replyParameters Optional. Description of the message to reply to + /// @param replyMarkup Optional. 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. + /// One of InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or ForceReply. + /// @returns sent Message object on success. + /// @throws Exception on failure + /// @ref https://core.telegram.org/bots/api#sendsticker + Ptr sendSticker(const std::variant& chatId, + const std::variant& sticker, + std::int32_t messageThreadId = 0, + const std::string& emoji = "", + bool disableNotification = false, + bool protectContent = false, + const Ptr& replyParameters = nullptr, + const Ptr& replyMarkup = nullptr) const; + + /// @brief Use this method to get a sticker set. On success, a StickerSet object is returned. + /// @param name Name of the sticker set + /// @returns StickerSet object is returned on success. + /// @throws Exception on failure + /// @ref https://core.telegram.org/bots/api#getstickerset + Ptr getStickerSet(const std::string& name) 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. /// No more than 50 results per query are allowed. diff --git a/src/Api.cpp b/src/Api.cpp index 5c51ae032..91858e8eb 100644 --- a/src/Api.cpp +++ b/src/Api.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -309,7 +311,7 @@ Ptr Api::sendPhoto(const std::variant& chatI } Ptr Api::sendAudio(const std::variant& chatId, - std::variant audio, + const std::variant& audio, std::int32_t messageThreadId, const std::string& caption, const std::string& parseMode, @@ -733,10 +735,9 @@ std::vector> Api::sendMediaGroup(const std::variant& m: media) - { + for (const Ptr& m: media) { nl::json mJson = m->toJson(); - switch(m->media.index()) { + switch (m->media.index()) { case 0: // cpr::File (Local File) { const cpr::File& file = std::get(m->media); @@ -2057,7 +2058,7 @@ bool Api::answerInlineQuery(const std::string& inlineQueryId, data.parts.emplace_back("cache_time", cacheTime); if (isPersonal) data.parts.emplace_back("is_personal", isPersonal); - if(not nextOffset.empty()) + if (not nextOffset.empty()) data.parts.emplace_back("next_offset", nextOffset); if (button) data.parts.emplace_back("button", button->toJson().dump()); @@ -2077,6 +2078,67 @@ Ptr Api::answerWebAppQuery(const std::string& webAppQueryId, } +///////////////////////////////////////////////////////////////////////////////////////////////// + +Ptr Api::sendSticker(const std::variant& chatId, + const std::variant& sticker, + std::int32_t messageThreadId, + const std::string& emoji, + bool disableNotification, + bool protectContent, + const Ptr& replyParameters, + const Ptr& replyMarkup) const { + cpr::Multipart data{}; + data.parts.reserve(8); + switch (chatId.index()) { + case 0: // std::int64_t + if (std::int64_t chatIdInt = std::get(chatId); chatIdInt != 0) { + data.parts.emplace_back("chat_id", std::to_string(chatIdInt)); + } + break; + case 1: // std::string + if (std::string chatIdStr = std::get(chatId); not chatIdStr.empty()) { + data.parts.emplace_back("chat_id", chatIdStr); + } + break; + default: + break; + } + if (sticker.index() == 0) /* cpr::File */ { + const cpr::File& file = std::get(sticker); + data.parts.emplace_back("sticker", cpr::Files{file}); + } else /* std::string (fileId or Url) */ { + const std::string& fileIdOrUrl = std::get(sticker); + data.parts.emplace_back("sticker", fileIdOrUrl); + } + if (messageThreadId) + data.parts.emplace_back("message_thread_id", messageThreadId); + if (not emoji.empty()) + data.parts.emplace_back("emoji", emoji); + if (disableNotification) + data.parts.emplace_back("disable_notification", disableNotification); + if (protectContent) + data.parts.emplace_back("protect_content", protectContent); + if (replyParameters) + data.parts.emplace_back("reply_parameters", replyParameters->toJson().dump()); + if (replyMarkup) + data.parts.emplace_back("reply_markup", replyMarkup->toJson().dump()); + + nl::json sentMessageObj = sendRequest("sendSticker", data); + Ptr message(new Message(sentMessageObj)); + return message; +} + + +Ptr Api::getStickerSet(const std::string& name) const { + cpr::Multipart data{}; + data.parts.reserve(1); + data.parts.emplace_back("name", name); + + nl::json stickerSetObj = sendRequest("getStickerSet", data); + Ptr stickerSet(new StickerSet(stickerSetObj)); + return stickerSet; +} ///////////////////////////////////////////////////////////////////////////////////////////////// void Api::setUrl(const std::string& url) noexcept { m_apiUrl = url; diff --git a/tests/manual_tests.cpp b/tests/manual_tests.cpp index 5aa99fc65..2b58f7bd5 100644 --- a/tests/manual_tests.cpp +++ b/tests/manual_tests.cpp @@ -101,10 +101,13 @@ class MyBot : public Bot { Ptr createInvoiceLink(new BotCommand()); createInvoiceLink->command = "/create_invoice_link"; createInvoiceLink->description = "You will receive a test invoice link"; + Ptr sendSticker(new BotCommand()); + sendSticker->command = "/send_sticker"; + sendSticker->description = "You will receive a sticker"; getApi()->setMyCommands({greet, stop, photo, inlineButtons, replyKeyboardButtons, audio, document, animation, voice, mediaGroup, location, userProfilePhotos, ban, poll, quiz, webhookInfo, botName, menuButtonWebApp, menuButtonDefault, showAdministratorRights, editMessageText, - deleteMessage, sendInvoice, createInvoiceLink}); // The above commands will be shown in the bot chat menu (bottom left) + deleteMessage, sendInvoice, createInvoiceLink, sendSticker}); // The above commands will be shown in the bot chat menu (bottom left) std::cout << __func__ << ": " << api()->getMyName()->name << " bot started!" << std::endl; } @@ -366,6 +369,8 @@ class MyBot : public Bot { std::string link = api()->createInvoiceLink("Product name", "Product description", "payload", providerToken, "USD", prices); // Send link to user api()->sendMessage(message->chat->id, link); + } else if (message->text == "/send_sticker") { + api()->sendSticker(message->chat->id, "https://t.ly/uQ6Zx"); } }