diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 8dd8a621c..8526cbc5b 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4297,6 +4297,12 @@ sendCallDebugInformation call_id:int32 debug_information:string = Ok; //@description Creates a group call in a chat. Available only for supergroups; requires can_manage_calls rights @chat_id Chat identifier createChatGroupCall chat_id:int53 = GroupCallId; +//@description Leaves a group call @group_call_id Group call identifier @source Caller source identifier +leaveGroupCall group_call_id:string source:int32 = Ok; + +//@description Discards a group call. Requires can_manage_calls rights in the corresponding chat @group_call_id Group call identifier +discardGroupCall group_call_id:string = Ok; + //@description Changes the block state of a message sender. Currently, only users and supergroup chats can be blocked @sender Message Sender @is_blocked New value of is_blocked toggleMessageSenderIsBlocked sender:MessageSender is_blocked:Bool = Ok; diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp new file mode 100644 index 000000000..47b1c92d9 --- /dev/null +++ b/td/telegram/GroupCallManager.cpp @@ -0,0 +1,229 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/GroupCallManager.h" + +#include "td/telegram/ContactsManager.h" +#include "td/telegram/Global.h" +#include "td/telegram/Td.h" +#include "td/telegram/UpdatesManager.h" + +#include "td/utils/Random.h" + +namespace td { + +class CreateGroupCallQuery : public Td::ResultHandler { + Promise promise_; + ChannelId channel_id_; + + public: + explicit CreateGroupCallQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id) { + channel_id_ = channel_id; + + auto input_channel = td->contacts_manager_->get_input_channel(channel_id); + CHECK(input_channel != nullptr); + + send_query(G()->net_query_creator().create( + telegram_api::phone_createGroupCall(std::move(input_channel), Random::secure_int32()))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for CreateGroupCallQuery: " << to_string(ptr); + + auto group_call_ids = td->updates_manager_->get_update_new_group_call_ids(ptr.get()); + if (group_call_ids.size() != 1) { + LOG(ERROR) << "Receive wrong CreateGroupCallQuery response " << to_string(ptr); + return on_error(id, Status::Error(500, "Receive wrong response")); + } + + td->updates_manager_->on_get_updates(std::move(ptr)); + + // TODO set promise after updates are processed + promise_.set_value(std::move(group_call_ids[0])); + } + + void on_error(uint64 id, Status status) override { + td->contacts_manager_->on_get_channel_error(channel_id_, status, "CreateGroupCallQuery"); + promise_.set_error(std::move(status)); + } +}; + +class LeaveGroupCallQuery : public Td::ResultHandler { + Promise promise_; + + public: + explicit LeaveGroupCallQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(InputGroupCallId group_call_id, int32 source) { + send_query(G()->net_query_creator().create( + telegram_api::phone_leaveGroupCall(group_call_id.get_input_group_call(), source))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for LeaveGroupCallQuery: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr)); + + // TODO set promise after updates are processed + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + +class DiscardGroupCallQuery : public Td::ResultHandler { + Promise promise_; + + public: + explicit DiscardGroupCallQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(InputGroupCallId group_call_id) { + send_query( + G()->net_query_creator().create(telegram_api::phone_discardGroupCall(group_call_id.get_input_group_call()))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for DiscardGroupCallQuery: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr)); + + // TODO set promise after updates are processed + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + +struct GroupCallManager::GroupCall { + bool is_active = false; + int32 member_count = 0; + int32 version = -1; + int32 duration = 0; +}; + +GroupCallManager::GroupCallManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { +} + +void GroupCallManager::tear_down() { + parent_.reset(); +} + +void GroupCallManager::create_group_call(ChannelId channel_id, Promise &&promise) { + td_->create_handler(std::move(promise))->send(channel_id); +} + +void GroupCallManager::leave_group_call(InputGroupCallId group_call_id, int32 source, Promise &&promise) { + td_->create_handler(std::move(promise))->send(group_call_id, source); +} + +void GroupCallManager::discard_group_call(InputGroupCallId group_call_id, Promise &&promise) { + td_->create_handler(std::move(promise))->send(group_call_id); +} + +void GroupCallManager::on_update_group_call(tl_object_ptr group_call_ptr) { + auto call_id = update_group_call(group_call_ptr); + if (call_id.is_valid()) { + LOG(INFO) << "Update " << call_id; + } else { + LOG(ERROR) << "Receive invalid " << to_string(group_call_ptr); + } +} + +InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptr &group_call_ptr) { + if (group_call_ptr == nullptr) { + return {}; + } + + InputGroupCallId call_id; + GroupCall call; + switch (group_call_ptr->get_id()) { + case telegram_api::groupCall::ID: { + auto group_call = static_cast(group_call_ptr.get()); + call_id = InputGroupCallId(group_call->id_, group_call->access_hash_); + call.is_active = true; + call.member_count = group_call->participants_count_; + call.version = group_call->version_; + break; + } + case telegram_api::groupCallDiscarded::ID: { + auto group_call = static_cast(group_call_ptr.get()); + call_id = InputGroupCallId(group_call->id_, group_call->access_hash_); + call.duration = group_call->duration_; + break; + } + default: + UNREACHABLE(); + } + if (!call_id.is_valid() || call.member_count < 0) { + return {}; + } + + auto &group_call = group_calls_[call_id]; + bool need_update = false; + if (group_call == nullptr) { + group_call = make_unique(); + *group_call = std::move(call); + need_update = true; + } else { + if (!group_call->is_active) { + // never update ended calls + } else if (!call.is_active) { + // always update to an ended call + *group_call = std::move(call); + need_update = true; + } else { + if (call.version > group_call->version) { + need_update = call.member_count != group_call->member_count; + *group_call = std::move(call); + } + } + } + + if (need_update) { + send_closure(G()->td(), &Td::send_update, get_update_group_call_object(call_id, group_call.get())); + } + + return call_id; +} + +tl_object_ptr GroupCallManager::get_group_call_object(InputGroupCallId group_call_id, + const GroupCall *group_call) { + CHECK(group_call != nullptr); + return td_api::make_object(group_call_id.get_group_call_id(), group_call->is_active, + group_call->member_count, group_call->duration); +} + +tl_object_ptr GroupCallManager::get_update_group_call_object(InputGroupCallId group_call_id, + const GroupCall *group_call) { + return td_api::make_object(get_group_call_object(group_call_id, group_call)); +} + +} // namespace td diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h new file mode 100644 index 000000000..871306f99 --- /dev/null +++ b/td/telegram/GroupCallManager.h @@ -0,0 +1,54 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/ChannelId.h" +#include "td/telegram/InputGroupCallId.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" + +#include "td/actor/actor.h" +#include "td/actor/PromiseFuture.h" + +#include + +namespace td { + +class Td; + +class GroupCallManager : public Actor { + public: + GroupCallManager(Td *td, ActorShared<> parent); + + void create_group_call(ChannelId channel_id, Promise &&promise); + + void leave_group_call(InputGroupCallId group_call_id, int32 source, Promise &&promise); + + void discard_group_call(InputGroupCallId group_call_id, Promise &&promise); + + void on_update_group_call(tl_object_ptr group_call_ptr); + + private: + struct GroupCall; + + void tear_down() override; + + InputGroupCallId update_group_call(const tl_object_ptr &group_call_ptr); + + static tl_object_ptr get_update_group_call_object(InputGroupCallId group_call_id, + const GroupCall *group_call); + + static tl_object_ptr get_group_call_object(InputGroupCallId group_call_id, + const GroupCall *group_call); + + Td *td_; + ActorShared<> parent_; + + std::unordered_map, InputGroupCallIdHash> group_calls_; +}; + +} // namespace td diff --git a/td/telegram/InputGroupCallId.cpp b/td/telegram/InputGroupCallId.cpp index 296dc3d89..1bc29c7d0 100644 --- a/td/telegram/InputGroupCallId.cpp +++ b/td/telegram/InputGroupCallId.cpp @@ -15,8 +15,11 @@ InputGroupCallId::InputGroupCallId(const tl_object_ptrid_), access_hash(input_group_call->access_hash_) { } -Result InputGroupCallId::from_group_call_id(const string &group_call_id) { +Result InputGroupCallId::from_group_call_id(const string &group_call_id, bool allow_empty) { if (group_call_id.empty()) { + if (!allow_empty) { + return Status::Error(400, "Empty group call identifier specified"); + } return InputGroupCallId(); } @@ -24,7 +27,7 @@ Result InputGroupCallId::from_group_call_id(const string &grou auto r_group_call_id = to_integer_safe(splitted.first); auto r_access_hash = to_integer_safe(splitted.second); if (r_group_call_id.is_error() || r_access_hash.is_error()) { - return Status::Error("Invalid group call identifier specified"); + return Status::Error(400, "Invalid group call identifier specified"); } return InputGroupCallId{r_group_call_id.ok(), r_access_hash.ok()}; diff --git a/td/telegram/InputGroupCallId.h b/td/telegram/InputGroupCallId.h index b8583e1e6..e8f1d1313 100644 --- a/td/telegram/InputGroupCallId.h +++ b/td/telegram/InputGroupCallId.h @@ -28,7 +28,7 @@ class InputGroupCallId { InputGroupCallId(int64 group_call_id, int64 access_hash) : group_call_id(group_call_id), access_hash(access_hash) { } - static Result from_group_call_id(const string &group_call_id); + static Result from_group_call_id(const string &group_call_id, bool allow_empty = false); string get_group_call_id() const; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index c90d3abeb..de3dc3087 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6081,6 +6081,22 @@ void Td::on_request(uint64 id, const td_api::createChatGroupCall &request) { contacts_manager_->create_channel_group_call(DialogId(request.chat_id_), std::move(query_promise)); } +void Td::on_request(uint64 id, const td_api::leaveGroupCall &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + TRY_RESULT_PROMISE(promise, group_call_id, InputGroupCallId::from_group_call_id(request.group_call_id_)); + + group_call_manager_->leave_group_call(group_call_id, request.source_, std::move(promise)); +} + +void Td::on_request(uint64 id, const td_api::discardGroupCall &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + TRY_RESULT_PROMISE(promise, group_call_id, InputGroupCallId::from_group_call_id(request.group_call_id_)); + + group_call_manager_->discard_group_call(group_call_id, std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::upgradeBasicGroupChatToSupergroupChat &request) { CHECK_IS_USER(); CREATE_REQUEST(UpgradeGroupChatToSupergroupChatRequest, request.chat_id_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 797158a4e..1b55ce3b5 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -707,6 +707,10 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::createChatGroupCall &request); + void on_request(uint64 id, const td_api::leaveGroupCall &request); + + void on_request(uint64 id, const td_api::discardGroupCall &request); + void on_request(uint64 id, const td_api::upgradeBasicGroupChatToSupergroupChat &request); void on_request(uint64 id, const td_api::getChatListsToAddChat &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index d12ae41b2..eda8f587a 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2839,6 +2839,10 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_call_id(args), "{}")); } else if (op == "ccgc") { send_request(td_api::make_object(as_chat_id(args))); + } else if (op == "lgc") { + send_request(td_api::make_object(args, 123)); + } else if (op == "dgc") { + send_request(td_api::make_object(args)); } else if (op == "gcil") { send_request(td_api::make_object(as_chat_id(args))); } else if (op == "ccil") {