Add joinGroupCall method.
This commit is contained in:
parent
32295e2b6e
commit
162c06d00a
@ -2014,7 +2014,7 @@ callDiscardReasonHungUp = CallDiscardReason;
|
||||
//@udp_reflector True, if connection through UDP reflectors is supported
|
||||
//@min_layer The minimum supported API layer; use 65
|
||||
//@max_layer The maximum supported API layer; use 65
|
||||
//@library_versions List of supported libtgvoip versions
|
||||
//@library_versions List of supported tgcalls versions
|
||||
callProtocol udp_p2p:Bool udp_reflector:Bool min_layer:int32 max_layer:int32 library_versions:vector<string> = CallProtocol;
|
||||
|
||||
|
||||
@ -2062,6 +2062,20 @@ callStateError error:error = CallState;
|
||||
//@description Describes a group call @id Group call identifier @is_active True, if the call is active @member_count Number of members in the group call @duration Call duration; for ended calls only
|
||||
groupCall id:string is_active:Bool member_count:int32 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;
|
||||
|
||||
//@description Describes a payload for interaction with tgcalls @ufrag Value of the field ufrag @pwd Value of the field pwd @fingerprints The list of fingerprints
|
||||
groupCallPayload ufrag:string pwd:string fingerprints:vector<groupCallPayloadFingerprint> = GroupCallPayload;
|
||||
|
||||
//@description Describes a join response candidate for interaction with tgcalls @port Value of the field port @protocol Value of the field protocol @network Value of the field network
|
||||
//@generation Value of the field generation @id Value of the field id @component Value of the field component @foundation Value of the field foundation @priority Value of the field priority
|
||||
//@ip Value of the field ip @type Value of the field type @tcp_type Value of the field tcp_type @rel_addr Value of the field rel_addr @rel_port Value of the field rel_port
|
||||
groupCallJoinResponseCandidate port:string protocol:string network:string generation:string id:string component:string foundation:string priority:string ip:string type:string tcp_type:string rel_addr:string rel_port:string = GroupCallJoinResponseCandidate;
|
||||
|
||||
//@description Describes a join response for interaction with tgcalls @payload Join response payload to pass to tgcalls @candidates Join response candidates to pass to tgcalls
|
||||
groupCallJoinResponse payload:groupCallPayload candidates:vector<groupCallJoinResponseCandidate> = GroupCallJoinResponse;
|
||||
|
||||
|
||||
//@class CallProblem @description Describes the exact type of a problem with a call
|
||||
|
||||
@ -4293,6 +4307,9 @@ 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 Joins a group call @group_call_id Group call identifier @payload Group join payload, received from tgcalls @source Caller source identifier, received from tgcalls @is_muted True, if the user's microphone is muted
|
||||
joinGroupCall group_call_id:string payload:groupCallPayload source:int32 is_muted:Bool = GroupCallJoinResponse;
|
||||
|
||||
//@description Leaves a group call @group_call_id Group call identifier @source Caller source identifier
|
||||
leaveGroupCall group_call_id:string source:int32 = Ok;
|
||||
|
||||
|
Binary file not shown.
@ -8,9 +8,11 @@
|
||||
|
||||
#include "td/telegram/ContactsManager.h"
|
||||
#include "td/telegram/Global.h"
|
||||
#include "td/telegram/misc.h"
|
||||
#include "td/telegram/Td.h"
|
||||
#include "td/telegram/UpdatesManager.h"
|
||||
|
||||
#include "td/utils/JsonBuilder.h"
|
||||
#include "td/utils/Random.h"
|
||||
|
||||
namespace td {
|
||||
@ -60,6 +62,43 @@ class CreateGroupCallQuery : public Td::ResultHandler {
|
||||
}
|
||||
};
|
||||
|
||||
class JoinGroupCallQuery : public Td::ResultHandler {
|
||||
Promise<Unit> promise_;
|
||||
InputGroupCallId group_call_id_;
|
||||
uint64 generation_ = 0;
|
||||
|
||||
public:
|
||||
explicit JoinGroupCallQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
|
||||
}
|
||||
|
||||
void send(InputGroupCallId group_call_id, const string &payload, bool is_muted, uint64 generation) {
|
||||
group_call_id_ = group_call_id;
|
||||
generation_ = generation;
|
||||
|
||||
int32 flags = 0;
|
||||
if (is_muted) {
|
||||
flags |= telegram_api::phone_joinGroupCall::MUTED_MASK;
|
||||
}
|
||||
send_query(G()->net_query_creator().create(
|
||||
telegram_api::phone_joinGroupCall(flags, false /*ignored*/, group_call_id.get_input_group_call(),
|
||||
make_tl_object<telegram_api::dataJSON>(payload))));
|
||||
}
|
||||
|
||||
void on_result(uint64 id, BufferSlice packet) override {
|
||||
auto result_ptr = fetch_result<telegram_api::phone_joinGroupCall>(packet);
|
||||
if (result_ptr.is_error()) {
|
||||
return on_error(id, result_ptr.move_as_error());
|
||||
}
|
||||
|
||||
td->group_call_manager_->process_join_group_call_response(group_call_id_, generation_, result_ptr.move_as_ok(),
|
||||
std::move(promise_));
|
||||
}
|
||||
|
||||
void on_error(uint64 id, Status status) override {
|
||||
promise_.set_error(std::move(status));
|
||||
}
|
||||
};
|
||||
|
||||
class LeaveGroupCallQuery : public Td::ResultHandler {
|
||||
Promise<Unit> promise_;
|
||||
|
||||
@ -140,6 +179,175 @@ void GroupCallManager::create_group_call(ChannelId channel_id, Promise<InputGrou
|
||||
td_->create_handler<CreateGroupCallQuery>(std::move(promise))->send(channel_id);
|
||||
}
|
||||
|
||||
void GroupCallManager::join_group_call(InputGroupCallId 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) {
|
||||
if (pending_join_requests_.count(group_call_id)) {
|
||||
auto it = pending_join_requests_.find(group_call_id);
|
||||
CHECK(it != pending_join_requests_.end());
|
||||
it->second.promise.set_error(Status::Error(200, "Cancelled by another joinGroupCall request"));
|
||||
pending_join_requests_.erase(it);
|
||||
}
|
||||
|
||||
if (payload == nullptr) {
|
||||
return promise.set_error(Status::Error(400, "Payload must be non-empty"));
|
||||
}
|
||||
if (!clean_input_string(payload->ufrag_)) {
|
||||
return promise.set_error(Status::Error(400, "Payload ufrag must be encoded in UTF-8"));
|
||||
}
|
||||
if (!clean_input_string(payload->pwd_)) {
|
||||
return promise.set_error(Status::Error(400, "Payload pwd must be encoded in UTF-8"));
|
||||
}
|
||||
for (auto &fingerprint : payload->fingerprints_) {
|
||||
if (fingerprint == nullptr) {
|
||||
return promise.set_error(Status::Error(400, "Payload fingerprint must be non-empty"));
|
||||
}
|
||||
if (!clean_input_string(fingerprint->hash_)) {
|
||||
return promise.set_error(Status::Error(400, "Fingerprint hash must be encoded in UTF-8"));
|
||||
}
|
||||
if (!clean_input_string(fingerprint->setup_)) {
|
||||
return promise.set_error(Status::Error(400, "Fingerprint setup must be encoded in UTF-8"));
|
||||
}
|
||||
if (!clean_input_string(fingerprint->fingerprint_)) {
|
||||
return promise.set_error(Status::Error(400, "Fingerprint must be encoded in UTF-8"));
|
||||
}
|
||||
}
|
||||
|
||||
auto json_payload = json_encode<string>(json_object([&payload, source](auto &o) {
|
||||
o("ufrag", payload->ufrag_);
|
||||
o("pwd", payload->pwd_);
|
||||
o("fingerprints", json_array(payload->fingerprints_,
|
||||
[](const td_api::object_ptr<td_api::groupCallPayloadFingerprint> &fingerprint) {
|
||||
return json_object([&fingerprint](auto &o) {
|
||||
o("hash", fingerprint->hash_);
|
||||
o("setup", fingerprint->setup_);
|
||||
o("fingerprint", fingerprint->fingerprint_);
|
||||
});
|
||||
}));
|
||||
o("ssrc", source);
|
||||
}));
|
||||
|
||||
auto generation = join_group_request_generation_++;
|
||||
auto &request = pending_join_requests_[group_call_id];
|
||||
request.generation = generation;
|
||||
request.promise = std::move(promise);
|
||||
|
||||
auto query_promise =
|
||||
PromiseCreator::lambda([actor_id = actor_id(this), generation, group_call_id](Result<Unit> &&result) {
|
||||
CHECK(result.is_error());
|
||||
send_closure(actor_id, &GroupCallManager::finish_join_group_call, group_call_id, generation,
|
||||
result.move_as_error());
|
||||
});
|
||||
td_->create_handler<JoinGroupCallQuery>(std::move(query_promise))
|
||||
->send(group_call_id, json_payload, is_muted, generation);
|
||||
}
|
||||
|
||||
void GroupCallManager::process_join_group_call_response(InputGroupCallId group_call_id, uint64 generation,
|
||||
tl_object_ptr<telegram_api::Updates> &&updates,
|
||||
Promise<Unit> &&promise) {
|
||||
auto it = pending_join_requests_.find(group_call_id);
|
||||
if (it == pending_join_requests_.end() || it->second.generation != generation) {
|
||||
LOG(INFO) << "Ignore JoinGroupCallQuery response with " << group_call_id << " and generation " << generation;
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Receive result for JoinGroupCallQuery: " << to_string(updates);
|
||||
td_->updates_manager_->on_get_updates(std::move(updates));
|
||||
|
||||
promise.set_error(Status::Error(500, "Wrong join response received"));
|
||||
}
|
||||
|
||||
Result<td_api::object_ptr<td_api::groupCallJoinResponse>> GroupCallManager::get_group_call_join_response_object(
|
||||
string json_response) {
|
||||
auto r_value = json_decode(json_response);
|
||||
if (r_value.is_error()) {
|
||||
return Status::Error("Can't parse JSON object");
|
||||
}
|
||||
|
||||
auto value = r_value.move_as_ok();
|
||||
if (value.type() != JsonValue::Type::Object) {
|
||||
return Status::Error("Expected an Object");
|
||||
}
|
||||
|
||||
auto &value_object = value.get_object();
|
||||
TRY_RESULT(transport, get_json_object_field(value_object, "transport", JsonValue::Type::Object, false));
|
||||
CHECK(transport.type() == JsonValue::Type::Object);
|
||||
auto &transport_object = transport.get_object();
|
||||
|
||||
TRY_RESULT(candidates, get_json_object_field(transport_object, "candidates", JsonValue::Type::Array, false));
|
||||
TRY_RESULT(fingerprints, get_json_object_field(transport_object, "fingerprints", JsonValue::Type::Array, false));
|
||||
TRY_RESULT(ufrag, get_json_object_string_field(transport_object, "ufrag", false));
|
||||
TRY_RESULT(pwd, get_json_object_string_field(transport_object, "pwd", false));
|
||||
// skip "xmlns", "rtcp-mux"
|
||||
|
||||
vector<td_api::object_ptr<td_api::groupCallPayloadFingerprint>> fingerprints_object;
|
||||
for (auto &fingerprint : fingerprints.get_array()) {
|
||||
if (fingerprint.type() != JsonValue::Type::Object) {
|
||||
return Status::Error("Expected JSON object as fingerprint");
|
||||
}
|
||||
auto &fingerprint_object = fingerprint.get_object();
|
||||
TRY_RESULT(hash, get_json_object_string_field(fingerprint_object, "hash", false));
|
||||
TRY_RESULT(setup, get_json_object_string_field(fingerprint_object, "setup", false));
|
||||
TRY_RESULT(fingerprint_value, get_json_object_string_field(fingerprint_object, "fingerprint", false));
|
||||
fingerprints_object.push_back(
|
||||
td_api::make_object<td_api::groupCallPayloadFingerprint>(hash, setup, fingerprint_value));
|
||||
}
|
||||
|
||||
vector<td_api::object_ptr<td_api::groupCallJoinResponseCandidate>> candidates_object;
|
||||
for (auto &candidate : candidates.get_array()) {
|
||||
if (candidate.type() != JsonValue::Type::Object) {
|
||||
return Status::Error("Expected JSON object as candidate");
|
||||
}
|
||||
auto &candidate_object = candidate.get_object();
|
||||
TRY_RESULT(port, get_json_object_string_field(candidate_object, "port", false));
|
||||
TRY_RESULT(protocol, get_json_object_string_field(candidate_object, "protocol", false));
|
||||
TRY_RESULT(network, get_json_object_string_field(candidate_object, "network", false));
|
||||
TRY_RESULT(generation, get_json_object_string_field(candidate_object, "generation", false));
|
||||
TRY_RESULT(id, get_json_object_string_field(candidate_object, "id", false));
|
||||
TRY_RESULT(component, get_json_object_string_field(candidate_object, "component", false));
|
||||
TRY_RESULT(foundation, get_json_object_string_field(candidate_object, "foundation", false));
|
||||
TRY_RESULT(priority, get_json_object_string_field(candidate_object, "priority", false));
|
||||
TRY_RESULT(ip, get_json_object_string_field(candidate_object, "ip", false));
|
||||
TRY_RESULT(type, get_json_object_string_field(candidate_object, "type", false));
|
||||
TRY_RESULT(tcp_type, get_json_object_string_field(candidate_object, "tcptype"));
|
||||
TRY_RESULT(rel_addr, get_json_object_string_field(candidate_object, "rel-addr"));
|
||||
TRY_RESULT(rel_port, get_json_object_string_field(candidate_object, "rel-port"));
|
||||
candidates_object.push_back(td_api::make_object<td_api::groupCallJoinResponseCandidate>(
|
||||
port, protocol, network, generation, id, component, foundation, priority, ip, type, tcp_type, rel_addr,
|
||||
rel_port));
|
||||
}
|
||||
|
||||
auto payload = td_api::make_object<td_api::groupCallPayload>(ufrag, pwd, std::move(fingerprints_object));
|
||||
return td_api::make_object<td_api::groupCallJoinResponse>(std::move(payload), std::move(candidates_object));
|
||||
}
|
||||
|
||||
void GroupCallManager::on_join_group_call_response(InputGroupCallId group_call_id, string json_response) {
|
||||
auto it = pending_join_requests_.find(group_call_id);
|
||||
if (it == pending_join_requests_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto result = get_group_call_join_response_object(std::move(json_response));
|
||||
if (result.is_error()) {
|
||||
LOG(ERROR) << "Failed to parse join response JSON object: " << result.error().message();
|
||||
it->second.promise.set_error(Status::Error(500, "Receive invalid join group call response payload"));
|
||||
} else {
|
||||
it->second.promise.set_value(result.move_as_ok());
|
||||
}
|
||||
pending_join_requests_.erase(it);
|
||||
}
|
||||
|
||||
void GroupCallManager::finish_join_group_call(InputGroupCallId group_call_id, uint64 generation, Status error) {
|
||||
CHECK(error.is_error());
|
||||
auto it = pending_join_requests_.find(group_call_id);
|
||||
if (it == pending_join_requests_.end() || it->second.generation != generation) {
|
||||
return;
|
||||
}
|
||||
it->second.promise.set_error(std::move(error));
|
||||
pending_join_requests_.erase(it);
|
||||
}
|
||||
|
||||
void GroupCallManager::leave_group_call(InputGroupCallId group_call_id, int32 source, Promise<Unit> &&promise) {
|
||||
td_->create_handler<LeaveGroupCallQuery>(std::move(promise))->send(group_call_id, source);
|
||||
}
|
||||
@ -169,6 +377,9 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptr<telegra
|
||||
call.is_active = true;
|
||||
call.member_count = group_call->participants_count_;
|
||||
call.version = group_call->version_;
|
||||
if (group_call->params_ != nullptr) {
|
||||
on_join_group_call_response(call_id, std::move(group_call->params_->data_));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case telegram_api::groupCallDiscarded::ID: {
|
||||
|
@ -26,19 +26,33 @@ class GroupCallManager : public Actor {
|
||||
|
||||
void create_group_call(ChannelId channel_id, Promise<InputGroupCallId> &&promise);
|
||||
|
||||
void join_group_call(InputGroupCallId 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);
|
||||
|
||||
void leave_group_call(InputGroupCallId group_call_id, int32 source, Promise<Unit> &&promise);
|
||||
|
||||
void discard_group_call(InputGroupCallId group_call_id, Promise<Unit> &&promise);
|
||||
|
||||
void on_update_group_call(tl_object_ptr<telegram_api::GroupCall> group_call_ptr);
|
||||
|
||||
void process_join_group_call_response(InputGroupCallId group_call_id, uint64 generation,
|
||||
tl_object_ptr<telegram_api::Updates> &&updates, Promise<Unit> &&promise);
|
||||
|
||||
private:
|
||||
struct GroupCall;
|
||||
|
||||
void tear_down() override;
|
||||
|
||||
void on_join_group_call_response(InputGroupCallId group_call_id, string json_response);
|
||||
|
||||
void finish_join_group_call(InputGroupCallId group_call_id, uint64 generation, Status error);
|
||||
|
||||
InputGroupCallId update_group_call(const tl_object_ptr<telegram_api::GroupCall> &group_call_ptr);
|
||||
|
||||
static Result<td_api::object_ptr<td_api::groupCallJoinResponse>> get_group_call_join_response_object(
|
||||
string json_response);
|
||||
|
||||
static tl_object_ptr<td_api::updateGroupCall> get_update_group_call_object(InputGroupCallId group_call_id,
|
||||
const GroupCall *group_call);
|
||||
|
||||
@ -49,6 +63,13 @@ class GroupCallManager : public Actor {
|
||||
ActorShared<> parent_;
|
||||
|
||||
std::unordered_map<InputGroupCallId, unique_ptr<GroupCall>, InputGroupCallIdHash> group_calls_;
|
||||
|
||||
struct PendingJoinRequest {
|
||||
uint64 generation = 0;
|
||||
Promise<td_api::object_ptr<td_api::groupCallJoinResponse>> promise;
|
||||
};
|
||||
std::unordered_map<InputGroupCallId, PendingJoinRequest, InputGroupCallIdHash> pending_join_requests_;
|
||||
uint64 join_group_request_generation_ = 0;
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
|
@ -6041,6 +6041,15 @@ 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, td_api::joinGroupCall &request) {
|
||||
CHECK_IS_USER();
|
||||
CREATE_REQUEST_PROMISE();
|
||||
TRY_RESULT_PROMISE(promise, group_call_id, InputGroupCallId::from_group_call_id(request.group_call_id_));
|
||||
|
||||
group_call_manager_->join_group_call(group_call_id, std::move(request.payload_), request.source_, request.is_muted_,
|
||||
std::move(promise));
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::leaveGroupCall &request) {
|
||||
CHECK_IS_USER();
|
||||
CREATE_OK_REQUEST_PROMISE();
|
||||
|
@ -694,6 +694,8 @@ class Td final : public NetQueryCallback {
|
||||
|
||||
void on_request(uint64 id, const td_api::createChatGroupCall &request);
|
||||
|
||||
void on_request(uint64 id, td_api::joinGroupCall &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::leaveGroupCall &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::discardGroupCall &request);
|
||||
|
@ -2836,6 +2836,12 @@ class CliClient final : public Actor {
|
||||
send_request(td_api::make_object<td_api::sendCallDebugInformation>(as_call_id(args), "{}"));
|
||||
} else if (op == "ccgc") {
|
||||
send_request(td_api::make_object<td_api::createChatGroupCall>(as_chat_id(args)));
|
||||
} else if (op == "jgc") {
|
||||
vector<td_api::object_ptr<td_api::groupCallPayloadFingerprint>> fingerprints;
|
||||
fingerprints.push_back(td_api::make_object<td_api::groupCallPayloadFingerprint>("hash", "setup", "fingerprint"));
|
||||
fingerprints.push_back(td_api::make_object<td_api::groupCallPayloadFingerprint>("h2", "s2", "fingerprint2"));
|
||||
send_request(td_api::make_object<td_api::joinGroupCall>(
|
||||
args, td_api::make_object<td_api::groupCallPayload>("ufrag", "pwd", std::move(fingerprints)), 123, true));
|
||||
} else if (op == "lgc") {
|
||||
send_request(td_api::make_object<td_api::leaveGroupCall>(args, 123));
|
||||
} else if (op == "dgc") {
|
||||
|
Loading…
Reference in New Issue
Block a user