diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 5f3b585f2..f443c93d4 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2089,7 +2089,7 @@ groupCallJoinResponse payload:groupCallPayload candidates:vector participants_next_offset:string users:Vector = phone.GroupCall; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index b82d2a66b..4c470231e 100644 Binary files a/td/generate/scheme/telegram_api.tlo and b/td/generate/scheme/telegram_api.tlo differ diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index a8a5e308b..41162a6d9 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -379,6 +379,12 @@ struct GroupCallManager::GroupCall { int32 source = 0; }; +struct GroupCallManager::GroupCallParticipants { + vector participants; + string next_offset; + int64 min_order = std::numeric_limits::max(); +}; + struct GroupCallManager::GroupCallRecentSpeakers { vector> users; // user + time; sorted by time bool is_changed = false; @@ -583,6 +589,20 @@ void GroupCallManager::finish_get_group_call(InputGroupCallId input_group_call_i } } +bool GroupCallManager::need_group_call_participants(InputGroupCallId input_group_call_id) const { + auto *group_call = get_group_call(input_group_call_id); + if (group_call == nullptr || !group_call->is_inited) { + return false; + } + if (group_call->is_joined) { + return true; + } + if (pending_join_requests_.count(input_group_call_id) != 0) { + return true; + } + return false; +} + void GroupCallManager::on_get_group_call_participants( InputGroupCallId input_group_call_id, tl_object_ptr &&participants, bool is_load) { @@ -591,16 +611,115 @@ void GroupCallManager::on_get_group_call_participants( CHECK(participants != nullptr); td_->contacts_manager_->on_get_users(std::move(participants->users_), "on_get_group_call_participants"); - process_group_call_participants(std::move(participants->participants_)); + process_group_call_participants(input_group_call_id, std::move(participants->participants_), false); + + // TODO use version if (is_load) { - // TODO use count, next_offset, version + // TODO use count, next_offset } } void GroupCallManager::process_group_call_participants( - vector> &&participants) { - // TODO + InputGroupCallId input_group_call_id, vector> &&participants, + bool from_update) { + if (!need_group_call_participants(input_group_call_id)) { + return; + } + + auto group_call = get_group_call(input_group_call_id); + CHECK(group_call != nullptr && group_call->is_inited); + if (from_update) { + CHECK(group_call->version != -1); + group_call->version += static_cast(participants.size()); + } + auto old_participant_count = group_call->participant_count; + for (auto &participant : participants) { + int diff = process_group_call_participant(input_group_call_id, GroupCallParticipant(participant)); + if (from_update) { + group_call->participant_count += diff; + } + } + if (group_call->participant_count) { + LOG(ERROR) << "Participant count became negative in " << input_group_call_id; + group_call->participant_count = 0; + } + if (group_call->participant_count != old_participant_count) { + send_update_group_call(group_call); + } +} + +int GroupCallManager::process_group_call_participant(InputGroupCallId input_group_call_id, + GroupCallParticipant &&participant) { + if (!participant.is_valid()) { + LOG(ERROR) << "Receive invalid " << participant; + return 0; + } + if (!need_group_call_participants(input_group_call_id)) { + return 0; + } + + auto &participants = group_call_participants_[input_group_call_id]; + if (participants == nullptr) { + participants = make_unique(); + } + + for (size_t i = 0; i < participants->participants.size(); i++) { + auto &old_participant = participants->participants[i]; + if (old_participant.user_id == participant.user_id) { + if (participant.joined_date == 0) { + // removed participant + if (old_participant.order != 0) { + send_update_group_call_participant(input_group_call_id, participant); + } + participants->participants.erase(participants->participants.begin() + i); + return -1; + } + + if (participant.joined_date < old_participant.joined_date) { + LOG(ERROR) << "Join date of " << participant.user_id << " in " << input_group_call_id << " decreased from " + << old_participant.joined_date << " to " << participant.joined_date; + participant.joined_date = old_participant.joined_date; + } + if (participant.active_date < old_participant.active_date) { + participant.active_date = old_participant.active_date; + } + participant.local_active_date = old_participant.local_active_date; + participant.is_speaking = old_participant.is_speaking; + auto real_order = participant.get_real_order(); + if (real_order >= participants->min_order) { + participant.order = real_order; + } + participant.is_just_joined = false; + + if (old_participant != participant) { + bool need_update = old_participant.order != 0 || participant.order != 0; + old_participant = std::move(participant); + if (need_update) { + send_update_group_call_participant(input_group_call_id, old_participant); + } + } + return 0; + } + } + + if (participant.joined_date == 0) { + // unknown removed participant + return -1; + } + + // unknown added or edited participant + int diff = participant.is_just_joined ? 1 : 0; + auto real_order = participant.get_real_order(); + if (real_order >= participants->min_order) { + participant.order = real_order; + } + participant.is_just_joined = false; + participants->participants.push_back(std::move(participant)); + if (participants->participants.back().order != 0) { + send_update_group_call_participant(input_group_call_id, participants->participants.back()); + } + return diff; } void GroupCallManager::join_group_call(GroupCallId group_call_id, @@ -785,6 +904,7 @@ bool GroupCallManager::on_join_group_call_response(InputGroupCallId input_group_ need_update = true; } pending_join_requests_.erase(it); + try_clear_group_call_participants(input_group_call_id); return need_update; } @@ -796,6 +916,7 @@ void GroupCallManager::finish_join_group_call(InputGroupCallId input_group_call_ } it->second->promise.set_error(std::move(error)); pending_join_requests_.erase(it); + try_clear_group_call_participants(input_group_call_id); } void GroupCallManager::toggle_group_call_mute_new_participants(GroupCallId group_call_id, bool mute_new_participants, @@ -923,6 +1044,8 @@ void GroupCallManager::on_group_call_left(InputGroupCallId input_group_call_id, group_call->is_speaking = false; group_call->source = 0; send_update_group_call(group_call); + + try_clear_group_call_participants(input_group_call_id); } } @@ -949,6 +1072,28 @@ void GroupCallManager::on_update_group_call(tl_object_ptrsecond); + CHECK(participants != nullptr); + group_call_participants_.erase(participants_it); + + for (auto &participant : participants->participants) { + if (participant.order != 0) { + CHECK(participant.order >= participants->min_order); + participant.order = 0; + send_update_group_call_participant(input_group_call_id, participant); + } + } +} + InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptr &group_call_ptr, ChannelId channel_id) { CHECK(group_call_ptr != nullptr); @@ -1035,6 +1180,7 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptrunix_time()); } else if (!recursive) { @@ -1130,7 +1277,6 @@ void GroupCallManager::on_source_speaking_in_group_call(GroupCallId group_call_i send_closure(actor_id, &GroupCallManager::on_source_speaking_in_group_call, group_call_id, source, date, true); } }); - auto input_group_call_id = get_input_group_call_id(group_call_id).move_as_ok(); td_->create_handler(std::move(query_promise)) ->send(input_group_call_id, {}, {source}); } @@ -1150,8 +1296,17 @@ void GroupCallManager::on_group_call_recent_speakers_updated(const GroupCall *gr recent_speaker_update_timeout_.set_timeout_in(group_call->group_call_id.get(), MAX_RECENT_SPEAKER_UPDATE_DELAY); } -UserId GroupCallManager::get_group_call_participant_by_source(GroupCallId group_call_id, int32 source) { - // TODO +UserId GroupCallManager::get_group_call_participant_by_source(InputGroupCallId input_group_call_id, int32 source) { + auto participants_it = group_call_participants_.find(input_group_call_id); + if (participants_it == group_call_participants_.end()) { + return UserId(); + } + + for (auto &participant : participants_it->second->participants) { + if (participant.source == source) { + return participant.user_id; + } + } return UserId(); } @@ -1213,9 +1368,27 @@ tl_object_ptr GroupCallManager::get_update_group_call_o get_group_call_object(group_call, std::move(recent_speaker_user_ids))); } +tl_object_ptr GroupCallManager::get_update_group_call_participant_object( + GroupCallId group_call_id, const GroupCallParticipant &participant) { + return td_api::make_object( + group_call_id.get(), participant.get_group_call_participant_object(td_->contacts_manager_.get())); +} + void GroupCallManager::send_update_group_call(const GroupCall *group_call) { send_closure(G()->td(), &Td::send_update, get_update_group_call_object(group_call, get_recent_speaker_user_ids(group_call, true))); } +void GroupCallManager::send_update_group_call_participant(GroupCallId group_call_id, + const GroupCallParticipant &participant) { + send_closure(G()->td(), &Td::send_update, get_update_group_call_participant_object(group_call_id, participant)); +} + +void GroupCallManager::send_update_group_call_participant(InputGroupCallId input_group_call_id, + const GroupCallParticipant &participant) { + auto group_call = get_group_call(input_group_call_id); + CHECK(group_call != nullptr && group_call->is_inited); + send_update_group_call_participant(group_call->group_call_id, participant); +} + } // namespace td diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index 66e9bfe53..9b67a90dd 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -8,6 +8,7 @@ #include "td/telegram/ChannelId.h" #include "td/telegram/GroupCallId.h" +#include "td/telegram/GroupCallParticipant.h" #include "td/telegram/InputGroupCallId.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -73,6 +74,7 @@ class GroupCallManager : public Actor { private: struct GroupCall; + struct GroupCallParticipants; struct GroupCallRecentSpeakers; struct PendingJoinRequest; @@ -103,7 +105,13 @@ class GroupCallManager : public Actor { void finish_get_group_call(InputGroupCallId input_group_call_id, Result> &&result); - void process_group_call_participants(vector> &&participants); + bool need_group_call_participants(InputGroupCallId input_group_call_id) const; + + void process_group_call_participants(InputGroupCallId group_call_id, + vector> &&participants, + bool from_update); + + int process_group_call_participant(InputGroupCallId group_call_id, GroupCallParticipant &&participant); bool on_join_group_call_response(InputGroupCallId input_group_call_id, string json_response); @@ -118,11 +126,13 @@ class GroupCallManager : public Actor { void on_group_call_recent_speakers_updated(const GroupCall *group_call, GroupCallRecentSpeakers *recent_speakers); - UserId get_group_call_participant_by_source(GroupCallId group_call_id, int32 source); + UserId get_group_call_participant_by_source(InputGroupCallId input_group_call_id, int32 source); static Result> get_group_call_join_response_object( string json_response); + void try_clear_group_call_participants(InputGroupCallId input_group_call_id); + vector get_recent_speaker_user_ids(const GroupCall *group_call, bool for_update); tl_object_ptr get_update_group_call_object(const GroupCall *group_call, @@ -131,8 +141,16 @@ class GroupCallManager : public Actor { tl_object_ptr get_group_call_object(const GroupCall *group_call, vector recent_speaker_user_ids) const; + tl_object_ptr get_update_group_call_participant_object( + GroupCallId group_call_id, const GroupCallParticipant &participant); + void send_update_group_call(const GroupCall *group_call); + void send_update_group_call_participant(GroupCallId group_call_id, const GroupCallParticipant &participant); + + void send_update_group_call_participant(InputGroupCallId input_group_call_id, + const GroupCallParticipant &participant); + Td *td_; ActorShared<> parent_; @@ -142,6 +160,9 @@ class GroupCallManager : public Actor { std::unordered_map, InputGroupCallIdHash> group_calls_; + std::unordered_map, InputGroupCallIdHash> + group_call_participants_; + std::unordered_map, GroupCallIdHash> group_call_recent_speakers_; std::unordered_map>>, InputGroupCallIdHash> diff --git a/td/telegram/GroupCallParticipant.cpp b/td/telegram/GroupCallParticipant.cpp index dfe25ca45..841a4ea75 100644 --- a/td/telegram/GroupCallParticipant.cpp +++ b/td/telegram/GroupCallParticipant.cpp @@ -7,13 +7,10 @@ #include "td/telegram/GroupCallParticipant.h" #include "td/telegram/ContactsManager.h" -#include "td/telegram/Td.h" - -#include "td/utils/common.h" namespace td { -GroupCallParticipant::GroupCallParticipant(tl_object_ptr &&participant) { +GroupCallParticipant::GroupCallParticipant(const tl_object_ptr &participant) { CHECK(participant != nullptr); user_id = UserId(participant->user_id_); source = participant->source_; @@ -24,17 +21,36 @@ GroupCallParticipant::GroupCallParticipant(tl_object_ptrflags_ & telegram_api::groupCallParticipant::ACTIVE_DATE_MASK) != 0) { active_date = participant->active_date_; } + if (joined_date < 0 || active_date < 0) { + LOG(ERROR) << "Receive invalid " << to_string(participant); + joined_date = 0; + active_date = 0; + } } + is_just_joined = participant->just_joined_; } -td_api::object_ptr GroupCallParticipant::get_group_call_participant_object(Td *td) const { +td_api::object_ptr GroupCallParticipant::get_group_call_participant_object( + ContactsManager *contacts_manager) const { if (!is_valid()) { return nullptr; } return td_api::make_object( - td->contacts_manager_->get_user_id_object(user_id, "get_group_call_participant_object"), source, is_speaking, - is_muted, can_self_unmute, order); + contacts_manager->get_user_id_object(user_id, "get_group_call_participant_object"), source, is_speaking, is_muted, + can_self_unmute, order); +} + +bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs) { + return lhs.user_id == rhs.user_id && lhs.source == rhs.source && lhs.is_muted == rhs.is_muted && + lhs.can_self_unmute == rhs.can_self_unmute && + max(lhs.active_date, lhs.local_active_date) == max(rhs.active_date, rhs.local_active_date) && + lhs.joined_date == rhs.joined_date && lhs.is_speaking == rhs.is_speaking || + lhs.order != rhs.order; +} + +bool operator!=(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs) { + return !(lhs == rhs); } StringBuilder &operator<<(StringBuilder &string_builder, const GroupCallParticipant &group_call_participant) { diff --git a/td/telegram/GroupCallParticipant.h b/td/telegram/GroupCallParticipant.h index b79c8521a..398ac2720 100644 --- a/td/telegram/GroupCallParticipant.h +++ b/td/telegram/GroupCallParticipant.h @@ -15,7 +15,7 @@ namespace td { -class Td; +class ContactsManager; struct GroupCallParticipant { UserId user_id; @@ -25,20 +25,31 @@ struct GroupCallParticipant { int32 joined_date = 0; int32 active_date = 0; + bool is_just_joined = false; bool is_speaking = false; + int32 local_active_date = 0; int64 order = 0; + int64 get_real_order() const { + return (static_cast(max(active_date, local_active_date)) << 32) + joined_date; + } + bool is_valid() const { return user_id.is_valid(); } GroupCallParticipant() = default; - GroupCallParticipant(tl_object_ptr &&participant); + explicit GroupCallParticipant(const tl_object_ptr &participant); - td_api::object_ptr get_group_call_participant_object(Td *td) const; + td_api::object_ptr get_group_call_participant_object( + ContactsManager *contacts_manager) const; }; +bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs); + +bool operator!=(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs); + StringBuilder &operator<<(StringBuilder &string_builder, const GroupCallParticipant &group_call_participant); } // namespace td