Add td_api::setPollAnswer.

GitOrigin-RevId: 40c409ea8b4bba6d1ee0137a5cab8dca889d71dd
This commit is contained in:
levlam 2019-02-21 15:23:05 +03:00
parent d22dfb2a0c
commit b852bd145f
12 changed files with 249 additions and 15 deletions

View File

@ -213,7 +213,7 @@ abstract class TlDocumentationGenerator
foreach ($info as $name => $value) {
if (!$value) {
$this->printError("info[$name] for $class_name is empty");
} elseif ($value[0] < 'A' || $value[0] > 'Z') {
} elseif (($value[0] < 'A' || $value[0] > 'Z') && ($value[0] < '0' || $value[0] > '9')) {
$this->printError("info[$name] for $class_name doesn't begins with capital letter");
}
}

View File

@ -2906,6 +2906,11 @@ getJsonValue json:string = JsonValue;
getJsonString json_value:JsonValue = Text;
//@description Changes user answer to a poll @chat_id Identifier of the chat to which the poll belongs @message_id Identifier of the message containing the poll
//@option_ids 0-based identifiers of options, chosen by the user. Currently user can't choose more than 1 option
setPollAnswer chat_id:int53 message_id:int53 option_ids:vector<int32> = Ok;
//@description Sends an inline query to a bot and returns its results. Returns an error with code 502 if the bot fails to answer the query before the query timeout expires @bot_user_id The identifier of the target bot
//@chat_id Identifier of the chat, where the query was sent @user_location Location of the user, only if needed @query Text of the query @offset Offset of the first entry to return
getInlineQueryResults bot_user_id:int32 chat_id:int53 user_location:location query:string offset:string = InlineQueryResults;

Binary file not shown.

View File

@ -2679,6 +2679,13 @@ void set_message_content_web_page_id(MessageContent *content, WebPageId web_page
static_cast<MessageText *>(content)->web_page_id = web_page_id;
}
void set_message_content_poll_answer(Td *td, MessageContent *content, FullMessageId full_message_id,
vector<int32> &&option_ids, Promise<Unit> &&promise) {
CHECK(content->get_type() == MessageContentType::Poll);
td->poll_manager_->set_poll_answer(static_cast<MessagePoll *>(content)->poll_id, full_message_id,
std::move(option_ids), std::move(promise));
}
static void merge_location_access_hash(const Location &first, const Location &second) {
if (second.get_access_hash() != 0) {
first.set_access_hash(second.get_access_hash());

View File

@ -179,6 +179,9 @@ WebPageId get_message_content_web_page_id(const MessageContent *content);
void set_message_content_web_page_id(MessageContent *content, WebPageId web_page_id);
void set_message_content_poll_answer(Td *td, MessageContent *content, FullMessageId full_message_id,
vector<int32> &&option_ids, Promise<Unit> &&promise);
void merge_message_contents(Td *td, const MessageContent *old_content, MessageContent *new_content,
bool need_message_changed_warning, DialogId dialog_id, bool need_merge_files,
bool &is_content_changed, bool &need_update);

View File

@ -24874,12 +24874,32 @@ void MessagesManager::suffix_load_till_message_id(Dialog *d, MessageId message_i
suffix_load_add_query(d, std::make_pair(std::move(promise), std::move(condition)));
}
void MessagesManager::set_poll_answer(FullMessageId full_message_id, vector<int32> &&option_ids,
Promise<Unit> &&promise) {
auto m = get_message_force(full_message_id);
if (m == nullptr) {
return promise.set_error(Status::Error(5, "Message not found"));
}
if (!have_input_peer(full_message_id.get_dialog_id(), AccessRights::Read)) {
return promise.set_error(Status::Error(3, "Can't access the chat"));
}
if (m->content->get_type() != MessageContentType::Poll) {
return promise.set_error(Status::Error(5, "Message is not a poll"));
}
auto message_id = full_message_id.get_message_id();
if (!message_id.is_server()) {
return promise.set_error(Status::Error(5, "Poll can't be answered"));
}
set_message_content_poll_answer(td_, m->content.get(), full_message_id, std::move(option_ids), std::move(promise));
}
Result<ServerMessageId> MessagesManager::get_invoice_message_id(FullMessageId full_message_id) {
auto message = get_message_force(full_message_id);
if (message == nullptr) {
auto m = get_message_force(full_message_id);
if (m == nullptr) {
return Status::Error(5, "Message not found");
}
if (message->content->get_type() != MessageContentType::Invoice) {
if (m->content->get_type() != MessageContentType::Invoice) {
return Status::Error(5, "Message has no invoice");
}
auto message_id = full_message_id.get_message_id();
@ -24926,11 +24946,11 @@ void MessagesManager::send_payment_form(FullMessageId full_message_id, const str
void MessagesManager::get_payment_receipt(FullMessageId full_message_id,
Promise<tl_object_ptr<td_api::paymentReceipt>> &&promise) {
auto message = get_message_force(full_message_id);
if (message == nullptr) {
auto m = get_message_force(full_message_id);
if (m == nullptr) {
return promise.set_error(Status::Error(5, "Message not found"));
}
if (message->content->get_type() != MessageContentType::PaymentSuccessful) {
if (m->content->get_type() != MessageContentType::PaymentSuccessful) {
return promise.set_error(Status::Error(5, "Message has wrong type"));
}
auto message_id = full_message_id.get_message_id();

View File

@ -702,6 +702,8 @@ class MessagesManager : public Actor {
void on_binlog_events(vector<BinlogEvent> &&events);
void set_poll_answer(FullMessageId full_message_id, vector<int32> &&option_ids, Promise<Unit> &&promise);
void get_payment_form(FullMessageId full_message_id, Promise<tl_object_ptr<td_api::paymentForm>> &&promise);
void validate_order_info(FullMessageId full_message_id, tl_object_ptr<td_api::orderInfo> order_info, bool allow_save,

View File

@ -9,9 +9,12 @@
#include "td/telegram/Global.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/net/NetActor.h"
#include "td/telegram/PollManager.hpp"
#include "td/telegram/SequenceDispatcher.h"
#include "td/telegram/TdDb.h"
#include "td/telegram/Td.h"
#include "td/telegram/UpdatesManager.h"
#include "td/db/SqliteKeyValue.h"
#include "td/db/SqliteKeyValueAsync.h"
@ -20,8 +23,53 @@
#include "td/utils/misc.h"
#include "td/utils/Status.h"
#include <algorithm>
namespace td {
class SetPollAnswerQuery : public NetActorOnce {
Promise<Unit> promise_;
DialogId dialog_id_;
public:
explicit SetPollAnswerQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(FullMessageId full_message_id, vector<BufferSlice> &&options, uint64 generation) {
dialog_id_ = full_message_id.get_dialog_id();
auto input_peer = td->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read);
if (input_peer == nullptr) {
LOG(INFO) << "Can't set poll answer, because have no read access to " << dialog_id_;
return on_error(0, Status::Error(400, "Can't access the chat"));
}
auto message_id = full_message_id.get_message_id().get_server_message_id().get();
auto query = G()->net_query_creator().create(
create_storer(telegram_api::messages_sendVote(std::move(input_peer), message_id, std::move(options))));
auto sequence_id = -1;
send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback,
std::move(query), actor_shared(this), sequence_id);
}
void on_result(uint64 id, BufferSlice packet) override {
auto result_ptr = fetch_result<telegram_api::messages_sendVote>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
auto result = result_ptr.move_as_ok();
LOG(INFO) << "Receive sendVote result: " << to_string(result);
td->updates_manager_->on_get_updates(std::move(result));
promise_.set_value(Unit());
}
void on_error(uint64 id, Status status) override {
td->messages_manager_->on_get_dialog_error(dialog_id_, status, "SetPollAnswerQuery");
promise_.set_error(std::move(status));
}
};
PollManager::PollManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
}
@ -130,8 +178,28 @@ td_api::object_ptr<td_api::pollOption> PollManager::get_poll_option_object(const
td_api::object_ptr<td_api::poll> PollManager::get_poll_object(PollId poll_id) const {
auto poll = get_poll(poll_id);
CHECK(poll != nullptr);
return td_api::make_object<td_api::poll>(poll->question, transform(poll->options, get_poll_option_object),
poll->total_voter_count, poll->is_closed);
vector<td_api::object_ptr<td_api::pollOption>> poll_options;
auto it = pending_answers_.find(poll_id);
int32 voter_count_diff = 0;
if (it == pending_answers_.end()) {
poll_options = transform(poll->options, get_poll_option_object);
} else {
auto &chosen_options = it->second.options_;
for (auto &poll_option : poll->options) {
auto is_chosen =
std::find(chosen_options.begin(), chosen_options.end(), poll_option.data) != chosen_options.end();
if (poll_option.is_chosen) {
voter_count_diff = -1;
}
poll_options.push_back(
td_api::make_object<td_api::pollOption>(poll_option.text, poll_option.voter_count - static_cast<int32>(poll_option.is_chosen) + static_cast<int32>(is_chosen), is_chosen));
}
if (!chosen_options.empty()) {
voter_count_diff++;
}
}
return td_api::make_object<td_api::poll>(poll->question, std::move(poll_options), poll->total_voter_count + voter_count_diff,
poll->is_closed);
}
telegram_api::object_ptr<telegram_api::pollAnswer> PollManager::get_input_poll_option(const PollOption &poll_option) {
@ -166,6 +234,107 @@ void PollManager::unregister_poll(PollId poll_id, FullMessageId full_message_id)
poll_messages_[poll_id].erase(full_message_id);
}
void PollManager::set_poll_answer(PollId poll_id, FullMessageId full_message_id, vector<int32> &&option_ids,
Promise<Unit> &&promise) {
if (option_ids.size() > 1) {
return promise.set_error(Status::Error(400, "Can't choose more than 1 option"));
}
if (is_local_poll_id(poll_id)) {
return promise.set_error(Status::Error(5, "Poll can't be answered"));
}
auto poll = get_poll(poll_id);
CHECK(poll != nullptr);
if (poll->is_closed) {
return promise.set_error(Status::Error(400, "Can't answer closed poll"));
}
vector<string> options;
for (auto &option_id : option_ids) {
auto index = static_cast<size_t>(option_id);
if (index >= poll->options.size()) {
return promise.set_error(Status::Error(400, "Invalid option id specified"));
}
options.push_back(poll->options[index].data);
}
do_set_poll_answer(poll_id, full_message_id, std::move(options), 0, std::move(promise));
}
void PollManager::do_set_poll_answer(PollId poll_id, FullMessageId full_message_id, vector<string> &&options,
uint64 logevent_id, Promise<Unit> &&promise) {
auto &pending_answer = pending_answers_[poll_id];
if (!pending_answer.promises_.empty() && pending_answer.options_ == options) {
pending_answer.promises_.push_back(std::move(promise));
return;
}
if (logevent_id == 0 && G()->parameters().use_message_db) {
// TODO add logevent or rewrite pending_answer.logevent_id_
}
if (!pending_answer.promises_.empty()) {
auto promises = std::move(pending_answer.promises_);
pending_answer.promises_.clear();
for (auto &old_promise : promises) {
old_promise.set_value(Unit());
}
}
vector<BufferSlice> sent_options;
for (auto &option : options) {
sent_options.emplace_back(option);
}
auto generation = ++current_generation_;
pending_answer.options_ = std::move(options);
pending_answer.promises_.push_back(std::move(promise));
pending_answer.generation_ = generation;
pending_answer.logevent_id_ = logevent_id;
notify_on_poll_update(poll_id);
auto query_promise = PromiseCreator::lambda([poll_id, generation, actor_id = actor_id(this)](Result<Unit> &&result) {
send_closure(actor_id, &PollManager::on_set_poll_answer, poll_id, generation, std::move(result));
});
send_closure(td_->create_net_actor<SetPollAnswerQuery>(std::move(query_promise)), &SetPollAnswerQuery::send,
full_message_id, std::move(sent_options), generation);
}
void PollManager::on_set_poll_answer(PollId poll_id, uint64 generation, Result<Unit> &&result) {
if (G()->close_flag() && result.is_error()) {
// request will be resent after restart
return;
}
auto it = pending_answers_.find(poll_id);
if (it == pending_answers_.end()) {
// can happen if this is an answer with mismatched generation and server has ignored invoke-after
return;
}
auto &pending_answer = it->second;
CHECK(!pending_answer.promises_.empty());
if (pending_answer.generation_ != generation) {
return;
}
if (pending_answer.logevent_id_ != 0) {
// TODO delete logevent
}
auto promises = std::move(pending_answer.promises_);
for (auto &promise : promises) {
if (result.is_ok()) {
promise.set_value(Unit());
} else {
promise.set_error(result.error().clone());
}
}
pending_answers_.erase(it);
}
void PollManager::close_poll(PollId poll_id) {
auto poll = get_poll_editable(poll_id);
CHECK(poll != nullptr);

View File

@ -39,6 +39,9 @@ class PollManager : public Actor {
void unregister_poll(PollId poll_id, FullMessageId full_message_id);
void set_poll_answer(PollId poll_id, FullMessageId full_message_id, vector<int32> &&option_ids,
Promise<Unit> &&promise);
void close_poll(PollId poll_id);
tl_object_ptr<telegram_api::InputMedia> get_input_media(PollId poll_id) const;
@ -83,7 +86,7 @@ class PollManager : public Actor {
static bool is_local_poll_id(PollId poll_id);
static td_api::object_ptr<td_api::pollOption> get_poll_option_object(const PollOption &poll_option);
static td_api::object_ptr<td_api::pollOption> PollManager::get_poll_option_object(const PollOption &poll_option);
static telegram_api::object_ptr<telegram_api::pollAnswer> get_input_poll_option(const PollOption &poll_option);
@ -107,14 +110,29 @@ class PollManager : public Actor {
Poll *get_poll_force(PollId poll_id);
void do_set_poll_answer(PollId poll_id, FullMessageId full_message_id, vector<string> &&options, uint64 logevent_id,
Promise<Unit> &&promise);
void on_set_poll_answer(PollId poll_id, uint64 generation, Result<Unit> &&result);
Td *td_;
ActorShared<> parent_;
std::unordered_map<PollId, unique_ptr<Poll>, PollIdHash> polls_;
std::unordered_map<PollId, std::unordered_set<FullMessageId, FullMessageIdHash>, PollIdHash> poll_messages_;
struct PendingPollAnswer {
vector<string> options_;
vector<Promise<Unit>> promises_;
uint64 generation_ = 0;
uint64 logevent_id_ = 0;
};
std::unordered_map<PollId, PendingPollAnswer, PollIdHash> pending_answers_;
int64 current_local_poll_id_ = 0;
uint64 current_generation_ = 0;
std::unordered_set<PollId, PollIdHash> loaded_from_database_polls_;
};

View File

@ -6540,13 +6540,14 @@ void Td::on_request(uint64 id, td_api::setOption &request) {
return send_error_raw(id, 3, "Option can't be set");
}
/*
void Td::on_request(uint64 id, td_api::setPollAnswers &request) {
void Td::on_request(uint64 id, td_api::setPollAnswer &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
messages_manager_->set_poll_answers({DialogId(request.chat_id_), MessageId(request.message_id_)}, std::move(request.option_ids_));
messages_manager_->set_poll_answer({DialogId(request.chat_id_), MessageId(request.message_id_)},
std::move(request.option_ids_), std::move(promise));
}
*/
void Td::on_request(uint64 id, td_api::getInlineQueryResults &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.query_);

View File

@ -830,7 +830,7 @@ class Td final : public NetQueryCallback {
void on_request(uint64 id, td_api::setOption &request);
// void on_request(uint64 id, td_api::setPollAnswers &request);
void on_request(uint64 id, td_api::setPollAnswer &request);
void on_request(uint64 id, td_api::getInlineQueryResults &request);

View File

@ -3212,6 +3212,15 @@ class CliClient final : public Actor {
std::tie(chat_id, user_ids) = split(args);
send_request(make_tl_object<td_api::addChatMembers>(as_chat_id(chat_id), as_user_ids(user_ids, ',')));
} else if (op == "spolla") {
string chat_id;
string message_id;
string option_ids;
std::tie(chat_id, args) = split(args);
std::tie(message_id, option_ids) = split(args);
send_request(make_tl_object<td_api::setPollAnswer>(as_chat_id(chat_id), as_message_id(message_id),
to_integers<int32>(option_ids)));
} else {
op_not_found_count++;
}