From 23f1927c07c04f27a7e4f5dcd31a03fbb337322b Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 4 Dec 2020 17:06:37 +0300 Subject: [PATCH] Improve sending speaking in voice chat typings. --- td/generate/scheme/td_api.tl | 4 ++ td/generate/scheme/td_api.tlo | Bin 189428 -> 189608 bytes td/telegram/DialogAction.cpp | 18 ++++---- td/telegram/DialogAction.h | 4 +- td/telegram/GroupCallManager.cpp | 69 +++++++++++++++++++++++++++++-- td/telegram/GroupCallManager.h | 10 +++++ td/telegram/Td.cpp | 16 ++++--- td/telegram/Td.h | 2 + td/telegram/cli.cpp | 8 ++++ 9 files changed, 110 insertions(+), 21 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 5096b0444..d53228e2b 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4344,6 +4344,10 @@ toggleGroupCallMuteNewMembers group_call_id:int32 mute_new_members:Bool = Ok; //@group_call_id Group call identifier @user_ids User identifiers inviteGroupCallMembers group_call_id:int32 user_ids:vector = Ok; +//@description Informs TDLib that a group call member speaking state has changed @group_call_id Group call identifier +//@source Group call member's synchronization source identifier @is_speaking True, if the user is speaking +setGroupCallMemberIsSpeaking group_call_id:int32 source:int32 is_speaking:Bool = Ok; + //@description Toggles whether a group call member is muted. Requires can_manage_calls rights to mute other group call members //@group_call_id Group call identifier @user_id User identifier @is_muted Pass true if the user must be muted and false otherwise toggleGroupCallMemberIsMuted group_call_id:int32 user_id:int32 is_muted:Bool = Ok; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 82d99404622e3559cea68e876a23574dafe4a054..f0041a91045c083d4ad372680ae42a38680bae64 100644 GIT binary patch delta 94 zcmexzoO{Jd?uHh|Ele#kj1#uE$}knLRAl-QASF|rTH;=mUs~Xtn3Ln1nwykbSsY&slAhkE#iYOe$5Ey~vH;6|B>MmW delta 27 jcmZ2+lKabX?uHh|Ele#kjQ!hNWtfUrZeMeZ>5nV`sA>!7 diff --git a/td/telegram/DialogAction.cpp b/td/telegram/DialogAction.cpp index 9da36b9a3..c39065ad2 100644 --- a/td/telegram/DialogAction.cpp +++ b/td/telegram/DialogAction.cpp @@ -81,7 +81,7 @@ DialogAction::DialogAction(tl_object_ptr &&action) { break; } case td_api::chatActionSpeakingInCall::ID: - init(Type::SpeakingInCall); + init(Type::SpeakingInVoiceChat); break; default: UNREACHABLE(); @@ -141,7 +141,7 @@ DialogAction::DialogAction(tl_object_ptr &&acti break; } case telegram_api::speakingInGroupCallAction::ID: - init(Type::SpeakingInCall); + init(Type::SpeakingInVoiceChat); break; default: UNREACHABLE(); @@ -177,7 +177,7 @@ tl_object_ptr DialogAction::get_input_send_mess return make_tl_object(); case Type::UploadingVideoNote: return make_tl_object(progress_); - case Type::SpeakingInCall: + case Type::SpeakingInVoiceChat: return make_tl_object(); default: UNREACHABLE(); @@ -213,7 +213,7 @@ tl_object_ptr DialogAction::get_secret_input_send return make_tl_object(); case Type::UploadingVideoNote: return make_tl_object(); - case Type::SpeakingInCall: + case Type::SpeakingInVoiceChat: return make_tl_object(); default: UNREACHABLE(); @@ -249,7 +249,7 @@ tl_object_ptr DialogAction::get_chat_action_object() const { return td_api::make_object(); case Type::UploadingVideoNote: return td_api::make_object(progress_); - case Type::SpeakingInCall: + case Type::SpeakingInVoiceChat: return td_api::make_object(); default: UNREACHABLE(); @@ -350,6 +350,10 @@ DialogAction DialogAction::get_typing_action() { return DialogAction(Type::Typing, 0); } +DialogAction DialogAction::get_speaking_action() { + return DialogAction(Type::SpeakingInVoiceChat, 0); +} + StringBuilder &operator<<(StringBuilder &string_builder, const DialogAction &action) { string_builder << "ChatAction"; const char *type = [action_type = action.type_] { @@ -380,8 +384,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DialogAction &act return "RecordingVideoNote"; case DialogAction::Type::UploadingVideoNote: return "UploadingVideoNote"; - case DialogAction::Type::SpeakingInCall: - return "SpeakingInCall"; + case DialogAction::Type::SpeakingInVoiceChat: + return "SpeakingInVoiceChat"; default: UNREACHABLE(); return "Cancel"; diff --git a/td/telegram/DialogAction.h b/td/telegram/DialogAction.h index fc8cc9888..db740dd79 100644 --- a/td/telegram/DialogAction.h +++ b/td/telegram/DialogAction.h @@ -31,7 +31,7 @@ class DialogAction { StartPlayingGame, RecordingVideoNote, UploadingVideoNote, - SpeakingInCall + SpeakingInVoiceChat }; Type type_ = Type::Cancel; int32 progress_ = 0; @@ -61,6 +61,8 @@ class DialogAction { static DialogAction get_typing_action(); + static DialogAction get_speaking_action(); + friend bool operator==(const DialogAction &lhs, const DialogAction &rhs) { return lhs.type_ == rhs.type_ && lhs.progress_ == rhs.progress_; } diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index 66c6fcb97..421376155 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -9,6 +9,7 @@ #include "td/telegram/AuthManager.h" #include "td/telegram/ContactsManager.h" #include "td/telegram/Global.h" +#include "td/telegram/MessagesManager.h" #include "td/telegram/misc.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/Td.h" @@ -259,7 +260,7 @@ class CheckGroupCallQuery : public Td::ResultHandler { if (success) { promise_.set_value(Unit()); } else { - promise_.set_error(Status::Error(200, "Group call left")); + promise_.set_error(Status::Error(400, "GROUP_CALL_JOIN_MISSING")); } } @@ -336,6 +337,7 @@ struct GroupCallManager::GroupCall { bool is_inited = false; bool is_active = false; bool is_joined = false; + bool is_speaking = false; bool mute_new_members = false; bool allowed_change_mute_new_members = false; int32 member_count = 0; @@ -352,6 +354,8 @@ struct GroupCallManager::PendingJoinRequest { }; GroupCallManager::GroupCallManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { + pending_send_speaking_action_timeout_.set_callback(on_pending_send_speaking_action_timeout_callback); + pending_send_speaking_action_timeout_.set_callback_data(static_cast(this)); } GroupCallManager::~GroupCallManager() = default; @@ -360,8 +364,36 @@ void GroupCallManager::tear_down() { parent_.reset(); } +void GroupCallManager::on_pending_send_speaking_action_timeout_callback(void *group_call_manager_ptr, + int64 group_call_id_int) { + if (G()->close_flag()) { + return; + } + + auto group_call_manager = static_cast(group_call_manager_ptr); + send_closure_later(group_call_manager->actor_id(group_call_manager), + &GroupCallManager::on_send_speaking_action_timeout, + GroupCallId(narrow_cast(group_call_id_int))); +} + +void GroupCallManager::on_send_speaking_action_timeout(GroupCallId group_call_id) { + LOG(INFO) << "Receive send_speaking_action timeout in " << group_call_id; + auto input_group_call_id = get_input_group_call_id(group_call_id).move_as_ok(); + + auto *group_call = get_group_call(input_group_call_id); + CHECK(group_call != nullptr && group_call->is_inited && group_call->channel_id.is_valid()); + if (!group_call->is_joined || !group_call->is_speaking) { + return; + } + + pending_send_speaking_action_timeout_.add_timeout_in(group_call_id.get(), 4.0); + + td_->messages_manager_->send_dialog_action(DialogId(group_call->channel_id), MessageId(), + DialogAction::get_speaking_action(), Promise()); +} + GroupCallId GroupCallManager::get_group_call_id(InputGroupCallId input_group_call_id, ChannelId channel_id) { - if (td_->auth_manager_->is_bot()) { + if (td_->auth_manager_->is_bot() || !input_group_call_id.is_valid()) { return GroupCallId(); } return add_group_call(input_group_call_id, channel_id)->group_call_id; @@ -375,7 +407,9 @@ Result GroupCallManager::get_input_group_call_id(GroupCallId g return Status::Error(400, "Wrong group call identifier specified"); } CHECK(static_cast(group_call_id.get()) <= input_group_call_ids_.size()); - return input_group_call_ids_[group_call_id.get() - 1]; + auto input_group_call_id = input_group_call_ids_[group_call_id.get() - 1]; + LOG(DEBUG) << "Found " << input_group_call_id; + return input_group_call_id; } GroupCallId GroupCallManager::get_next_group_call_id(InputGroupCallId input_group_call_id) { @@ -391,6 +425,7 @@ GroupCallManager::GroupCall *GroupCallManager::add_group_call(InputGroupCallId i if (group_call == nullptr) { group_call = make_unique(); group_call->group_call_id = get_next_group_call_id(input_group_call_id); + LOG(INFO) << "Add " << input_group_call_id << " from " << channel_id << " as " << group_call->group_call_id; } if (!group_call->channel_id.is_valid()) { group_call->channel_id = channel_id; @@ -693,6 +728,31 @@ void GroupCallManager::invite_group_call_members(GroupCallId group_call_id, vect td_->create_handler(std::move(promise))->send(input_group_call_id, std::move(input_users)); } +void GroupCallManager::set_group_call_member_is_speaking(GroupCallId group_call_id, int32 source, bool is_speaking, + Promise &&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_value(Unit()); + } + if (group_call->source == source) { + if (!group_call->channel_id.is_valid()) { + return promise.set_value(Unit()); + } + if (group_call->is_speaking != is_speaking) { + group_call->is_speaking = is_speaking; + if (is_speaking) { + pending_send_speaking_action_timeout_.add_timeout_in(group_call_id.get(), 0.0); + } + } + return promise.set_value(Unit()); + } + + // TODO process others speaking actions + promise.set_value(Unit()); +} + void GroupCallManager::toggle_group_call_member_is_muted(GroupCallId group_call_id, UserId user_id, bool is_muted, Promise &&promise) { TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id)); @@ -746,6 +806,7 @@ void GroupCallManager::on_group_call_left(InputGroupCallId 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; } } @@ -821,7 +882,7 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptris_active) { // never update ended calls } else if (!call.is_active) { - // always update to an ended call, droping also is_joined flag + // always update to an ended call, droping also is_joined and is_speaking flags *group_call = std::move(call); need_update = true; } else { diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index 84e301cee..9be0c8537 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -15,6 +15,7 @@ #include "td/actor/actor.h" #include "td/actor/PromiseFuture.h" +#include "td/actor/Timeout.h" #include "td/utils/Status.h" @@ -46,6 +47,9 @@ class GroupCallManager : public Actor { void invite_group_call_members(GroupCallId group_call_id, vector &&user_ids, Promise &&promise); + void set_group_call_member_is_speaking(GroupCallId group_call_id, int32 source, bool is_speaking, + Promise &&promise); + void toggle_group_call_member_is_muted(GroupCallId group_call_id, UserId user_id, bool is_muted, Promise &&promise); @@ -66,6 +70,10 @@ class GroupCallManager : public Actor { void tear_down() override; + static void on_pending_send_speaking_action_timeout_callback(void *group_call_manager_ptr, int64 group_call_id_int); + + void on_send_speaking_action_timeout(GroupCallId group_call_id); + Result get_input_group_call_id(GroupCallId group_call_id); GroupCallId get_next_group_call_id(InputGroupCallId input_group_call_id); @@ -111,6 +119,8 @@ class GroupCallManager : public Actor { std::unordered_map, InputGroupCallIdHash> pending_join_requests_; uint64 join_group_request_generation_ = 0; + + MultiTimeout pending_send_speaking_action_timeout_{"PendingSendSpeakingActionTimeout"}; }; } // namespace td diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 98c9cf7cf..740dc2b02 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6037,21 +6037,18 @@ void Td::on_request(uint64 id, const td_api::createVoiceChat &request) { promise.set_value(td_api::make_object(result.ok().get())); } }); - contacts_manager_->create_channel_voice_chat(DialogId(request.chat_id_), std::move(query_promise)); } void Td::on_request(uint64 id, const td_api::getGroupCall &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); - group_call_manager_->get_group_call(GroupCallId(request.group_call_id_), std::move(promise)); } void Td::on_request(uint64 id, td_api::joinGroupCall &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); - group_call_manager_->join_group_call(GroupCallId(request.group_call_id_), std::move(request.payload_), request.source_, request.is_muted_, std::move(promise)); } @@ -6059,7 +6056,6 @@ void Td::on_request(uint64 id, td_api::joinGroupCall &request) { void Td::on_request(uint64 id, const td_api::toggleGroupCallMuteNewMembers &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - group_call_manager_->toggle_group_call_mute_new_members(GroupCallId(request.group_call_id_), request.mute_new_members_, std::move(promise)); } @@ -6067,7 +6063,6 @@ void Td::on_request(uint64 id, const td_api::toggleGroupCallMuteNewMembers &requ void Td::on_request(uint64 id, const td_api::inviteGroupCallMembers &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - vector user_ids; for (auto &user_id : request.user_ids_) { user_ids.emplace_back(user_id); @@ -6076,10 +6071,16 @@ void Td::on_request(uint64 id, const td_api::inviteGroupCallMembers &request) { std::move(promise)); } +void Td::on_request(uint64 id, const td_api::setGroupCallMemberIsSpeaking &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + group_call_manager_->set_group_call_member_is_speaking(GroupCallId(request.group_call_id_), request.source_, + request.is_speaking_, std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::toggleGroupCallMemberIsMuted &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - group_call_manager_->toggle_group_call_member_is_muted(GroupCallId(request.group_call_id_), UserId(request.user_id_), request.is_muted_, std::move(promise)); } @@ -6087,21 +6088,18 @@ void Td::on_request(uint64 id, const td_api::toggleGroupCallMemberIsMuted &reque void Td::on_request(uint64 id, const td_api::checkGroupCallIsJoined &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - group_call_manager_->check_group_call_is_joined(GroupCallId(request.group_call_id_), std::move(promise)); } void Td::on_request(uint64 id, const td_api::leaveGroupCall &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - group_call_manager_->leave_group_call(GroupCallId(request.group_call_id_), std::move(promise)); } void Td::on_request(uint64 id, const td_api::discardGroupCall &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - group_call_manager_->discard_group_call(GroupCallId(request.group_call_id_), std::move(promise)); } diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 4052064af..0934775a6 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -702,6 +702,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::inviteGroupCallMembers &request); + void on_request(uint64 id, const td_api::setGroupCallMemberIsSpeaking &request); + void on_request(uint64 id, const td_api::toggleGroupCallMemberIsMuted &request); void on_request(uint64 id, const td_api::checkGroupCallIsJoined &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index e2f1604ab..14d0c5d06 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2853,6 +2853,14 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_group_call_id(args), nullptr, 123, true)); } else if (op == "tgcmnm" || op == "tgcmnme") { send_request(td_api::make_object(as_group_call_id(args), op == "tgcmnme")); + } else if (op == "sgcmis") { + string group_call_id; + string source; + string is_speaking; + std::tie(group_call_id, args) = split(args); + std::tie(source, is_speaking) = split(args); + send_request(td_api::make_object( + as_group_call_id(group_call_id), to_integer(source), as_bool(is_speaking))); } else if (op == "igcm") { string group_call_id; string user_ids;