diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 04940b090..7a229d6b4 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4783,6 +4783,10 @@ setChatDraftMessage chat_id:int53 message_thread_id:int53 draft_message:draftMes //@chat_id Chat identifier @notification_settings New notification settings for the chat. If the chat is muted for more than 1 week, it is considered to be muted forever setChatNotificationSettings chat_id:int53 notification_settings:chatNotificationSettings = Ok; +//@description Changes the ability of users to save, forward, or copy chat content. Supported only for basic groups, supergroups and channels. Requires owner privileges +//@chat_id Chat identifier @allow_saving_content True, if chat content can be saved locally, forwarded, or copied +toggleChatAllowSavingContent chat_id:int53 allow_saving_content:Bool = Ok; + //@description Changes the marked as unread state of a chat @chat_id Chat identifier @is_marked_as_unread New value of is_marked_as_unread toggleChatIsMarkedAsUnread chat_id:int53 is_marked_as_unread:Bool = Ok; diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index a47ebc9d3..4162c940e 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -3731,7 +3731,7 @@ void ContactsManager::Chat::store(StorerT &storer) const { STORE_FLAG(has_default_permissions_version); STORE_FLAG(has_pinned_message_version); STORE_FLAG(has_cache_version); - STORE_FLAG(allow_saving_content); + STORE_FLAG(noforwards); END_STORE_FLAGS(); store(title, storer); @@ -3782,7 +3782,7 @@ void ContactsManager::Chat::parse(ParserT &parser) { PARSE_FLAG(has_default_permissions_version); PARSE_FLAG(has_pinned_message_version); PARSE_FLAG(has_cache_version); - PARSE_FLAG(allow_saving_content); + PARSE_FLAG(noforwards); END_PARSE_FLAGS(); parse(title, parser); @@ -3942,7 +3942,7 @@ void ContactsManager::Channel::store(StorerT &storer) const { STORE_FLAG(legacy_has_active_group_call); STORE_FLAG(is_fake); STORE_FLAG(is_gigagroup); - STORE_FLAG(allow_saving_content); + STORE_FLAG(noforwards); END_STORE_FLAGS(); store(status, storer); @@ -4012,7 +4012,7 @@ void ContactsManager::Channel::parse(ParserT &parser) { PARSE_FLAG(legacy_has_active_group_call); PARSE_FLAG(is_fake); PARSE_FLAG(is_gigagroup); - PARSE_FLAG(allow_saving_content); + PARSE_FLAG(noforwards); END_PARSE_FLAGS(); if (use_new_rights) { @@ -4681,7 +4681,7 @@ bool ContactsManager::get_chat_allow_saving_content(ChatId chat_id) const { if (c == nullptr) { return false; } - return c->allow_saving_content; + return !c->noforwards; } bool ContactsManager::get_channel_allow_saving_content(ChannelId channel_id) const { @@ -4689,7 +4689,7 @@ bool ContactsManager::get_channel_allow_saving_content(ChannelId channel_id) con if (c == nullptr) { return false; } - return c->allow_saving_content; + return !c->noforwards; } string ContactsManager::get_user_private_forward_name(UserId user_id) { @@ -9763,9 +9763,9 @@ void ContactsManager::update_chat(Chat *c, ChatId chat_id, bool from_binlog, boo } c->is_status_changed = false; } - if (c->is_allow_saving_content_changed) { + if (c->is_noforwards_changed) { td_->messages_manager_->on_dialog_allow_saving_content_updated(DialogId(chat_id)); - c->is_allow_saving_content_changed = false; + c->is_noforwards_changed = false; } LOG(DEBUG) << "Update " << chat_id << ": need_save_to_database = " << c->need_save_to_database @@ -9853,9 +9853,9 @@ void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from } c->is_default_permissions_changed = false; } - if (c->is_allow_saving_content_changed) { + if (c->is_noforwards_changed) { td_->messages_manager_->on_dialog_allow_saving_content_updated(DialogId(channel_id)); - c->is_allow_saving_content_changed = false; + c->is_noforwards_changed = false; } if (!td_->auth_manager_->is_bot()) { @@ -12909,12 +12909,11 @@ void ContactsManager::on_update_chat_default_permissions(Chat *c, ChatId chat_id } } -void ContactsManager::on_update_chat_allow_saving_content(Chat *c, ChatId chat_id, bool allow_saving_content) { - if (c->allow_saving_content != allow_saving_content) { - LOG(INFO) << "Update " << chat_id << " allow_saving_content from " << c->allow_saving_content << " to " - << allow_saving_content; - c->allow_saving_content = allow_saving_content; - c->is_allow_saving_content_changed = true; +void ContactsManager::on_update_chat_noforwards(Chat *c, ChatId chat_id, bool noforwards) { + if (c->noforwards != noforwards) { + LOG(INFO) << "Update " << chat_id << " noforwards from " << c->noforwards << " to " << noforwards; + c->noforwards = noforwards; + c->is_noforwards_changed = true; c->need_save_to_database = true; } } @@ -13240,13 +13239,11 @@ void ContactsManager::on_update_channel_default_permissions(Channel *c, ChannelI } } -void ContactsManager::on_update_channel_allow_saving_content(Channel *c, ChannelId channel_id, - bool allow_saving_content) { - if (c->allow_saving_content != allow_saving_content) { - LOG(INFO) << "Update " << channel_id << " allow_saving_content from " << c->allow_saving_content << " to " - << allow_saving_content; - c->allow_saving_content = allow_saving_content; - c->is_allow_saving_content_changed = true; +void ContactsManager::on_update_channel_noforwards(Channel *c, ChannelId channel_id, bool noforwards) { + if (c->noforwards != noforwards) { + LOG(INFO) << "Update " << channel_id << " noforwards from " << c->noforwards << " to " << noforwards; + c->noforwards = noforwards; + c->is_noforwards_changed = true; c->need_save_to_database = true; } } @@ -15298,7 +15295,7 @@ void ContactsManager::on_chat_update(telegram_api::chat &chat, const char *sourc chat.version_); on_update_chat_photo(c, chat_id, std::move(chat.photo_)); on_update_chat_active(c, chat_id, is_active); - on_update_chat_allow_saving_content(c, chat_id, !chat.noforwards_); + on_update_chat_noforwards(c, chat_id, chat.noforwards_); on_update_chat_migrated_to_channel_id(c, chat_id, migrated_to_channel_id); LOG_IF(INFO, !is_active && !migrated_to_channel_id.is_valid()) << chat_id << " is deactivated" << debug_str; if (c->cache_version != Chat::CACHE_VERSION) { @@ -15435,7 +15432,7 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char on_update_channel_photo(c, channel_id, std::move(channel.photo_)); on_update_channel_default_permissions(c, channel_id, get_restricted_rights(std::move(channel.default_banned_rights_))); - on_update_channel_allow_saving_content(c, channel_id, !channel.noforwards_); + on_update_channel_noforwards(c, channel_id, channel.noforwards_); if (c->has_linked_channel != has_linked_channel || c->has_location != has_location || c->is_slow_mode_enabled != is_slow_mode_enabled || c->is_megagroup != is_megagroup || @@ -15494,7 +15491,7 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char on_update_channel_username(c, channel_id, std::move(channel.username_)); // uses status, must be called after on_update_channel_default_permissions(c, channel_id, get_restricted_rights(std::move(channel.default_banned_rights_))); - on_update_channel_allow_saving_content(c, channel_id, !channel.noforwards_); + on_update_channel_noforwards(c, channel_id, channel.noforwards_); bool need_update_participant_count = have_participant_count && participant_count != c->participant_count; if (need_update_participant_count) { @@ -15588,7 +15585,7 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co // on_update_channel_username(c, channel_id, ""); // don't know if channel username is empty, so don't update it tl_object_ptr banned_rights; // == nullptr on_update_channel_default_permissions(c, channel_id, get_restricted_rights(std::move(banned_rights))); - on_update_channel_allow_saving_content(c, channel_id, true); + on_update_channel_noforwards(c, channel_id, false); td_->messages_manager_->on_update_dialog_group_call(DialogId(channel_id), false, false, "receive channelForbidden"); bool sign_messages = false; diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 664d285d1..cfe654a03 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -723,14 +723,14 @@ class ContactsManager final : public Actor { uint32 cache_version = 0; bool is_active = false; - bool allow_saving_content = true; + bool noforwards = true; bool is_title_changed = true; bool is_photo_changed = true; bool is_default_permissions_changed = true; bool is_status_changed = true; bool is_is_active_changed = true; - bool is_allow_saving_content_changed = true; + bool is_noforwards_changed = true; bool is_changed = true; // have new changes that need to be sent to the client and database bool need_save_to_database = true; // have new changes that need only to be saved to the database bool is_update_basic_group_sent = false; @@ -799,7 +799,7 @@ class ContactsManager final : public Actor { bool has_location = false; bool sign_messages = false; bool is_slow_mode_enabled = false; - bool allow_saving_content = true; + bool noforwards = true; bool is_megagroup = false; bool is_gigagroup = false; @@ -812,7 +812,7 @@ class ContactsManager final : public Actor { bool is_photo_changed = true; bool is_default_permissions_changed = true; bool is_status_changed = true; - bool is_allow_saving_content_changed = true; + bool is_noforwards_changed = true; bool had_read_access = true; bool was_member = false; bool is_changed = true; // have new changes that need to be sent to the client and database @@ -1216,7 +1216,7 @@ class ContactsManager final : public Actor { static void on_update_chat_title(Chat *c, ChatId chat_id, string &&title); static void on_update_chat_active(Chat *c, ChatId chat_id, bool is_active); static void on_update_chat_migrated_to_channel_id(Chat *c, ChatId chat_id, ChannelId migrated_to_channel_id); - static void on_update_chat_allow_saving_content(Chat *c, ChatId chat_id, bool allow_saving_content); + static void on_update_chat_noforwards(Chat *c, ChatId chat_id, bool noforwards); void on_update_chat_full_photo(ChatFull *chat_full, ChatId chat_id, Photo photo); bool on_update_chat_full_participants_short(ChatFull *chat_full, ChatId chat_id, int32 version); @@ -1232,7 +1232,7 @@ class ContactsManager final : public Actor { void on_update_channel_status(Channel *c, ChannelId channel_id, DialogParticipantStatus &&status); static void on_update_channel_default_permissions(Channel *c, ChannelId channel_id, RestrictedRights default_permissions); - static void on_update_channel_allow_saving_content(Channel *c, ChannelId channel_id, bool allow_saving_content); + static void on_update_channel_noforwards(Channel *c, ChannelId channel_id, bool noforwards); void on_update_channel_bot_user_ids(ChannelId channel_id, vector &&bot_user_ids); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 1eafa1dee..0cb1d690e 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -1477,6 +1477,44 @@ class EditChatDefaultBannedRightsQuery final : public Td::ResultHandler { } }; +class ToggleNoForwardsQuery final : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit ToggleNoForwardsQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, bool allow_saving_content) { + dialog_id_ = dialog_id; + auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + CHECK(input_peer != nullptr); + send_query(G()->net_query_creator().create( + telegram_api::messages_toggleNoForwards(std::move(input_peer), !allow_saving_content))); + } + + 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()); + } + + auto ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for ToggleNoForwardsQuery: " << to_string(ptr); + td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_)); + } + + void on_error(Status status) final { + if (status.message() == "CHAT_NOT_MODIFIED") { + promise_.set_value(Unit()); + return; + } else { + td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "ToggleNoForwardsQuery"); + } + promise_.set_error(std::move(status)); + } +}; + class SaveDraftMessageQuery final : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; @@ -31927,7 +31965,7 @@ void MessagesManager::set_dialog_title(DialogId dialog_id, const string &title, UNREACHABLE(); } - // TODO this can be wrong if there was previous change title requests + // TODO this can be wrong if there were previous change title requests if (get_dialog_title(dialog_id) == new_title) { return promise.set_value(Unit()); } @@ -32005,9 +32043,6 @@ void MessagesManager::set_dialog_message_ttl_setting(DialogId dialog_id, int32 t void MessagesManager::set_dialog_permissions(DialogId dialog_id, const td_api::object_ptr &permissions, Promise &&promise) { - LOG(INFO) << "Receive setChatPermissions request to change permissions of " << dialog_id << " to " - << to_string(permissions); - if (!have_dialog_force(dialog_id, "set_dialog_permissions")) { return promise.set_error(Status::Error(400, "Chat not found")); } @@ -32049,7 +32084,7 @@ void MessagesManager::set_dialog_permissions(DialogId dialog_id, auto new_permissions = get_restricted_rights(permissions); - // TODO this can be wrong if there was previous change permissions requests + // TODO this can be wrong if there were previous change permissions requests if (get_dialog_default_permissions(dialog_id) == new_permissions) { return promise.set_value(Unit()); } @@ -32058,9 +32093,49 @@ void MessagesManager::set_dialog_permissions(DialogId dialog_id, td_->create_handler(std::move(promise))->send(dialog_id, new_permissions); } -void MessagesManager::set_dialog_theme(DialogId dialog_id, const string &theme_name, Promise &&promise) { - LOG(INFO) << "Receive setChatTheme request to change theme of " << dialog_id << " to " << theme_name; +void MessagesManager::toggle_dialog_allow_saving_content(DialogId dialog_id, bool allow_saving_content, + Promise &&promise) { + if (!have_dialog_force(dialog_id, "toggle_dialog_allow_saving_content")) { + 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")); + } + switch (dialog_id.get_type()) { + case DialogType::User: + case DialogType::SecretChat: + return promise.set_error(Status::Error(400, "Can't restrict saving content in the chat")); + case DialogType::Chat: { + auto chat_id = dialog_id.get_chat_id(); + auto status = td_->contacts_manager_->get_chat_status(chat_id); + if (!status.is_creator()) { + return promise.set_error(Status::Error(400, "Only owner can restrict saving content")); + } + break; + } + case DialogType::Channel: { + auto status = td_->contacts_manager_->get_channel_status(dialog_id.get_channel_id()); + if (!status.is_creator()) { + return promise.set_error(Status::Error(400, "Only owner can restrict saving content")); + } + break; + } + case DialogType::None: + default: + UNREACHABLE(); + } + + // TODO this can be wrong if there were previous toggle_dialog_allow_saving_content requests + if (get_dialog_allow_saving_content(dialog_id) == allow_saving_content) { + return promise.set_value(Unit()); + } + + // TODO invoke after + td_->create_handler(std::move(promise))->send(dialog_id, allow_saving_content); +} + +void MessagesManager::set_dialog_theme(DialogId dialog_id, const string &theme_name, Promise &&promise) { auto d = get_dialog_force(dialog_id, "set_dialog_theme"); if (d == nullptr) { return promise.set_error(Status::Error(400, "Chat not found")); @@ -32088,7 +32163,7 @@ void MessagesManager::set_dialog_theme(DialogId dialog_id, const string &theme_n UNREACHABLE(); } - // TODO this can be wrong if there was previous change theme requests + // TODO this can be wrong if there were previous change theme requests if (d->theme_name == theme_name) { return promise.set_value(Unit()); } diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 3bb98ee45..8a633ad54 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -501,6 +501,8 @@ class MessagesManager final : public Actor { void set_dialog_permissions(DialogId dialog_id, const td_api::object_ptr &permissions, Promise &&promise); + void toggle_dialog_allow_saving_content(DialogId dialog_id, bool allow_saving_content, Promise &&promise); + void set_dialog_theme(DialogId dialog_id, const string &theme_name, Promise &&promise); void pin_dialog_message(DialogId dialog_id, MessageId message_id, bool disable_notification, bool only_for_self, diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 8f3ae6f29..e18a0391c 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6167,6 +6167,13 @@ void Td::on_request(uint64 id, td_api::setChatDraftMessage &request) { std::move(request.draft_message_))); } +void Td::on_request(uint64 id, const td_api::toggleChatAllowSavingContent &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + messages_manager_->toggle_dialog_allow_saving_content(DialogId(request.chat_id_), request.allow_saving_content_, + std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::toggleChatIsPinned &request) { CHECK_IS_USER(); answer_ok_query(id, messages_manager_->toggle_dialog_is_pinned(DialogListId(request.chat_list_), diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 7a5f8864a..916a2cc1c 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -828,6 +828,8 @@ class Td final : public Actor { void on_request(uint64 id, td_api::setChatDraftMessage &request); + void on_request(uint64 id, const td_api::toggleChatAllowSavingContent &request); + void on_request(uint64 id, const td_api::toggleChatIsPinned &request); void on_request(uint64 id, const td_api::toggleChatIsMarkedAsUnread &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index e9e4c246e..bc1139818 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3187,6 +3187,12 @@ class CliClient final : public Actor { as_chat_id(chat_id), as_message_thread_id(message_thread_id), std::move(draft_message))); } else if (op == "cadm") { send_request(td_api::make_object()); + } else if (op == "tcasc") { + string chat_id; + bool allow_saving_content; + get_args(args, chat_id, allow_saving_content); + send_request( + td_api::make_object(as_chat_id(chat_id), allow_saving_content)); } else if (op == "tcip" || op == "tcipa" || begins_with(op, "tcip-")) { string chat_id; bool is_pinned;