diff --git a/td/telegram/DialogManager.cpp b/td/telegram/DialogManager.cpp index 7a19c7d02..1afcc32fa 100644 --- a/td/telegram/DialogManager.cpp +++ b/td/telegram/DialogManager.cpp @@ -13,9 +13,11 @@ #include "td/telegram/ContactsManager.h" #include "td/telegram/FileReferenceManager.h" #include "td/telegram/files/FileManager.h" +#include "td/telegram/files/FileType.h" #include "td/telegram/Global.h" #include "td/telegram/MessagesManager.h" #include "td/telegram/misc.h" +#include "td/telegram/ReportReason.h" #include "td/telegram/SecretChatId.h" #include "td/telegram/StickerPhotoSize.h" #include "td/telegram/Td.h" @@ -246,6 +248,118 @@ class ToggleNoForwardsQuery final : public Td::ResultHandler { } }; +class ReportPeerQuery final : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit ReportPeerQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, const vector &message_ids, ReportReason &&report_reason) { + dialog_id_ = dialog_id; + + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); + CHECK(input_peer != nullptr); + + if (message_ids.empty()) { + send_query(G()->net_query_creator().create(telegram_api::account_reportPeer( + std::move(input_peer), report_reason.get_input_report_reason(), report_reason.get_message()))); + } else { + send_query(G()->net_query_creator().create( + telegram_api::messages_report(std::move(input_peer), MessageId::get_server_message_ids(message_ids), + report_reason.get_input_report_reason(), report_reason.get_message()))); + } + } + + void on_result(BufferSlice packet) final { + static_assert( + std::is_same::value, + ""); + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + bool result = result_ptr.ok(); + if (!result) { + return on_error(Status::Error(400, "Receive false as result")); + } + + promise_.set_value(Unit()); + } + + void on_error(Status status) final { + td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "ReportPeerQuery"); + td_->messages_manager_->reget_dialog_action_bar(dialog_id_, "ReportPeerQuery"); + promise_.set_error(std::move(status)); + } +}; + +class ReportProfilePhotoQuery final : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + FileId file_id_; + string file_reference_; + ReportReason report_reason_; + + public: + explicit ReportProfilePhotoQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, FileId file_id, tl_object_ptr &&input_photo, + ReportReason &&report_reason) { + dialog_id_ = dialog_id; + file_id_ = file_id; + file_reference_ = FileManager::extract_file_reference(input_photo); + report_reason_ = std::move(report_reason); + + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); + CHECK(input_peer != nullptr); + + send_query(G()->net_query_creator().create(telegram_api::account_reportProfilePhoto( + std::move(input_peer), std::move(input_photo), report_reason_.get_input_report_reason(), + report_reason_.get_message()))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + bool result = result_ptr.ok(); + if (!result) { + return on_error(Status::Error(400, "Receive false as result")); + } + + promise_.set_value(Unit()); + } + + void on_error(Status status) final { + LOG(INFO) << "Receive error for report chat photo: " << status; + if (!td_->auth_manager_->is_bot() && FileReferenceManager::is_file_reference_error(status)) { + VLOG(file_references) << "Receive " << status << " for " << file_id_; + td_->file_manager_->delete_file_reference(file_id_, file_reference_); + td_->file_reference_manager_->repair_file_reference( + file_id_, + PromiseCreator::lambda([dialog_id = dialog_id_, file_id = file_id_, report_reason = std::move(report_reason_), + promise = std::move(promise_)](Result result) mutable { + if (result.is_error()) { + LOG(INFO) << "Reported photo " << file_id << " is likely to be deleted"; + return promise.set_value(Unit()); + } + send_closure(G()->dialog_manager(), &DialogManager::report_dialog_photo, dialog_id, file_id, + std::move(report_reason), std::move(promise)); + })); + return; + } + + td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "ReportProfilePhotoQuery"); + promise_.set_error(std::move(status)); + } +}; + class DialogManager::UploadDialogPhotoCallback final : public FileManager::UploadCallback { public: void on_upload_ok(FileId file_id, telegram_api::object_ptr input_file) final { @@ -1329,6 +1443,95 @@ void DialogManager::set_dialog_description(DialogId dialog_id, const string &des } } +bool DialogManager::can_report_dialog(DialogId dialog_id) const { + // doesn't include possibility of report from action bar + switch (dialog_id.get_type()) { + case DialogType::User: + return td_->contacts_manager_->can_report_user(dialog_id.get_user_id()); + case DialogType::Chat: + return false; + case DialogType::Channel: + return !td_->contacts_manager_->get_channel_status(dialog_id.get_channel_id()).is_creator(); + case DialogType::SecretChat: + return false; + case DialogType::None: + default: + UNREACHABLE(); + return false; + } +} + +void DialogManager::report_dialog(DialogId dialog_id, const vector &message_ids, ReportReason &&reason, + Promise &&promise) { + if (!have_dialog_force(dialog_id, "report_dialog")) { + return promise.set_error(Status::Error(400, "Chat not found")); + } + + if (!have_input_peer(dialog_id, AccessRights::Read)) { + return promise.set_error(Status::Error(400, "Can't access the chat")); + } + + MessagesManager::ReportDialogFromActionBar report_from_action_bar; + if (reason.is_spam() && message_ids.empty()) { + // can be a report from action bar + report_from_action_bar = td_->messages_manager_->report_dialog_from_action_bar(dialog_id, promise); + if (report_from_action_bar.is_reported_) { + return; + } + } + + if (!can_report_dialog(dialog_id)) { + if (report_from_action_bar.know_action_bar_) { + return promise.set_value(Unit()); + } + + return promise.set_error(Status::Error(400, "Chat can't be reported")); + } + + vector server_message_ids; + for (auto message_id : message_ids) { + if (message_id.is_scheduled()) { + return promise.set_error(Status::Error(400, "Can't report scheduled messages")); + } + if (message_id.is_valid() && message_id.is_server()) { + server_message_ids.push_back(message_id); + } + } + + if (dialog_id.get_type() == DialogType::Channel && reason.is_unrelated_location()) { + td_->messages_manager_->hide_dialog_action_bar(dialog_id); + } + + td_->create_handler(std::move(promise))->send(dialog_id, server_message_ids, std::move(reason)); +} + +void DialogManager::report_dialog_photo(DialogId dialog_id, FileId file_id, ReportReason &&reason, + Promise &&promise) { + if (!have_dialog_force(dialog_id, "report_dialog_photo")) { + return promise.set_error(Status::Error(400, "Chat not found")); + } + + if (!have_input_peer(dialog_id, AccessRights::Read)) { + return promise.set_error(Status::Error(400, "Can't access the chat")); + } + + if (!can_report_dialog(dialog_id)) { + return promise.set_error(Status::Error(400, "Chat photo can't be reported")); + } + + auto file_view = td_->file_manager_->get_file_view(file_id); + if (file_view.empty()) { + return promise.set_error(Status::Error(400, "Unknown file identifier")); + } + if (get_main_file_type(file_view.get_type()) != FileType::Photo || !file_view.has_remote_location() || + !file_view.remote_location().is_photo()) { + return promise.set_error(Status::Error(400, "Only full chat photos can be reported")); + } + + td_->create_handler(std::move(promise)) + ->send(dialog_id, file_id, file_view.remote_location().as_input_photo(), std::move(reason)); +} + Status DialogManager::can_pin_messages(DialogId dialog_id) const { switch (dialog_id.get_type()) { case DialogType::User: diff --git a/td/telegram/DialogManager.h b/td/telegram/DialogManager.h index 6ab6935ad..58148d629 100644 --- a/td/telegram/DialogManager.h +++ b/td/telegram/DialogManager.h @@ -14,6 +14,7 @@ #include "td/telegram/EmojiStatus.h" #include "td/telegram/files/FileId.h" #include "td/telegram/InputDialogId.h" +#include "td/telegram/MessageId.h" #include "td/telegram/NotificationSettingsScope.h" #include "td/telegram/Photo.h" #include "td/telegram/td_api.h" @@ -30,6 +31,7 @@ namespace td { +class ReportReason; class Td; class DialogManager final : public Actor { @@ -140,6 +142,13 @@ class DialogManager final : public Actor { void set_dialog_description(DialogId dialog_id, const string &description, Promise &&promise); + bool can_report_dialog(DialogId dialog_id) const; + + void report_dialog(DialogId dialog_id, const vector &message_ids, ReportReason &&reason, + Promise &&promise); + + void report_dialog_photo(DialogId dialog_id, FileId file_id, ReportReason &&reason, Promise &&promise); + Status can_pin_messages(DialogId dialog_id) const; bool is_dialog_removed_from_dialog_list(DialogId dialog_id) const; diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 6e453a52c..a73b4c8fc 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -68,7 +68,6 @@ #include "td/telegram/RepliedMessageInfo.hpp" #include "td/telegram/ReplyMarkup.h" #include "td/telegram/ReplyMarkup.hpp" -#include "td/telegram/ReportReason.h" #include "td/telegram/SecretChatsManager.h" #include "td/telegram/SponsoredMessageManager.h" #include "td/telegram/StickerType.h" @@ -4083,118 +4082,6 @@ class ReportEncryptedSpamQuery final : public Td::ResultHandler { } }; -class ReportPeerQuery final : public Td::ResultHandler { - Promise promise_; - DialogId dialog_id_; - - public: - explicit ReportPeerQuery(Promise &&promise) : promise_(std::move(promise)) { - } - - void send(DialogId dialog_id, const vector &message_ids, ReportReason &&report_reason) { - dialog_id_ = dialog_id; - - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); - CHECK(input_peer != nullptr); - - if (message_ids.empty()) { - send_query(G()->net_query_creator().create(telegram_api::account_reportPeer( - std::move(input_peer), report_reason.get_input_report_reason(), report_reason.get_message()))); - } else { - send_query(G()->net_query_creator().create( - telegram_api::messages_report(std::move(input_peer), MessageId::get_server_message_ids(message_ids), - report_reason.get_input_report_reason(), report_reason.get_message()))); - } - } - - void on_result(BufferSlice packet) final { - static_assert( - std::is_same::value, - ""); - auto result_ptr = fetch_result(packet); - if (result_ptr.is_error()) { - return on_error(result_ptr.move_as_error()); - } - - bool result = result_ptr.ok(); - if (!result) { - return on_error(Status::Error(400, "Receive false as result")); - } - - promise_.set_value(Unit()); - } - - void on_error(Status status) final { - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "ReportPeerQuery"); - td_->messages_manager_->reget_dialog_action_bar(dialog_id_, "ReportPeerQuery"); - promise_.set_error(std::move(status)); - } -}; - -class ReportProfilePhotoQuery final : public Td::ResultHandler { - Promise promise_; - DialogId dialog_id_; - FileId file_id_; - string file_reference_; - ReportReason report_reason_; - - public: - explicit ReportProfilePhotoQuery(Promise &&promise) : promise_(std::move(promise)) { - } - - void send(DialogId dialog_id, FileId file_id, tl_object_ptr &&input_photo, - ReportReason &&report_reason) { - dialog_id_ = dialog_id; - file_id_ = file_id; - file_reference_ = FileManager::extract_file_reference(input_photo); - report_reason_ = std::move(report_reason); - - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); - CHECK(input_peer != nullptr); - - send_query(G()->net_query_creator().create(telegram_api::account_reportProfilePhoto( - std::move(input_peer), std::move(input_photo), report_reason_.get_input_report_reason(), - report_reason_.get_message()))); - } - - void on_result(BufferSlice packet) final { - auto result_ptr = fetch_result(packet); - if (result_ptr.is_error()) { - return on_error(result_ptr.move_as_error()); - } - - bool result = result_ptr.ok(); - if (!result) { - return on_error(Status::Error(400, "Receive false as result")); - } - - promise_.set_value(Unit()); - } - - void on_error(Status status) final { - LOG(INFO) << "Receive error for report chat photo: " << status; - if (!td_->auth_manager_->is_bot() && FileReferenceManager::is_file_reference_error(status)) { - VLOG(file_references) << "Receive " << status << " for " << file_id_; - td_->file_manager_->delete_file_reference(file_id_, file_reference_); - td_->file_reference_manager_->repair_file_reference( - file_id_, - PromiseCreator::lambda([dialog_id = dialog_id_, file_id = file_id_, report_reason = std::move(report_reason_), - promise = std::move(promise_)](Result result) mutable { - if (result.is_error()) { - LOG(INFO) << "Reported photo " << file_id << " is likely to be deleted"; - return promise.set_value(Unit()); - } - send_closure(G()->messages_manager(), &MessagesManager::report_dialog_photo, dialog_id, file_id, - std::move(report_reason), std::move(promise)); - })); - return; - } - - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "ReportProfilePhotoQuery"); - promise_.set_error(std::move(status)); - } -}; - class EditPeerFoldersQuery final : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; @@ -8366,109 +8253,6 @@ void MessagesManager::toggle_dialog_report_spam_state_on_server(DialogId dialog_ } } -bool MessagesManager::can_report_dialog(DialogId dialog_id) const { - // doesn't include possibility of report from action bar - switch (dialog_id.get_type()) { - case DialogType::User: - return td_->contacts_manager_->can_report_user(dialog_id.get_user_id()); - case DialogType::Chat: - return false; - case DialogType::Channel: - return !td_->contacts_manager_->get_channel_status(dialog_id.get_channel_id()).is_creator(); - case DialogType::SecretChat: - return false; - case DialogType::None: - default: - UNREACHABLE(); - return false; - } -} - -void MessagesManager::report_dialog(DialogId dialog_id, const vector &message_ids, ReportReason &&reason, - Promise &&promise) { - Dialog *d = get_dialog_force(dialog_id, "report_dialog"); - if (d == nullptr) { - return promise.set_error(Status::Error(400, "Chat not found")); - } - - if (!td_->dialog_manager_->have_input_peer(dialog_id, AccessRights::Read)) { - return promise.set_error(Status::Error(400, "Can't access the chat")); - } - - Dialog *user_d = d; - bool is_dialog_spam_report = false; - bool can_report_spam = false; - if (reason.is_spam() && message_ids.empty()) { - // report from action bar - if (dialog_id.get_type() == DialogType::SecretChat) { - auto user_dialog_id = DialogId(td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id())); - user_d = get_dialog_force(user_dialog_id, "report_dialog 2"); - if (user_d == nullptr) { - return promise.set_error(Status::Error(400, "Chat with the user not found")); - } - } - is_dialog_spam_report = user_d->know_action_bar; - can_report_spam = user_d->action_bar != nullptr && user_d->action_bar->can_report_spam(); - } - - if (is_dialog_spam_report && can_report_spam) { - hide_dialog_action_bar(user_d); - return toggle_dialog_report_spam_state_on_server(dialog_id, true, 0, std::move(promise)); - } - - if (!can_report_dialog(dialog_id)) { - if (is_dialog_spam_report) { - return promise.set_value(Unit()); - } - - return promise.set_error(Status::Error(400, "Chat can't be reported")); - } - - vector server_message_ids; - for (auto message_id : message_ids) { - if (message_id.is_scheduled()) { - return promise.set_error(Status::Error(400, "Can't report scheduled messages")); - } - if (message_id.is_valid() && message_id.is_server()) { - server_message_ids.push_back(message_id); - } - } - - if (dialog_id.get_type() == DialogType::Channel && reason.is_unrelated_location()) { - hide_dialog_action_bar(d); - } - - td_->create_handler(std::move(promise))->send(dialog_id, server_message_ids, std::move(reason)); -} - -void MessagesManager::report_dialog_photo(DialogId dialog_id, FileId file_id, ReportReason &&reason, - Promise &&promise) { - Dialog *d = get_dialog_force(dialog_id, "report_dialog_photo"); - if (d == nullptr) { - return promise.set_error(Status::Error(400, "Chat not found")); - } - - if (!td_->dialog_manager_->have_input_peer(dialog_id, AccessRights::Read)) { - return promise.set_error(Status::Error(400, "Can't access the chat")); - } - - if (!can_report_dialog(dialog_id)) { - return promise.set_error(Status::Error(400, "Chat photo can't be reported")); - } - - auto file_view = td_->file_manager_->get_file_view(file_id); - if (file_view.empty()) { - return promise.set_error(Status::Error(400, "Unknown file ID")); - } - if (get_main_file_type(file_view.get_type()) != FileType::Photo || !file_view.has_remote_location() || - !file_view.remote_location().is_photo()) { - return promise.set_error(Status::Error(400, "Only full chat photos can be reported")); - } - - td_->create_handler(std::move(promise)) - ->send(dialog_id, file_id, file_view.remote_location().as_input_photo(), std::move(reason)); -} - void MessagesManager::on_get_peer_settings(DialogId dialog_id, tl_object_ptr &&peer_settings, bool ignore_privacy_exception) { @@ -17691,6 +17475,32 @@ bool MessagesManager::is_message_edited_recently(MessageFullId message_full_id, return m->edit_date >= G()->unix_time() - seconds; } +MessagesManager::ReportDialogFromActionBar MessagesManager::report_dialog_from_action_bar(DialogId dialog_id, + Promise &promise) { + ReportDialogFromActionBar result; + Dialog *d = nullptr; + if (dialog_id.get_type() == DialogType::SecretChat) { + auto user_dialog_id = DialogId(td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id())); + d = get_dialog_force(user_dialog_id, "report_dialog_from_action_bar"); + if (d == nullptr) { + promise.set_error(Status::Error(400, "Chat with the user not found")); + result.is_reported_ = true; + return result; + } + } else { + d = get_dialog(dialog_id); + CHECK(d != nullptr); + } + result.know_action_bar_ = d->know_action_bar; + + if (d->know_action_bar && d->action_bar != nullptr && d->action_bar->can_report_spam()) { + result.is_reported_ = true; + hide_dialog_action_bar(d); + toggle_dialog_report_spam_state_on_server(dialog_id, true, 0, std::move(promise)); + } + return result; +} + Status MessagesManager::can_get_media_timestamp_link(DialogId dialog_id, const Message *m) { if (m == nullptr) { return Status::Error(400, "Message not found"); @@ -19947,11 +19757,11 @@ td_api::object_ptr MessagesManager::get_chat_object(const Dialog * get_chat_positions_object(d), get_default_message_sender_object(d), block_list_id.get_block_list_object(), td_->dialog_manager_->get_dialog_has_protected_content(d->dialog_id), is_translatable, d->is_marked_as_unread, get_dialog_view_as_topics(d), get_dialog_has_scheduled_messages(d), can_delete.for_self_, - can_delete.for_all_users_, can_report_dialog(d->dialog_id), d->notification_settings.silent_send_message, - d->server_unread_count + d->local_unread_count, d->last_read_inbox_message_id.get(), - d->last_read_outbox_message_id.get(), d->unread_mention_count, d->unread_reaction_count, - get_chat_notification_settings_object(&d->notification_settings), std::move(available_reactions), - d->message_ttl.get_message_auto_delete_time_object(), + can_delete.for_all_users_, td_->dialog_manager_->can_report_dialog(d->dialog_id), + d->notification_settings.silent_send_message, d->server_unread_count + d->local_unread_count, + d->last_read_inbox_message_id.get(), d->last_read_outbox_message_id.get(), d->unread_mention_count, + d->unread_reaction_count, get_chat_notification_settings_object(&d->notification_settings), + std::move(available_reactions), d->message_ttl.get_message_auto_delete_time_object(), td_->dialog_manager_->get_dialog_emoji_status_object(d->dialog_id), get_chat_background_object(d), get_dialog_theme_name(d), get_chat_action_bar_object(d), get_video_chat_object(d), get_chat_join_requests_info_object(d), d->reply_markup_message_id.get(), std::move(draft_message), diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index d5e177858..351588b79 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -111,7 +111,6 @@ class DraftMessage; struct InputMessageContent; class MessageContent; struct MessageReactions; -class ReportReason; class Td; class MessagesManager final : public Actor { @@ -611,6 +610,12 @@ class MessagesManager final : public Actor { bool is_message_edited_recently(MessageFullId message_full_id, int32 seconds); + struct ReportDialogFromActionBar { + bool know_action_bar_ = false; + bool is_reported_ = false; + }; + ReportDialogFromActionBar report_dialog_from_action_bar(DialogId dialog_id, Promise &promise); + bool is_deleted_secret_chat(DialogId dialog_id) const; Result> get_message_link(MessageFullId message_full_id, int32 media_timestamp, bool for_group, @@ -851,11 +856,6 @@ class MessagesManager final : public Actor { void reget_dialog_action_bar(DialogId dialog_id, const char *source, bool is_repair = true); - void report_dialog(DialogId dialog_id, const vector &message_ids, ReportReason &&reason, - Promise &&promise); - - void report_dialog_photo(DialogId dialog_id, FileId file_id, ReportReason &&reason, Promise &&promise); - void on_get_peer_settings(DialogId dialog_id, tl_object_ptr &&peer_settings, bool ignore_privacy_exception = false); @@ -1752,8 +1752,6 @@ class MessagesManager final : public Actor { bool can_edit_message(DialogId dialog_id, const Message *m, bool is_editing, bool only_reply_markup = false) const; - bool can_report_dialog(DialogId dialog_id) const; - static Status can_get_media_timestamp_link(DialogId dialog_id, const Message *m); bool can_report_message_reactions(DialogId dialog_id, const Message *m) const; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 762e6e063..9a172f4b4 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -8239,8 +8239,8 @@ void Td::on_request(uint64 id, td_api::reportChat &request) { return send_error_raw(id, r_report_reason.error().code(), r_report_reason.error().message()); } CREATE_OK_REQUEST_PROMISE(); - messages_manager_->report_dialog(DialogId(request.chat_id_), MessageId::get_message_ids(request.message_ids_), - r_report_reason.move_as_ok(), std::move(promise)); + dialog_manager_->report_dialog(DialogId(request.chat_id_), MessageId::get_message_ids(request.message_ids_), + r_report_reason.move_as_ok(), std::move(promise)); } void Td::on_request(uint64 id, td_api::reportChatPhoto &request) { @@ -8250,8 +8250,8 @@ void Td::on_request(uint64 id, td_api::reportChatPhoto &request) { return send_error_raw(id, r_report_reason.error().code(), r_report_reason.error().message()); } CREATE_OK_REQUEST_PROMISE(); - messages_manager_->report_dialog_photo(DialogId(request.chat_id_), FileId(request.file_id_, 0), - r_report_reason.move_as_ok(), std::move(promise)); + dialog_manager_->report_dialog_photo(DialogId(request.chat_id_), FileId(request.file_id_, 0), + r_report_reason.move_as_ok(), std::move(promise)); } void Td::on_request(uint64 id, const td_api::reportMessageReactions &request) {