diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index c2c63337b..811b7f113 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -580,6 +580,9 @@ chatLocation location:location address:string = ChatLocation; //@description Represents a birthdate of a user @day Day of the month; 1-31 @month Month of the year; 1-12 @year Birth year; 0 if unknown birthdate day:int32 month:int32 year:int32 = Birthdate; +//@description Describes a user that had or will have a birthday soon @user_id User identifier @birthdate Birthdate of the user +closeBirthdayUser user_id:int53 birthdate:birthdate = CloseBirthdayUser; + //@class BusinessAwayMessageSchedule @description Describes conditions for sending of away messages by a Telegram Business account @@ -7079,6 +7082,9 @@ updateAnimationSearchParameters provider:string emojis:vector = Update; //@description The list of suggested to the user actions has changed @added_actions Added suggested actions @removed_actions Removed suggested actions updateSuggestedActions added_actions:vector removed_actions:vector = Update; +//@description The list of contacts that had birthdays recently or will have birthday soon has changed @close_birthday_users List of contact users with close birthday +updateContactCloseBirthdays close_birthday_users:vector = Update; + //@description Adding users to a chat has failed because of their privacy settings. An invite link can be shared with the users if appropriate @chat_id Chat identifier @user_ids Identifiers of users, which weren't added because of their privacy settings updateAddChatMembersPrivacyForbidden chat_id:int53 user_ids:vector = Update; diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 17a02c06e..c090e6d42 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -121,6 +121,28 @@ class GetContactsQuery final : public Td::ResultHandler { } }; +class GetContactsBirthdaysQuery final : public Td::ResultHandler { + public: + void send() { + send_query(G()->net_query_creator().create(telegram_api::contacts_getBirthdays())); + } + + 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 GetContactBirthdaysQuery: " << to_string(ptr); + td_->contacts_manager_->on_get_contact_birthdates(std::move(ptr)); + } + + void on_error(Status status) final { + td_->contacts_manager_->on_get_contact_birthdates(nullptr); + } +}; + class GetContactsStatusesQuery final : public Td::ResultHandler { public: void send() { @@ -5892,6 +5914,48 @@ std::pair> ContactsManager::search_contacts(const string & return {narrow_cast(result.first), std::move(user_ids)}; } +void ContactsManager::reload_contact_birthdates(bool force) { + if (!G()->close_flag() && !td_->auth_manager_->is_bot() && !contact_birthdates_.is_being_synced_ && + (contact_birthdates_.next_sync_time_ < Time::now() || force)) { + contact_birthdates_.is_being_synced_ = true; + td_->create_handler()->send(); + } +} + +void ContactsManager::on_get_contact_birthdates( + telegram_api::object_ptr &&birthdays) { + CHECK(contact_birthdates_.is_being_synced_); + contact_birthdates_.is_being_synced_ = false; + if (birthdays == nullptr) { + contact_birthdates_.next_sync_time_ = Time::now() + Random::fast(120, 180); + return; + } + contact_birthdates_.next_sync_time_ = Time::now() + Random::fast(86400 / 4, 86400 / 3); + + td_->contacts_manager_->on_get_users(std::move(birthdays->users_), "on_get_contact_birthdates"); + vector> users; + for (auto &contact : birthdays->contacts_) { + UserId user_id(contact->contact_id_); + if (is_user_contact(user_id)) { + Birthdate birthdate(std::move(contact->birthday_)); + UserFull *user_full = get_user_full_force(user_id, "on_get_contact_birthdates"); + if (user_full != nullptr && user_full->birthdate != birthdate) { + user_full->birthdate = birthdate; + user_full->is_changed = true; + update_user_full(user_full, user_id, "on_get_contact_birthdates"); + } + if (!birthdate.is_empty()) { + users.emplace_back(user_id, birthdate); + } + } + } + if (contact_birthdates_.users_ != users) { + contact_birthdates_.users_ = std::move(users); + send_closure(G()->td(), &Td::send_update, get_update_contact_close_birthdays()); + } + // there is no need to save them between restarts +} + vector ContactsManager::get_close_friends(Promise &&promise) { if (!are_contacts_loaded_) { load_contacts(std::move(promise)); @@ -10500,8 +10564,7 @@ void ContactsManager::on_get_user_full(tl_object_ptr &&u user_full->broadcast_administrator_rights != broadcast_administrator_rights || user_full->premium_gift_options != premium_gift_options || user_full->voice_messages_forbidden != voice_messages_forbidden || - user_full->can_pin_messages != can_pin_messages || user_full->has_pinned_stories != has_pinned_stories || - user_full->birthdate != birthdate) { + user_full->can_pin_messages != can_pin_messages || user_full->has_pinned_stories != has_pinned_stories) { user_full->can_be_called = can_be_called; user_full->supports_video_calls = supports_video_calls; user_full->has_private_calls = has_private_calls; @@ -10511,10 +10574,18 @@ void ContactsManager::on_get_user_full(tl_object_ptr &&u user_full->voice_messages_forbidden = voice_messages_forbidden; user_full->can_pin_messages = can_pin_messages; user_full->has_pinned_stories = has_pinned_stories; - user_full->birthdate = birthdate; user_full->is_changed = true; } + if (user_full->birthdate != birthdate) { + user_full->birthdate = birthdate; + user_full->is_changed = true; + + if (u->is_contact) { + reload_contact_birthdates(true); + } + } + if (user_full->private_forward_name != user->private_forward_name_) { if (user_full->private_forward_name.empty() != user->private_forward_name_.empty()) { user_full->is_changed = true; @@ -11624,6 +11695,8 @@ void ContactsManager::on_update_user_is_contact(User *u, UserId user_id, bool is if (u->is_contact != is_contact) { u->is_contact = is_contact; u->is_is_contact_changed = true; + + reload_contact_birthdates(true); } if (u->is_mutual_contact != is_mutual_contact) { u->is_mutual_contact = is_mutual_contact; @@ -16206,6 +16279,14 @@ bool ContactsManager::get_user_has_unread_stories(const User *u) { return u->max_active_story_id.get() > u->max_read_story_id.get(); } +td_api::object_ptr ContactsManager::get_update_contact_close_birthdays() const { + return td_api::make_object( + transform(contact_birthdates_.users_, [this](auto &user) { + return td_api::make_object(get_user_id_object(user.first, "closeBirthdayUser"), + user.second.get_birthdate_object()); + })); +} + td_api::object_ptr ContactsManager::get_update_user_object(UserId user_id, const User *u) const { if (u == nullptr) { return get_update_unknown_user_object(user_id); @@ -16653,6 +16734,10 @@ void ContactsManager::get_current_state(vector( chat_id.get(), get_basic_group_full_info_object(chat_id, chat_full.get()))); }); + + if (!contact_birthdates_.users_.empty()) { + updates.push_back(get_update_contact_close_birthdays()); + } } } // namespace td diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 9117c4f92..6d27bb57d 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -213,10 +213,14 @@ class ContactsManager final : public Actor { void on_get_contacts_failed(Status error); + void on_get_contact_birthdates(telegram_api::object_ptr &&birthdays); + void on_get_contacts_statuses(vector> &&statuses); void reload_contacts(bool force); + void reload_contact_birthdates(bool force); + void on_get_user(tl_object_ptr &&user, const char *source); void on_get_users(vector> &&users, const char *source); @@ -1589,6 +1593,8 @@ class ContactsManager final : public Actor { static bool get_user_has_unread_stories(const User *u); + td_api::object_ptr get_update_contact_close_birthdays() const; + td_api::object_ptr get_update_user_object(UserId user_id, const User *u) const; td_api::object_ptr get_update_unknown_user_object(UserId user_id) const; @@ -1812,6 +1818,13 @@ class ContactsManager final : public Actor { WaitFreeHashSet restricted_user_ids_; WaitFreeHashSet restricted_channel_ids_; + struct ContactBirthdates { + vector> users_; + double next_sync_time_ = 0.0; + bool is_being_synced_ = false; + }; + ContactBirthdates contact_birthdates_; + vector next_all_imported_contacts_; vector imported_contacts_unique_id_; vector imported_contacts_pos_; diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 56a664274..bfefc7a16 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -2265,6 +2265,7 @@ void UpdatesManager::try_reload_data() { LOG(INFO) << "Reload data"; td_->animations_manager_->reload_saved_animations(true); td_->autosave_manager_->reload_autosave_settings(); + td_->contacts_manager_->reload_contact_birthdates(false); td_->contacts_manager_->reload_created_public_dialogs(PublicDialogType::HasUsername, std::move(promise)); td_->contacts_manager_->reload_created_public_dialogs(PublicDialogType::IsLocationBased, Auto()); td_->contacts_manager_->reload_created_public_dialogs(PublicDialogType::ForPersonalDialog, Auto());