diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 46d64e19a..c4a542d59 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -455,7 +455,7 @@ chatMemberStatusCreator custom_title:string is_anonymous:Bool is_member:Bool = C //@description The user is a member of a chat and has some additional privileges. In basic groups, administrators can edit and delete messages sent by others, add new members, ban unprivileged members, and manage voice chats. In supergroups and channels, there are more detailed options for administrator privileges //@custom_title A custom title of the administrator; 0-16 characters without emojis; applicable to supergroups only //@can_be_edited True, if the current user can edit the administrator privileges for the called user -//@can_manage_chat True, if the administrator can get chat event log, get chat statistics, get message statistics in channels, get channel members, see anonymous administrators in supergoups and ignore slow mode. Implied by any other privilege; applicable to supergroups and channels only +//@can_manage_chat True, if the administrator can get chat event log, get chat statistics, get message statistics in channels, get channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other privilege; applicable to supergroups and channels only //@can_change_info True, if the administrator can change the chat title, photo, and other settings //@can_post_messages True, if the administrator can create channel posts; applicable to channels only //@can_edit_messages True, if the administrator can edit messages of other users and pin messages; applicable to channels only @@ -4604,6 +4604,12 @@ leaveGroupCall group_call_id:int32 = Ok; //@description Discards a group call. Requires groupCall.can_be_managed @group_call_id Group call identifier discardGroupCall group_call_id:int32 = Ok; +//@description Returns a file with a segment of a group call stream in a modified OGG format +//@group_call_id Group call identifier +//@time_offset Point in time when the stream segment begins; Unix timestamp in milliseconds +//@scale Segment duration scale; 0-1. Segment's duration is 1000/(2**scale) milliseconds +getGroupCallStreamSegment group_call_id:int32 time_offset:int53 scale:int32 = FilePart; + //@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 index 149d81a39..53209ba6f 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -32,6 +32,42 @@ namespace td { +class GetGroupCallStreamQuery : public Td::ResultHandler { + Promise promise_; + + public: + explicit GetGroupCallStreamQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(InputGroupCallId input_group_call_id, DcId stream_dc_id, int64 time_offset, int32 scale) { + auto input_stream = make_tl_object(input_group_call_id.get_input_group_call(), + time_offset, scale); + int32 flags = 0; + send_query(G()->net_query_creator().create( + telegram_api::upload_getFile(flags, false /*ignored*/, false /*ignored*/, std::move(input_stream), 0, 1 << 20), + stream_dc_id, NetQuery::Type::DownloadSmall)); + } + + 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(); + if (ptr->get_id() != telegram_api::upload_file::ID) { + return on_error(id, Status::Error(500, "Receive unexpected server response")); + } + + auto file = move_tl_object_as(ptr); + promise_.set_value(file->bytes_.as_slice().str()); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + class GetGroupCallJoinAsQuery : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; @@ -1653,6 +1689,21 @@ int32 GroupCallManager::cancel_join_group_call_request(InputGroupCallId input_gr return audio_source; } +void GroupCallManager::get_group_call_stream_segment(GroupCallId group_call_id, int64 time_offset, int32 scale, + 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); + CHECK(group_call != nullptr); + + if (!group_call->stream_dc_id.is_exact()) { + return promise.set_error(Status::Error(400, "Group call can't be streamed")); + } + + td_->create_handler(std::move(promise)) + ->send(input_group_call_id, group_call->stream_dc_id, time_offset, scale); +} + void GroupCallManager::join_group_call(GroupCallId group_call_id, DialogId as_dialog_id, td_api::object_ptr &&payload, int32 audio_source, bool is_muted, diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index 491097f5c..6f4e32679 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -56,6 +56,9 @@ class GroupCallManager : public Actor { void reload_group_call(InputGroupCallId input_group_call_id, Promise> &&promise); + void get_group_call_stream_segment(GroupCallId group_call_id, int64 time_offset, int32 scale, + Promise &&promise); + void join_group_call(GroupCallId group_call_id, DialogId as_dialog_id, td_api::object_ptr &&payload, int32 audio_source, bool is_muted, Promise> &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index bc1b04a4b..b902b099d 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6065,6 +6065,22 @@ void Td::on_request(uint64 id, const td_api::discardGroupCall &request) { group_call_manager_->discard_group_call(GroupCallId(request.group_call_id_), std::move(promise)); } +void Td::on_request(uint64 id, const td_api::getGroupCallStreamSegment &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + td_api::object_ptr file_part; + file_part->data_ = result.move_as_ok(); + promise.set_value(std::move(file_part)); + } + }); + group_call_manager_->get_group_call_stream_segment(GroupCallId(request.group_call_id_), request.time_offset_, + request.scale_, std::move(query_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 eba92303c..4e0cfdd77 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -725,6 +725,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::discardGroupCall &request); + void on_request(uint64 id, const td_api::getGroupCallStreamSegment &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 a84803e86..a6a5dbb80 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2671,6 +2671,9 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_chat_id(args))); } else if (op == "ggc") { send_request(td_api::make_object(as_group_call_id(args))); + } else if (op == "ggcss") { + send_request(td_api::make_object(as_group_call_id(args), + (std::time(nullptr) - 5) * 1000, 0)); } else if (op == "jgc") { string group_call_id; string participant_alias; diff --git a/td/telegram/files/FileManager.cpp b/td/telegram/files/FileManager.cpp index fc9c8de5f..fc184db27 100644 --- a/td/telegram/files/FileManager.cpp +++ b/td/telegram/files/FileManager.cpp @@ -3167,8 +3167,8 @@ Result FileManager::get_map_thumbnail_file_id(Location location, int32 z x = clamp(x, 0, size - 1); // just in case y = clamp(y, 0, size - 1); // just in case - string conversion = PSTRING() << "#map#" << zoom << "#" << x << "#" << y << "#" << width << "#" << height << "#" - << scale << "#"; + string conversion = PSTRING() << "#map#" << zoom << '#' << x << '#' << y << '#' << width << '#' << height << '#' + << scale << '#'; return register_generate( owner_dialog_id.get_type() == DialogType::SecretChat ? FileType::EncryptedThumbnail : FileType::Thumbnail, FileLocationSource::FromUser, string(), std::move(conversion), owner_dialog_id, 0);