This commit is contained in:
Andrea Cavalli 2020-12-25 20:09:39 +01:00
commit d0611518f6
13 changed files with 513 additions and 230686 deletions

View File

@ -125,8 +125,8 @@ TDLib has a simple and convenient C++11-interface for sending and receiving requ
See [example/cpp](https://github.com/tdlib/td/tree/master/example/cpp) for an example of TDLib usage from C++.
[td_example.cpp](https://github.com/tdlib/td/tree/master/example/cpp/td_example.cpp) contains an example of authorization, processing new incoming messages, getting a list of chats and sending a text message.
See also the source code of [Depecher](https://github.com/blacksailer/depecher) a Telegram app for Sailfish OS, [TELEports](https://gitlab.com/ubports/apps/teleports) a Qt-client for Ubuntu Touch, or
[tdlib-purple](https://github.com/ars3niy/tdlib-purple) - Telegram plugin for Pidgin, all of which are based on TDLib.
See also the source code of [Fernschreiber](https://github.com/Wunderfitz/harbour-fernschreiber) and [Depecher](https://github.com/blacksailer/depecher) Telegram apps for Sailfish OS,
[TELEports](https://gitlab.com/ubports/apps/teleports) a Qt-client for Ubuntu Touch, or [tdlib-purple](https://github.com/ars3niy/tdlib-purple) - Telegram plugin for Pidgin, all of which are based on TDLib.
<a name="swift"></a>
## Using TDLib in Swift projects

View File

@ -34,7 +34,9 @@ target_compile_definitions(tdsqlite PRIVATE
-DSQLITE_ENABLE_SORTER_REFERENCES
-DSQLITE_DIRECT_OVERFLOW_READ
-DSQLITE_ENABLE_MEMORY_MANAGEMENT
-DSQLITE_OMIT_DEPRECATED
)
target_compile_definitions(tdsqlite PRIVATE -DSQLITE_HAS_CODEC -DSQLITE_TEMP_STORE=2 -DSQLITE_ENABLE_FTS5 -DSQLITE_DISABLE_LFS -DOMIT_MEMLOCK)
if (NOT WIN32)
target_compile_definitions(tdsqlite PRIVATE -DHAVE_USLEEP -DNDEBUG=1)

230517
sqlite/sqlite/sqlite3.c vendored

File diff suppressed because it is too large Load Diff

View File

@ -2070,13 +2070,14 @@ callStateError error:error = CallState;
//@id Group call identifier
//@is_active True, if the call is active
//@is_joined True, if the call is joined
//@can_unmute_self True, if the user can unmute themself
//@participant_count Number of participants in the group call
//@loaded_all_participants True, if all group call participants are loaded
//@recent_speaker_user_ids Identifiers of recently speaking users in the group call
//@mute_new_participants True, if only group call administrators can unmute new participants
//@allowed_change_mute_new_participants True, if group call administrators can enable or disable mute_new_participants setting
//@duration Call duration; for ended calls only
groupCall id:int32 is_active:Bool is_joined:Bool participant_count:int32 loaded_all_participants:Bool recent_speaker_user_ids:vector<int32> mute_new_participants:Bool allowed_change_mute_new_participants:Bool duration:int32 = GroupCall;
groupCall id:int32 is_active:Bool is_joined:Bool can_unmute_self:Bool participant_count:int32 loaded_all_participants:Bool recent_speaker_user_ids:vector<int32> mute_new_participants:Bool allowed_change_mute_new_participants:Bool duration:int32 = GroupCall;
//@description Describes a payload fingerprint for interaction with tgcalls @hash Value of the field hash @setup Value of the field setup @fingerprint Value of the field fingerprint
groupCallPayloadFingerprint hash:string setup:string fingerprint:string = GroupCallPayloadFingerprint;
@ -2093,9 +2094,9 @@ groupCallJoinResponseCandidate port:string protocol:string network:string genera
groupCallJoinResponse payload:groupCallPayload candidates:vector<groupCallJoinResponseCandidate> = GroupCallJoinResponse;
//@description Represents a group call participant @user_id Identifier of the user @source User's synchronization source
//@is_speaking True, if the user is speaking as set by setGroupCallParticipantIsSpeaking @is_muted True, if the user is muted @can_self_unmute True, if the user can unmute themself
//@is_speaking True, if the user is speaking as set by setGroupCallParticipantIsSpeaking @is_muted True, if the user is muted @can_unmute_self True, if the user can unmute themself
//@order User's order in the group call participant list. The bigger is order, the higher is user in the list. If order is 0, the user must be removed from the participant list
groupCallParticipant user_id:int32 source:int32 is_speaking:Bool is_muted:Bool can_self_unmute:Bool order:int64 = GroupCallParticipant;
groupCallParticipant user_id:int32 source:int32 is_speaking:Bool is_muted:Bool can_unmute_self:Bool order:int64 = GroupCallParticipant;
//@class CallProblem @description Describes the exact type of a problem with a call
@ -4369,7 +4370,7 @@ createVoiceChat chat_id:int53 = GroupCallId;
//@description Returns information about a group call @group_call_id Group call identifier
getGroupCall group_call_id:int32 = GroupCall;
//@description Joins a group call @group_call_id Group call identifier @payload Group join payload, received from tgcalls @source Caller synchronization source identifier; received from tgcalls @is_muted True, if the user's microphone is muted
//@description Joins a group call @group_call_id Group call identifier @payload Group join payload, received from tgcalls. Use null to cancel previous joinGroupCall request @source Caller synchronization source identifier; received from tgcalls @is_muted True, if the user's microphone is muted
joinGroupCall group_call_id:int32 payload:groupCallPayload source:int32 is_muted:Bool = GroupCallJoinResponse;
//@description Toggles whether new participants of a group call can be unmuted only by administrators of the group call. Requires can_manage_voice_chats rights in the corresponding chat and allowed_change_mute_mew_participants group call flag
@ -4391,7 +4392,9 @@ toggleGroupCallParticipantIsMuted group_call_id:int32 user_id:int32 is_muted:Boo
//@description Checks whether a group call is still joined. Should be called every 10 seconds when tgcalls notifies about lost connection with the server @group_call_id Group call identifier
checkGroupCallIsJoined group_call_id:int32 = Ok;
//@description Loads more group call participants. The loaded participants will be received through updates @group_call_id Group call identifier @limit Maximum number of participants to load
//@description Loads more group call participants. The loaded participants will be received through updates. Use the field groupCall.loaded_all_participants to check whether all participants has already been loaded
//@group_call_id Group call identifier. The group call must be previously received through getGroupCall and must be joined or being joined
//@limit Maximum number of participants to load
loadGroupCallParticipants group_call_id:int32 limit:int32 = Ok;
//@description Leaves a group call @group_call_id Group call identifier

View File

@ -10983,16 +10983,17 @@ void ContactsManager::drop_channel_photos(ChannelId channel_id, bool is_empty, b
}
}
void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool drop_invite_link, bool drop_slow_mode_delay) {
void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool need_drop_invite_link,
bool need_drop_slow_mode_delay) {
LOG(INFO) << "Invalidate supergroup full for " << channel_id;
// drop channel full cache
auto channel_full = get_channel_full_force(channel_id, "invalidate_channel_full");
if (channel_full != nullptr) {
channel_full->expires_at = 0.0;
if (drop_invite_link) {
if (need_drop_invite_link) {
on_update_channel_full_invite_link(channel_full, nullptr);
}
if (drop_slow_mode_delay && channel_full->slow_mode_delay != 0) {
if (need_drop_slow_mode_delay && channel_full->slow_mode_delay != 0) {
channel_full->slow_mode_delay = 0;
channel_full->slow_mode_next_send_date = 0;
channel_full->is_slow_mode_next_send_date_changed = true;
@ -11000,7 +11001,7 @@ void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool drop_in
}
update_channel_full(channel_full, channel_id);
}
if (drop_invite_link) {
if (need_drop_invite_link) {
remove_dialog_access_by_invite_link(DialogId(channel_id));
auto it = dialog_invite_links_.find(DialogId(channel_id));
@ -11705,7 +11706,8 @@ void ContactsManager::on_update_chat_delete_user(ChatId chat_id, UserId user_id,
void ContactsManager::on_update_chat_status(Chat *c, ChatId chat_id, DialogParticipantStatus status) {
if (c->status != status) {
LOG(INFO) << "Update " << chat_id << " status from " << c->status << " to " << status;
bool drop_invite_link = c->status.is_left() != status.is_left();
bool need_drop_invite_link = c->status.is_left() != status.is_left();
bool need_reload_group_call = c->status.can_manage_calls() != status.can_manage_calls();
c->status = status;
@ -11717,12 +11719,15 @@ void ContactsManager::on_update_chat_status(Chat *c, ChatId chat_id, DialogParti
drop_chat_full(chat_id);
}
if (drop_invite_link) {
if (need_drop_invite_link) {
auto it = dialog_invite_links_.find(DialogId(chat_id));
if (it != dialog_invite_links_.end()) {
invalidate_invite_link_info(it->second);
}
}
if (need_reload_group_call) {
td_->messages_manager_->reload_dialog_group_call(DialogId(chat_id));
}
c->is_changed = true;
}
@ -12048,9 +12053,10 @@ void ContactsManager::on_channel_status_changed(Channel *c, ChannelId channel_id
const DialogParticipantStatus &new_status) {
CHECK(c->is_update_supergroup_sent);
bool drop_invite_link = old_status.is_administrator() != new_status.is_administrator() ||
old_status.is_member() != new_status.is_member();
invalidate_channel_full(channel_id, drop_invite_link, !c->is_slow_mode_enabled);
bool need_drop_invite_link = old_status.is_administrator() != new_status.is_administrator() ||
old_status.is_member() != new_status.is_member();
bool need_reload_group_call = old_status.can_manage_calls() != new_status.can_manage_calls();
invalidate_channel_full(channel_id, need_drop_invite_link, !c->is_slow_mode_enabled);
if (old_status.is_creator() != new_status.is_creator()) {
for (size_t i = 0; i < 2; i++) {
@ -12061,6 +12067,9 @@ void ContactsManager::on_channel_status_changed(Channel *c, ChannelId channel_id
send_get_channel_full_query(nullptr, channel_id, Auto(), "update channel owner");
reload_dialog_administrators(DialogId(channel_id), 0, Auto());
}
if (need_reload_group_call) {
td_->messages_manager_->reload_dialog_group_call(DialogId(channel_id));
}
}
void ContactsManager::on_update_channel_default_permissions(Channel *c, ChannelId channel_id,
@ -13832,7 +13841,8 @@ void ContactsManager::on_chat_update(telegram_api::chat &chat, const char *sourc
bool has_active_group_call = (chat.flags_ & CHAT_FLAG_HAS_ACTIVE_GROUP_CALL) != 0;
bool is_group_call_empty = (chat.flags_ & CHAT_FLAG_IS_GROUP_CALL_NON_EMPTY) == 0;
td_->messages_manager_->on_update_dialog_group_call(DialogId(chat_id), has_active_group_call, is_group_call_empty);
td_->messages_manager_->on_update_dialog_group_call(DialogId(chat_id), has_active_group_call, is_group_call_empty,
"receive chat");
}
void ContactsManager::on_chat_update(telegram_api::chatForbidden &chat, const char *source) {
@ -14025,7 +14035,8 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char
bool has_active_group_call = (channel.flags_ & CHANNEL_FLAG_HAS_ACTIVE_GROUP_CALL) != 0;
bool is_group_call_empty = (channel.flags_ & CHANNEL_FLAG_IS_GROUP_CALL_NON_EMPTY) == 0;
td_->messages_manager_->on_update_dialog_group_call(DialogId(channel_id), has_active_group_call, is_group_call_empty);
td_->messages_manager_->on_update_dialog_group_call(DialogId(channel_id), has_active_group_call, is_group_call_empty,
"receive channel");
}
void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, const char *source) {
@ -14064,7 +14075,7 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co
// on_update_channel_username(c, channel_id, ""); // don't know if channel username is empty, so don't update it
tl_object_ptr<telegram_api::chatBannedRights> banned_rights; // == nullptr
on_update_channel_default_permissions(c, channel_id, get_restricted_rights(banned_rights));
td_->messages_manager_->on_update_dialog_group_call(DialogId(channel_id), false, false);
td_->messages_manager_->on_update_dialog_group_call(DialogId(channel_id), false, false, "receive channelForbidden");
bool sign_messages = false;
bool is_slow_mode_enabled = false;

View File

@ -219,7 +219,7 @@ class ContactsManager : public Actor {
void speculative_delete_channel_participant(ChannelId channel_id, UserId deleted_user_id, bool by_me);
void invalidate_channel_full(ChannelId channel_id, bool drop_invite_link, bool drop_slow_mode_delay);
void invalidate_channel_full(ChannelId channel_id, bool need_drop_invite_link, bool need_drop_slow_mode_delay);
bool on_get_channel_error(ChannelId channel_id, const Status &status, const string &source);
@ -721,7 +721,7 @@ class ContactsManager : public Actor {
DialogParticipantStatus status = DialogParticipantStatus::Banned(0);
RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false};
static constexpr uint32 CACHE_VERSION = 2;
static constexpr uint32 CACHE_VERSION = 3;
uint32 cache_version = 0;
bool is_active = false;
@ -788,7 +788,7 @@ class ContactsManager : public Actor {
int32 date = 0;
int32 participant_count = 0;
static constexpr uint32 CACHE_VERSION = 5;
static constexpr uint32 CACHE_VERSION = 6;
uint32 cache_version = 0;
bool has_linked_channel = false;

View File

@ -405,6 +405,8 @@ struct GroupCallManager::GroupCall {
bool is_active = false;
bool is_joined = false;
bool is_speaking = false;
bool can_self_unmute = false;
bool syncing_participants = false;
bool loaded_all_participants = false;
bool mute_new_participants = false;
bool allowed_change_mute_new_participants = false;
@ -590,6 +592,46 @@ GroupCallManager::GroupCall *GroupCallManager::get_group_call(InputGroupCallId i
}
}
Status GroupCallManager::can_manage_group_calls(DialogId dialog_id) {
switch (dialog_id.get_type()) {
case DialogType::Chat: {
auto chat_id = dialog_id.get_chat_id();
if (!td_->contacts_manager_->get_chat_permissions(chat_id).can_manage_calls()) {
return Status::Error(400, "Not enough rights in the chat");
}
break;
}
case DialogType::Channel: {
auto channel_id = dialog_id.get_channel_id();
switch (td_->contacts_manager_->get_channel_type(channel_id)) {
case ChannelType::Unknown:
return Status::Error(400, "Chat info not found");
case ChannelType::Megagroup:
// OK
break;
case ChannelType::Broadcast:
return Status::Error(400, "Chat is not a group");
default:
UNREACHABLE();
break;
}
if (!td_->contacts_manager_->get_channel_permissions(channel_id).can_manage_calls()) {
return Status::Error(400, "Not enough rights in the chat");
}
break;
}
case DialogType::User:
case DialogType::SecretChat:
return Status::Error(400, "Chat can't have a voice chat");
case DialogType::None:
// OK
break;
default:
UNREACHABLE();
}
return Status::OK();
}
void GroupCallManager::create_voice_chat(DialogId dialog_id, Promise<GroupCallId> &&promise) {
if (!dialog_id.is_valid()) {
return promise.set_error(Status::Error(400, "Invalid chat identifier specified"));
@ -601,39 +643,7 @@ void GroupCallManager::create_voice_chat(DialogId dialog_id, Promise<GroupCallId
return promise.set_error(Status::Error(400, "Can't access chat"));
}
switch (dialog_id.get_type()) {
case DialogType::Chat: {
auto chat_id = dialog_id.get_chat_id();
if (!td_->contacts_manager_->get_chat_permissions(chat_id).can_manage_calls()) {
return promise.set_error(Status::Error(400, "Not enough rights in the chat"));
}
break;
}
case DialogType::Channel: {
auto channel_id = dialog_id.get_channel_id();
switch (td_->contacts_manager_->get_channel_type(channel_id)) {
case ChannelType::Unknown:
return promise.set_error(Status::Error(400, "Chat info not found"));
case ChannelType::Megagroup:
// OK
break;
case ChannelType::Broadcast:
return promise.set_error(Status::Error(400, "Chat is not a group"));
default:
UNREACHABLE();
break;
}
if (!td_->contacts_manager_->get_channel_permissions(channel_id).can_manage_calls()) {
return promise.set_error(Status::Error(400, "Not enough rights in the chat"));
}
break;
}
case DialogType::User:
case DialogType::SecretChat:
return promise.set_error(Status::Error(400, "Chat can't have a voice chat"));
default:
UNREACHABLE();
}
TRY_STATUS_PROMISE(promise, can_manage_group_calls(dialog_id));
auto query_promise = PromiseCreator::lambda(
[actor_id = actor_id(this), dialog_id, promise = std::move(promise)](Result<InputGroupCallId> result) mutable {
@ -656,7 +666,7 @@ void GroupCallManager::on_voice_chat_created(DialogId dialog_id, InputGroupCallI
return promise.set_error(Status::Error(500, "Receive invalid group call identifier"));
}
td_->messages_manager_->on_update_dialog_group_call(dialog_id, true, true);
td_->messages_manager_->on_update_dialog_group_call(dialog_id, true, true, "on_voice_chat_created");
td_->messages_manager_->on_update_dialog_group_call_id(dialog_id, input_group_call_id);
promise.set_value(get_group_call_id(input_group_call_id, dialog_id));
@ -701,16 +711,6 @@ void GroupCallManager::finish_get_group_call(InputGroupCallId input_group_call_i
if (update_group_call(result.ok()->call_, DialogId()) != input_group_call_id) {
LOG(ERROR) << "Expected " << input_group_call_id << ", but received " << to_string(result.ok());
result = Status::Error(500, "Receive another group call");
} else {
process_group_call_participants(input_group_call_id, std::move(result.ok_ref()->participants_), true);
auto participants_it = group_call_participants_.find(input_group_call_id);
if (participants_it != group_call_participants_.end()) {
CHECK(participants_it->second != nullptr);
if (participants_it->second->next_offset.empty()) {
participants_it->second->next_offset = std::move(result.ok_ref()->participants_next_offset_);
}
}
}
}
@ -721,15 +721,29 @@ void GroupCallManager::finish_get_group_call(InputGroupCallId input_group_call_i
return;
}
auto call = result.move_as_ok();
process_group_call_participants(input_group_call_id, std::move(call->participants_), true, false);
if (need_group_call_participants(input_group_call_id)) {
auto participants_it = group_call_participants_.find(input_group_call_id);
if (participants_it != group_call_participants_.end()) {
CHECK(participants_it->second != nullptr);
if (participants_it->second->next_offset.empty()) {
participants_it->second->next_offset = std::move(call->participants_next_offset_);
}
}
}
auto group_call = get_group_call(input_group_call_id);
for (auto &promise : promises) {
promise.set_value(get_group_call_object(group_call, get_recent_speaker_user_ids(group_call, false)));
if (promise) {
promise.set_value(get_group_call_object(group_call, get_recent_speaker_user_ids(group_call, false)));
}
}
}
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) {
if (group_call == nullptr || !group_call->is_inited || !group_call->is_active) {
return false;
}
if (group_call->is_joined) {
@ -753,13 +767,33 @@ void GroupCallManager::on_get_group_call_participants(
return;
}
auto is_empty = participants->participants_.empty();
process_group_call_participants(input_group_call_id, std::move(participants->participants_), is_load);
bool is_sync = is_load && offset.empty();
if (is_sync) {
auto group_call = get_group_call(input_group_call_id);
is_sync = group_call->syncing_participants;
if (is_sync) {
group_call->syncing_participants = false;
on_receive_group_call_version(input_group_call_id, participants->version_);
if (group_call->version >= participants->version_) {
LOG(INFO) << "Ignore result of outdated participants sync with version " << participants->version_ << " in "
<< input_group_call_id << " from " << group_call->dialog_id << ", because current version is "
<< group_call->version;
return;
}
LOG(INFO) << "Finish syncing participants in " << input_group_call_id << " from " << group_call->dialog_id
<< " with version " << participants->version_;
group_call->version = participants->version_;
}
}
auto is_empty = participants->participants_.empty();
process_group_call_participants(input_group_call_id, std::move(participants->participants_), is_load, is_sync);
if (!is_sync) {
on_receive_group_call_version(input_group_call_id, participants->version_);
}
if (is_load) {
// TODO use count
auto participants_it = group_call_participants_.find(input_group_call_id);
if (participants_it != group_call_participants_.end()) {
CHECK(participants_it->second != nullptr);
@ -768,26 +802,43 @@ void GroupCallManager::on_get_group_call_participants(
}
}
if (is_empty) {
if (is_empty || is_sync) {
bool need_update = false;
auto group_call = get_group_call(input_group_call_id);
CHECK(group_call != nullptr && group_call->is_inited);
if (!group_call->loaded_all_participants) {
if (is_empty && !group_call->loaded_all_participants) {
group_call->loaded_all_participants = true;
need_update = true;
}
auto real_participant_count = participants_it != group_call_participants_.end()
? static_cast<int32>(participants_it->second->participants.size())
: 0;
auto real_participant_count = participants->count_;
if (is_empty) {
auto known_participant_count = participants_it != group_call_participants_.end()
? static_cast<int32>(participants_it->second->participants.size())
: 0;
if (real_participant_count != known_participant_count) {
LOG(ERROR) << "Receive participant count " << real_participant_count << ", but know "
<< known_participant_count << " participants in " << input_group_call_id << " from "
<< group_call->dialog_id;
real_participant_count = known_participant_count;
}
}
if (real_participant_count != group_call->participant_count) {
LOG(ERROR) << "Have participant count " << group_call->participant_count << " instead of "
<< real_participant_count << " in " << input_group_call_id;
if (!is_sync) {
LOG(ERROR) << "Have participant count " << group_call->participant_count << " instead of "
<< real_participant_count << " in " << input_group_call_id << " from " << group_call->dialog_id;
}
group_call->participant_count = real_participant_count;
need_update = true;
update_group_call_dialog(group_call, "on_get_group_call_participants");
}
if (!is_empty && is_sync && group_call->loaded_all_participants && group_call->participant_count > 50) {
group_call->loaded_all_participants = false;
need_update = true;
}
if (need_update) {
send_update_group_call(group_call);
send_update_group_call(group_call, "on_get_group_call_participants");
}
}
}
@ -797,7 +848,45 @@ void GroupCallManager::on_update_group_call_participants(
InputGroupCallId input_group_call_id, vector<tl_object_ptr<telegram_api::groupCallParticipant>> &&participants,
int32 version) {
if (!need_group_call_participants(input_group_call_id)) {
LOG(INFO) << "Ignore updateGroupCallParticipants in " << input_group_call_id;
int32 diff = 0;
bool need_update = false;
auto group_call = get_group_call(input_group_call_id);
for (auto &group_call_participant : participants) {
GroupCallParticipant participant(group_call_participant);
if (participant.user_id == td_->contacts_manager_->get_my_id() && group_call != nullptr &&
group_call->is_inited && group_call->is_joined &&
(participant.joined_date == 0) == (participant.source == group_call->source)) {
on_group_call_left_impl(group_call);
need_update = true;
}
if (participant.joined_date == 0) {
diff--;
remove_recent_group_call_speaker(input_group_call_id, participant.user_id);
} else {
if (participant.is_just_joined) {
diff++;
}
on_participant_speaking_in_group_call(input_group_call_id, participant);
}
}
if (group_call != nullptr && group_call->is_inited && group_call->is_active && group_call->version == -1) {
if (diff != 0 && (group_call->participant_count != 0 || diff > 0)) {
group_call->participant_count += diff;
if (group_call->participant_count < 0) {
LOG(ERROR) << "Participant count became negative in " << input_group_call_id << " from "
<< group_call->dialog_id;
group_call->participant_count = 0;
}
update_group_call_dialog(group_call, "on_update_group_call_participants");
need_update = true;
}
}
if (need_update) {
send_update_group_call(group_call, "on_update_group_call_participants");
}
LOG(INFO) << "Ignore updateGroupCallParticipants in " << input_group_call_id << " from " << group_call->dialog_id;
return;
}
if (version <= 0) {
@ -816,8 +905,8 @@ void GroupCallManager::on_update_group_call_participants(
}
auto &pending_updates = group_call_participants->pending_updates_[version];
if (!pending_updates.empty()) {
LOG(ERROR) << "Receive duplicate updateGroupCallParticipants with version " << version << " in "
<< input_group_call_id;
LOG(INFO) << "Receive duplicate updateGroupCallParticipants with version " << version << " in "
<< input_group_call_id;
sync_group_call_participants(input_group_call_id);
return;
}
@ -838,11 +927,12 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup
auto &pending_updates = participants_it->second->pending_updates_;
auto group_call = get_group_call(input_group_call_id);
CHECK(group_call != nullptr && group_call->is_inited);
if (group_call->version == -1) {
if (group_call->version == -1 || !group_call->is_active) {
return false;
}
int32 diff = 0;
bool is_left = false;
while (!pending_updates.empty()) {
auto it = pending_updates.begin();
auto version = it->first;
@ -853,23 +943,35 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup
if (participant.user_id == td_->contacts_manager_->get_my_id()) {
process_group_call_participant(input_group_call_id, std::move(participant));
}
on_participant_speaking_in_group_call(input_group_call_id, participant);
}
LOG(INFO) << "Ignore already applied updateGroupCallParticipants with version " << version << " in "
<< input_group_call_id;
<< input_group_call_id << " from " << group_call->dialog_id;
pending_updates.erase(it);
continue;
}
if (version < group_call->version + static_cast<int32>(participants.size())) {
LOG(INFO) << "Receive " << participants.size() << " group call participant updates with version " << version
<< ", but current version is " << group_call->version;
sync_group_call_participants(input_group_call_id);
break;
}
if (version == group_call->version + static_cast<int32>(participants.size())) {
group_call->version = version;
diff += process_group_call_participants_from_updates(input_group_call_id, std::move(participants));
for (auto &participant : participants) {
GroupCallParticipant group_call_participant(participant);
if (group_call_participant.user_id == td_->contacts_manager_->get_my_id() && group_call->is_joined &&
(group_call_participant.joined_date == 0) == (group_call_participant.source == group_call->source)) {
is_left = true;
}
diff += process_group_call_participant(input_group_call_id, std::move(group_call_participant));
}
pending_updates.erase(it);
} else {
} else if (!group_call->syncing_participants) {
// found a gap
LOG(INFO) << "Receive " << participants.size() << " group call participant updates with version " << version
<< ", but current version is " << group_call->version;
sync_participants_timeout_.add_timeout_in(group_call->group_call_id.get(), 1.0);
break;
}
@ -877,16 +979,26 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup
if (pending_updates.empty()) {
sync_participants_timeout_.cancel_timeout(group_call->group_call_id.get());
}
bool need_update = false;
if (diff != 0 && (group_call->participant_count != 0 || diff > 0)) {
group_call->participant_count += diff;
if (group_call->participant_count < 0) {
LOG(ERROR) << "Participant count became negative in " << input_group_call_id;
LOG(ERROR) << "Participant count became negative in " << input_group_call_id << " from " << group_call->dialog_id;
group_call->participant_count = 0;
}
send_update_group_call(group_call);
return true;
need_update = true;
update_group_call_dialog(group_call, "process_pending_group_call_participant_updates");
}
return false;
if (is_left && group_call->is_joined) {
on_group_call_left_impl(group_call);
need_update = true;
}
if (need_update) {
send_update_group_call(group_call, "process_pending_group_call_participant_updates");
}
return need_update;
}
void GroupCallManager::sync_group_call_participants(InputGroupCallId input_group_call_id) {
@ -899,17 +1011,60 @@ void GroupCallManager::sync_group_call_participants(InputGroupCallId input_group
sync_participants_timeout_.cancel_timeout(group_call->group_call_id.get());
LOG(INFO) << "Force participants synchronization in " << input_group_call_id;
// TODO
if (group_call->syncing_participants) {
return;
}
group_call->syncing_participants = true;
LOG(INFO) << "Force participants synchronization in " << input_group_call_id << " from " << group_call->dialog_id;
auto promise = PromiseCreator::lambda([actor_id = actor_id(this), input_group_call_id](Result<Unit> &&result) {
if (result.is_error()) {
send_closure(actor_id, &GroupCallManager::on_sync_group_call_participants_failed, input_group_call_id);
}
});
td_->create_handler<GetGroupCallParticipantsQuery>(std::move(promise))->send(input_group_call_id, string(), 100);
}
void GroupCallManager::on_sync_group_call_participants_failed(InputGroupCallId input_group_call_id) {
if (G()->close_flag() || !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);
CHECK(group_call->syncing_participants);
group_call->syncing_participants = false;
sync_participants_timeout_.add_timeout_in(group_call->group_call_id.get(), 1.0);
}
void GroupCallManager::process_group_call_participants(
InputGroupCallId input_group_call_id, vector<tl_object_ptr<telegram_api::groupCallParticipant>> &&participants,
bool is_load) {
bool is_load, bool is_sync) {
if (!need_group_call_participants(input_group_call_id)) {
for (auto &participant : participants) {
GroupCallParticipant group_call_participant(participant);
if (!group_call_participant.is_valid()) {
LOG(ERROR) << "Receive invalid " << to_string(participant);
continue;
}
on_participant_speaking_in_group_call(input_group_call_id, group_call_participant);
}
return;
}
std::unordered_set<UserId, UserIdHash> old_participant_user_ids;
if (is_sync) {
auto participants_it = group_call_participants_.find(input_group_call_id);
if (participants_it != group_call_participants_.end()) {
CHECK(participants_it->second != nullptr);
for (auto &participant : participants_it->second->participants) {
old_participant_user_ids.insert(participant.user_id);
}
}
}
int64 min_order = std::numeric_limits<int64>::max();
for (auto &participant : participants) {
GroupCallParticipant group_call_participant(participant);
@ -925,14 +1080,44 @@ void GroupCallManager::process_group_call_participants(
} else {
min_order = real_order;
}
process_group_call_participant(input_group_call_id, GroupCallParticipant(participant));
if (is_sync) {
old_participant_user_ids.erase(group_call_participant.user_id);
}
process_group_call_participant(input_group_call_id, std::move(group_call_participant));
}
if (is_sync) {
auto participants_it = group_call_participants_.find(input_group_call_id);
if (participants_it != group_call_participants_.end()) {
CHECK(participants_it->second != nullptr);
auto &group_participants = participants_it->second->participants;
for (auto participant_it = group_participants.begin(); participant_it != group_participants.end();) {
auto &participant = *participant_it;
if (old_participant_user_ids.count(participant.user_id) == 0) {
CHECK(participant.order == 0 || participant.order >= min_order);
++participant_it;
continue;
}
// not synced user, needs to be deleted
if (participant.order != 0) {
CHECK(participant.order >= participants_it->second->min_order);
participant.order = 0;
send_update_group_call_participant(input_group_call_id, participant);
}
participant_it = group_participants.erase(participant_it);
}
if (participants_it->second->min_order < min_order) {
// if previously known more users, adjust min_order
participants_it->second->min_order = min_order;
}
}
}
if (is_load) {
auto participants_it = group_call_participants_.find(input_group_call_id);
if (participants_it != group_call_participants_.end()) {
CHECK(participants_it->second != nullptr);
if (participants_it->second->min_order > min_order) {
auto old_min_order = participants_it->second->min_order;
auto old_min_order = participants_it->second->min_order;
if (old_min_order > min_order) {
participants_it->second->min_order = min_order;
for (auto &participant : participants_it->second->participants) {
@ -948,15 +1133,6 @@ void GroupCallManager::process_group_call_participants(
}
}
int32 GroupCallManager::process_group_call_participants_from_updates(
InputGroupCallId input_group_call_id, vector<tl_object_ptr<telegram_api::groupCallParticipant>> &&participants) {
int32 diff = 0;
for (auto &participant : participants) {
diff += process_group_call_participant(input_group_call_id, GroupCallParticipant(participant));
}
return diff;
}
int GroupCallManager::process_group_call_participant(InputGroupCallId input_group_call_id,
GroupCallParticipant &&participant) {
if (!participant.is_valid()) {
@ -967,6 +1143,17 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou
return 0;
}
LOG(INFO) << "Process " << participant << " in " << input_group_call_id;
if (participant.user_id == td_->contacts_manager_->get_my_id()) {
auto *group_call = get_group_call(input_group_call_id);
CHECK(group_call != nullptr && group_call->is_inited);
if (group_call->is_joined && group_call->is_active && group_call->can_self_unmute != participant.can_self_unmute) {
group_call->can_self_unmute = participant.can_self_unmute;
send_update_group_call(group_call, "process_group_call_participant");
}
}
auto &participants = group_call_participants_[input_group_call_id];
if (participants == nullptr) {
participants = make_unique<GroupCallParticipants>();
@ -976,14 +1163,16 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou
auto &old_participant = participants->participants[i];
if (old_participant.user_id == participant.user_id) {
if (participant.joined_date == 0) {
// removed participant
LOG(INFO) << "Remove " << old_participant;
if (old_participant.order != 0) {
send_update_group_call_participant(input_group_call_id, participant);
}
remove_recent_group_call_speaker(input_group_call_id, participant.user_id);
participants->participants.erase(participants->participants.begin() + i);
return -1;
}
LOG(INFO) << "Edit " << old_participant;
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;
@ -1006,18 +1195,24 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou
if (need_update) {
send_update_group_call_participant(input_group_call_id, old_participant);
}
on_participant_speaking_in_group_call(input_group_call_id, participant);
}
return 0;
}
}
if (participant.joined_date == 0) {
// unknown removed participant
LOG(INFO) << "Remove unknown " << participant;
remove_recent_group_call_speaker(input_group_call_id, participant.user_id);
return -1;
}
// unknown added or edited participant
int diff = participant.is_just_joined ? 1 : 0;
if (participant.is_just_joined) {
LOG(INFO) << "Add new " << participant;
} else {
LOG(INFO) << "Receive new " << participant;
}
auto real_order = participant.get_real_order();
if (real_order >= participants->min_order) {
participant.order = real_order;
@ -1027,6 +1222,7 @@ int GroupCallManager::process_group_call_participant(InputGroupCallId input_grou
if (participants->participants.back().order != 0) {
send_update_group_call_participant(input_group_call_id, participants->participants.back());
}
on_participant_speaking_in_group_call(input_group_call_id, participants->participants.back());
return diff;
}
@ -1281,7 +1477,7 @@ void GroupCallManager::set_group_call_participant_is_speaking(GroupCallId group_
} else {
recursive = true;
}
UserId user_id = get_group_call_participant_by_source(input_group_call_id, source);
UserId user_id = set_group_call_participant_is_speaking_by_source(input_group_call_id, source, is_speaking, date);
if (!user_id.is_valid()) {
if (!recursive) {
auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), group_call_id, source, is_speaking,
@ -1299,7 +1495,8 @@ void GroupCallManager::set_group_call_participant_is_speaking(GroupCallId group_
td_->create_handler<GetGroupCallParticipantQuery>(std::move(query_promise))
->send(input_group_call_id, {}, {source});
} else {
LOG(INFO) << "Failed to find participant with source " << source << " in " << group_call_id;
LOG(INFO) << "Failed to find participant with source " << source << " in " << group_call_id << " from "
<< group_call->dialog_id;
promise.set_value(Unit());
}
return;
@ -1318,8 +1515,6 @@ void GroupCallManager::set_group_call_participant_is_speaking(GroupCallId group_
}
}
// TODO update participant list by speaking actions
promise.set_value(Unit());
}
@ -1327,9 +1522,22 @@ void GroupCallManager::toggle_group_call_participant_is_muted(GroupCallId group_
Promise<Unit> &&promise) {
TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id));
auto *group_call = get_group_call(input_group_call_id);
if (group_call == nullptr || !group_call->is_inited || !group_call->is_active || !group_call->is_joined) {
return promise.set_error(Status::Error(400, "GROUP_CALL_JOIN_MISSING"));
}
if (!td_->contacts_manager_->have_input_user(user_id)) {
return promise.set_error(Status::Error(400, "Have no access to the user"));
}
if (user_id != td_->contacts_manager_->get_my_id()) {
TRY_STATUS_PROMISE(promise, can_manage_group_calls(group_call->dialog_id));
} else {
if (!is_muted && !group_call->can_self_unmute) {
return promise.set_error(Status::Error(400, "Can't unmute self"));
}
}
td_->create_handler<EditGroupCallMemberQuery>(std::move(promise))->send(input_group_call_id, user_id, is_muted);
}
@ -1368,6 +1576,9 @@ void GroupCallManager::load_group_call_participants(GroupCallId group_call_id, i
}
auto *group_call = get_group_call(input_group_call_id);
CHECK(group_call != nullptr && group_call->is_inited);
if (group_call->loaded_all_participants) {
return promise.set_value(Unit());
}
string next_offset;
auto participants_it = group_call_participants_.find(input_group_call_id);
@ -1391,6 +1602,7 @@ void GroupCallManager::leave_group_call(GroupCallId group_call_id, Promise<Unit>
auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), input_group_call_id, source,
promise = std::move(promise)](Result<Unit> &&result) mutable {
if (result.is_ok()) {
// just in case
send_closure(actor_id, &GroupCallManager::on_group_call_left, input_group_call_id, source);
}
promise.set_result(std::move(result));
@ -1402,15 +1614,22 @@ void GroupCallManager::on_group_call_left(InputGroupCallId input_group_call_id,
auto *group_call = get_group_call(input_group_call_id);
CHECK(group_call != nullptr && group_call->is_inited);
if (group_call->is_joined && group_call->source == source) {
group_call->is_joined = false;
group_call->is_speaking = false;
group_call->source = 0;
send_update_group_call(group_call);
try_clear_group_call_participants(input_group_call_id);
on_group_call_left_impl(group_call);
send_update_group_call(group_call, "on_group_call_left");
}
}
void GroupCallManager::on_group_call_left_impl(GroupCall *group_call) {
CHECK(group_call != nullptr && group_call->is_inited && group_call->is_joined);
group_call->is_joined = false;
group_call->is_speaking = false;
group_call->can_self_unmute = false;
group_call->source = 0;
group_call->loaded_all_participants = false;
group_call->version = -1;
try_clear_group_call_participants(get_input_group_call_id(group_call->group_call_id).ok());
}
void GroupCallManager::discard_group_call(GroupCallId group_call_id, Promise<Unit> &&promise) {
TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id));
td_->create_handler<DiscardGroupCallQuery>(std::move(promise))->send(input_group_call_id);
@ -1427,7 +1646,7 @@ void GroupCallManager::on_update_group_call(tl_object_ptr<telegram_api::GroupCal
}
auto input_group_call_id = update_group_call(group_call_ptr, dialog_id);
if (input_group_call_id.is_valid()) {
LOG(INFO) << "Update " << input_group_call_id;
LOG(INFO) << "Update " << input_group_call_id << " from " << dialog_id;
} else {
LOG(ERROR) << "Receive invalid " << to_string(group_call_ptr);
}
@ -1448,9 +1667,10 @@ void GroupCallManager::try_clear_group_call_participants(InputGroupCallId input_
auto group_call = get_group_call(input_group_call_id);
CHECK(group_call != nullptr && group_call->is_inited);
LOG(INFO) << "Clear participants in " << input_group_call_id << " from " << group_call->dialog_id;
if (group_call->loaded_all_participants) {
group_call->loaded_all_participants = false;
send_update_group_call(group_call);
send_update_group_call(group_call, "try_clear_group_call_participants");
}
group_call->version = -1;
@ -1504,11 +1724,24 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptr<telegra
auto *group_call = add_group_call(input_group_call_id, dialog_id);
call.group_call_id = group_call->group_call_id;
call.dialog_id = dialog_id.is_valid() ? dialog_id : group_call->dialog_id;
call.can_self_unmute =
call.is_active && (!call.mute_new_participants || can_manage_group_calls(call.dialog_id).is_ok());
if (!group_call->dialog_id.is_valid()) {
group_call->dialog_id = dialog_id;
}
LOG(INFO) << "Update " << call.group_call_id << " with " << group_call->participant_count
<< " participants and version " << group_call->version;
if (!group_call->is_inited) {
*group_call = std::move(call);
if (need_group_call_participants(input_group_call_id)) {
// init version
group_call->version = call.version;
if (process_pending_group_call_participant_updates(input_group_call_id)) {
need_update = false;
}
} else {
group_call->version = -1;
}
need_update = true;
} else {
if (!group_call->is_active) {
@ -1517,9 +1750,6 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptr<telegra
// always update to an ended call, droping also is_joined and is_speaking flags
*group_call = std::move(call);
need_update = true;
if (group_call->dialog_id.is_valid()) {
td_->messages_manager_->on_update_dialog_group_call(group_call->dialog_id, false, false);
}
} else {
auto mute_flags_changed =
call.mute_new_participants != group_call->mute_new_participants ||
@ -1535,24 +1765,26 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptr<telegra
on_receive_group_call_version(input_group_call_id, call.version);
} else {
if (call.participant_count != group_call->participant_count) {
LOG(INFO) << "Set " << call.group_call_id << " participant count to " << call.participant_count;
group_call->participant_count = call.participant_count;
need_update = true;
}
if (need_group_call_participants(input_group_call_id)) {
// init version
if (need_group_call_participants(input_group_call_id) && !join_params.empty()) {
LOG(INFO) << "Init " << call.group_call_id << " version to " << call.version;
if (group_call->can_self_unmute != call.can_self_unmute) {
group_call->can_self_unmute = call.can_self_unmute;
need_update = true;
}
group_call->version = call.version;
if (process_pending_group_call_participant_updates(input_group_call_id)) {
need_update = false;
}
}
}
if (group_call->dialog_id.is_valid()) {
td_->messages_manager_->on_update_dialog_group_call(group_call->dialog_id, true,
group_call->participant_count == 0);
}
}
}
}
update_group_call_dialog(group_call, "update_group_call");
if (!group_call->is_active && group_call_recent_speakers_.erase(group_call->group_call_id) != 0) {
need_update = true;
}
@ -1560,7 +1792,7 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptr<telegra
need_update |= on_join_group_call_response(input_group_call_id, std::move(join_params));
}
if (need_update) {
send_update_group_call(group_call);
send_update_group_call(group_call, "update_group_call");
}
try_clear_group_call_participants(input_group_call_id);
return input_group_call_id;
@ -1579,6 +1811,9 @@ void GroupCallManager::on_receive_group_call_version(InputGroupCallId input_grou
if (version <= group_call->version) {
return;
}
if (group_call->syncing_participants) {
return;
}
// found a gap
auto &group_call_participants = group_call_participants_[input_group_call_id];
@ -1589,6 +1824,20 @@ void GroupCallManager::on_receive_group_call_version(InputGroupCallId input_grou
sync_participants_timeout_.add_timeout_in(group_call->group_call_id.get(), 1.0);
}
void GroupCallManager::on_participant_speaking_in_group_call(InputGroupCallId input_group_call_id,
const GroupCallParticipant &participant) {
if (participant.active_date < G()->unix_time() - RECENT_SPEAKER_TIMEOUT) {
return;
}
auto *group_call = get_group_call(input_group_call_id);
if (group_call == nullptr) {
return;
}
on_user_speaking_in_group_call(group_call->group_call_id, participant.user_id, participant.active_date, true);
}
void GroupCallManager::on_user_speaking_in_group_call(GroupCallId group_call_id, UserId user_id, int32 date,
bool recursive) {
if (G()->close_flag()) {
@ -1665,21 +1914,47 @@ void GroupCallManager::on_user_speaking_in_group_call(GroupCallId group_call_id,
on_group_call_recent_speakers_updated(group_call, recent_speakers.get());
}
void GroupCallManager::remove_recent_group_call_speaker(InputGroupCallId input_group_call_id, UserId user_id) {
auto *group_call = get_group_call(input_group_call_id);
if (group_call != nullptr && group_call->is_inited && !group_call->is_active) {
return;
}
auto recent_speakers_it = group_call_recent_speakers_.find(group_call->group_call_id);
if (recent_speakers_it == group_call_recent_speakers_.end()) {
return;
}
auto &recent_speakers = recent_speakers_it->second;
CHECK(recent_speakers != nullptr);
for (size_t i = 0; i < recent_speakers->users.size(); i++) {
if (recent_speakers->users[i].first == user_id) {
LOG(INFO) << "Remove " << user_id << " from recent speakers in " << input_group_call_id << " from "
<< group_call->dialog_id;
recent_speakers->users.erase(recent_speakers->users.begin() + i);
on_group_call_recent_speakers_updated(group_call, recent_speakers.get());
return;
}
}
}
void GroupCallManager::on_group_call_recent_speakers_updated(const GroupCall *group_call,
GroupCallRecentSpeakers *recent_speakers) {
if (group_call == nullptr || !group_call->is_inited || recent_speakers->is_changed) {
LOG(INFO) << "Don't need to send update of recent speakers in " << group_call->group_call_id;
LOG(INFO) << "Don't need to send update of recent speakers in " << group_call->group_call_id << " from "
<< group_call->dialog_id;
return;
}
recent_speakers->is_changed = true;
LOG(INFO) << "Schedule update of recent speakers in " << group_call->group_call_id;
LOG(INFO) << "Schedule update of recent speakers in " << group_call->group_call_id << " from "
<< group_call->dialog_id;
const double MAX_RECENT_SPEAKER_UPDATE_DELAY = 0.5;
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(InputGroupCallId input_group_call_id, int32 source) {
UserId GroupCallManager::set_group_call_participant_is_speaking_by_source(InputGroupCallId input_group_call_id,
int32 source, bool is_speaking, int32 date) {
auto participants_it = group_call_participants_.find(input_group_call_id);
if (participants_it == group_call_participants_.end()) {
return UserId();
@ -1687,12 +1962,35 @@ UserId GroupCallManager::get_group_call_participant_by_source(InputGroupCallId i
for (auto &participant : participants_it->second->participants) {
if (participant.source == source) {
if (participant.is_speaking != is_speaking) {
participant.is_speaking = is_speaking;
if (is_speaking) {
participant.local_active_date = max(participant.local_active_date, date);
}
auto real_order = participant.get_real_order();
if (real_order >= participants_it->second->min_order) {
participant.order = real_order;
}
if (participant.order != 0) {
send_update_group_call_participant(input_group_call_id, participant);
}
}
return participant.user_id;
}
}
return UserId();
}
void GroupCallManager::update_group_call_dialog(const GroupCall *group_call, const char *source) {
if (!group_call->dialog_id.is_valid()) {
return;
}
td_->messages_manager_->on_update_dialog_group_call(group_call->dialog_id, group_call->is_active,
group_call->participant_count == 0, source);
}
vector<int32> GroupCallManager::get_recent_speaker_user_ids(const GroupCall *group_call, bool for_update) {
CHECK(group_call != nullptr && group_call->is_inited);
@ -1704,7 +2002,8 @@ vector<int32> GroupCallManager::get_recent_speaker_user_ids(const GroupCall *gro
auto *recent_speakers = recent_speakers_it->second.get();
CHECK(recent_speakers != nullptr);
LOG(INFO) << "Found " << recent_speakers->users.size() << " recent speakers in " << group_call->group_call_id;
LOG(INFO) << "Found " << recent_speakers->users.size() << " recent speakers in " << group_call->group_call_id
<< " from " << group_call->dialog_id;
while (!recent_speakers->users.empty() &&
recent_speakers->users.back().second < G()->unix_time() - RECENT_SPEAKER_TIMEOUT) {
recent_speakers->users.pop_back();
@ -1728,7 +2027,7 @@ vector<int32> GroupCallManager::get_recent_speaker_user_ids(const GroupCall *gro
if (!for_update) {
// the change must be received through update first
send_update_group_call(group_call);
send_update_group_call(group_call, "get_recent_speaker_user_ids");
}
}
return recent_speaker_user_ids;
@ -1740,9 +2039,9 @@ tl_object_ptr<td_api::groupCall> GroupCallManager::get_group_call_object(const G
CHECK(group_call->is_inited);
return td_api::make_object<td_api::groupCall>(
group_call->group_call_id.get(), group_call->is_active, group_call->is_joined, group_call->participant_count,
group_call->loaded_all_participants, std::move(recent_speaker_user_ids), group_call->mute_new_participants,
group_call->allowed_change_mute_new_participants, group_call->duration);
group_call->group_call_id.get(), group_call->is_active, group_call->is_joined, group_call->can_self_unmute,
group_call->participant_count, group_call->loaded_all_participants, std::move(recent_speaker_user_ids),
group_call->mute_new_participants, group_call->allowed_change_mute_new_participants, group_call->duration);
}
tl_object_ptr<td_api::updateGroupCall> GroupCallManager::get_update_group_call_object(
@ -1757,7 +2056,8 @@ tl_object_ptr<td_api::updateGroupCallParticipant> GroupCallManager::get_update_g
group_call_id.get(), participant.get_group_call_participant_object(td_->contacts_manager_.get()));
}
void GroupCallManager::send_update_group_call(const GroupCall *group_call) {
void GroupCallManager::send_update_group_call(const GroupCall *group_call, const char *source) {
LOG(INFO) << "Send update about " << group_call->group_call_id << " from " << source;
send_closure(G()->td(), &Td::send_update,
get_update_group_call_object(group_call, get_recent_speaker_user_ids(group_call, true)));
}

View File

@ -41,6 +41,9 @@ class GroupCallManager : public Actor {
void get_group_call(GroupCallId group_call_id, Promise<td_api::object_ptr<td_api::groupCall>> &&promise);
void reload_group_call(InputGroupCallId input_group_call_id,
Promise<td_api::object_ptr<td_api::groupCall>> &&promise);
void join_group_call(GroupCallId group_call_id, td_api::object_ptr<td_api::groupCallPayload> &&payload, int32 source,
bool is_muted, Promise<td_api::object_ptr<td_api::groupCallJoinResponse>> &&promise);
@ -109,10 +112,9 @@ class GroupCallManager : public Actor {
const GroupCall *get_group_call(InputGroupCallId input_group_call_id) const;
GroupCall *get_group_call(InputGroupCallId input_group_call_id);
void on_voice_chat_created(DialogId dialog_id, InputGroupCallId input_group_call_id, Promise<GroupCallId> &&promise);
Status can_manage_group_calls(DialogId dialog_id);
void reload_group_call(InputGroupCallId input_group_call_id,
Promise<td_api::object_ptr<td_api::groupCall>> &&promise);
void on_voice_chat_created(DialogId dialog_id, InputGroupCallId input_group_call_id, Promise<GroupCallId> &&promise);
void finish_get_group_call(InputGroupCallId input_group_call_id,
Result<tl_object_ptr<telegram_api::phone_groupCall>> &&result);
@ -123,12 +125,11 @@ class GroupCallManager : public Actor {
void sync_group_call_participants(InputGroupCallId input_group_call_id);
void on_sync_group_call_participants_failed(InputGroupCallId input_group_call_id);
void process_group_call_participants(InputGroupCallId group_call_id,
vector<tl_object_ptr<telegram_api::groupCallParticipant>> &&participants,
bool is_load);
int32 process_group_call_participants_from_updates(
InputGroupCallId group_call_id, vector<tl_object_ptr<telegram_api::groupCallParticipant>> &&participants);
bool is_load, bool is_sync);
int process_group_call_participant(InputGroupCallId group_call_id, GroupCallParticipant &&participant);
@ -138,19 +139,29 @@ class GroupCallManager : public Actor {
void on_group_call_left(InputGroupCallId input_group_call_id, int32 source);
void on_group_call_left_impl(GroupCall *group_call);
InputGroupCallId update_group_call(const tl_object_ptr<telegram_api::GroupCall> &group_call_ptr, DialogId dialog_id);
void on_receive_group_call_version(InputGroupCallId input_group_call_id, int32 version);
void on_participant_speaking_in_group_call(InputGroupCallId input_group_call_id,
const GroupCallParticipant &participant);
void remove_recent_group_call_speaker(InputGroupCallId input_group_call_id, UserId user_id);
void on_group_call_recent_speakers_updated(const GroupCall *group_call, GroupCallRecentSpeakers *recent_speakers);
UserId get_group_call_participant_by_source(InputGroupCallId input_group_call_id, int32 source);
UserId set_group_call_participant_is_speaking_by_source(InputGroupCallId input_group_call_id, int32 source,
bool is_speaking, int32 date);
static Result<td_api::object_ptr<td_api::groupCallJoinResponse>> get_group_call_join_response_object(
string json_response);
void try_clear_group_call_participants(InputGroupCallId input_group_call_id);
void update_group_call_dialog(const GroupCall *group_call, const char *source);
vector<int32> get_recent_speaker_user_ids(const GroupCall *group_call, bool for_update);
tl_object_ptr<td_api::updateGroupCall> get_update_group_call_object(const GroupCall *group_call,
@ -162,7 +173,7 @@ class GroupCallManager : public Actor {
tl_object_ptr<td_api::updateGroupCallParticipant> 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(const GroupCall *group_call, const char *source);
void send_update_group_call_participant(GroupCallId group_call_id, const GroupCallParticipant &participant);

View File

@ -8110,6 +8110,7 @@ void MessagesManager::remove_dialog_action_bar(DialogId dialog_id, Promise<Unit>
void MessagesManager::repair_dialog_active_group_call_id(DialogId dialog_id) {
if (have_input_peer(dialog_id, AccessRights::Read)) {
LOG(INFO) << "Repair active voice chat ID in " << dialog_id;
create_actor<SleepActor>("RepairChatActiveVoiceChatId", 1.0,
PromiseCreator::lambda([actor_id = actor_id(this), dialog_id](Result<Unit> result) {
send_closure(actor_id, &MessagesManager::do_repair_dialog_active_group_call_id,
@ -10817,6 +10818,22 @@ void MessagesManager::delete_dialog(DialogId dialog_id) {
close_dialog(d);
}
void MessagesManager::reload_dialog_group_call(DialogId dialog_id) {
if (td_->auth_manager_->is_bot()) {
return;
}
auto d = get_dialog(dialog_id);
if (d == nullptr) {
// nothing to do
return;
}
if (d->active_group_call_id.is_valid()) {
td_->group_call_manager_->reload_group_call(d->active_group_call_id, Auto());
}
}
void MessagesManager::read_all_dialog_mentions(DialogId dialog_id, Promise<Unit> &&promise) {
bool is_bot = td_->auth_manager_->is_bot();
if (is_bot) {
@ -29293,14 +29310,18 @@ void MessagesManager::do_set_dialog_folder_id(Dialog *d, FolderId folder_id) {
}
void MessagesManager::on_update_dialog_group_call(DialogId dialog_id, bool has_active_group_call,
bool is_group_call_empty) {
bool is_group_call_empty, const char *source) {
if (td_->auth_manager_->is_bot()) {
return;
}
LOG(INFO) << "Update voice chat in " << dialog_id << " with has_active_voice_chat = " << has_active_group_call
<< " and is_voice_chat_empty = " << is_group_call_empty << " from " << source;
CHECK(dialog_id.is_valid());
Dialog *d = get_dialog(dialog_id); // must not create the Dialog, because is called from on_get_chat
if (d == nullptr) {
LOG(INFO) << "Can't find " << dialog_id;
pending_dialog_group_call_updates_[dialog_id] = {has_active_group_call, is_group_call_empty};
return;
}
@ -29309,6 +29330,7 @@ void MessagesManager::on_update_dialog_group_call(DialogId dialog_id, bool has_a
is_group_call_empty = false;
}
if (d->has_active_group_call == has_active_group_call && d->is_group_call_empty == is_group_call_empty) {
LOG(INFO) << "Nothing changed in " << dialog_id;
return;
}

View File

@ -337,7 +337,8 @@ class MessagesManager : public Actor {
void on_update_dialog_folder_id(DialogId dialog_id, FolderId folder_id);
void on_update_dialog_group_call(DialogId dialog_id, bool has_active_group_call, bool is_group_call_empty);
void on_update_dialog_group_call(DialogId dialog_id, bool has_active_group_call, bool is_group_call_empty,
const char *source);
void on_update_dialog_group_call_id(DialogId dialog_id, InputGroupCallId input_group_call_id);
@ -400,6 +401,8 @@ class MessagesManager : public Actor {
void delete_dialog(DialogId dialog_id);
void reload_dialog_group_call(DialogId dialog_id);
void read_all_dialog_mentions(DialogId dialog_id, Promise<Unit> &&promise);
Status add_recently_found_dialog(DialogId dialog_id) TD_WARN_UNUSED_RESULT;

View File

@ -1500,6 +1500,10 @@ vector<td_api::object_ptr<td_api::closedVectorPath>> StickersManager::get_sticke
make_point(last_end_control_point_x, last_end_control_point_y), make_point(x, y)));
break;
}
case 'm':
case 'M':
pos--;
// falltrough
case 'z':
case 'Z':
if (x != start_x || y != start_y) {

View File

@ -3233,17 +3233,17 @@ void Td::on_get_promo_data(Result<telegram_api::object_ptr<telegram_api::help_Pr
auto promo_data_ptr = r_promo_data.move_as_ok();
CHECK(promo_data_ptr != nullptr);
LOG(DEBUG) << "Receive " << to_string(promo_data_ptr);
int32 expires = 0;
int32 expires_at = 0;
switch (promo_data_ptr->get_id()) {
case telegram_api::help_promoDataEmpty::ID: {
auto promo = telegram_api::move_object_as<telegram_api::help_promoDataEmpty>(promo_data_ptr);
expires = promo->expires_;
expires_at = promo->expires_;
messages_manager_->remove_sponsored_dialog();
break;
}
case telegram_api::help_promoData::ID: {
auto promo = telegram_api::move_object_as<telegram_api::help_promoData>(promo_data_ptr);
expires = promo->expires_;
expires_at = promo->expires_;
bool is_proxy = (promo->flags_ & telegram_api::help_promoData::PROXY_MASK) != 0;
messages_manager_->on_get_sponsored_dialog(
std::move(promo->peer_),
@ -3255,23 +3255,11 @@ void Td::on_get_promo_data(Result<telegram_api::object_ptr<telegram_api::help_Pr
default:
UNREACHABLE();
}
if (expires != 0) {
expires -= G()->unix_time();
}
schedule_get_promo_data(expires);
schedule_get_promo_data(expires_at == 0 ? 0 : expires_at - G()->unix_time());
}
void Td::schedule_get_promo_data(int32 expires_in) {
if (expires_in < 0) {
LOG(ERROR) << "Receive wrong expires_in: " << expires_in;
expires_in = 0;
}
if (expires_in != 0 && expires_in < 60) {
expires_in = 60;
}
if (expires_in > 86400) {
expires_in = 86400;
}
expires_in = clamp(expires_in, 60, 86400);
if (!close_flag_ && auth_manager_->is_authorized() && !auth_manager_->is_bot()) {
LOG(INFO) << "Schedule getPromoData in " << expires_in;
alarm_timeout_.set_timeout_in(PROMO_DATA_ALARM_ID, expires_in);

View File

@ -472,7 +472,7 @@ class SslStreamImpl {
return 0;
}
}
/* fall through */
/* fallthrough */
default:
LOG(DEBUG) << "SSL_ERROR Default";
return create_openssl_error(1, "SSL error ");