diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index ede5431c8..570e917d8 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -216,8 +216,10 @@ pollOption text:string voter_count:int32 vote_percentage:int32 is_chosen:Bool is //@description A regular poll @allow_multiple_answers True, if multiple answer options can be chosen simultaneously pollTypeRegular allow_multiple_answers:Bool = PollType; -//@description A poll in quiz mode, which has exactly one correct answer option and can be answered only once @correct_option_id 0-based identifier of the correct answer option; -1 for a yet unanswered poll -pollTypeQuiz correct_option_id:int32 = PollType; +//@description A poll in quiz mode, which has exactly one correct answer option and can be answered only once +//@correct_option_id 0-based identifier of the correct answer option; -1 for a yet unanswered poll +//@explanation Text shown after an incorrect answer has chosen; empty for a yet unanswered poll +pollTypeQuiz correct_option_id:int32 explanation:formattedText = PollType; //@description Describes an animation file. The animation must be encoded in GIF or MPEG4 format @duration Duration of the animation, in seconds; as defined by the sender @width Width of the animation @height Height of the animation diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index c518f5551..bbacd9665 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index 64e2afbf5..a27843e16 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -1749,6 +1749,7 @@ static Result create_input_message_content( bool allow_multiple_answers = false; bool is_quiz = false; int32 correct_option_id = -1; + FormattedText explanation; if (input_poll->type_ == nullptr) { return Status::Error(400, "Poll type must not be empty"); } @@ -1765,6 +1766,12 @@ static Result create_input_message_content( if (correct_option_id < 0 || correct_option_id >= static_cast(input_poll->options_.size())) { return Status::Error(400, "Wrong correct option ID specified"); } + auto r_explanation = + process_input_caption(td->contacts_manager_.get(), DialogId(), std::move(type->explanation_), is_bot); + if (r_explanation.is_error()) { + return r_explanation.move_as_error(); + } + explanation = r_explanation.move_as_ok(); break; } default: @@ -1777,9 +1784,10 @@ static Result create_input_message_content( close_date = 0; } bool is_closed = is_bot ? input_poll->is_closed_ : false; - content = make_unique(td->poll_manager_->create_poll( - std::move(input_poll->question_), std::move(input_poll->options_), input_poll->is_anonymous_, - allow_multiple_answers, is_quiz, correct_option_id, close_date, close_period, is_closed)); + content = make_unique( + td->poll_manager_->create_poll(std::move(input_poll->question_), std::move(input_poll->options_), + input_poll->is_anonymous_, allow_multiple_answers, is_quiz, correct_option_id, + std::move(explanation), close_date, close_period, is_closed)); break; } default: diff --git a/td/telegram/MessageEntity.cpp b/td/telegram/MessageEntity.cpp index 4524c8ced..79895c4bf 100644 --- a/td/telegram/MessageEntity.cpp +++ b/td/telegram/MessageEntity.cpp @@ -3899,6 +3899,9 @@ void add_formatted_text_dependencies(Dependencies &dependencies, const Formatted } bool need_skip_bot_commands(const ContactsManager *contacts_manager, DialogId dialog_id, bool is_bot) { + if (!dialog_id.is_valid()) { + return true; + } if (is_bot) { return false; } diff --git a/td/telegram/PollManager.cpp b/td/telegram/PollManager.cpp index e9757a762..443d1a6cc 100644 --- a/td/telegram/PollManager.cpp +++ b/td/telegram/PollManager.cpp @@ -15,6 +15,7 @@ #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/logevent/LogEventHelper.h" #include "td/telegram/MessagesManager.h" +#include "td/telegram/misc.h" #include "td/telegram/net/NetActor.h" #include "td/telegram/PollId.hpp" #include "td/telegram/PollManager.hpp" @@ -531,7 +532,8 @@ td_api::object_ptr PollManager::get_poll_object(PollId poll_id, co td_api::object_ptr poll_type; if (poll->is_quiz) { auto correct_option_id = is_local_poll_id(poll_id) ? -1 : poll->correct_option_id; - poll_type = td_api::make_object(correct_option_id); + poll_type = td_api::make_object( + correct_option_id, get_formatted_text_object(is_local_poll_id(poll_id) ? FormattedText() : poll->explanation)); } else { poll_type = td_api::make_object(poll->allow_multiple_answers); } @@ -560,8 +562,8 @@ telegram_api::object_ptr PollManager::get_input_poll_o } PollId PollManager::create_poll(string &&question, vector &&options, bool is_anonymous, - bool allow_multiple_answers, bool is_quiz, int32 correct_option_id, int32 close_date, - int32 close_period, bool is_closed) { + bool allow_multiple_answers, bool is_quiz, int32 correct_option_id, + FormattedText &&explanation, int32 close_date, int32 close_period, bool is_closed) { auto poll = make_unique(); poll->question = std::move(question); int pos = '0'; @@ -575,6 +577,7 @@ PollId PollManager::create_poll(string &&question, vector &&options, boo poll->allow_multiple_answers = allow_multiple_answers; poll->is_quiz = is_quiz; poll->correct_option_id = correct_option_id; + poll->explanation = std::move(explanation); poll->close_date = close_date; poll->close_period = close_period; poll->is_closed = is_closed; @@ -1196,13 +1199,18 @@ tl_object_ptr PollManager::get_input_media(PollId poll CHECK(poll->correct_option_id >= 0); CHECK(static_cast(poll->correct_option_id) < poll->options.size()); correct_answers.push_back(BufferSlice(poll->options[poll->correct_option_id].data)); + + if (!poll->explanation.text.empty()) { + flags |= telegram_api::inputMediaPoll::SOLUTION_MASK; + } } return telegram_api::make_object( flags, telegram_api::make_object( 0, poll_flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, poll->question, transform(poll->options, get_input_poll_option), poll->close_period, poll->close_date), - std::move(correct_answers), string(), Auto()); + std::move(correct_answers), poll->explanation.text, + get_input_message_entities(td_->contacts_manager_.get(), poll->explanation.entities, "get_input_media_poll")); } vector PollManager::get_poll_options( @@ -1384,14 +1392,33 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptrtotal_voter_count = max_total_voter_count; } } + + auto entities = + get_message_entities(td_->contacts_manager_.get(), std::move(poll_results->solution_entities_), "on_get_poll"); + auto status = fix_formatted_text(poll_results->solution_, entities, true, true, true, false); + if (status.is_error()) { + if (!clean_input_string(poll_results->solution_)) { + poll_results->solution_.clear(); + } + entities = find_entities(poll_results->solution_, true); + } + FormattedText explanation{std::move(poll_results->solution_), std::move(entities)}; + if (poll->is_quiz) { if (poll->correct_option_id != correct_option_id) { poll->correct_option_id = correct_option_id; is_changed = true; } - } else if (correct_option_id != -1) { - LOG(ERROR) << "Receive correct option " << correct_option_id << " in non-quiz " << poll_id; + if (poll->explanation != explanation && (!is_min || poll->is_closed)) { + poll->explanation = std::move(explanation); + is_changed = true; + } + } else { + if (correct_option_id != -1) { + LOG(ERROR) << "Receive correct option " << correct_option_id << " in non-quiz " << poll_id; + } } + vector recent_voter_user_ids; if (!is_bot) { for (auto &user_id_int : poll_results->recent_voters_) { diff --git a/td/telegram/PollManager.h b/td/telegram/PollManager.h index 635788241..155693291 100644 --- a/td/telegram/PollManager.h +++ b/td/telegram/PollManager.h @@ -7,6 +7,7 @@ #pragma once #include "td/telegram/FullMessageId.h" +#include "td/telegram/MessageEntity.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/PollId.h" #include "td/telegram/ReplyMarkup.h" @@ -45,7 +46,8 @@ class PollManager : public Actor { static bool is_local_poll_id(PollId poll_id); PollId create_poll(string &&question, vector &&options, bool is_anonymous, bool allow_multiple_answers, - bool is_quiz, int32 correct_option_id, int32 close_date, int32 close_period, bool is_closed); + bool is_quiz, int32 correct_option_id, FormattedText &&explanation, int32 close_date, + int32 close_period, bool is_closed); void register_poll(PollId poll_id, FullMessageId full_message_id, const char *source); @@ -106,6 +108,7 @@ class PollManager : public Actor { string question; vector options; vector recent_voter_user_ids; + FormattedText explanation; int32 total_voter_count = 0; int32 correct_option_id = -1; int32 close_date = 0; diff --git a/td/telegram/PollManager.hpp b/td/telegram/PollManager.hpp index d54ff5863..850f7c50a 100644 --- a/td/telegram/PollManager.hpp +++ b/td/telegram/PollManager.hpp @@ -46,6 +46,7 @@ void PollManager::Poll::store(StorerT &storer) const { bool has_recent_voters = !recent_voter_user_ids.empty(); bool has_close_date = close_date != 0; bool has_close_period = close_period != 0; + bool has_explanation = !explanation.text.empty(); BEGIN_STORE_FLAGS(); STORE_FLAG(is_closed); STORE_FLAG(is_public); @@ -54,6 +55,7 @@ void PollManager::Poll::store(StorerT &storer) const { STORE_FLAG(has_recent_voters); STORE_FLAG(has_close_date); STORE_FLAG(has_close_period); + STORE_FLAG(has_explanation); END_STORE_FLAGS(); store(question, storer); @@ -71,6 +73,9 @@ void PollManager::Poll::store(StorerT &storer) const { if (has_close_period) { store(close_period, storer); } + if (has_explanation) { + store(explanation, storer); + } } template @@ -80,6 +85,7 @@ void PollManager::Poll::parse(ParserT &parser) { bool has_recent_voters; bool has_close_date; bool has_close_period; + bool has_explanation; BEGIN_PARSE_FLAGS(); PARSE_FLAG(is_closed); PARSE_FLAG(is_public); @@ -88,6 +94,7 @@ void PollManager::Poll::parse(ParserT &parser) { PARSE_FLAG(has_recent_voters); PARSE_FLAG(has_close_date); PARSE_FLAG(has_close_period); + PARSE_FLAG(has_explanation); END_PARSE_FLAGS(); is_anonymous = !is_public; @@ -109,6 +116,9 @@ void PollManager::Poll::parse(ParserT &parser) { if (has_close_period) { parse(close_period, parser); } + if (has_explanation) { + parse(explanation, parser); + } } template @@ -119,6 +129,7 @@ void PollManager::store_poll(PollId poll_id, StorerT &storer) const { CHECK(poll != nullptr); bool has_close_date = poll->close_date != 0; bool has_close_period = poll->close_period != 0; + bool has_explanation = !poll->explanation.text.empty(); BEGIN_STORE_FLAGS(); STORE_FLAG(poll->is_closed); STORE_FLAG(poll->is_anonymous); @@ -126,6 +137,7 @@ void PollManager::store_poll(PollId poll_id, StorerT &storer) const { STORE_FLAG(poll->is_quiz); STORE_FLAG(has_close_date); STORE_FLAG(has_close_period); + STORE_FLAG(has_explanation); END_STORE_FLAGS(); store(poll->question, storer); vector options = transform(poll->options, [](const PollOption &option) { return option.text; }); @@ -139,6 +151,9 @@ void PollManager::store_poll(PollId poll_id, StorerT &storer) const { if (has_close_period) { store(poll->close_period, storer); } + if (has_explanation) { + store(poll->explanation, storer); + } } } @@ -150,6 +165,7 @@ PollId PollManager::parse_poll(ParserT &parser) { if (is_local_poll_id(poll_id)) { string question; vector options; + FormattedText explanation; int32 close_date = 0; int32 close_period = 0; bool is_closed = false; @@ -158,6 +174,7 @@ PollId PollManager::parse_poll(ParserT &parser) { bool is_quiz = false; bool has_close_date = false; bool has_close_period = false; + bool has_explanation = false; int32 correct_option_id = -1; if (parser.version() >= static_cast(Version::SupportPolls2_0)) { @@ -168,6 +185,7 @@ PollId PollManager::parse_poll(ParserT &parser) { PARSE_FLAG(is_quiz); PARSE_FLAG(has_close_date); PARSE_FLAG(has_close_period); + PARSE_FLAG(has_explanation); END_PARSE_FLAGS(); } parse(question, parser); @@ -184,11 +202,14 @@ PollId PollManager::parse_poll(ParserT &parser) { if (has_close_period) { parse(close_period, parser); } + if (has_explanation) { + parse(explanation, parser); + } if (parser.get_error() != nullptr) { return PollId(); } return create_poll(std::move(question), std::move(options), is_anonymous, allow_multiple_answers, is_quiz, - correct_option_id, close_date, close_period, is_closed); + correct_option_id, std::move(explanation), close_date, close_period, is_closed); } auto poll = get_poll_force(poll_id); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 867b0daef..a31a79ad4 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3227,7 +3227,8 @@ class CliClient final : public Actor { td_api::object_ptr poll_type; if (op == "squiz") { - poll_type = td_api::make_object(narrow_cast(options.size() - 1)); + poll_type = td_api::make_object(narrow_cast(options.size() - 1), + as_formatted_text("_te*st*_")); } else { poll_type = td_api::make_object(op == "spollm"); }