diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | include/tgbot/Api.h | 24 | ||||
-rw-r--r-- | include/tgbot/TgTypeParser.h | 8 | ||||
-rw-r--r-- | include/tgbot/types/KeyboardButton.h | 20 | ||||
-rw-r--r-- | include/tgbot/types/KeyboardButtonPollType.h | 28 | ||||
-rw-r--r-- | include/tgbot/types/MessageEntity.h | 5 | ||||
-rw-r--r-- | include/tgbot/types/Poll.h | 83 | ||||
-rw-r--r-- | include/tgbot/types/PollAnswer.h | 39 | ||||
-rw-r--r-- | include/tgbot/types/Update.h | 9 | ||||
-rw-r--r-- | include/tgbot/types/User.h | 25 | ||||
-rw-r--r-- | src/Api.cpp | 28 | ||||
-rw-r--r-- | src/TgTypeParser.cpp | 68 |
12 files changed, 283 insertions, 56 deletions
@@ -12,7 +12,7 @@ Documentation is located [here](http://reo7sp.github.io/tgbot-cpp). ## State - [x] Bot API 3.0 ~ 3.6 -- [x] Bot API 4.0 ~ 4.5 (Implemented all APIs except 'Telegram Passport') +- [x] Bot API 4.0 ~ 4.6 (Implemented all APIs except 'Telegram Passport') ## Sample diff --git a/include/tgbot/Api.h b/include/tgbot/Api.h index f5b325e..517c8fe 100644 --- a/include/tgbot/Api.h +++ b/include/tgbot/Api.h @@ -801,17 +801,23 @@ public: std::string downloadFile(const std::string& filePath, const std::vector<HttpReqArg>& args = std::vector<HttpReqArg>()) const; /** - * @brief Use this method to send a poll. - * @param chatId Unique identifier for the target chat or username of the target channel. - * @param question Poll question, 1-255 characters. - * @param options List of answer options, 2-10 strings 1-100 characters each. - * @param disableNotification Optional. Sends the message silenty. - * @param replyToMessageId Optional. If the message is a reply, ID of the original message. - * @param replyMarkup Optional. Additional interface options. An object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user. - * + * @brief Use this method to send a native poll. + * @param chatId Unique identifier for the target chat or username of the target channel (in the format @channelusername) + * @param question Poll question, 1-255 characters + * @param options A JSON-serialized list of answer options, 2-10 strings 1-100 characters each + * @param disableNotification Optional. Sends the message silently. Users will receive a notification with no sound. + * @param replyToMessageId Optional. If the message is a reply, ID of the original message + * @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. + * @param isAnonymous Optional. True, if the poll needs to be anonymous, defaults to True + * @param type Optional. Poll type, “quiz” or “regular”, defaults to “regular” + * @param allowsMultipleAnswers Optional. True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to False + * @param correctOptionId Optional. 0-based identifier of the correct answer option, required for polls in quiz mode + * @param isClosed Optional. Pass True, if the poll needs to be immediately closed. This can be useful for poll preview. * @return On success, the sent message is returned. */ - Message::Ptr sendPoll(std::int64_t chatId, const std::string& question, const std::vector<std::string>& options, bool disableNotification = false, std::int32_t replyToMessageId = 0, GenericReply::Ptr replyMarkup = std::make_shared<GenericReply>()) const; + Message::Ptr sendPoll(std::int64_t chatId, const std::string& question, const std::vector<std::string>& options, bool disableNotification = false, std::int32_t replyToMessageId = 0, + GenericReply::Ptr replyMarkup = std::make_shared<GenericReply>(), bool isAnonymous = true, const std::string& type = "", bool allowsMultipleAnswers = false, + std::int32_t correctOptionId = 0, bool isClosed = false) const; /** * @brief Use this method to stop a poll which was sent by the bot. diff --git a/include/tgbot/TgTypeParser.h b/include/tgbot/TgTypeParser.h index 5961bbe..1c43297 100644 --- a/include/tgbot/TgTypeParser.h +++ b/include/tgbot/TgTypeParser.h @@ -11,6 +11,7 @@ #include "tgbot/types/Sticker.h" #include "tgbot/types/StickerSet.h" #include "tgbot/types/Poll.h" +#include "tgbot/types/PollAnswer.h" #include "tgbot/types/PollOption.h" #include "tgbot/types/ChatPermissions.h" #include "tgbot/types/MaskPosition.h" @@ -24,6 +25,7 @@ #include "tgbot/types/File.h" #include "tgbot/types/ReplyKeyboardMarkup.h" #include "tgbot/types/KeyboardButton.h" +#include "tgbot/types/KeyboardButtonPollType.h" #include "tgbot/types/ReplyKeyboardRemove.h" #include "tgbot/types/ForceReply.h" #include "tgbot/types/ChatMember.h" @@ -135,6 +137,9 @@ public: Poll::Ptr parseJsonAndGetPoll(const boost::property_tree::ptree& data) const; std::string parsePoll(const Poll::Ptr& object) const; + PollAnswer::Ptr parseJsonAndGetPollAnswer(const boost::property_tree::ptree& data) const; + std::string parsePollAnswer(const PollAnswer::Ptr& object) const; + PollOption::Ptr parseJsonAndGetPollOption(const boost::property_tree::ptree& data) const; std::string parsePollOption(const PollOption::Ptr& object) const; @@ -183,6 +188,9 @@ public: KeyboardButton::Ptr parseJsonAndGetKeyboardButton(const boost::property_tree::ptree& data) const; std::string parseKeyboardButton(const KeyboardButton::Ptr& object) const; + KeyboardButtonPollType::Ptr parseJsonAndGetKeyboardButtonPollType(const boost::property_tree::ptree& data) const; + std::string parseKeyboardButtonPollType(const KeyboardButtonPollType::Ptr& object) const; + ReplyKeyboardRemove::Ptr parseJsonAndGetReplyKeyboardRemove(const boost::property_tree::ptree& data) const; std::string parseReplyKeyboardRemove(const ReplyKeyboardRemove::Ptr& object) const; diff --git a/include/tgbot/types/KeyboardButton.h b/include/tgbot/types/KeyboardButton.h index 867d014..f2b6791 100644 --- a/include/tgbot/types/KeyboardButton.h +++ b/include/tgbot/types/KeyboardButton.h @@ -1,6 +1,8 @@ #ifndef TGBOT_CPP_KEYBOARDBUTTON_H #define TGBOT_CPP_KEYBOARDBUTTON_H +#include "tgbot/types/KeyboardButtonPollType.h" + #include <string> #include <memory> @@ -9,8 +11,8 @@ namespace TgBot { /** * @brief This object represents one button of the reply keyboard. * - * For simple text buttons String can be used instead of this - * object to specify text of the button. Optional fields are mutually exclusive. + * For simple text buttons String can be used instead of this object to specify text of the button. + * Optional fields request_contact, request_location, and request_poll are mutually exclusive. * * @ingroup types */ @@ -20,23 +22,25 @@ public: typedef std::shared_ptr<KeyboardButton> Ptr; /** - * @brief Text of the button. If none of the optional fields are used, - * it will be sent to the bot as a message when the button is pressed + * @brief Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed */ std::string text; /** - * @brief Optional. If True, the user's phone number will be sent as a contact - * when the button is pressed. Available in private chats only + * @brief Optional. If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only */ bool requestContact = false; /** - * @brief Optional. If True, the user's current location will be sent when the button is pressed. Available in private chats only. + * @brief Optional. If True, the user's current location will be sent when the button is pressed. Available in private chats only */ bool requestLocation = false; -}; + /** + * @brief Optional. If specified, the user will be asked to create a poll and send it to the bot when the button is pressed. Available in private chats only + */ + KeyboardButtonPollType::Ptr requestPoll; +}; } #endif //TGBOT_CPP_KEYBOARDBUTTON_H diff --git a/include/tgbot/types/KeyboardButtonPollType.h b/include/tgbot/types/KeyboardButtonPollType.h new file mode 100644 index 0000000..1bead32 --- /dev/null +++ b/include/tgbot/types/KeyboardButtonPollType.h @@ -0,0 +1,28 @@ +#ifndef TGBOT_CPP_KEYBOARDBUTTONPOLLTYPE_H +#define TGBOT_CPP_KEYBOARDBUTTONPOLLTYPE_H + +#include <string> +#include <memory> + +namespace TgBot { + +/** + * @brief This object represents type of a poll, which is allowed to be created and sent when the corresponding button is pressed. + * + * @ingroup types + */ +class KeyboardButtonPollType { + +public: + typedef std::shared_ptr<KeyboardButtonPollType> Ptr; + + /** + * @brief Optional. If quiz is passed, the user will be allowed to create only polls in the quiz mode. + * + * If regular is passed, only regular polls will be allowed. Otherwise, the user will be allowed to create a poll of any type. + */ + std::string type; +}; +} + +#endif //TGBOT_CPP_KEYBOARDBUTTONPOLLTYPE_H diff --git a/include/tgbot/types/MessageEntity.h b/include/tgbot/types/MessageEntity.h index 9aa283d..21d9b99 100644 --- a/include/tgbot/types/MessageEntity.h +++ b/include/tgbot/types/MessageEntity.h @@ -44,6 +44,11 @@ public: * @brief Optional. For “text_mention” only, the mentioned user */ User::Ptr user; + + /** + * @brief Optional. For “pre” only, the programming language of the entity text + */ + std::string language; }; } diff --git a/include/tgbot/types/Poll.h b/include/tgbot/types/Poll.h index 9f4b04d..9c81dc7 100644 --- a/include/tgbot/types/Poll.h +++ b/include/tgbot/types/Poll.h @@ -9,35 +9,64 @@ #include <vector> namespace TgBot { + +/** + * @brief This object contains information about a poll. + * + * @ingroup types + */ +class Poll { + +public: + typedef std::shared_ptr<Poll> Ptr; + + /** + * @brief Unique poll identifier + */ + std::int64_t id; + + /** + * @brief Poll question, 1-255 characters + */ + std::string question; + + /** + * @brief List of poll options + */ + std::vector<PollOption::Ptr> options; + + /** + * @brief Total number of users that voted in the poll + */ + std::int32_t totalVoterCount; + + /** + * @brief True, if the poll is closed + */ + bool isClosed; + + /** + * @brief True, if the poll is anonymous + */ + bool isAnonymous; + /** - * @brief This object represents a Poll. + * @brief Poll type, currently can be “regular” or “quiz” + */ + std::string type; + + /** + * @brief True, if the poll allows multiple answers + */ + bool allowsMultipleAnswers; + + /** + * @brief Optional. 0-based identifier of the correct answer option. * - * @ingroup types - */ - class Poll { - public: - typedef std::shared_ptr<Poll> Ptr; - - /** - * @brief Unique poll identifier. - */ - std::int64_t id; - - /** - * @brief Poll question, 1-255 characters. - */ - std::string question; - - /** - * @brief List of poll options. - */ - std::vector<PollOption::Ptr> options; - - /** - * @brief True, if the poll is closed. - */ - bool isClosed; - }; + * Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot. + */ + std::int32_t correctOptionId; +}; } #endif //TGBOT_POLL_H diff --git a/include/tgbot/types/PollAnswer.h b/include/tgbot/types/PollAnswer.h new file mode 100644 index 0000000..730248b --- /dev/null +++ b/include/tgbot/types/PollAnswer.h @@ -0,0 +1,39 @@ +#ifndef TGBOT_CPP_POLLANSWER_H +#define TGBOT_CPP_POLLANSWER_H + +#include "tgbot/types/User.h" + +#include <string> +#include <memory> +#include <vector> + +namespace TgBot { + +/** + * @brief This object represents an answer of a user in a non-anonymous poll. + * + * @ingroup types + */ +class PollAnswer { + +public: + typedef std::shared_ptr<PollAnswer> Ptr; + + /** + * @brief Unique poll identifier + */ + std::string pollId; + + /** + * @brief The user, who changed the answer to the poll + */ + User::Ptr user; + + /** + * @brief 0-based identifiers of answer options, chosen by the user. May be empty if the user retracted their vote. + */ + std::vector<std::int32_t> optionIds; +}; +} + +#endif //TGBOT_CPP_POLLANSWER_H diff --git a/include/tgbot/types/Update.h b/include/tgbot/types/Update.h index 328ba2a..a80bd4e 100644 --- a/include/tgbot/types/Update.h +++ b/include/tgbot/types/Update.h @@ -8,6 +8,7 @@ #include "tgbot/types/ShippingQuery.h" #include "tgbot/types/PreCheckoutQuery.h" #include "tgbot/types/Poll.h" +#include "tgbot/types/PollAnswer.h" #include <cstdint> #include <memory> @@ -84,8 +85,14 @@ public: * Bots receive only updates about stopped polls and polls, which are sent by the bot */ Poll::Ptr poll; -}; + /** + * @brief Optional. A user changed their answer in a non-anonymous poll. + * + * Bots receive new votes only in polls that were sent by the bot itself. + */ + PollAnswer::Ptr pollAnswer; +}; } #endif //TGBOT_CPP_UPDATE_H diff --git a/include/tgbot/types/User.h b/include/tgbot/types/User.h index da1024d..7c7ceaf 100644 --- a/include/tgbot/types/User.h +++ b/include/tgbot/types/User.h @@ -18,7 +18,7 @@ public: typedef std::shared_ptr<User> Ptr; /** - * @brief Unique identifier for this user or bot. + * @brief Unique identifier for this user or bot */ std::int64_t id; @@ -28,24 +28,39 @@ public: bool isBot = false; /** - * @brief User‘s or bot’s first name. + * @brief User‘s or bot’s first name */ std::string firstName; /** - * @brief Optional. User‘s or bot’s last name. + * @brief Optional. User‘s or bot’s last name */ std::string lastName; /** - * @brief Optional. User‘s or bot’s username. + * @brief Optional. User‘s or bot’s username */ std::string username; /** - * @brief Optional. IETF language tag of the user's language. + * @brief Optional. IETF language tag of the user's language */ std::string languageCode; + + /** + * @brief Optional. True, if the bot can be invited to groups. Returned only in getMe. + */ + bool canJoinGroups; + + /** + * @brief Optional. True, if privacy mode is disabled for the bot. Returned only in getMe. + */ + bool canReadAllGroupMessages; + + /** + * @brief Optional. True, if the bot supports inline queries. Returned only in getMe. + */ + bool supportsInlineQueries; }; } diff --git a/src/Api.cpp b/src/Api.cpp index b1a2f69..f1a1065 100644 --- a/src/Api.cpp +++ b/src/Api.cpp @@ -1155,24 +1155,42 @@ void Api::deleteMessage(std::int64_t chatId, std::int32_t messageId) const { sendRequest("deleteMessage", { HttpReqArg("chat_id", chatId), HttpReqArg("message_id", messageId) }); } -Message::Ptr Api::sendPoll(std::int64_t chatId, const std::string& question, const std::vector<std::string>& options, bool disableNotification, std::int32_t replyToMessageId, const GenericReply::Ptr replyMarkup) const { +Message::Ptr Api::sendPoll(std::int64_t chatId, const std::string& question, const std::vector<std::string>& options, bool disableNotification, std::int32_t replyToMessageId, + const GenericReply::Ptr replyMarkup, bool isAnonymous, const std::string& type, bool allowsMultipleAnswers, + std::int32_t correctOptionId, bool isClosed) const { vector<HttpReqArg> args; - args.reserve(6); + args.reserve(11); args.emplace_back("chat_id", chatId); args.emplace_back("question", question); args.emplace_back("options", _tgTypeParser.parseArray<std::string>([] (const std::string& option) -> std::string { return StringTools::urlEncode(option); }, options)); - if (disableNotification){ + if (!isAnonymous) { + args.emplace_back("is_anonymous", isAnonymous); + } + if (!type.empty()) { + args.emplace_back("type", type); + } + if (allowsMultipleAnswers) { + args.emplace_back("allows_multiple_answers", allowsMultipleAnswers); + } + if (correctOptionId != 0) { + args.emplace_back("correct_option_id", correctOptionId); + } + if (isClosed) { + args.emplace_back("is_closed", isClosed); + } + if (disableNotification) { args.emplace_back("disable_notification", disableNotification); } - if (replyToMessageId != 0){ + if (replyToMessageId != 0) { args.emplace_back("reply_to_message_id", replyToMessageId); } - if (replyMarkup){ + if (replyMarkup) { args.emplace_back("reply_markup", _tgTypeParser.parseGenericReply(replyMarkup)); } + return _tgTypeParser.parseJsonAndGetMessage(sendRequest("sendPoll", args)); } diff --git a/src/TgTypeParser.cpp b/src/TgTypeParser.cpp index db2e750..4528d62 100644 --- a/src/TgTypeParser.cpp +++ b/src/TgTypeParser.cpp @@ -80,6 +80,9 @@ User::Ptr TgTypeParser::parseJsonAndGetUser(const ptree& data) const { result->lastName = data.get("last_name", ""); result->username = data.get("username", ""); result->languageCode = data.get("language_code", ""); + result->canJoinGroups = data.get<bool>("can_join_groups", false); + result->canReadAllGroupMessages = data.get<bool>("can_read_all_group_messages", false); + result->supportsInlineQueries = data.get<bool>("supports_inline_queries", false); return result; } @@ -95,6 +98,9 @@ string TgTypeParser::parseUser(const User::Ptr& object) const { appendToJson(result, "last_name", object->lastName); appendToJson(result, "username", object->username); appendToJson(result, "language_code", object->languageCode); + appendToJson(result, "can_join_groups", object->canJoinGroups); + appendToJson(result, "can_read_all_group_messages", object->canReadAllGroupMessages); + appendToJson(result, "supports_inline_queries", object->supportsInlineQueries); removeLastComma(result); result += '}'; return result; @@ -107,6 +113,7 @@ MessageEntity::Ptr TgTypeParser::parseJsonAndGetMessageEntity(const ptree& data) result->length = data.get<int32_t>("length"); result->url = data.get<string>("url", ""); result->user = tryParseJson<User>(&TgTypeParser::parseJsonAndGetUser, data, "user"); + result->language = data.get<string>("language", ""); return result; } @@ -121,6 +128,7 @@ string TgTypeParser::parseMessageEntity(const MessageEntity::Ptr& object) const appendToJson(result, "length", object->length); appendToJson(result, "url", object->url); appendToJson(result, "user", parseUser(object->user)); + appendToJson(result, "language", object->url); removeLastComma(result); result += '}'; return result; @@ -405,7 +413,12 @@ Poll::Ptr TgTypeParser::parseJsonAndGetPoll(const ptree& data) const { result->id = data.get("id", 0); result->question = data.get("question", ""); result->options = parseJsonAndGetArray<PollOption>(&TgTypeParser::parseJsonAndGetPollOption, data, "options"); + result->totalVoterCount = data.get("total_voter_count", 0); result->isClosed = data.get<bool>("is_closed"); + result->isAnonymous = data.get<bool>("is_anonymous"); + result->type = data.get("type", ""); + result->allowsMultipleAnswers = data.get<bool>("allows_multiple_answers"); + result->correctOptionId = data.get("correct_option_id", 0); return result; } @@ -418,7 +431,39 @@ string TgTypeParser::parsePoll(const Poll::Ptr& object) const { appendToJson(result, "id", object->id); appendToJson(result, "question", object->question); appendToJson(result, "options", parseArray(&TgTypeParser::parsePollOption, object->options)); + appendToJson(result, "total_voter_count", object->totalVoterCount); appendToJson(result, "is_closed", object->isClosed); + appendToJson(result, "is_anonymous", object->isAnonymous); + appendToJson(result, "type", object->type); + appendToJson(result, "allows_multiple_answers", object->allowsMultipleAnswers); + appendToJson(result, "correct_option_id", object->correctOptionId); + removeLastComma(result); + result += '}'; + return result; +} + +PollAnswer::Ptr TgTypeParser::parseJsonAndGetPollAnswer(const ptree& data) const { + auto result(make_shared<PollAnswer>()); + result->pollId = data.get("poll_id", ""); + result->user = tryParseJson<User>(&TgTypeParser::parseJsonAndGetUser, data, "user"); + result->optionIds = parseJsonAndGetArray<std::int32_t>([] (const ptree& innerData)->std::int32_t { + return innerData.get<std::int32_t>(0); + }, data, "option_ids"); + + return result; +} + +string TgTypeParser::parsePollAnswer(const PollAnswer::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "poll_id", object->pollId); + appendToJson(result, "user", parseUser(object->user)); + appendToJson(result, "option_ids", parseArray<std::int32_t>([] (std::int32_t i)->std::int32_t { + return i; + }, object->optionIds)); removeLastComma(result); result += '}'; return result; @@ -705,6 +750,7 @@ Update::Ptr TgTypeParser::parseJsonAndGetUpdate(const ptree& data) const { result->shippingQuery = tryParseJson<ShippingQuery>(&TgTypeParser::parseJsonAndGetShippingQuery, data, "shipping_query"); result->preCheckoutQuery = tryParseJson<PreCheckoutQuery>(&TgTypeParser::parseJsonAndGetPreCheckoutQuery, data, "pre_checkout_query"); result->poll = tryParseJson<Poll>(&TgTypeParser::parseJsonAndGetPoll, data, "poll"); + result->pollAnswer = tryParseJson<PollAnswer>(&TgTypeParser::parseJsonAndGetPollAnswer, data, "poll_answer"); return result; } @@ -725,6 +771,7 @@ string TgTypeParser::parseUpdate(const Update::Ptr& object) const { appendToJson(result, "shipping_query", parseShippingQuery(object->shippingQuery)); appendToJson(result, "pre_checkout_query", parsePreCheckoutQuery(object->preCheckoutQuery)); appendToJson(result, "poll", parsePoll(object->poll)); + appendToJson(result, "poll_answer", parsePollAnswer(object->pollAnswer)); removeLastComma(result); result += '}'; return result; @@ -920,6 +967,8 @@ KeyboardButton::Ptr TgTypeParser::parseJsonAndGetKeyboardButton(const boost::pro result->text = data.get<string>("text"); result->requestContact = data.get<bool>("request_contact", false); result->requestLocation = data.get<bool>("request_location", false); + result->requestPoll = tryParseJson<KeyboardButtonPollType>(&TgTypeParser::parseJsonAndGetKeyboardButtonPollType, data, "request_poll"); + return result; } @@ -932,6 +981,25 @@ std::string TgTypeParser::parseKeyboardButton(const KeyboardButton::Ptr& object) appendToJson(result, "text", object->text); appendToJson(result, "request_contact", object->requestContact); appendToJson(result, "request_location", object->requestLocation); + appendToJson(result, "request_poll", parseKeyboardButtonPollType(object->requestPoll)); + removeLastComma(result); + result += '}'; + return result; +} + +KeyboardButtonPollType::Ptr TgTypeParser::parseJsonAndGetKeyboardButtonPollType(const ptree& data) const { + auto result(make_shared<KeyboardButtonPollType>()); + result->type = data.get<string>("type"); + return result; +} + +string TgTypeParser::parseKeyboardButtonPollType(const KeyboardButtonPollType::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "type", object->type); removeLastComma(result); result += '}'; return result; |