diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index f6e230081..ed41a5fb4 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -11,7 +11,6 @@ #include "td/telegram/AuthManager.h" #include "td/telegram/BlockListId.h" #include "td/telegram/BotMenuButton.h" -#include "td/telegram/ChannelParticipantFilter.h" #include "td/telegram/CommonDialogManager.h" #include "td/telegram/ConfigManager.h" #include "td/telegram/Dependencies.h" @@ -2737,53 +2736,6 @@ class GetFullChannelQuery final : public Td::ResultHandler { } }; -class GetChannelParticipantsQuery final : public Td::ResultHandler { - Promise> promise_; - ChannelId channel_id_; - - public: - explicit GetChannelParticipantsQuery(Promise> &&promise) - : promise_(std::move(promise)) { - } - - void send(ChannelId channel_id, const ChannelParticipantFilter &filter, int32 offset, int32 limit) { - auto input_channel = td_->contacts_manager_->get_input_channel(channel_id); - if (input_channel == nullptr) { - return promise_.set_error(Status::Error(400, "Supergroup not found")); - } - - channel_id_ = channel_id; - send_query(G()->net_query_creator().create(telegram_api::channels_getParticipants( - std::move(input_channel), filter.get_input_channel_participants_filter(), offset, limit, 0))); - } - - 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 participants_ptr = result_ptr.move_as_ok(); - LOG(INFO) << "Receive result for GetChannelParticipantsQuery: " << to_string(participants_ptr); - switch (participants_ptr->get_id()) { - case telegram_api::channels_channelParticipants::ID: { - promise_.set_value(telegram_api::move_object_as(participants_ptr)); - break; - } - case telegram_api::channels_channelParticipantsNotModified::ID: - LOG(ERROR) << "Receive channelParticipantsNotModified"; - return on_error(Status::Error(500, "Receive channelParticipantsNotModified")); - default: - UNREACHABLE(); - } - } - - void on_error(Status status) final { - td_->contacts_manager_->on_get_channel_error(channel_id_, status, "GetChannelParticipantsQuery"); - promise_.set_error(std::move(status)); - } -}; - class GetSupportUserQuery final : public Td::ResultHandler { Promise promise_; @@ -13124,6 +13076,10 @@ void ContactsManager::update_channel_online_member_count(ChannelId channel_id, b td_->dialog_participant_manager_->update_dialog_online_member_count(it->second, DialogId(channel_id), is_from_server); } +void ContactsManager::set_cached_channel_participants(ChannelId channel_id, vector participants) { + cached_channel_participants_[channel_id] = std::move(participants); +} + void ContactsManager::drop_cached_channel_participants(ChannelId channel_id) { cached_channel_participants_.erase(channel_id); } @@ -13322,181 +13278,6 @@ bool ContactsManager::is_user_contact(const User *u, UserId user_id, bool is_mut return u != nullptr && (is_mutual ? u->is_mutual_contact : u->is_contact) && user_id != get_my_id(); } -void ContactsManager::on_get_channel_participants( - ChannelId channel_id, ChannelParticipantFilter &&filter, int32 offset, int32 limit, string additional_query, - int32 additional_limit, tl_object_ptr &&channel_participants, - Promise &&promise) { - TRY_STATUS_PROMISE(promise, G()->close_status()); - - on_get_users(std::move(channel_participants->users_), "on_get_channel_participants"); - on_get_chats(std::move(channel_participants->chats_), "on_get_channel_participants"); - int32 total_count = channel_participants->count_; - auto participants = std::move(channel_participants->participants_); - LOG(INFO) << "Receive " << participants.size() << " " << filter << " members in " << channel_id; - - bool is_full = offset == 0 && static_cast(participants.size()) < limit && total_count < limit; - bool has_hidden_participants = - get_channel_effective_has_hidden_participants(channel_id, "on_get_channel_participants"); - bool is_full_recent = is_full && filter.is_recent() && !has_hidden_participants; - - auto channel_type = get_channel_type(channel_id); - vector result; - for (auto &participant_ptr : participants) { - auto debug_participant = to_string(participant_ptr); - result.emplace_back(std::move(participant_ptr), channel_type); - const auto &participant = result.back(); - UserId participant_user_id; - if (participant.dialog_id_.get_type() == DialogType::User) { - participant_user_id = participant.dialog_id_.get_user_id(); - } - if (!participant.is_valid() || (filter.is_bots() && !is_user_bot(participant_user_id)) || - (filter.is_administrators() && !participant.status_.is_administrator()) || - ((filter.is_recent() || filter.is_contacts() || filter.is_search()) && !participant.status_.is_member()) || - (filter.is_contacts() && !is_user_contact(participant_user_id)) || - (filter.is_restricted() && !participant.status_.is_restricted()) || - (filter.is_banned() && !participant.status_.is_banned())) { - bool skip_error = ((filter.is_administrators() || filter.is_bots()) && is_user_deleted(participant_user_id)) || - (filter.is_contacts() && participant_user_id == get_my_id()); - if (!skip_error) { - LOG(ERROR) << "Receive " << participant << ", while searching for " << filter << " in " << channel_id - << " with offset " << offset << " and limit " << limit << ": " << oneline(debug_participant); - } - result.pop_back(); - total_count--; - } - } - - if (total_count < narrow_cast(result.size())) { - LOG(ERROR) << "Receive total_count = " << total_count << ", but have at least " << result.size() << " " << filter - << " members in " << channel_id; - total_count = static_cast(result.size()); - } else if (is_full && total_count > static_cast(result.size())) { - LOG(ERROR) << "Fix total number of " << filter << " members from " << total_count << " to " << result.size() - << " in " << channel_id << " for request with limit " << limit << " and received " << participants.size() - << " results"; - total_count = static_cast(result.size()); - } - - const auto max_participant_count = is_megagroup_channel(channel_id) ? 975 : 195; - auto participant_count = - filter.is_recent() && !has_hidden_participants && total_count != 0 && total_count < max_participant_count - ? total_count - : -1; - int32 administrator_count = - filter.is_administrators() || (filter.is_recent() && has_hidden_participants) ? total_count : -1; - if (is_full && (filter.is_administrators() || filter.is_bots() || filter.is_recent())) { - vector administrators; - vector bot_user_ids; - { - if (filter.is_recent()) { - for (const auto &participant : result) { - if (participant.dialog_id_.get_type() == DialogType::User) { - auto participant_user_id = participant.dialog_id_.get_user_id(); - if (participant.status_.is_administrator()) { - administrators.emplace_back(participant_user_id, participant.status_.get_rank(), - participant.status_.is_creator()); - } - if (is_full_recent && is_user_bot(participant_user_id)) { - bot_user_ids.push_back(participant_user_id); - } - } - } - administrator_count = narrow_cast(administrators.size()); - - if (is_megagroup_channel(channel_id) && !td_->auth_manager_->is_bot() && is_full_recent) { - cached_channel_participants_[channel_id] = result; - update_channel_online_member_count(channel_id, true); - } - } else if (filter.is_administrators()) { - for (const auto &participant : result) { - if (participant.dialog_id_.get_type() == DialogType::User) { - administrators.emplace_back(participant.dialog_id_.get_user_id(), participant.status_.get_rank(), - participant.status_.is_creator()); - } - } - } else if (filter.is_bots()) { - bot_user_ids = transform(result, [](const DialogParticipant &participant) { - CHECK(participant.dialog_id_.get_type() == DialogType::User); - return participant.dialog_id_.get_user_id(); - }); - } - } - if (filter.is_administrators() || filter.is_recent()) { - td_->dialog_participant_manager_->on_update_dialog_administrators(DialogId(channel_id), std::move(administrators), - true, false); - } - if (filter.is_bots() || is_full_recent) { - on_update_channel_bot_user_ids(channel_id, std::move(bot_user_ids)); - } - } - if (td_->dialog_participant_manager_->have_channel_participant_cache(channel_id)) { - for (const auto &participant : result) { - td_->dialog_participant_manager_->add_channel_participant_to_cache(channel_id, participant, false); - } - } - - if (participant_count != -1 || administrator_count != -1) { - auto channel_full = get_channel_full_force(channel_id, true, "on_get_channel_participants"); - if (channel_full != nullptr) { - if (administrator_count == -1) { - administrator_count = channel_full->administrator_count; - } - if (participant_count == -1) { - participant_count = channel_full->participant_count; - } - if (participant_count < administrator_count) { - participant_count = administrator_count; - } - if (channel_full->participant_count != participant_count) { - channel_full->participant_count = participant_count; - channel_full->is_changed = true; - } - if (channel_full->administrator_count != administrator_count) { - channel_full->administrator_count = administrator_count; - channel_full->is_changed = true; - } - update_channel_full(channel_full, channel_id, "on_get_channel_participants"); - } - if (participant_count != -1) { - auto c = get_channel(channel_id); - if (c != nullptr && c->participant_count != participant_count) { - c->participant_count = participant_count; - c->is_changed = true; - update_channel(c, channel_id); - } - } - } - - if (!additional_query.empty()) { - auto dialog_ids = transform(result, [](const DialogParticipant &participant) { return participant.dialog_id_; }); - std::pair> result_dialog_ids = - search_among_dialogs(dialog_ids, additional_query, additional_limit); - - total_count = result_dialog_ids.first; - FlatHashSet result_dialog_ids_set; - for (auto result_dialog_id : result_dialog_ids.second) { - CHECK(result_dialog_id.is_valid()); - result_dialog_ids_set.insert(result_dialog_id); - } - auto all_participants = std::move(result); - result.clear(); - for (auto &participant : all_participants) { - if (result_dialog_ids_set.count(participant.dialog_id_)) { - result_dialog_ids_set.erase(participant.dialog_id_); - result.push_back(std::move(participant)); - } - } - } - - vector participant_dialog_ids; - for (const auto &participant : result) { - participant_dialog_ids.push_back(participant.dialog_id_); - } - on_view_dialog_active_stories(std::move(participant_dialog_ids)); - - promise.set_value(DialogParticipants{total_count, std::move(result)}); -} - bool ContactsManager::speculative_add_count(int32 &count, int32 delta_count, int32 min_count) { auto new_count = count + delta_count; if (new_count < min_count) { @@ -16646,45 +16427,6 @@ void ContactsManager::finish_get_chat_participant(ChatId chat_id, UserId user_id promise.set_value(DialogParticipant(*participant)); } -void ContactsManager::get_channel_participants(ChannelId channel_id, - tl_object_ptr &&filter, - string additional_query, int32 offset, int32 limit, - int32 additional_limit, Promise &&promise) { - if (limit <= 0) { - return promise.set_error(Status::Error(400, "Parameter limit must be positive")); - } - if (limit > MAX_GET_CHANNEL_PARTICIPANTS) { - limit = MAX_GET_CHANNEL_PARTICIPANTS; - } - - if (offset < 0) { - return promise.set_error(Status::Error(400, "Parameter offset must be non-negative")); - } - - auto channel_full = get_channel_full_force(channel_id, true, "get_channel_participants"); - if (channel_full != nullptr && !channel_full->is_expired() && !channel_full->can_get_participants) { - return promise.set_error(Status::Error(400, "Member list is inaccessible")); - } - if (is_broadcast_channel(channel_id) && !get_channel_status(channel_id).is_administrator()) { - return promise.set_error(Status::Error(400, "Member list is inaccessible")); - } - - ChannelParticipantFilter participant_filter(filter); - auto get_channel_participants_promise = PromiseCreator::lambda( - [actor_id = actor_id(this), channel_id, filter = participant_filter, - additional_query = std::move(additional_query), offset, limit, additional_limit, promise = std::move(promise)]( - Result> &&result) mutable { - if (result.is_error()) { - promise.set_error(result.move_as_error()); - } else { - send_closure(actor_id, &ContactsManager::on_get_channel_participants, channel_id, std::move(filter), offset, - limit, std::move(additional_query), additional_limit, result.move_as_ok(), std::move(promise)); - } - }); - td_->create_handler(std::move(get_channel_participants_promise)) - ->send(channel_id, participant_filter, offset, limit); -} - void ContactsManager::on_update_channel_administrator_count(ChannelId channel_id, int32 administrator_count) { auto channel_full = get_channel_full_force(channel_id, true, "on_update_channel_administrator_count"); if (channel_full != nullptr && channel_full->administrator_count != administrator_count) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index b7d46f9cd..9251cdf8a 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -68,8 +68,6 @@ namespace td { struct BinlogEvent; -class ChannelParticipantFilter; - struct MinChannel; class Td; @@ -359,6 +357,10 @@ class ContactsManager final : public Actor { void update_channel_online_member_count(ChannelId channel_id, bool is_from_server); + void on_update_channel_bot_user_ids(ChannelId channel_id, vector &&bot_user_ids); + + void set_cached_channel_participants(ChannelId channel_id, vector participants); + void drop_cached_channel_participants(ChannelId channel_id); void on_update_username_is_active(UserId user_id, string &&username, bool is_active, Promise &&promise); @@ -667,10 +669,6 @@ class ContactsManager final : public Actor { std::pair> search_among_dialogs(const vector &dialog_ids, const string &query, int32 limit) const; - void get_channel_participants(ChannelId channel_id, tl_object_ptr &&filter, - string additional_query, int32 offset, int32 limit, int32 additional_limit, - Promise &&promise); - int64 get_user_id_object(UserId user_id, const char *source) const; tl_object_ptr get_user_object(UserId user_id) const; @@ -1177,11 +1175,10 @@ class ContactsManager final : public Actor { class ChannelLogEvent; class SecretChatLogEvent; - 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_TITLE_LENGTH = 128; // server side limit for chat title - static constexpr size_t MAX_DESCRIPTION_LENGTH = 255; // server side limit for chat/channel description - static constexpr int32 MAX_GET_CHANNEL_PARTICIPANTS = 200; // server side limit + 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_TITLE_LENGTH = 128; // server side limit for chat title + static constexpr size_t MAX_DESCRIPTION_LENGTH = 255; // server side limit for chat/channel description static constexpr int32 MAX_ACTIVE_STORY_ID_RELOAD_TIME = 3600; // some reasonable limit static constexpr int32 CHANNEL_RECOMMENDATIONS_CACHE_TIME = 86400; // some reasonable limit @@ -1503,8 +1500,6 @@ class ContactsManager final : public Actor { StoryId max_read_story_id); void on_update_channel_max_read_story_id(Channel *c, ChannelId channel_id, StoryId max_read_story_id); - void on_update_channel_bot_user_ids(ChannelId channel_id, vector &&bot_user_ids); - void on_update_channel_full_photo(ChannelFull *channel_full, ChannelId channel_id, Photo photo); void on_update_channel_full_invite_link(ChannelFull *channel_full, tl_object_ptr &&invite_link); @@ -1790,11 +1785,6 @@ class ContactsManager final : public Actor { void update_dialogs_for_discussion(DialogId dialog_id, bool is_suitable); - void on_get_channel_participants(ChannelId channel_id, ChannelParticipantFilter &&filter, int32 offset, int32 limit, - string additional_query, int32 additional_limit, - tl_object_ptr &&channel_participants, - Promise &&promise); - void get_channel_statistics_dc_id_impl(ChannelId channel_id, bool for_full_statistics, Promise &&promise); void on_get_support_user(UserId user_id, Promise> &&promise); diff --git a/td/telegram/DialogParticipantManager.cpp b/td/telegram/DialogParticipantManager.cpp index 3aa80ab61..54ee872f7 100644 --- a/td/telegram/DialogParticipantManager.cpp +++ b/td/telegram/DialogParticipantManager.cpp @@ -9,6 +9,7 @@ #include "td/telegram/AccessRights.h" #include "td/telegram/AuthManager.h" #include "td/telegram/ChannelId.h" +#include "td/telegram/ChannelParticipantFilter.h" #include "td/telegram/ChannelType.h" #include "td/telegram/ContactsManager.h" #include "td/telegram/DialogManager.h" @@ -29,6 +30,7 @@ #include "td/utils/algorithm.h" #include "td/utils/buffer.h" +#include "td/utils/FlatHashSet.h" #include "td/utils/format.h" #include "td/utils/logging.h" #include "td/utils/misc.h" @@ -369,6 +371,54 @@ class GetChannelParticipantQuery final : public Td::ResultHandler { } }; +class GetChannelParticipantsQuery final : public Td::ResultHandler { + Promise> promise_; + ChannelId channel_id_; + + public: + explicit GetChannelParticipantsQuery( + Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id, const ChannelParticipantFilter &filter, int32 offset, int32 limit) { + auto input_channel = td_->contacts_manager_->get_input_channel(channel_id); + if (input_channel == nullptr) { + return promise_.set_error(Status::Error(400, "Supergroup not found")); + } + + channel_id_ = channel_id; + send_query(G()->net_query_creator().create(telegram_api::channels_getParticipants( + std::move(input_channel), filter.get_input_channel_participants_filter(), offset, limit, 0))); + } + + 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 participants_ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetChannelParticipantsQuery: " << to_string(participants_ptr); + switch (participants_ptr->get_id()) { + case telegram_api::channels_channelParticipants::ID: { + promise_.set_value(telegram_api::move_object_as(participants_ptr)); + break; + } + case telegram_api::channels_channelParticipantsNotModified::ID: + LOG(ERROR) << "Receive channelParticipantsNotModified"; + return on_error(Status::Error(500, "Receive channelParticipantsNotModified")); + default: + UNREACHABLE(); + } + } + + void on_error(Status status) final { + td_->contacts_manager_->on_get_channel_error(channel_id_, status, "GetChannelParticipantsQuery"); + promise_.set_error(std::move(status)); + } +}; + class AddChatUserQuery final : public Td::ResultHandler { Promise promise_; ChatId chat_id_; @@ -821,9 +871,8 @@ void DialogParticipantManager::on_update_dialog_online_member_count_timeout(Dial if (participant_count == 0 || participant_count >= 195 || has_hidden_participants) { td_->create_handler()->send(dialog_id); } else { - td_->contacts_manager_->get_channel_participants(dialog_id.get_channel_id(), - td_api::make_object(), - string(), 0, 200, 200, Auto()); + get_channel_participants(dialog_id.get_channel_id(), td_api::make_object(), + string(), 0, 200, 200, Auto()); } return; } @@ -1676,6 +1725,194 @@ void DialogParticipantManager::do_search_chat_participants(ChatId chat_id, const promise.set_value(DialogParticipants{total_count, std::move(dialog_participants)}); } +void DialogParticipantManager::get_channel_participants(ChannelId channel_id, + td_api::object_ptr &&filter, + string additional_query, int32 offset, int32 limit, + int32 additional_limit, Promise &&promise) { + if (limit <= 0) { + return promise.set_error(Status::Error(400, "Parameter limit must be positive")); + } + if (limit > MAX_GET_CHANNEL_PARTICIPANTS) { + limit = MAX_GET_CHANNEL_PARTICIPANTS; + } + + if (offset < 0) { + return promise.set_error(Status::Error(400, "Parameter offset must be non-negative")); + } + + if (td_->contacts_manager_->is_broadcast_channel(channel_id) && + !td_->contacts_manager_->get_channel_status(channel_id).is_administrator()) { + return promise.set_error(Status::Error(400, "Member list is inaccessible")); + } + + ChannelParticipantFilter participant_filter(filter); + auto get_channel_participants_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), channel_id, filter = participant_filter, + additional_query = std::move(additional_query), offset, limit, additional_limit, promise = std::move(promise)]( + Result> &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &DialogParticipantManager::on_get_channel_participants, channel_id, std::move(filter), + offset, limit, std::move(additional_query), additional_limit, result.move_as_ok(), + std::move(promise)); + } + }); + td_->create_handler(std::move(get_channel_participants_promise)) + ->send(channel_id, participant_filter, offset, limit); +} + +void DialogParticipantManager::on_get_channel_participants( + ChannelId channel_id, ChannelParticipantFilter &&filter, int32 offset, int32 limit, string additional_query, + int32 additional_limit, telegram_api::object_ptr &&channel_participants, + Promise &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + + td_->contacts_manager_->on_get_users(std::move(channel_participants->users_), "on_get_channel_participants"); + td_->contacts_manager_->on_get_chats(std::move(channel_participants->chats_), "on_get_channel_participants"); + int32 total_count = channel_participants->count_; + auto participants = std::move(channel_participants->participants_); + LOG(INFO) << "Receive " << participants.size() << " " << filter << " members in " << channel_id; + + bool is_full = offset == 0 && static_cast(participants.size()) < limit && total_count < limit; + bool has_hidden_participants = + td_->contacts_manager_->get_channel_effective_has_hidden_participants(channel_id, "on_get_channel_participants"); + bool is_full_recent = is_full && filter.is_recent() && !has_hidden_participants; + + auto channel_type = td_->contacts_manager_->get_channel_type(channel_id); + vector result; + for (auto &participant_ptr : participants) { + auto debug_participant = to_string(participant_ptr); + result.emplace_back(std::move(participant_ptr), channel_type); + const auto &participant = result.back(); + UserId participant_user_id; + if (participant.dialog_id_.get_type() == DialogType::User) { + participant_user_id = participant.dialog_id_.get_user_id(); + } + if (!participant.is_valid() || (filter.is_bots() && !td_->contacts_manager_->is_user_bot(participant_user_id)) || + (filter.is_administrators() && !participant.status_.is_administrator()) || + ((filter.is_recent() || filter.is_contacts() || filter.is_search()) && !participant.status_.is_member()) || + (filter.is_contacts() && !td_->contacts_manager_->is_user_contact(participant_user_id)) || + (filter.is_restricted() && !participant.status_.is_restricted()) || + (filter.is_banned() && !participant.status_.is_banned())) { + bool skip_error = ((filter.is_administrators() || filter.is_bots()) && + td_->contacts_manager_->is_user_deleted(participant_user_id)) || + (filter.is_contacts() && participant_user_id == td_->contacts_manager_->get_my_id()); + if (!skip_error) { + LOG(ERROR) << "Receive " << participant << ", while searching for " << filter << " in " << channel_id + << " with offset " << offset << " and limit " << limit << ": " << oneline(debug_participant); + } + result.pop_back(); + total_count--; + } + } + + if (total_count < narrow_cast(result.size())) { + LOG(ERROR) << "Receive total_count = " << total_count << ", but have at least " << result.size() << " " << filter + << " members in " << channel_id; + total_count = static_cast(result.size()); + } else if (is_full && total_count > static_cast(result.size())) { + LOG(ERROR) << "Fix total number of " << filter << " members from " << total_count << " to " << result.size() + << " in " << channel_id << " for request with limit " << limit << " and received " << participants.size() + << " results"; + total_count = static_cast(result.size()); + } + + auto is_megagroup = td_->contacts_manager_->is_megagroup_channel(channel_id); + const auto max_participant_count = is_megagroup ? 975 : 195; + auto participant_count = + filter.is_recent() && !has_hidden_participants && total_count != 0 && total_count < max_participant_count + ? total_count + : -1; + int32 administrator_count = + filter.is_administrators() || (filter.is_recent() && has_hidden_participants) ? total_count : -1; + if (is_full && (filter.is_administrators() || filter.is_bots() || filter.is_recent())) { + vector administrators; + vector bot_user_ids; + { + if (filter.is_recent()) { + for (const auto &participant : result) { + if (participant.dialog_id_.get_type() == DialogType::User) { + auto participant_user_id = participant.dialog_id_.get_user_id(); + if (participant.status_.is_administrator()) { + administrators.emplace_back(participant_user_id, participant.status_.get_rank(), + participant.status_.is_creator()); + } + if (is_full_recent && td_->contacts_manager_->is_user_bot(participant_user_id)) { + bot_user_ids.push_back(participant_user_id); + } + } + } + administrator_count = narrow_cast(administrators.size()); + + if (is_megagroup && !td_->auth_manager_->is_bot() && is_full_recent) { + td_->contacts_manager_->set_cached_channel_participants(channel_id, result); + td_->contacts_manager_->update_channel_online_member_count(channel_id, true); + } + } else if (filter.is_administrators()) { + for (const auto &participant : result) { + if (participant.dialog_id_.get_type() == DialogType::User) { + administrators.emplace_back(participant.dialog_id_.get_user_id(), participant.status_.get_rank(), + participant.status_.is_creator()); + } + } + } else if (filter.is_bots()) { + bot_user_ids = transform(result, [](const DialogParticipant &participant) { + CHECK(participant.dialog_id_.get_type() == DialogType::User); + return participant.dialog_id_.get_user_id(); + }); + } + } + if (filter.is_administrators() || filter.is_recent()) { + on_update_dialog_administrators(DialogId(channel_id), std::move(administrators), true, false); + } + if (filter.is_bots() || is_full_recent) { + td_->contacts_manager_->on_update_channel_bot_user_ids(channel_id, std::move(bot_user_ids)); + } + } + if (have_channel_participant_cache(channel_id)) { + for (const auto &participant : result) { + add_channel_participant_to_cache(channel_id, participant, false); + } + } + + if (participant_count != -1) { + td_->contacts_manager_->on_update_channel_participant_count(channel_id, participant_count); + } + if (administrator_count != -1) { + td_->contacts_manager_->on_update_channel_administrator_count(channel_id, administrator_count); + } + + if (!additional_query.empty()) { + auto dialog_ids = transform(result, [](const DialogParticipant &participant) { return participant.dialog_id_; }); + std::pair> result_dialog_ids = + td_->contacts_manager_->search_among_dialogs(dialog_ids, additional_query, additional_limit); + + total_count = result_dialog_ids.first; + FlatHashSet result_dialog_ids_set; + for (auto result_dialog_id : result_dialog_ids.second) { + CHECK(result_dialog_id.is_valid()); + result_dialog_ids_set.insert(result_dialog_id); + } + auto all_participants = std::move(result); + result.clear(); + for (auto &participant : all_participants) { + if (result_dialog_ids_set.count(participant.dialog_id_)) { + result_dialog_ids_set.erase(participant.dialog_id_); + result.push_back(std::move(participant)); + } + } + } + + vector participant_dialog_ids; + for (const auto &participant : result) { + participant_dialog_ids.push_back(participant.dialog_id_); + } + td_->contacts_manager_->on_view_dialog_active_stories(std::move(participant_dialog_ids)); + + promise.set_value(DialogParticipants{total_count, std::move(result)}); +} + void DialogParticipantManager::search_dialog_participants(DialogId dialog_id, const string &query, int32 limit, DialogParticipantFilter filter, Promise &&promise) { @@ -1696,12 +1933,11 @@ void DialogParticipantManager::search_dialog_participants(DialogId dialog_id, co case DialogType::Channel: { auto channel_id = dialog_id.get_channel_id(); if (filter.has_query()) { - return td_->contacts_manager_->get_channel_participants( - channel_id, filter.get_supergroup_members_filter_object(query), string(), 0, limit, 0, std::move(promise)); + return get_channel_participants(channel_id, filter.get_supergroup_members_filter_object(query), string(), 0, + limit, 0, std::move(promise)); } else { - return td_->contacts_manager_->get_channel_participants(channel_id, - filter.get_supergroup_members_filter_object(string()), - query, 0, 100, limit, std::move(promise)); + return get_channel_participants(channel_id, filter.get_supergroup_members_filter_object(string()), query, 0, + 100, limit, std::move(promise)); } } case DialogType::SecretChat: { diff --git a/td/telegram/DialogParticipantManager.h b/td/telegram/DialogParticipantManager.h index f7b7ebf77..37a69446a 100644 --- a/td/telegram/DialogParticipantManager.h +++ b/td/telegram/DialogParticipantManager.h @@ -27,6 +27,8 @@ namespace td { +class ChannelParticipantFilter; + class Td; class DialogParticipantManager final : public Actor { @@ -90,6 +92,10 @@ class DialogParticipantManager final : public Actor { void get_channel_participant(ChannelId channel_id, DialogId participant_dialog_id, Promise &&promise); + void get_channel_participants(ChannelId channel_id, td_api::object_ptr &&filter, + string additional_query, int32 offset, int32 limit, int32 additional_limit, + Promise &&promise); + void search_dialog_participants(DialogId dialog_id, const string &query, int32 limit, DialogParticipantFilter filter, Promise &&promise); @@ -139,6 +145,8 @@ class DialogParticipantManager final : public Actor { static constexpr int32 CHANNEL_PARTICIPANT_CACHE_TIME = 1800; // some reasonable limit + static constexpr int32 MAX_GET_CHANNEL_PARTICIPANTS = 200; // server side limit + void tear_down() final; static void on_update_dialog_online_member_count_timeout_callback(void *dialog_participant_manager_ptr, @@ -191,6 +199,12 @@ class DialogParticipantManager final : public Actor { void do_search_chat_participants(ChatId chat_id, const string &query, int32 limit, DialogParticipantFilter filter, Promise &&promise); + void on_get_channel_participants( + ChannelId channel_id, ChannelParticipantFilter &&filter, int32 offset, int32 limit, string additional_query, + int32 additional_limit, + telegram_api::object_ptr &&channel_participants, + Promise &&promise); + void set_chat_participant_status(ChatId chat_id, UserId user_id, DialogParticipantStatus status, bool is_recursive, Promise &&promise); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 6a6d2cc00..425be4389 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -16790,7 +16790,7 @@ void MessagesManager::on_get_message_viewers(DialogId dialog_id, MessageViewers return td_->contacts_manager_->reload_chat_full(dialog_id.get_chat_id(), std::move(query_promise), "on_get_message_viewers"); case DialogType::Channel: - return td_->contacts_manager_->get_channel_participants( + return td_->dialog_participant_manager_->get_channel_participants( dialog_id.get_channel_id(), td_api::make_object(), string(), 0, 200, 200, PromiseCreator::lambda([query_promise = std::move(query_promise)](DialogParticipants) mutable { query_promise.set_value(Unit()); @@ -18940,7 +18940,7 @@ void MessagesManager::open_dialog(Dialog *d) { auto has_hidden_participants = td_->contacts_manager_->get_channel_effective_has_hidden_participants( dialog_id.get_channel_id(), "open_dialog"); if (participant_count < 195 && !has_hidden_participants) { // include unknown participant_count - td_->contacts_manager_->get_channel_participants( + td_->dialog_participant_manager_->get_channel_participants( channel_id, td_api::make_object(), string(), 0, 200, 200, Auto()); } } else { diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 501525bf7..e3a28db88 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -7887,8 +7887,9 @@ void Td::on_request(uint64 id, td_api::getSupergroupMembers &request) { promise.set_value(result.ok().get_chat_members_object(td, "getSupergroupMembers")); } }); - contacts_manager_->get_channel_participants(ChannelId(request.supergroup_id_), std::move(request.filter_), string(), - request.offset_, request.limit_, -1, std::move(query_promise)); + dialog_participant_manager_->get_channel_participants(ChannelId(request.supergroup_id_), std::move(request.filter_), + string(), request.offset_, request.limit_, -1, + std::move(query_promise)); } void Td::on_request(uint64 id, td_api::closeSecretChat &request) {