diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 63b5c0b6d..681be7a3c 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -3548,7 +3548,11 @@ unblockUser user_id:int32 = Ok; getBlockedUsers offset:int32 limit:int32 = Users; -//@description Adds new contacts or edits existing contacts; contacts' user identifiers are ignored @contacts The list of contacts to import or edit, contact's vCard are ignored and are not imported +//@description Adds a user to the contacts list or edits an existing contact by their user_id @contact The contact to add or edit; phone number can be empty and needs to be specified only if known, vCard is ignored +//@share_phone_number True, if the user needs to be allowed to see current user's phone number. A corresponding rule to userPrivacySettingShowPhoneNumber will be added if needed +addContact contact:contact share_phone_number:Bool = Ok; + +//@description Adds new contacts or edits existing contacts by their phone numbers; contacts' user identifiers are ignored @contacts The list of contacts to import or edit; contacts' vCard are ignored and are not imported importContacts contacts:vector = ImportedContacts; //@description Returns all user contacts diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 3ef10aaed..c7de16c36 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index f6bc2c329..f47739823 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -488,6 +488,42 @@ class GetContactsStatusesQuery : public Td::ResultHandler { } }; +class AddContactQuery : public Td::ResultHandler { + Promise promise_; + + public: + explicit AddContactQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(tl_object_ptr &&input_user, const string &first_name, const string &last_name, + const string &phone_number, bool share_phone_number) { + int32 flags = 0; + if (share_phone_number) { + flags |= telegram_api::contacts_addContact::ADD_PHONE_PRIVACY_EXCEPTION_MASK; + } + send_query(G()->net_query_creator().create(create_storer(telegram_api::contacts_addContact( + flags, false /*ignored*/, std::move(input_user), first_name, last_name, phone_number)))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for AddContactQuery: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr)); + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + td->contacts_manager_->reload_contacts(true); + } +}; + class ImportContactsQuery : public Td::ResultHandler { Promise promise_; vector input_contacts_; @@ -593,7 +629,7 @@ class DeleteContactsQuery : public Td::ResultHandler { explicit DeleteContactsQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(vector &&user_ids, vector> &&input_users) { + void send(vector> &&input_users) { send_query( G()->net_query_creator().create(create_storer(telegram_api::contacts_deleteContacts(std::move(input_users))))); } @@ -3941,6 +3977,37 @@ void ContactsManager::reload_contacts(bool force) { } } +void ContactsManager::add_contact(td_api::object_ptr &&contact, bool share_phone_number, + Promise &&promise) { + if (contact == nullptr) { + return promise.set_error(Status::Error(400, "Added contact must be non-empty")); + } + + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + if (!are_contacts_loaded_) { + load_contacts(PromiseCreator::lambda([actor_id = actor_id(this), contact = std::move(contact), share_phone_number, + promise = std::move(promise)](Result &&) mutable { + send_closure(actor_id, &ContactsManager::add_contact, std::move(contact), share_phone_number, std::move(promise)); + })); + return; + } + + LOG(INFO) << "Add " << oneline(to_string(contact)) << " with share_phone_number = " << share_phone_number; + + UserId user_id{contact->user_id_}; + auto input_user = get_input_user(user_id); + if (input_user == nullptr) { + return promise.set_error(Status::Error(3, "User not found")); + } + + td_->create_handler(std::move(promise)) + ->send(std::move(input_user), contact->first_name_, contact->last_name_, contact->phone_number_, + share_phone_number); +} + std::pair, vector> ContactsManager::import_contacts( const vector> &contacts, int64 &random_id, Promise &&promise) { if (!are_contacts_loaded_) { @@ -3961,7 +4028,7 @@ std::pair, vector> ContactsManager::import_contacts( } for (auto &contact : contacts) { if (contact == nullptr) { - promise.set_error(Status::Error(400, "Imported contacts should not be empty")); + promise.set_error(Status::Error(400, "Imported contacts must be non-empty")); return {}; } } @@ -4004,8 +4071,7 @@ void ContactsManager::remove_contacts(vector user_ids, Promise &&p return promise.set_value(Unit()); } - td_->create_handler(std::move(promise)) - ->send(std::move(to_delete_user_ids), std::move(input_users)); + td_->create_handler(std::move(promise))->send(std::move(input_users)); } void ContactsManager::remove_contacts_by_phone_number(vector user_phone_numbers, vector user_ids, diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 584672120..3e5bf9cde 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -260,6 +260,8 @@ class ContactsManager : public Actor { tl_object_ptr get_blocked_users_object(int64 random_id); + void add_contact(td_api::object_ptr &&contact, bool share_phone_number, Promise &&promise); + std::pair, vector> import_contacts(const vector> &contacts, int64 &random_id, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 3086c09c7..d6fbc837d 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6331,6 +6331,18 @@ void Td::on_request(uint64 id, const td_api::getBlockedUsers &request) { CREATE_REQUEST(GetBlockedUsersRequest, request.offset_, request.limit_); } +void Td::on_request(uint64 id, td_api::addContact &request) { + CHECK_IS_USER(); + if (request.contact_ == nullptr) { + return send_error_raw(id, 5, "Contact must not be empty"); + } + CLEAN_INPUT_STRING(request.contact_->phone_number_); + CLEAN_INPUT_STRING(request.contact_->first_name_); + CLEAN_INPUT_STRING(request.contact_->last_name_); + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->add_contact(std::move(request.contact_), request.share_phone_number_, std::move(promise)); +} + void Td::on_request(uint64 id, td_api::importContacts &request) { CHECK_IS_USER(); for (auto &contact : request.contacts_) { diff --git a/td/telegram/Td.h b/td/telegram/Td.h index dbcf20430..b57362b73 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -726,6 +726,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::getBlockedUsers &request); + void on_request(uint64 id, td_api::addContact &request); + void on_request(uint64 id, td_api::importContacts &request); void on_request(uint64 id, const td_api::getContacts &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 00ca58e51..de3061240 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1597,6 +1597,15 @@ class CliClient final : public Actor { auto limit = to_integer(args); send_request(td_api::make_object("", limit)); } + } else if (op == "AddContact") { + string user_id; + string first_name; + string last_name; + std::tie(user_id, args) = split(args); + std::tie(first_name, last_name) = split(args); + + send_request(td_api::make_object( + td_api::make_object(string(), first_name, last_name, string(), as_user_id(user_id)), false)); } else if (op == "ImportContacts" || op == "cic") { vector contacts_str = full_split(args, ';'); vector> contacts;