From c2732781d43d7faa3ec64cf6f6dad17b4720b42e Mon Sep 17 00:00:00 2001 From: Bader Date: Sat, 7 Oct 2023 00:17:59 +0100 Subject: [PATCH] added Api getFile and downloadFile methods Use Api::getFile method to get basic information about a file and prepare it for downloading. Use Api::downloadFile method to download a file from Telegram and save it in memory. --- include/tgbotxx/Api.hpp | 19 +++++++++++++++++++ src/Api.cpp | 32 +++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/include/tgbotxx/Api.hpp b/include/tgbotxx/Api.hpp index 35d1263d9..67f1fb14e 100644 --- a/include/tgbotxx/Api.hpp +++ b/include/tgbotxx/Api.hpp @@ -311,6 +311,25 @@ namespace tgbotxx { bool allowSendingWithoutReply = false, const Ptr& replyMarkup = nullptr) const; + /// @brief Use this method to get basic information about a file and prepare it for downloading. + /// For the moment, bots can download files of up to 20MB in size. + /// The file can then be downloaded using Api::downloadFile or via the link https://api.telegram.org/file/bot/, where is taken from the response. + /// @param fileId File identifier to get information about + /// @returns a File object is returned on success. + /// @note It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling getFile again. + /// @note This function may not preserve the original file name and MIME type. You should save the file's MIME type and name (if available) when the File object is received. + /// @ref https://core.telegram.org/bots/api#getfile + Ptr getFile(const std::string& fileId) const; + + /// @brief Use this method to download a file from Telegram and save it in memory. + /// For the moment, bots can download files of up to 20MB in size. See Api::getFile. + /// The file can then be downloaded via the link https://api.telegram.org/file/bot/, where is taken from the response. + /// @param filePath Telegram file path from Api::getFile(fileId) -> File::filePath + /// @returns std::string contains downloaded file contents. + /// @ref https://core.telegram.org/bots/api#getfile + /// @throws Exception on failure + std::string downloadFile(const std::string& filePath) const; + /// @brief Use this method to remove webhook integration if you decide to switch back to getUpdates. /// Returns True on success. /// @param dropPendingUpdates: Pass True to drop all pending updates. diff --git a/src/Api.cpp b/src/Api.cpp index eb248b7f9..a9bc11fc4 100644 --- a/src/Api.cpp +++ b/src/Api.cpp @@ -1,3 +1,6 @@ +#include "cpr/accept_encoding.h" +#include "cpr/response.h" +#include "cpr/status_codes.h" #include "cpr/timeout.h" #include #include @@ -13,7 +16,7 @@ nl::json Api::sendRequest(const std::string& endpoint, const cpr::Multipart& dat // You can initiate multiple concurrent requests to the Telegram API, which means // You can call sendMessage while getUpdates long polling is still pending, and you can't do that with a single cpr::Session instance. bool hasFiles = std::any_of(data.parts.begin(), data.parts.end(), [](const cpr::Part& part) noexcept { return part.is_file; }); - if(hasFiles) // Files can take longer to upload + if (hasFiles)// Files can take longer to upload session.SetTimeout(FILES_UPLOAD_TIMEOUT); else session.SetTimeout(TIMEOUT); @@ -319,7 +322,7 @@ Ptr Api::sendAudio(std::int64_t chatId, data.parts.emplace_back("performer", performer); if (not title.empty()) data.parts.emplace_back("title", title); - if(thumbnail.has_value()) { + 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}); @@ -367,7 +370,7 @@ Ptr Api::sendDocument(std::int64_t chatId, } if (messageThreadId) data.parts.emplace_back("message_thread_id", messageThreadId); - if(thumbnail.has_value()) { + 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}); @@ -401,3 +404,26 @@ Ptr Api::sendDocument(std::int64_t chatId, Ptr message(new Message(sentMessageObj)); return message; } + +Ptr Api::getFile(const std::string& fileId) const { + cpr::Multipart data{}; + data.parts.reserve(1); + data.parts.emplace_back("file_id", fileId); + nl::json fileObj = sendRequest("getFile", data); + Ptr file(new File(fileObj)); + return file; +} + +std::string Api::downloadFile(const std::string& filePath) const { + std::ostringstream oss; + oss << BASE_URL << "/file/bot" << m_token << "/" << filePath; + + cpr::Url url{oss.str()}; + cpr::Timeout timeout{FILES_UPLOAD_TIMEOUT}; + cpr::AcceptEncoding encoding = cpr::AcceptEncoding{{cpr::AcceptEncodingMethods::deflate, cpr::AcceptEncodingMethods::gzip, cpr::AcceptEncodingMethods::zlib}}; + cpr::Response res = cpr::Get(url, timeout, encoding); + if (res.status_code == cpr::status::HTTP_OK) { + return res.text; + } + throw Exception("Failed to download file " + filePath + " with status code: " + std::to_string(res.status_code)); +}