diff options
author | Mataes <mataes2007@gmail.com> | 2018-04-27 20:39:22 +0300 |
---|---|---|
committer | Mataes <mataes2007@gmail.com> | 2018-04-27 20:39:22 +0300 |
commit | b9ce1d4d98525490ca1a38e2d9fd4f3369adb3e0 (patch) | |
tree | 787c80a909776c1c4d099b638c83c7977bb070e2 /libs/tdlib/td/td/telegram/CallActor.cpp | |
parent | 5ed0126c16d061d6e87aa20c718e14608c66feec (diff) |
added tdlib library
Diffstat (limited to 'libs/tdlib/td/td/telegram/CallActor.cpp')
-rw-r--r-- | libs/tdlib/td/td/telegram/CallActor.cpp | 656 |
1 files changed, 656 insertions, 0 deletions
diff --git a/libs/tdlib/td/td/telegram/CallActor.cpp b/libs/tdlib/td/td/telegram/CallActor.cpp new file mode 100644 index 0000000000..2dd70218e0 --- /dev/null +++ b/libs/tdlib/td/td/telegram/CallActor.cpp @@ -0,0 +1,656 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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/CallActor.h" + +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" +#include "td/telegram/telegram_api.hpp" + +#include "td/mtproto/crypto.h" + +#include "td/telegram/ConfigShared.h" +#include "td/telegram/ContactsManager.h" +#include "td/telegram/DhCache.h" +#include "td/telegram/Global.h" +#include "td/telegram/misc.h" +#include "td/telegram/net/NetQueryCreator.h" +#include "td/telegram/net/NetQueryDispatcher.h" +#include "td/telegram/Td.h" +#include "td/telegram/UpdatesManager.h" + +#include "td/utils/buffer.h" +#include "td/utils/crypto.h" +#include "td/utils/logging.h" +#include "td/utils/Random.h" + +#include <tuple> + +namespace td { +// CallProtocol +CallProtocol CallProtocol::from_telegram_api(const telegram_api::phoneCallProtocol &protocol) { + CallProtocol res; + res.udp_p2p = protocol.udp_p2p_; + res.udp_reflector = protocol.udp_reflector_; + res.min_layer = protocol.min_layer_; + res.max_layer = protocol.max_layer_; + return res; +} + +tl_object_ptr<telegram_api::phoneCallProtocol> CallProtocol::as_telegram_api() const { + int32 flags = 0; + if (udp_p2p) { + flags |= telegram_api::phoneCallProtocol::Flags::UDP_P2P_MASK; + } + if (udp_reflector) { + flags |= telegram_api::phoneCallProtocol::Flags::UDP_REFLECTOR_MASK; + } + return make_tl_object<telegram_api::phoneCallProtocol>(flags, udp_p2p, udp_reflector, min_layer, max_layer); +} + +CallProtocol CallProtocol::from_td_api(const td_api::callProtocol &protocol) { + CallProtocol res; + res.udp_p2p = protocol.udp_p2p_; + res.udp_reflector = protocol.udp_reflector_; + res.min_layer = protocol.min_layer_; + res.max_layer = protocol.max_layer_; + return res; +} +tl_object_ptr<td_api::callProtocol> CallProtocol::as_td_api() const { + return make_tl_object<td_api::callProtocol>(udp_p2p, udp_reflector, min_layer, max_layer); +} + +CallConnection CallConnection::from_telegram_api(const telegram_api::phoneConnection &connection) { + CallConnection res; + res.id = connection.id_; + res.ip = connection.ip_; + res.ipv6 = connection.ipv6_; + res.port = connection.port_; + res.peer_tag = connection.peer_tag_.as_slice().str(); + return res; +} +tl_object_ptr<telegram_api::phoneConnection> CallConnection::as_telegram_api() const { + return make_tl_object<telegram_api::phoneConnection>(id, ip, ipv6, port, BufferSlice(peer_tag)); +} +tl_object_ptr<td_api::callConnection> CallConnection::as_td_api() const { + return make_tl_object<td_api::callConnection>(id, ip, ipv6, port, peer_tag); +} + +// CallState +tl_object_ptr<td_api::CallState> CallState::as_td_api() const { + switch (type) { + case Type::Pending: + return make_tl_object<td_api::callStatePending>(is_created, is_received); + case Type::ExchangingKey: + return make_tl_object<td_api::callStateExchangingKeys>(); + case Type::Ready: { + std::vector<tl_object_ptr<td_api::callConnection>> v; + for (auto &c : connections) { + v.push_back(c.as_td_api()); + } + return make_tl_object<td_api::callStateReady>(protocol.as_td_api(), std::move(v), config, key, + vector<string>(emojis_fingerprint)); + } + case Type::HangingUp: + return make_tl_object<td_api::callStateHangingUp>(); + case Type::Discarded: + return make_tl_object<td_api::callStateDiscarded>(get_call_discard_reason_object(discard_reason), need_rating, + need_debug_information); + case Type::Error: + CHECK(error.is_error()); + return make_tl_object<td_api::callStateError>(make_tl_object<td_api::error>(error.code(), error.message().str())); + case Type::Empty: + default: + UNREACHABLE(); + return nullptr; + } +} + +// CallActor +CallActor::CallActor(CallId call_id, ActorShared<> parent, Promise<int64> promise) + : parent_(std::move(parent)), call_id_promise_(std::move(promise)), local_call_id_(call_id) { +} + +void CallActor::create_call(UserId user_id, tl_object_ptr<telegram_api::InputUser> &&input_user, + CallProtocol &&protocol, Promise<CallId> &&promise) { + CHECK(state_ == State::Empty); + state_ = State::SendRequestQuery; + is_outgoing_ = true; + user_id_ = user_id; + input_user_ = std::move(input_user); + call_state_.protocol = std::move(protocol); + call_state_.type = CallState::Type::Pending; + call_state_.is_received = false; + call_state_need_flush_ = true; + loop(); + promise.set_value(CallId(local_call_id_)); +} + +void CallActor::discard_call(bool is_disconnected, int32 duration, int64 connection_id, Promise<> promise) { + promise.set_value(Unit()); + if (state_ == State::Discarded || state_ == State::WaitDiscardResult || state_ == State::SendDiscardQuery) { + return; + } + + switch (call_state_.type) { + case CallState::Type::Empty: + case CallState::Type::Pending: + if (is_outgoing_) { + call_state_.discard_reason = CallDiscardReason::Missed; + } else { + call_state_.discard_reason = CallDiscardReason::Declined; + } + break; + case CallState::Type::ExchangingKey: + call_state_.discard_reason = is_disconnected ? CallDiscardReason::Disconnected : CallDiscardReason::HungUp; + break; + case CallState::Type::Ready: + call_state_.discard_reason = is_disconnected ? CallDiscardReason::Disconnected : CallDiscardReason::HungUp; + duration_ = duration; + connection_id_ = connection_id; + break; + case CallState::Type::HangingUp: + case CallState::Type::Discarded: + case CallState::Type::Error: + default: + UNREACHABLE(); + return; + } + + call_state_.type = CallState::Type::HangingUp; + call_state_need_flush_ = true; + + state_ = State::SendDiscardQuery; + loop(); +} + +void CallActor::accept_call(CallProtocol &&protocol, Promise<> promise) { + if (state_ != State::SendAcceptQuery) { + return promise.set_error(Status::Error(400, "Unexpected acceptCall")); + } + is_accepted_ = true; + call_state_.protocol = std::move(protocol); + promise.set_value(Unit()); + loop(); +} + +void CallActor::rate_call(int32 rating, string comment, Promise<> promise) { + if (!call_state_.need_rating) { + return promise.set_error(Status::Error(400, "Unexpected sendCallRating")); + } + promise.set_value(Unit()); + auto tl_query = telegram_api::phone_setCallRating(get_input_phone_call(), rating, std::move(comment)); + auto query = G()->net_query_creator().create(create_storer(tl_query)); + send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { + send_closure(actor_id, &CallActor::on_set_rating_query_result, std::move(net_query)); + })); + loop(); +} + +void CallActor::on_set_rating_query_result(NetQueryPtr net_query) { + auto res = fetch_result<telegram_api::phone_setCallRating>(std::move(net_query)); + if (res.is_error()) { + return on_error(res.move_as_error()); + } + call_state_.need_rating = false; + send_closure(G()->updates_manager(), &UpdatesManager::on_get_updates, res.move_as_ok()); +} + +void CallActor::send_call_debug_information(string data, Promise<> promise) { + if (!call_state_.need_debug_information) { + return promise.set_error(Status::Error(400, "Unexpected sendCallDebugInformation")); + } + promise.set_value(Unit()); + auto tl_query = telegram_api::phone_saveCallDebug(get_input_phone_call(), + make_tl_object<telegram_api::dataJSON>(std::move(data))); + auto query = G()->net_query_creator().create(create_storer(tl_query)); + send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { + send_closure(actor_id, &CallActor::on_set_debug_query_result, std::move(net_query)); + })); + loop(); +} + +void CallActor::on_set_debug_query_result(NetQueryPtr net_query) { + auto res = fetch_result<telegram_api::phone_saveCallDebug>(std::move(net_query)); + if (res.is_error()) { + return on_error(res.move_as_error()); + } + call_state_.need_debug_information = false; +} + +//Updates +//phoneCallEmpty#5366c915 id:long = PhoneCall; +//phoneCallWaiting#1b8f4ad1 flags:# id:long access_hash:long date:int admin_id:int participant_id:int protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall; + +// Requests +//phone.discardCall#78d413a6 peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates; +void CallActor::update_call(tl_object_ptr<telegram_api::PhoneCall> call) { + Status status; + downcast_call(*call, [&](auto &call) { status = this->do_update_call(call); }); + if (status.is_error()) { + on_error(std::move(status)); + } + loop(); +} + +void CallActor::update_call_inner(tl_object_ptr<telegram_api::phone_phoneCall> call) { + send_closure(G()->contacts_manager(), &ContactsManager::on_get_users, std::move(call->users_)); + update_call(std::move(call->phone_call_)); +} + +Status CallActor::do_update_call(telegram_api::phoneCallEmpty &call) { + return Status::Error(400, "Call is finished"); +} + +//phoneCallWaiting#1b8f4ad1 flags:# id:long access_hash:long date:int admin_id:int participant_id:int protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall; +Status CallActor::do_update_call(telegram_api::phoneCallWaiting &call) { + if (state_ != State::WaitRequestResult && state_ != State::WaitAcceptResult) { + return Status::Error(500, PSLICE() << "Drop unexpected " << to_string(call)); + } + + if (state_ == State::WaitAcceptResult) { + call_state_.type = CallState::Type::ExchangingKey; + call_state_need_flush_ = true; + cancel_timeout(); + } else { + if ((call.flags_ & telegram_api::phoneCallWaiting::RECEIVE_DATE_MASK) != 0) { + call_state_.is_received = true; + call_state_need_flush_ = true; + int32 call_ring_timeout_ms = G()->shared_config().get_option_integer("call_ring_timeout_ms", 90000); + set_timeout_in(call_ring_timeout_ms * 0.001); + } + } + + call_id_ = call.id_; + call_access_hash_ = call.access_hash_; + call_admin_id_ = call.admin_id_; + call_participant_id_ = call.participant_id_; + if (call_id_promise_) { + call_id_promise_.set_value(std::move(call.id_)); + } + + if (!call_state_.is_created) { + call_state_.is_created = true; + call_state_need_flush_ = true; + } + + return Status::OK(); +} + +//phoneCallRequested#83761ce4 id:long access_hash:long date:int admin_id:int participant_id:int g_a_hash:bytes protocol:PhoneCallProtocol = PhoneCall; +Status CallActor::do_update_call(telegram_api::phoneCallRequested &call) { + if (state_ != State::Empty) { + return Status::Error(500, PSLICE() << "Drop unexpected " << to_string(call)); + } + call_id_ = call.id_; + call_access_hash_ = call.access_hash_; + call_admin_id_ = call.admin_id_; + call_participant_id_ = call.participant_id_; + if (call_id_promise_) { + call_id_promise_.set_value(std::move(call.id_)); + } + + dh_handshake_.set_g_a_hash(call.g_a_hash_.as_slice()); + state_ = State::SendAcceptQuery; + + call_state_.type = CallState::Type::Pending; + call_state_.is_created = true; + call_state_.is_received = true; + call_state_need_flush_ = true; + + send_received_query(); + return Status::OK(); +} + +tl_object_ptr<telegram_api::inputPhoneCall> CallActor::get_input_phone_call() { + CHECK(call_id_ != 0); + return make_tl_object<telegram_api::inputPhoneCall>(call_id_, call_access_hash_); +} + +//phoneCallAccepted#6d003d3f id:long access_hash:long date:int admin_id:int participant_id:int g_b:bytes protocol:PhoneCallProtocol = PhoneCall; +Status CallActor::do_update_call(telegram_api::phoneCallAccepted &call) { + if (state_ != State::WaitRequestResult) { + return Status::Error(500, PSLICE() << "Drop unexpected " << to_string(call)); + } + + dh_handshake_.set_g_a(call.g_b_.as_slice()); + TRY_STATUS(dh_handshake_.run_checks(DhCache::instance())); + std::tie(call_state_.key_fingerprint, call_state_.key) = dh_handshake_.gen_key(); + state_ = State::SendConfirmQuery; + call_state_.type = CallState::Type::ExchangingKey; + call_state_need_flush_ = true; + cancel_timeout(); + return Status::OK(); +} + +//phoneCall#ffe6ab67 id:long access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connection:PhoneConnection alternative_connections:Vector<PhoneConnection> start_date:int = PhoneCall; +Status CallActor::do_update_call(telegram_api::phoneCall &call) { + if (state_ != State::WaitAcceptResult && state_ != State::WaitConfirmResult) { + return Status::Error(500, PSLICE() << "Drop unexpected " << to_string(call)); + } + + if (state_ == State::WaitAcceptResult) { + dh_handshake_.set_g_a(call.g_a_or_b_.as_slice()); + TRY_STATUS(dh_handshake_.run_checks(DhCache::instance())); + std::tie(call_state_.key_fingerprint, call_state_.key) = dh_handshake_.gen_key(); + } + if (call_state_.key_fingerprint != call.key_fingerprint_) { + return Status::Error(400, "Key fingerprints mismatch"); + } + + call_state_.emojis_fingerprint = + get_emojis_fingerprint(call_state_.key, is_outgoing_ ? dh_handshake_.get_g_b() : dh_handshake_.get_g_a()); + + call_state_.connections.push_back(CallConnection::from_telegram_api(*call.connection_)); + for (auto &connection : call.alternative_connections_) { + call_state_.connections.push_back(CallConnection::from_telegram_api(*connection)); + } + call_state_.protocol = CallProtocol::from_telegram_api(*call.protocol_); + call_state_.type = CallState::Type::Ready; + call_state_need_flush_ = true; + + return Status::OK(); +} + +//phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall; +Status CallActor::do_update_call(telegram_api::phoneCallDiscarded &call) { + state_ = State::Discarded; + + auto reason = get_call_discard_reason(call.reason_); + if (call_state_.discard_reason == CallDiscardReason::Empty || reason != CallDiscardReason::Empty) { + call_state_.discard_reason = reason; + } + if (call_state_.type != CallState::Type::Error) { + call_state_.need_rating = call.need_rating_; + call_state_.need_debug_information = call.need_debug_; + call_state_.type = CallState::Type::Discarded; + call_state_need_flush_ = true; + } + return Status::OK(); +} + +bool CallActor::load_dh_config() { + if (dh_config_ready_) { + return true; + } + if (!dh_config_query_sent_) { + dh_config_query_sent_ = true; + do_load_dh_config(PromiseCreator::lambda([actor_id = actor_id(this)](Result<std::shared_ptr<DhConfig>> dh_config) { + send_closure(actor_id, &CallActor::on_dh_config, std::move(dh_config), false); + })); + } + return false; +} + +void CallActor::on_error(Status status) { + CHECK(status.is_error()); + + if (state_ == State::WaitDiscardResult || state_ == State::Discarded) { + state_ = State::Discarded; + } else { + state_ = State::SendDiscardQuery; + call_state_.discard_reason = + call_state_.type == CallState::Type::Pending ? CallDiscardReason::Missed : CallDiscardReason::Disconnected; + } + + call_state_.type = CallState::Type::Error; + call_state_.error = std::move(status); + call_state_need_flush_ = true; +} + +void CallActor::on_dh_config(Result<std::shared_ptr<DhConfig>> r_dh_config, bool dummy) { + if (r_dh_config.is_error()) { + return on_error(r_dh_config.move_as_error()); + } + dh_config_ready_ = true; + dh_config_ = r_dh_config.move_as_ok(); + yield(); +} + +void CallActor::do_load_dh_config(Promise<std::shared_ptr<DhConfig>> promise) { + //TODO: move to external actor + auto dh_config = G()->get_dh_config(); + int32 version = 0; + if (dh_config) { + version = dh_config->version; + } + int random_length = 0; + telegram_api::messages_getDhConfig tl_query(version, random_length); + auto query = G()->net_query_creator().create(create_storer(tl_query)); + send_with_promise(std::move(query), + PromiseCreator::lambda([actor_id = actor_id(this), old_dh_config = std::move(dh_config), + promise = std::move(promise)](Result<NetQueryPtr> result_query) mutable { + promise.set_result([&]() -> Result<std::shared_ptr<DhConfig>> { + TRY_RESULT(query, std::move(result_query)); + TRY_RESULT(new_dh_config, fetch_result<telegram_api::messages_getDhConfig>(std::move(query))); + if (new_dh_config->get_id() == telegram_api::messages_dhConfig::ID) { + auto dh = move_tl_object_as<telegram_api::messages_dhConfig>(new_dh_config); + auto dh_config = std::make_shared<DhConfig>(); + dh_config->version = dh->version_; + dh_config->prime = dh->p_.as_slice().str(); + dh_config->g = dh->g_; + G()->set_dh_config(dh_config); + return std::move(dh_config); + } + if (old_dh_config) { + return std::move(old_dh_config); + } + return Status::Error(500, "Can't load DhConfig"); + }()); + })); +} + +void CallActor::send_received_query() { + auto tl_query = telegram_api::phone_receivedCall(get_input_phone_call()); + auto query = G()->net_query_creator().create(create_storer(tl_query)); + send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { + send_closure(actor_id, &CallActor::on_received_query_result, std::move(net_query)); + })); +} + +void CallActor::on_received_query_result(NetQueryPtr net_query) { + auto res = fetch_result<telegram_api::phone_receivedCall>(std::move(net_query)); + if (res.is_error()) { + return on_error(res.move_as_error()); + } +} + +//phone.requestCall#5b95b3d4 user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall; +void CallActor::try_send_request_query() { + if (!load_dh_config()) { + return; + } + dh_handshake_.set_config(dh_config_->g, dh_config_->prime); + CHECK(input_user_ != nullptr); + auto tl_query = telegram_api::phone_requestCall(std::move(input_user_), Random::secure_int32(), + BufferSlice(dh_handshake_.get_g_b_hash()), + call_state_.protocol.as_telegram_api()); + auto query = G()->net_query_creator().create(create_storer(tl_query)); + state_ = State::WaitRequestResult; + send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { + send_closure(actor_id, &CallActor::on_request_query_result, std::move(net_query)); + })); + int32 call_receive_timeout_ms = G()->shared_config().get_option_integer("call_receive_timeout_ms", 20000); + set_timeout_in(call_receive_timeout_ms * 0.001); +} + +void CallActor::on_request_query_result(NetQueryPtr net_query) { + auto res = fetch_result<telegram_api::phone_requestCall>(std::move(net_query)); + if (res.is_error()) { + return on_error(res.move_as_error()); + } + update_call_inner(res.move_as_ok()); +} + +//phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall; +void CallActor::try_send_accept_query() { + if (!load_dh_config()) { + return; + } + if (!is_accepted_) { + return; + } + dh_handshake_.set_config(dh_config_->g, dh_config_->prime); + auto tl_query = telegram_api::phone_acceptCall(get_input_phone_call(), BufferSlice(dh_handshake_.get_g_b()), + call_state_.protocol.as_telegram_api()); + auto query = G()->net_query_creator().create(create_storer(tl_query)); + state_ = State::WaitAcceptResult; + send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { + send_closure(actor_id, &CallActor::on_accept_query_result, std::move(net_query)); + })); +} + +void CallActor::on_accept_query_result(NetQueryPtr net_query) { + auto res = fetch_result<telegram_api::phone_acceptCall>(std::move(net_query)); + if (res.is_error()) { + return on_error(res.move_as_error()); + } + update_call_inner(res.move_as_ok()); +} + +//phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall; +void CallActor::try_send_confirm_query() { + if (!load_dh_config()) { + return; + } + auto tl_query = telegram_api::phone_confirmCall(get_input_phone_call(), BufferSlice(dh_handshake_.get_g_b()), + call_state_.key_fingerprint, call_state_.protocol.as_telegram_api()); + auto query = G()->net_query_creator().create(create_storer(tl_query)); + state_ = State::WaitConfirmResult; + send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { + send_closure(actor_id, &CallActor::on_confirm_query_result, std::move(net_query)); + })); +} + +void CallActor::on_confirm_query_result(NetQueryPtr net_query) { + auto res = fetch_result<telegram_api::phone_confirmCall>(std::move(net_query)); + if (res.is_error()) { + return on_error(res.move_as_error()); + } + update_call_inner(res.move_as_ok()); +} + +void CallActor::try_send_discard_query() { + if (call_id_ == 0) { + state_ = State::Discarded; + yield(); + return; + } + auto tl_query = + telegram_api::phone_discardCall(get_input_phone_call(), duration_, + get_input_phone_call_discard_reason(call_state_.discard_reason), connection_id_); + auto query = G()->net_query_creator().create(create_storer(tl_query)); + state_ = State::WaitDiscardResult; + send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { + send_closure(actor_id, &CallActor::on_discard_query_result, std::move(net_query)); + })); +} + +void CallActor::on_discard_query_result(NetQueryPtr net_query) { + auto res = fetch_result<telegram_api::phone_discardCall>(std::move(net_query)); + if (res.is_error()) { + return on_error(res.move_as_error()); + } + send_closure(G()->updates_manager(), &UpdatesManager::on_get_updates, res.move_as_ok()); +} + +void CallActor::flush_call_state() { + if (call_state_need_flush_) { + if (call_state_.type == CallState::Type::Ready && !call_state_has_config_) { + return; + } + call_state_need_flush_ = false; + + // can't call const function + // send_closure(G()->contacts_manager(), &ContactsManager::get_user_id_object, user_id_, "flush_call_state"); + send_closure(G()->td(), &Td::send_update, + make_tl_object<td_api::updateCall>( + make_tl_object<td_api::call>(local_call_id_.get(), is_outgoing_ ? user_id_.get() : call_admin_id_, + is_outgoing_, call_state_.as_td_api()))); + } +} + +void CallActor::start_up() { + auto tl_query = telegram_api::phone_getCallConfig(); + auto query = G()->net_query_creator().create(create_storer(tl_query)); + send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { + send_closure(actor_id, &CallActor::on_get_call_config_result, std::move(net_query)); + })); +} + +void CallActor::on_get_call_config_result(NetQueryPtr net_query) { + auto res = fetch_result<telegram_api::phone_getCallConfig>(std::move(net_query)); + if (res.is_error()) { + return on_error(res.move_as_error()); + } + call_state_.config = res.ok()->data_; + call_state_has_config_ = true; +} + +void CallActor::loop() { + flush_call_state(); + switch (state_) { + case State::SendRequestQuery: + try_send_request_query(); + break; + case State::SendAcceptQuery: + try_send_accept_query(); + break; + case State::SendConfirmQuery: + try_send_confirm_query(); + break; + case State::SendDiscardQuery: + try_send_discard_query(); + break; + case State::Discarded: { + if (call_state_.type == CallState::Type::Discarded && + (call_state_.need_rating || call_state_.need_debug_information)) { + break; + } + LOG(INFO) << "Close call " << local_call_id_; + stop(); + break; + } + default: + break; + } +} + +void CallActor::timeout_expired() { + on_error(Status::Error(4005000, "Call timeout expired")); + yield(); +} + +void CallActor::on_result(NetQueryPtr query) { + auto token = get_link_token(); + container_.extract(token).set_value(std::move(query)); + yield(); // Call loop AFTER all events from the promise are executed +} + +void CallActor::send_with_promise(NetQueryPtr query, Promise<NetQueryPtr> promise) { + auto id = container_.create(std::move(promise)); + G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, id)); +} + +vector<string> CallActor::get_emojis_fingerprint(const string &key, const string &g_a) { + string str = key + g_a; + unsigned char sha256_buf[32]; + sha256(str, {sha256_buf, 32}); + + vector<string> result; + result.reserve(4); + for (int i = 0; i < 4; i++) { + uint64 num = + (static_cast<uint64>(sha256_buf[8 * i + 0]) << 56) | (static_cast<uint64>(sha256_buf[8 * i + 1]) << 48) | + (static_cast<uint64>(sha256_buf[8 * i + 2]) << 40) | (static_cast<uint64>(sha256_buf[8 * i + 3]) << 32) | + (static_cast<uint64>(sha256_buf[8 * i + 4]) << 24) | (static_cast<uint64>(sha256_buf[8 * i + 5]) << 16) | + (static_cast<uint64>(sha256_buf[8 * i + 6]) << 8) | (static_cast<uint64>(sha256_buf[8 * i + 7])); + result.push_back(get_emoji_fingerprint(num)); + } + return result; +} + +} // namespace td |