diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 6aa910676..2006b4634 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -2247,360 +2247,6 @@ class EditChatAdminQuery final : public Td::ResultHandler { } }; -class ExportChatInviteQuery final : public Td::ResultHandler { - Promise> promise_; - DialogId dialog_id_; - - public: - explicit ExportChatInviteQuery(Promise> &&promise) - : promise_(std::move(promise)) { - } - - void send(DialogId dialog_id, const string &title, int32 expire_date, int32 usage_limit, bool creates_join_request, - bool is_permanent) { - dialog_id_ = dialog_id; - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); - if (input_peer == nullptr) { - return on_error(Status::Error(400, "Can't access the chat")); - } - - int32 flags = 0; - if (expire_date > 0) { - flags |= telegram_api::messages_exportChatInvite::EXPIRE_DATE_MASK; - } - if (usage_limit > 0) { - flags |= telegram_api::messages_exportChatInvite::USAGE_LIMIT_MASK; - } - if (creates_join_request) { - flags |= telegram_api::messages_exportChatInvite::REQUEST_NEEDED_MASK; - } - if (is_permanent) { - flags |= telegram_api::messages_exportChatInvite::LEGACY_REVOKE_PERMANENT_MASK; - } - if (!title.empty()) { - flags |= telegram_api::messages_exportChatInvite::TITLE_MASK; - } - - send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite( - flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), expire_date, usage_limit, title))); - } - - 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 ExportChatInviteQuery: " << to_string(ptr); - - DialogInviteLink invite_link(std::move(ptr), false, "ExportChatInviteQuery"); - if (!invite_link.is_valid()) { - return on_error(Status::Error(500, "Receive invalid invite link")); - } - if (invite_link.get_creator_user_id() != td_->contacts_manager_->get_my_id()) { - return on_error(Status::Error(500, "Receive invalid invite link creator")); - } - if (invite_link.is_permanent()) { - td_->contacts_manager_->on_get_permanent_dialog_invite_link(dialog_id_, invite_link); - } - promise_.set_value(invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); - } - - void on_error(Status status) final { - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "ExportChatInviteQuery"); - promise_.set_error(std::move(status)); - } -}; - -class EditChatInviteLinkQuery final : public Td::ResultHandler { - Promise> promise_; - DialogId dialog_id_; - - public: - explicit EditChatInviteLinkQuery(Promise> &&promise) - : promise_(std::move(promise)) { - } - - void send(DialogId dialog_id, const string &invite_link, const string &title, int32 expire_date, int32 usage_limit, - bool creates_join_request) { - dialog_id_ = dialog_id; - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); - if (input_peer == nullptr) { - return on_error(Status::Error(400, "Can't access the chat")); - } - - int32 flags = telegram_api::messages_editExportedChatInvite::EXPIRE_DATE_MASK | - telegram_api::messages_editExportedChatInvite::USAGE_LIMIT_MASK | - telegram_api::messages_editExportedChatInvite::REQUEST_NEEDED_MASK | - telegram_api::messages_editExportedChatInvite::TITLE_MASK; - send_query(G()->net_query_creator().create( - telegram_api::messages_editExportedChatInvite(flags, false /*ignored*/, std::move(input_peer), invite_link, - expire_date, usage_limit, creates_join_request, title))); - } - - 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 result = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for EditChatInviteLinkQuery: " << to_string(result); - - if (result->get_id() != telegram_api::messages_exportedChatInvite::ID) { - return on_error(Status::Error(500, "Receive unexpected response from server")); - } - - auto invite = move_tl_object_as(result); - - td_->contacts_manager_->on_get_users(std::move(invite->users_), "EditChatInviteLinkQuery"); - - DialogInviteLink invite_link(std::move(invite->invite_), false, "EditChatInviteLinkQuery"); - if (!invite_link.is_valid()) { - return on_error(Status::Error(500, "Receive invalid invite link")); - } - promise_.set_value(invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); - } - - void on_error(Status status) final { - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "EditChatInviteLinkQuery"); - promise_.set_error(std::move(status)); - } -}; - -class GetExportedChatInviteQuery final : public Td::ResultHandler { - Promise> promise_; - DialogId dialog_id_; - - public: - explicit GetExportedChatInviteQuery(Promise> &&promise) - : promise_(std::move(promise)) { - } - - void send(DialogId dialog_id, const string &invite_link) { - dialog_id_ = dialog_id; - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); - if (input_peer == nullptr) { - return on_error(Status::Error(400, "Can't access the chat")); - } - - send_query(G()->net_query_creator().create( - telegram_api::messages_getExportedChatInvite(std::move(input_peer), invite_link))); - } - - 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()); - } - - if (result_ptr.ok()->get_id() != telegram_api::messages_exportedChatInvite::ID) { - LOG(ERROR) << "Receive wrong result for GetExportedChatInviteQuery: " << to_string(result_ptr.ok()); - return on_error(Status::Error(500, "Receive unexpected response")); - } - - auto result = move_tl_object_as(result_ptr.ok_ref()); - LOG(INFO) << "Receive result for GetExportedChatInviteQuery: " << to_string(result); - - td_->contacts_manager_->on_get_users(std::move(result->users_), "GetExportedChatInviteQuery"); - - DialogInviteLink invite_link(std::move(result->invite_), false, "GetExportedChatInviteQuery"); - if (!invite_link.is_valid()) { - LOG(ERROR) << "Receive invalid invite link in " << dialog_id_; - return on_error(Status::Error(500, "Receive invalid invite link")); - } - promise_.set_value(invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); - } - - void on_error(Status status) final { - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "GetExportedChatInviteQuery"); - promise_.set_error(std::move(status)); - } -}; - -class GetExportedChatInvitesQuery final : public Td::ResultHandler { - Promise> promise_; - DialogId dialog_id_; - - public: - explicit GetExportedChatInvitesQuery(Promise> &&promise) - : promise_(std::move(promise)) { - } - - void send(DialogId dialog_id, tl_object_ptr &&input_user, bool is_revoked, int32 offset_date, - const string &offset_invite_link, int32 limit) { - dialog_id_ = dialog_id; - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); - if (input_peer == nullptr) { - return on_error(Status::Error(400, "Can't access the chat")); - } - - int32 flags = 0; - if (!offset_invite_link.empty() || offset_date != 0) { - flags |= telegram_api::messages_getExportedChatInvites::OFFSET_DATE_MASK; - flags |= telegram_api::messages_getExportedChatInvites::OFFSET_LINK_MASK; - } - if (is_revoked) { - flags |= telegram_api::messages_getExportedChatInvites::REVOKED_MASK; - } - send_query(G()->net_query_creator().create( - telegram_api::messages_getExportedChatInvites(flags, false /*ignored*/, std::move(input_peer), - std::move(input_user), offset_date, offset_invite_link, limit))); - } - - 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 result = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for GetExportedChatInvitesQuery: " << to_string(result); - - td_->contacts_manager_->on_get_users(std::move(result->users_), "GetExportedChatInvitesQuery"); - - int32 total_count = result->count_; - if (total_count < static_cast(result->invites_.size())) { - LOG(ERROR) << "Receive wrong total count of invite links " << total_count << " in " << dialog_id_; - total_count = static_cast(result->invites_.size()); - } - vector> invite_links; - for (auto &invite : result->invites_) { - DialogInviteLink invite_link(std::move(invite), false, "GetExportedChatInvitesQuery"); - if (!invite_link.is_valid()) { - LOG(ERROR) << "Receive invalid invite link in " << dialog_id_; - total_count--; - continue; - } - invite_links.push_back(invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); - } - promise_.set_value(td_api::make_object(total_count, std::move(invite_links))); - } - - void on_error(Status status) final { - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "GetExportedChatInvitesQuery"); - promise_.set_error(std::move(status)); - } -}; - -class GetChatAdminWithInvitesQuery final : public Td::ResultHandler { - Promise> promise_; - DialogId dialog_id_; - - public: - explicit GetChatAdminWithInvitesQuery(Promise> &&promise) - : promise_(std::move(promise)) { - } - - void send(DialogId dialog_id) { - dialog_id_ = dialog_id; - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); - if (input_peer == nullptr) { - return on_error(Status::Error(400, "Can't access the chat")); - } - - send_query(G()->net_query_creator().create(telegram_api::messages_getAdminsWithInvites(std::move(input_peer)))); - } - - 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 result = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for GetChatAdminWithInvitesQuery: " << to_string(result); - - td_->contacts_manager_->on_get_users(std::move(result->users_), "GetChatAdminWithInvitesQuery"); - - vector> invite_link_counts; - for (auto &admin : result->admins_) { - UserId user_id(admin->admin_id_); - if (!user_id.is_valid()) { - LOG(ERROR) << "Receive invalid invite link creator " << user_id << " in " << dialog_id_; - continue; - } - invite_link_counts.push_back(td_api::make_object( - td_->contacts_manager_->get_user_id_object(user_id, "chatInviteLinkCount"), admin->invites_count_, - admin->revoked_invites_count_)); - } - promise_.set_value(td_api::make_object(std::move(invite_link_counts))); - } - - void on_error(Status status) final { - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "GetChatAdminWithInvitesQuery"); - promise_.set_error(std::move(status)); - } -}; - -class GetChatInviteImportersQuery final : public Td::ResultHandler { - Promise> promise_; - DialogId dialog_id_; - - public: - explicit GetChatInviteImportersQuery(Promise> &&promise) - : promise_(std::move(promise)) { - } - - void send(DialogId dialog_id, const string &invite_link, int32 offset_date, UserId offset_user_id, int32 limit) { - dialog_id_ = dialog_id; - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); - if (input_peer == nullptr) { - return on_error(Status::Error(400, "Can't access the chat")); - } - - auto r_input_user = td_->contacts_manager_->get_input_user(offset_user_id); - if (r_input_user.is_error()) { - r_input_user = make_tl_object(); - } - - int32 flags = telegram_api::messages_getChatInviteImporters::LINK_MASK; - send_query(G()->net_query_creator().create( - telegram_api::messages_getChatInviteImporters(flags, false /*ignored*/, std::move(input_peer), invite_link, - string(), offset_date, r_input_user.move_as_ok(), limit))); - } - - 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 result = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for GetChatInviteImportersQuery: " << to_string(result); - - td_->contacts_manager_->on_get_users(std::move(result->users_), "GetChatInviteImportersQuery"); - - int32 total_count = result->count_; - if (total_count < static_cast(result->importers_.size())) { - LOG(ERROR) << "Receive wrong total count of invite link users " << total_count << " in " << dialog_id_; - total_count = static_cast(result->importers_.size()); - } - vector> invite_link_members; - for (auto &importer : result->importers_) { - UserId user_id(importer->user_id_); - UserId approver_user_id(importer->approved_by_); - if (!user_id.is_valid() || (!approver_user_id.is_valid() && approver_user_id != UserId()) || - importer->requested_) { - LOG(ERROR) << "Receive invalid invite link importer: " << to_string(importer); - total_count--; - continue; - } - invite_link_members.push_back(td_api::make_object( - td_->contacts_manager_->get_user_id_object(user_id, "chatInviteLinkMember"), importer->date_, - importer->via_chatlist_, - td_->contacts_manager_->get_user_id_object(approver_user_id, "chatInviteLinkMember"))); - } - promise_.set_value(td_api::make_object(total_count, std::move(invite_link_members))); - } - - void on_error(Status status) final { - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "GetChatInviteImportersQuery"); - promise_.set_error(std::move(status)); - } -}; - class GetChatJoinRequestsQuery final : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; @@ -2769,150 +2415,6 @@ class HideAllChatJoinRequestsQuery final : public Td::ResultHandler { } }; -class RevokeChatInviteLinkQuery final : public Td::ResultHandler { - Promise> promise_; - DialogId dialog_id_; - - public: - explicit RevokeChatInviteLinkQuery(Promise> &&promise) - : promise_(std::move(promise)) { - } - - void send(DialogId dialog_id, const string &invite_link) { - dialog_id_ = dialog_id; - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); - if (input_peer == nullptr) { - return on_error(Status::Error(400, "Can't access the chat")); - } - - int32 flags = telegram_api::messages_editExportedChatInvite::REVOKED_MASK; - send_query(G()->net_query_creator().create(telegram_api::messages_editExportedChatInvite( - flags, false /*ignored*/, std::move(input_peer), invite_link, 0, 0, false, string()))); - } - - 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 result = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for RevokeChatInviteLinkQuery: " << to_string(result); - - vector> links; - switch (result->get_id()) { - case telegram_api::messages_exportedChatInvite::ID: { - auto invite = move_tl_object_as(result); - - td_->contacts_manager_->on_get_users(std::move(invite->users_), "RevokeChatInviteLinkQuery"); - - DialogInviteLink invite_link(std::move(invite->invite_), false, "RevokeChatInviteLinkQuery"); - if (!invite_link.is_valid()) { - return on_error(Status::Error(500, "Receive invalid invite link")); - } - links.push_back(invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); - break; - } - case telegram_api::messages_exportedChatInviteReplaced::ID: { - auto invite = move_tl_object_as(result); - - td_->contacts_manager_->on_get_users(std::move(invite->users_), "RevokeChatInviteLinkQuery replaced"); - - DialogInviteLink invite_link(std::move(invite->invite_), false, "RevokeChatInviteLinkQuery replaced"); - DialogInviteLink new_invite_link(std::move(invite->new_invite_), false, - "RevokeChatInviteLinkQuery new replaced"); - if (!invite_link.is_valid() || !new_invite_link.is_valid()) { - return on_error(Status::Error(500, "Receive invalid invite link")); - } - if (new_invite_link.get_creator_user_id() == td_->contacts_manager_->get_my_id() && - new_invite_link.is_permanent()) { - td_->contacts_manager_->on_get_permanent_dialog_invite_link(dialog_id_, new_invite_link); - } - links.push_back(invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); - links.push_back(new_invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); - break; - } - default: - UNREACHABLE(); - } - auto total_count = static_cast(links.size()); - promise_.set_value(td_api::make_object(total_count, std::move(links))); - } - - void on_error(Status status) final { - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "RevokeChatInviteLinkQuery"); - promise_.set_error(std::move(status)); - } -}; - -class DeleteExportedChatInviteQuery final : public Td::ResultHandler { - Promise promise_; - DialogId dialog_id_; - - public: - explicit DeleteExportedChatInviteQuery(Promise &&promise) : promise_(std::move(promise)) { - } - - void send(DialogId dialog_id, const string &invite_link) { - dialog_id_ = dialog_id; - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); - if (input_peer == nullptr) { - return on_error(Status::Error(400, "Can't access the chat")); - } - - send_query(G()->net_query_creator().create( - telegram_api::messages_deleteExportedChatInvite(std::move(input_peer), invite_link))); - } - - 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()); - } - - promise_.set_value(Unit()); - } - - void on_error(Status status) final { - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "DeleteExportedChatInviteQuery"); - promise_.set_error(std::move(status)); - } -}; - -class DeleteRevokedExportedChatInvitesQuery final : public Td::ResultHandler { - Promise promise_; - DialogId dialog_id_; - - public: - explicit DeleteRevokedExportedChatInvitesQuery(Promise &&promise) : promise_(std::move(promise)) { - } - - void send(DialogId dialog_id, tl_object_ptr &&input_user) { - dialog_id_ = dialog_id; - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); - if (input_peer == nullptr) { - return on_error(Status::Error(400, "Can't access the chat")); - } - - send_query(G()->net_query_creator().create( - telegram_api::messages_deleteRevokedExportedChatInvites(std::move(input_peer), std::move(input_user)))); - } - - 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()); - } - - promise_.set_value(Unit()); - } - - void on_error(Status status) final { - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "DeleteRevokedExportedChatInvitesQuery"); - promise_.set_error(std::move(status)); - } -}; - class DeleteChatUserQuery final : public Td::ResultHandler { Promise promise_; @@ -9267,41 +8769,30 @@ void ContactsManager::transfer_channel_ownership( ->send(channel_id, user_id, std::move(input_check_password)); } -Status ContactsManager::can_manage_dialog_invite_links(DialogId dialog_id, bool creator_only) { - if (!td_->dialog_manager_->have_dialog_force(dialog_id, "can_manage_dialog_invite_links")) { +Status ContactsManager::can_manage_chat_join_requests(DialogId dialog_id) { + if (!td_->dialog_manager_->have_dialog_force(dialog_id, "can_manage_chat_join_requests")) { return Status::Error(400, "Chat not found"); } switch (dialog_id.get_type()) { + case DialogType::SecretChat: case DialogType::User: - return Status::Error(400, "Can't invite members to a private chat"); + return Status::Error(400, "The chat can't have join requests"); case DialogType::Chat: { - const Chat *c = get_chat(dialog_id.get_chat_id()); - if (c == nullptr) { - return Status::Error(400, "Chat info not found"); - } - if (!c->is_active) { + auto chat_id = dialog_id.get_chat_id(); + if (!get_chat_is_active(chat_id)) { return Status::Error(400, "Chat is deactivated"); } - bool have_rights = creator_only ? c->status.is_creator() : c->status.can_manage_invite_links(); - if (!have_rights) { - return Status::Error(400, "Not enough rights to manage chat invite link"); + if (!get_chat_status(chat_id).can_manage_invite_links()) { + return Status::Error(400, "Not enough rights to manage chat join requests"); } break; } - case DialogType::Channel: { - const Channel *c = get_channel(dialog_id.get_channel_id()); - if (c == nullptr) { - return Status::Error(400, "Chat info not found"); - } - bool have_rights = creator_only ? c->status.is_creator() : c->status.can_manage_invite_links(); - if (!have_rights) { - return Status::Error(400, "Not enough rights to manage chat invite link"); + case DialogType::Channel: + if (!get_channel_status(dialog_id.get_channel_id()).can_manage_invite_links()) { + return Status::Error(400, "Not enough rights to manage chat join requests"); } break; - } - case DialogType::SecretChat: - return Status::Error(400, "Can't invite members to a secret chat"); case DialogType::None: default: UNREACHABLE(); @@ -9309,114 +8800,10 @@ Status ContactsManager::can_manage_dialog_invite_links(DialogId dialog_id, bool return Status::OK(); } -void ContactsManager::export_dialog_invite_link(DialogId dialog_id, string title, int32 expire_date, int32 usage_limit, - bool creates_join_request, bool is_permanent, - Promise> &&promise) { - get_me(PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, title = std::move(title), expire_date, - usage_limit, creates_join_request, is_permanent, - promise = std::move(promise)](Result &&result) mutable { - if (result.is_error()) { - promise.set_error(result.move_as_error()); - } else { - send_closure(actor_id, &ContactsManager::export_dialog_invite_link_impl, dialog_id, std::move(title), expire_date, - usage_limit, creates_join_request, is_permanent, std::move(promise)); - } - })); -} - -void ContactsManager::export_dialog_invite_link_impl(DialogId dialog_id, string title, int32 expire_date, - int32 usage_limit, bool creates_join_request, bool is_permanent, - Promise> &&promise) { - TRY_STATUS_PROMISE(promise, G()->close_status()); - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); - if (creates_join_request && usage_limit > 0) { - return promise.set_error( - Status::Error(400, "Member limit can't be specified for links requiring administrator approval")); - } - - auto new_title = clean_name(std::move(title), MAX_INVITE_LINK_TITLE_LENGTH); - td_->create_handler(std::move(promise)) - ->send(dialog_id, new_title, expire_date, usage_limit, creates_join_request, is_permanent); -} - -void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string &invite_link, string title, - int32 expire_date, int32 usage_limit, bool creates_join_request, - Promise> &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); - if (creates_join_request && usage_limit > 0) { - return promise.set_error( - Status::Error(400, "Member limit can't be specified for links requiring administrator approval")); - } - - if (invite_link.empty()) { - return promise.set_error(Status::Error(400, "Invite link must be non-empty")); - } - - auto new_title = clean_name(std::move(title), MAX_INVITE_LINK_TITLE_LENGTH); - td_->create_handler(std::move(promise)) - ->send(dialog_id, invite_link, new_title, expire_date, usage_limit, creates_join_request); -} - -void ContactsManager::get_dialog_invite_link(DialogId dialog_id, const string &invite_link, - Promise> &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, false)); - - if (invite_link.empty()) { - return promise.set_error(Status::Error(400, "Invite link must be non-empty")); - } - - td_->create_handler(std::move(promise))->send(dialog_id, invite_link); -} - -void ContactsManager::get_dialog_invite_link_counts( - DialogId dialog_id, Promise> &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, true)); - - td_->create_handler(std::move(promise))->send(dialog_id); -} - -void ContactsManager::get_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, bool is_revoked, - int32 offset_date, const string &offset_invite_link, int32 limit, - Promise> &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, creator_user_id != get_my_id())); - TRY_RESULT_PROMISE(promise, input_user, get_input_user(creator_user_id)); - - if (limit <= 0) { - return promise.set_error(Status::Error(400, "Parameter limit must be positive")); - } - - td_->create_handler(std::move(promise)) - ->send(dialog_id, std::move(input_user), is_revoked, offset_date, offset_invite_link, limit); -} - -void ContactsManager::get_dialog_invite_link_users( - DialogId dialog_id, const string &invite_link, td_api::object_ptr offset_member, - int32 limit, Promise> &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); - - if (limit <= 0) { - return promise.set_error(Status::Error(400, "Parameter limit must be positive")); - } - - if (invite_link.empty()) { - return promise.set_error(Status::Error(400, "Invite link must be non-empty")); - } - - UserId offset_user_id; - int32 offset_date = 0; - if (offset_member != nullptr) { - offset_user_id = UserId(offset_member->user_id_); - offset_date = offset_member->joined_chat_date_; - } - - td_->create_handler(std::move(promise)) - ->send(dialog_id, invite_link, offset_date, offset_user_id, limit); -} - void ContactsManager::get_dialog_join_requests(DialogId dialog_id, const string &invite_link, const string &query, td_api::object_ptr offset_request, int32 limit, Promise> &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + TRY_STATUS_PROMISE(promise, can_manage_chat_join_requests(dialog_id)); if (limit <= 0) { return promise.set_error(Status::Error(400, "Parameter limit must be positive")); @@ -9435,47 +8822,16 @@ void ContactsManager::get_dialog_join_requests(DialogId dialog_id, const string void ContactsManager::process_dialog_join_request(DialogId dialog_id, UserId user_id, bool approve, Promise &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + TRY_STATUS_PROMISE(promise, can_manage_chat_join_requests(dialog_id)); td_->create_handler(std::move(promise))->send(dialog_id, user_id, approve); } void ContactsManager::process_dialog_join_requests(DialogId dialog_id, const string &invite_link, bool approve, Promise &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + TRY_STATUS_PROMISE(promise, can_manage_chat_join_requests(dialog_id)); td_->create_handler(std::move(promise))->send(dialog_id, invite_link, approve); } -void ContactsManager::revoke_dialog_invite_link(DialogId dialog_id, const string &invite_link, - Promise> &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); - - if (invite_link.empty()) { - return promise.set_error(Status::Error(400, "Invite link must be non-empty")); - } - - td_->create_handler(std::move(promise))->send(dialog_id, invite_link); -} - -void ContactsManager::delete_revoked_dialog_invite_link(DialogId dialog_id, const string &invite_link, - Promise &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); - - if (invite_link.empty()) { - return promise.set_error(Status::Error(400, "Invite link must be non-empty")); - } - - td_->create_handler(std::move(promise))->send(dialog_id, invite_link); -} - -void ContactsManager::delete_all_revoked_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, - Promise &&promise) { - TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, creator_user_id != get_my_id())); - TRY_RESULT_PROMISE(promise, input_user, get_input_user(creator_user_id)); - - td_->create_handler(std::move(promise)) - ->send(dialog_id, std::move(input_user)); -} - void ContactsManager::delete_chat_participant(ChatId chat_id, UserId user_id, bool revoke_messages, Promise &&promise) { const Chat *c = get_chat(chat_id); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 330b61afe..c88c134cd 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -539,28 +539,6 @@ class ContactsManager final : public Actor { void transfer_dialog_ownership(DialogId dialog_id, UserId user_id, const string &password, Promise &&promise); - void export_dialog_invite_link(DialogId dialog_id, string title, int32 expire_date, int32 usage_limit, - bool creates_join_request, bool is_permanent, - Promise> &&promise); - - void edit_dialog_invite_link(DialogId dialog_id, const string &link, string title, int32 expire_date, - int32 usage_limit, bool creates_join_request, - Promise> &&promise); - - void get_dialog_invite_link(DialogId dialog_id, const string &invite_link, - Promise> &&promise); - - void get_dialog_invite_link_counts(DialogId dialog_id, - Promise> &&promise); - - void get_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, bool is_revoked, int32 offset_date, - const string &offset_invite_link, int32 limit, - Promise> &&promise); - - void get_dialog_invite_link_users(DialogId dialog_id, const string &invite_link, - td_api::object_ptr offset_member, int32 limit, - Promise> &&promise); - void get_dialog_join_requests(DialogId dialog_id, const string &invite_link, const string &query, td_api::object_ptr offset_request, int32 limit, Promise> &&promise); @@ -570,13 +548,6 @@ class ContactsManager final : public Actor { void process_dialog_join_requests(DialogId dialog_id, const string &invite_link, bool approve, Promise &&promise); - void revoke_dialog_invite_link(DialogId dialog_id, const string &link, - Promise> &&promise); - - void delete_revoked_dialog_invite_link(DialogId dialog_id, const string &invite_link, Promise &&promise); - - void delete_all_revoked_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, Promise &&promise); - ChannelId migrate_chat_to_megagroup(ChatId chat_id, Promise &promise); void get_channel_recommendations(DialogId dialog_id, bool return_local, @@ -1231,7 +1202,6 @@ class ContactsManager final : public Actor { static constexpr int32 MAX_GET_PROFILE_PHOTOS = 100; // server side limit static constexpr size_t MAX_NAME_LENGTH = 64; // server side limit for first/last name static constexpr size_t MAX_DESCRIPTION_LENGTH = 255; // server side limit for chat/channel description - static constexpr size_t MAX_INVITE_LINK_TITLE_LENGTH = 32; // server side limit static constexpr int32 MAX_GET_CHANNEL_PARTICIPANTS = 200; // server side limit static constexpr int32 CHANNEL_PARTICIPANT_CACHE_TIME = 1800; // some reasonable limit @@ -1777,11 +1747,7 @@ class ContactsManager final : public Actor { void update_created_public_broadcasts(); - void export_dialog_invite_link_impl(DialogId dialog_id, string title, int32 expire_date, int32 usage_limit, - bool creates_join_request, bool is_permanent, - Promise> &&promise); - - Status can_manage_dialog_invite_links(DialogId dialog_id, bool creator_only = false); + Status can_manage_chat_join_requests(DialogId dialog_id); bool update_permanent_invite_link(DialogInviteLink &invite_link, DialogInviteLink new_invite_link); diff --git a/td/telegram/DialogInviteLinkManager.cpp b/td/telegram/DialogInviteLinkManager.cpp index 36c40cebd..c0aecff84 100644 --- a/td/telegram/DialogInviteLinkManager.cpp +++ b/td/telegram/DialogInviteLinkManager.cpp @@ -9,9 +9,11 @@ #include "td/telegram/ChannelId.h" #include "td/telegram/ChatId.h" #include "td/telegram/ContactsManager.h" +#include "td/telegram/DialogInviteLink.h" #include "td/telegram/DialogManager.h" #include "td/telegram/Global.h" #include "td/telegram/LinkManager.h" +#include "td/telegram/misc.h" #include "td/telegram/Td.h" #include "td/telegram/ThemeManager.h" #include "td/telegram/UpdatesManager.h" @@ -96,6 +98,504 @@ class ImportChatInviteQuery final : public Td::ResultHandler { } }; +class ExportChatInviteQuery final : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + + public: + explicit ExportChatInviteQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, const string &title, int32 expire_date, int32 usage_limit, bool creates_join_request, + bool is_permanent) { + dialog_id_ = dialog_id; + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(Status::Error(400, "Can't access the chat")); + } + + int32 flags = 0; + if (expire_date > 0) { + flags |= telegram_api::messages_exportChatInvite::EXPIRE_DATE_MASK; + } + if (usage_limit > 0) { + flags |= telegram_api::messages_exportChatInvite::USAGE_LIMIT_MASK; + } + if (creates_join_request) { + flags |= telegram_api::messages_exportChatInvite::REQUEST_NEEDED_MASK; + } + if (is_permanent) { + flags |= telegram_api::messages_exportChatInvite::LEGACY_REVOKE_PERMANENT_MASK; + } + if (!title.empty()) { + flags |= telegram_api::messages_exportChatInvite::TITLE_MASK; + } + + send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite( + flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), expire_date, usage_limit, title))); + } + + 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 ExportChatInviteQuery: " << to_string(ptr); + + DialogInviteLink invite_link(std::move(ptr), false, "ExportChatInviteQuery"); + if (!invite_link.is_valid()) { + return on_error(Status::Error(500, "Receive invalid invite link")); + } + if (invite_link.get_creator_user_id() != td_->contacts_manager_->get_my_id()) { + return on_error(Status::Error(500, "Receive invalid invite link creator")); + } + if (invite_link.is_permanent()) { + td_->contacts_manager_->on_get_permanent_dialog_invite_link(dialog_id_, invite_link); + } + promise_.set_value(invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); + } + + void on_error(Status status) final { + td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "ExportChatInviteQuery"); + promise_.set_error(std::move(status)); + } +}; + +class EditChatInviteLinkQuery final : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + + public: + explicit EditChatInviteLinkQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, const string &invite_link, const string &title, int32 expire_date, int32 usage_limit, + bool creates_join_request) { + dialog_id_ = dialog_id; + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(Status::Error(400, "Can't access the chat")); + } + + int32 flags = telegram_api::messages_editExportedChatInvite::EXPIRE_DATE_MASK | + telegram_api::messages_editExportedChatInvite::USAGE_LIMIT_MASK | + telegram_api::messages_editExportedChatInvite::REQUEST_NEEDED_MASK | + telegram_api::messages_editExportedChatInvite::TITLE_MASK; + send_query(G()->net_query_creator().create( + telegram_api::messages_editExportedChatInvite(flags, false /*ignored*/, std::move(input_peer), invite_link, + expire_date, usage_limit, creates_join_request, title))); + } + + 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 result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for EditChatInviteLinkQuery: " << to_string(result); + + if (result->get_id() != telegram_api::messages_exportedChatInvite::ID) { + return on_error(Status::Error(500, "Receive unexpected response from server")); + } + + auto invite = move_tl_object_as(result); + + td_->contacts_manager_->on_get_users(std::move(invite->users_), "EditChatInviteLinkQuery"); + + DialogInviteLink invite_link(std::move(invite->invite_), false, "EditChatInviteLinkQuery"); + if (!invite_link.is_valid()) { + return on_error(Status::Error(500, "Receive invalid invite link")); + } + promise_.set_value(invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); + } + + void on_error(Status status) final { + td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "EditChatInviteLinkQuery"); + promise_.set_error(std::move(status)); + } +}; + +class GetExportedChatInviteQuery final : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + + public: + explicit GetExportedChatInviteQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, const string &invite_link) { + dialog_id_ = dialog_id; + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(Status::Error(400, "Can't access the chat")); + } + + send_query(G()->net_query_creator().create( + telegram_api::messages_getExportedChatInvite(std::move(input_peer), invite_link))); + } + + 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()); + } + + if (result_ptr.ok()->get_id() != telegram_api::messages_exportedChatInvite::ID) { + LOG(ERROR) << "Receive wrong result for GetExportedChatInviteQuery: " << to_string(result_ptr.ok()); + return on_error(Status::Error(500, "Receive unexpected response")); + } + + auto result = move_tl_object_as(result_ptr.ok_ref()); + LOG(INFO) << "Receive result for GetExportedChatInviteQuery: " << to_string(result); + + td_->contacts_manager_->on_get_users(std::move(result->users_), "GetExportedChatInviteQuery"); + + DialogInviteLink invite_link(std::move(result->invite_), false, "GetExportedChatInviteQuery"); + if (!invite_link.is_valid()) { + LOG(ERROR) << "Receive invalid invite link in " << dialog_id_; + return on_error(Status::Error(500, "Receive invalid invite link")); + } + promise_.set_value(invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); + } + + void on_error(Status status) final { + td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "GetExportedChatInviteQuery"); + promise_.set_error(std::move(status)); + } +}; + +class GetExportedChatInvitesQuery final : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + + public: + explicit GetExportedChatInvitesQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, tl_object_ptr &&input_user, bool is_revoked, int32 offset_date, + const string &offset_invite_link, int32 limit) { + dialog_id_ = dialog_id; + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(Status::Error(400, "Can't access the chat")); + } + + int32 flags = 0; + if (!offset_invite_link.empty() || offset_date != 0) { + flags |= telegram_api::messages_getExportedChatInvites::OFFSET_DATE_MASK; + flags |= telegram_api::messages_getExportedChatInvites::OFFSET_LINK_MASK; + } + if (is_revoked) { + flags |= telegram_api::messages_getExportedChatInvites::REVOKED_MASK; + } + send_query(G()->net_query_creator().create( + telegram_api::messages_getExportedChatInvites(flags, false /*ignored*/, std::move(input_peer), + std::move(input_user), offset_date, offset_invite_link, limit))); + } + + 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 result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetExportedChatInvitesQuery: " << to_string(result); + + td_->contacts_manager_->on_get_users(std::move(result->users_), "GetExportedChatInvitesQuery"); + + int32 total_count = result->count_; + if (total_count < static_cast(result->invites_.size())) { + LOG(ERROR) << "Receive wrong total count of invite links " << total_count << " in " << dialog_id_; + total_count = static_cast(result->invites_.size()); + } + vector> invite_links; + for (auto &invite : result->invites_) { + DialogInviteLink invite_link(std::move(invite), false, "GetExportedChatInvitesQuery"); + if (!invite_link.is_valid()) { + LOG(ERROR) << "Receive invalid invite link in " << dialog_id_; + total_count--; + continue; + } + invite_links.push_back(invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); + } + promise_.set_value(td_api::make_object(total_count, std::move(invite_links))); + } + + void on_error(Status status) final { + td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "GetExportedChatInvitesQuery"); + promise_.set_error(std::move(status)); + } +}; + +class GetChatAdminWithInvitesQuery final : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + + public: + explicit GetChatAdminWithInvitesQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id) { + dialog_id_ = dialog_id; + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(Status::Error(400, "Can't access the chat")); + } + + send_query(G()->net_query_creator().create(telegram_api::messages_getAdminsWithInvites(std::move(input_peer)))); + } + + 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 result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetChatAdminWithInvitesQuery: " << to_string(result); + + td_->contacts_manager_->on_get_users(std::move(result->users_), "GetChatAdminWithInvitesQuery"); + + vector> invite_link_counts; + for (auto &admin : result->admins_) { + UserId user_id(admin->admin_id_); + if (!user_id.is_valid()) { + LOG(ERROR) << "Receive invalid invite link creator " << user_id << " in " << dialog_id_; + continue; + } + invite_link_counts.push_back(td_api::make_object( + td_->contacts_manager_->get_user_id_object(user_id, "chatInviteLinkCount"), admin->invites_count_, + admin->revoked_invites_count_)); + } + promise_.set_value(td_api::make_object(std::move(invite_link_counts))); + } + + void on_error(Status status) final { + td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "GetChatAdminWithInvitesQuery"); + promise_.set_error(std::move(status)); + } +}; + +class GetChatInviteImportersQuery final : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + + public: + explicit GetChatInviteImportersQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, const string &invite_link, int32 offset_date, UserId offset_user_id, int32 limit) { + dialog_id_ = dialog_id; + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(Status::Error(400, "Can't access the chat")); + } + + auto r_input_user = td_->contacts_manager_->get_input_user(offset_user_id); + if (r_input_user.is_error()) { + r_input_user = make_tl_object(); + } + + int32 flags = telegram_api::messages_getChatInviteImporters::LINK_MASK; + send_query(G()->net_query_creator().create( + telegram_api::messages_getChatInviteImporters(flags, false /*ignored*/, std::move(input_peer), invite_link, + string(), offset_date, r_input_user.move_as_ok(), limit))); + } + + 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 result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetChatInviteImportersQuery: " << to_string(result); + + td_->contacts_manager_->on_get_users(std::move(result->users_), "GetChatInviteImportersQuery"); + + int32 total_count = result->count_; + if (total_count < static_cast(result->importers_.size())) { + LOG(ERROR) << "Receive wrong total count of invite link users " << total_count << " in " << dialog_id_; + total_count = static_cast(result->importers_.size()); + } + vector> invite_link_members; + for (auto &importer : result->importers_) { + UserId user_id(importer->user_id_); + UserId approver_user_id(importer->approved_by_); + if (!user_id.is_valid() || (!approver_user_id.is_valid() && approver_user_id != UserId()) || + importer->requested_) { + LOG(ERROR) << "Receive invalid invite link importer: " << to_string(importer); + total_count--; + continue; + } + invite_link_members.push_back(td_api::make_object( + td_->contacts_manager_->get_user_id_object(user_id, "chatInviteLinkMember"), importer->date_, + importer->via_chatlist_, + td_->contacts_manager_->get_user_id_object(approver_user_id, "chatInviteLinkMember"))); + } + promise_.set_value(td_api::make_object(total_count, std::move(invite_link_members))); + } + + void on_error(Status status) final { + td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "GetChatInviteImportersQuery"); + promise_.set_error(std::move(status)); + } +}; + +class RevokeChatInviteLinkQuery final : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + + public: + explicit RevokeChatInviteLinkQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, const string &invite_link) { + dialog_id_ = dialog_id; + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(Status::Error(400, "Can't access the chat")); + } + + int32 flags = telegram_api::messages_editExportedChatInvite::REVOKED_MASK; + send_query(G()->net_query_creator().create(telegram_api::messages_editExportedChatInvite( + flags, false /*ignored*/, std::move(input_peer), invite_link, 0, 0, false, string()))); + } + + 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 result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for RevokeChatInviteLinkQuery: " << to_string(result); + + vector> links; + switch (result->get_id()) { + case telegram_api::messages_exportedChatInvite::ID: { + auto invite = move_tl_object_as(result); + + td_->contacts_manager_->on_get_users(std::move(invite->users_), "RevokeChatInviteLinkQuery"); + + DialogInviteLink invite_link(std::move(invite->invite_), false, "RevokeChatInviteLinkQuery"); + if (!invite_link.is_valid()) { + return on_error(Status::Error(500, "Receive invalid invite link")); + } + links.push_back(invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); + break; + } + case telegram_api::messages_exportedChatInviteReplaced::ID: { + auto invite = move_tl_object_as(result); + + td_->contacts_manager_->on_get_users(std::move(invite->users_), "RevokeChatInviteLinkQuery replaced"); + + DialogInviteLink invite_link(std::move(invite->invite_), false, "RevokeChatInviteLinkQuery replaced"); + DialogInviteLink new_invite_link(std::move(invite->new_invite_), false, + "RevokeChatInviteLinkQuery new replaced"); + if (!invite_link.is_valid() || !new_invite_link.is_valid()) { + return on_error(Status::Error(500, "Receive invalid invite link")); + } + if (new_invite_link.get_creator_user_id() == td_->contacts_manager_->get_my_id() && + new_invite_link.is_permanent()) { + td_->contacts_manager_->on_get_permanent_dialog_invite_link(dialog_id_, new_invite_link); + } + links.push_back(invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); + links.push_back(new_invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); + break; + } + default: + UNREACHABLE(); + } + auto total_count = static_cast(links.size()); + promise_.set_value(td_api::make_object(total_count, std::move(links))); + } + + void on_error(Status status) final { + td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "RevokeChatInviteLinkQuery"); + promise_.set_error(std::move(status)); + } +}; + +class DeleteExportedChatInviteQuery final : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit DeleteExportedChatInviteQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, const string &invite_link) { + dialog_id_ = dialog_id; + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(Status::Error(400, "Can't access the chat")); + } + + send_query(G()->net_query_creator().create( + telegram_api::messages_deleteExportedChatInvite(std::move(input_peer), invite_link))); + } + + 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()); + } + + promise_.set_value(Unit()); + } + + void on_error(Status status) final { + td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "DeleteExportedChatInviteQuery"); + promise_.set_error(std::move(status)); + } +}; + +class DeleteRevokedExportedChatInvitesQuery final : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit DeleteRevokedExportedChatInvitesQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, tl_object_ptr &&input_user) { + dialog_id_ = dialog_id; + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(Status::Error(400, "Can't access the chat")); + } + + send_query(G()->net_query_creator().create( + telegram_api::messages_deleteRevokedExportedChatInvites(std::move(input_peer), std::move(input_user)))); + } + + 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()); + } + + promise_.set_value(Unit()); + } + + void on_error(Status status) final { + td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "DeleteRevokedExportedChatInvitesQuery"); + promise_.set_error(std::move(status)); + } +}; + DialogInviteLinkManager::DialogInviteLinkManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { invite_link_info_expire_timeout_.set_callback(on_invite_link_info_expire_timeout_callback); invite_link_info_expire_timeout_.set_callback_data(static_cast(this)); @@ -417,4 +917,179 @@ void DialogInviteLinkManager::remove_dialog_access_by_invite_link(DialogId dialo invite_link_info_expire_timeout_.cancel_timeout(dialog_id.get()); } +Status DialogInviteLinkManager::can_manage_dialog_invite_links(DialogId dialog_id, bool creator_only) { + if (!td_->dialog_manager_->have_dialog_force(dialog_id, "can_manage_dialog_invite_links")) { + return Status::Error(400, "Chat not found"); + } + + switch (dialog_id.get_type()) { + case DialogType::User: + return Status::Error(400, "Can't invite members to a private chat"); + case DialogType::Chat: { + auto chat_id = dialog_id.get_chat_id(); + if (!td_->contacts_manager_->get_chat_is_active(chat_id)) { + return Status::Error(400, "Chat is deactivated"); + } + auto status = td_->contacts_manager_->get_chat_status(chat_id); + bool have_rights = creator_only ? status.is_creator() : status.can_manage_invite_links(); + if (!have_rights) { + return Status::Error(400, "Not enough rights to manage chat invite link"); + } + break; + } + case DialogType::Channel: { + auto channel_id = dialog_id.get_channel_id(); + auto status = td_->contacts_manager_->get_channel_status(channel_id); + bool have_rights = creator_only ? status.is_creator() : status.can_manage_invite_links(); + if (!have_rights) { + return Status::Error(400, "Not enough rights to manage chat invite link"); + } + break; + } + case DialogType::SecretChat: + return Status::Error(400, "Can't invite members to a secret chat"); + case DialogType::None: + default: + UNREACHABLE(); + } + return Status::OK(); +} + +void DialogInviteLinkManager::export_dialog_invite_link(DialogId dialog_id, string title, int32 expire_date, + int32 usage_limit, bool creates_join_request, bool is_permanent, + Promise> &&promise) { + td_->contacts_manager_->get_me(PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, title = std::move(title), + expire_date, usage_limit, creates_join_request, is_permanent, + promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &DialogInviteLinkManager::export_dialog_invite_link_impl, dialog_id, std::move(title), + expire_date, usage_limit, creates_join_request, is_permanent, std::move(promise)); + } + })); +} + +void DialogInviteLinkManager::export_dialog_invite_link_impl( + DialogId dialog_id, string title, int32 expire_date, int32 usage_limit, bool creates_join_request, + bool is_permanent, Promise> &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + if (creates_join_request && usage_limit > 0) { + return promise.set_error( + Status::Error(400, "Member limit can't be specified for links requiring administrator approval")); + } + + auto new_title = clean_name(std::move(title), MAX_INVITE_LINK_TITLE_LENGTH); + td_->create_handler(std::move(promise)) + ->send(dialog_id, new_title, expire_date, usage_limit, creates_join_request, is_permanent); +} + +void DialogInviteLinkManager::edit_dialog_invite_link(DialogId dialog_id, const string &invite_link, string title, + int32 expire_date, int32 usage_limit, bool creates_join_request, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + if (creates_join_request && usage_limit > 0) { + return promise.set_error( + Status::Error(400, "Member limit can't be specified for links requiring administrator approval")); + } + + if (invite_link.empty()) { + return promise.set_error(Status::Error(400, "Invite link must be non-empty")); + } + + auto new_title = clean_name(std::move(title), MAX_INVITE_LINK_TITLE_LENGTH); + td_->create_handler(std::move(promise)) + ->send(dialog_id, invite_link, new_title, expire_date, usage_limit, creates_join_request); +} + +void DialogInviteLinkManager::get_dialog_invite_link(DialogId dialog_id, const string &invite_link, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + + if (invite_link.empty()) { + return promise.set_error(Status::Error(400, "Invite link must be non-empty")); + } + + td_->create_handler(std::move(promise))->send(dialog_id, invite_link); +} + +void DialogInviteLinkManager::get_dialog_invite_link_counts( + DialogId dialog_id, Promise> &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id, true)); + + td_->create_handler(std::move(promise))->send(dialog_id); +} + +void DialogInviteLinkManager::get_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, bool is_revoked, + int32 offset_date, const string &offset_invite_link, int32 limit, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, + can_manage_dialog_invite_links(dialog_id, creator_user_id != td_->contacts_manager_->get_my_id())); + TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(creator_user_id)); + + if (limit <= 0) { + return promise.set_error(Status::Error(400, "Parameter limit must be positive")); + } + + td_->create_handler(std::move(promise)) + ->send(dialog_id, std::move(input_user), is_revoked, offset_date, offset_invite_link, limit); +} + +void DialogInviteLinkManager::get_dialog_invite_link_users( + DialogId dialog_id, const string &invite_link, td_api::object_ptr offset_member, + int32 limit, Promise> &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + + if (limit <= 0) { + return promise.set_error(Status::Error(400, "Parameter limit must be positive")); + } + + if (invite_link.empty()) { + return promise.set_error(Status::Error(400, "Invite link must be non-empty")); + } + + UserId offset_user_id; + int32 offset_date = 0; + if (offset_member != nullptr) { + offset_user_id = UserId(offset_member->user_id_); + offset_date = offset_member->joined_chat_date_; + } + + td_->create_handler(std::move(promise)) + ->send(dialog_id, invite_link, offset_date, offset_user_id, limit); +} + +void DialogInviteLinkManager::revoke_dialog_invite_link( + DialogId dialog_id, const string &invite_link, Promise> &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + + if (invite_link.empty()) { + return promise.set_error(Status::Error(400, "Invite link must be non-empty")); + } + + td_->create_handler(std::move(promise))->send(dialog_id, invite_link); +} + +void DialogInviteLinkManager::delete_revoked_dialog_invite_link(DialogId dialog_id, const string &invite_link, + Promise &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + + if (invite_link.empty()) { + return promise.set_error(Status::Error(400, "Invite link must be non-empty")); + } + + td_->create_handler(std::move(promise))->send(dialog_id, invite_link); +} + +void DialogInviteLinkManager::delete_all_revoked_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, + Promise &&promise) { + TRY_STATUS_PROMISE(promise, + can_manage_dialog_invite_links(dialog_id, creator_user_id != td_->contacts_manager_->get_my_id())); + TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(creator_user_id)); + + td_->create_handler(std::move(promise)) + ->send(dialog_id, std::move(input_user)); +} + } // namespace td diff --git a/td/telegram/DialogInviteLinkManager.h b/td/telegram/DialogInviteLinkManager.h index 919ed9e7d..39765991c 100644 --- a/td/telegram/DialogInviteLinkManager.h +++ b/td/telegram/DialogInviteLinkManager.h @@ -8,7 +8,6 @@ #include "td/telegram/AccentColorId.h" #include "td/telegram/DialogId.h" -#include "td/telegram/DialogInviteLink.h" #include "td/telegram/Photo.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -51,7 +50,38 @@ class DialogInviteLinkManager final : public Actor { void remove_dialog_access_by_invite_link(DialogId dialog_id); + void export_dialog_invite_link(DialogId dialog_id, string title, int32 expire_date, int32 usage_limit, + bool creates_join_request, bool is_permanent, + Promise> &&promise); + + void edit_dialog_invite_link(DialogId dialog_id, const string &link, string title, int32 expire_date, + int32 usage_limit, bool creates_join_request, + Promise> &&promise); + + void get_dialog_invite_link(DialogId dialog_id, const string &invite_link, + Promise> &&promise); + + void get_dialog_invite_link_counts(DialogId dialog_id, + Promise> &&promise); + + void get_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, bool is_revoked, int32 offset_date, + const string &offset_invite_link, int32 limit, + Promise> &&promise); + + void get_dialog_invite_link_users(DialogId dialog_id, const string &invite_link, + td_api::object_ptr offset_member, int32 limit, + Promise> &&promise); + + void revoke_dialog_invite_link(DialogId dialog_id, const string &link, + Promise> &&promise); + + void delete_revoked_dialog_invite_link(DialogId dialog_id, const string &invite_link, Promise &&promise); + + void delete_all_revoked_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, Promise &&promise); + private: + static constexpr size_t MAX_INVITE_LINK_TITLE_LENGTH = 32; // server side limit + void tear_down() final; static void on_invite_link_info_expire_timeout_callback(void *dialog_invite_link_manager_ptr, int64 dialog_id_long); @@ -62,6 +92,12 @@ class DialogInviteLinkManager final : public Actor { int32 get_dialog_accessible_by_invite_link_before_date(DialogId dialog_id) const; + void export_dialog_invite_link_impl(DialogId dialog_id, string title, int32 expire_date, int32 usage_limit, + bool creates_join_request, bool is_permanent, + Promise> &&promise); + + Status can_manage_dialog_invite_links(DialogId dialog_id, bool creator_only = false); + struct InviteLinkInfo { // known dialog DialogId dialog_id; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 5e53b7330..4c7933402 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -7015,56 +7015,57 @@ void Td::on_request(uint64 id, const td_api::getChatAdministrators &request) { void Td::on_request(uint64 id, const td_api::replacePrimaryChatInviteLink &request) { CREATE_REQUEST_PROMISE(); - contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), string(), 0, 0, false, true, - std::move(promise)); + dialog_invite_link_manager_->export_dialog_invite_link(DialogId(request.chat_id_), string(), 0, 0, false, true, + std::move(promise)); } void Td::on_request(uint64 id, td_api::createChatInviteLink &request) { CLEAN_INPUT_STRING(request.name_); CREATE_REQUEST_PROMISE(); - contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), std::move(request.name_), - request.expiration_date_, request.member_limit_, - request.creates_join_request_, false, std::move(promise)); + dialog_invite_link_manager_->export_dialog_invite_link(DialogId(request.chat_id_), std::move(request.name_), + request.expiration_date_, request.member_limit_, + request.creates_join_request_, false, std::move(promise)); } void Td::on_request(uint64 id, td_api::editChatInviteLink &request) { CLEAN_INPUT_STRING(request.name_); CLEAN_INPUT_STRING(request.invite_link_); CREATE_REQUEST_PROMISE(); - contacts_manager_->edit_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, std::move(request.name_), - request.expiration_date_, request.member_limit_, - request.creates_join_request_, std::move(promise)); + dialog_invite_link_manager_->edit_dialog_invite_link( + DialogId(request.chat_id_), request.invite_link_, std::move(request.name_), request.expiration_date_, + request.member_limit_, request.creates_join_request_, std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatInviteLink &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.invite_link_); CREATE_REQUEST_PROMISE(); - contacts_manager_->get_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, std::move(promise)); + dialog_invite_link_manager_->get_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, + std::move(promise)); } void Td::on_request(uint64 id, const td_api::getChatInviteLinkCounts &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); - contacts_manager_->get_dialog_invite_link_counts(DialogId(request.chat_id_), std::move(promise)); + dialog_invite_link_manager_->get_dialog_invite_link_counts(DialogId(request.chat_id_), std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatInviteLinks &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.offset_invite_link_); CREATE_REQUEST_PROMISE(); - contacts_manager_->get_dialog_invite_links(DialogId(request.chat_id_), UserId(request.creator_user_id_), - request.is_revoked_, request.offset_date_, request.offset_invite_link_, - request.limit_, std::move(promise)); + dialog_invite_link_manager_->get_dialog_invite_links(DialogId(request.chat_id_), UserId(request.creator_user_id_), + request.is_revoked_, request.offset_date_, + request.offset_invite_link_, request.limit_, std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatInviteLinkMembers &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.invite_link_); CREATE_REQUEST_PROMISE(); - contacts_manager_->get_dialog_invite_link_users(DialogId(request.chat_id_), request.invite_link_, - std::move(request.offset_member_), request.limit_, - std::move(promise)); + dialog_invite_link_manager_->get_dialog_invite_link_users(DialogId(request.chat_id_), request.invite_link_, + std::move(request.offset_member_), request.limit_, + std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatJoinRequests &request) { @@ -7093,22 +7094,23 @@ void Td::on_request(uint64 id, td_api::processChatJoinRequests &request) { void Td::on_request(uint64 id, td_api::revokeChatInviteLink &request) { CLEAN_INPUT_STRING(request.invite_link_); CREATE_REQUEST_PROMISE(); - contacts_manager_->revoke_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, std::move(promise)); + dialog_invite_link_manager_->revoke_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, + std::move(promise)); } void Td::on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.invite_link_); CREATE_OK_REQUEST_PROMISE(); - contacts_manager_->delete_revoked_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, - std::move(promise)); + dialog_invite_link_manager_->delete_revoked_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, + std::move(promise)); } void Td::on_request(uint64 id, const td_api::deleteAllRevokedChatInviteLinks &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - contacts_manager_->delete_all_revoked_dialog_invite_links(DialogId(request.chat_id_), - UserId(request.creator_user_id_), std::move(promise)); + dialog_invite_link_manager_->delete_all_revoked_dialog_invite_links( + DialogId(request.chat_id_), UserId(request.creator_user_id_), std::move(promise)); } void Td::on_request(uint64 id, td_api::checkChatInviteLink &request) {