Remove support for secret chat layers before 73.

This commit is contained in:
levlam 2021-06-08 18:31:29 +03:00
parent c30ccd1e0d
commit d80b613b55
10 changed files with 38 additions and 126 deletions

View File

@ -668,7 +668,7 @@ secretChatStateClosed = SecretChatState;
//@is_outbound True, if the chat was created by the current user; otherwise false
//@key_hash Hash of the currently used key for comparison with the hash of the chat partner's key. This is a string of 36 little-endian bytes, which must be split into groups of 2 bits, each denoting a pixel of one of 4 colors FFFFFF, D5E6F3, 2D5775, and 2F99C9.
//-The pixels must be used to make a 12x12 square image filled from left to right, top to bottom. Alternatively, the first 32 bytes of the hash can be converted to the hexadecimal format and printed as 32 2-digit hex numbers
//@layer Secret chat layer; determines features supported by the chat partner's application. Video notes are supported if the layer >= 66; nested text entities and underline and strikethrough entities are supported if the layer >= 101
//@layer Secret chat layer; determines features supported by the chat partner's application. Nested text entities and underline and strikethrough entities are supported if the layer >= 101
secretChat id:int32 user_id:int32 state:SecretChatState is_outbound:Bool key_hash:bytes layer:int32 = SecretChat;

View File

@ -18,7 +18,6 @@
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/misc.h"
#include "td/telegram/secret_api.h"
#include "td/telegram/SecretChatActor.h"
#include "td/telegram/Td.h"
#include "td/telegram/td_api.h"
#include "td/telegram/TdDb.h"
@ -402,8 +401,7 @@ tl_object_ptr<telegram_api::InputMedia> AnimationsManager::get_input_media(
SecretInputMedia AnimationsManager::get_secret_input_media(FileId animation_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
const string &caption, BufferSlice thumbnail,
int32 layer) const {
const string &caption, BufferSlice thumbnail) const {
auto *animation = get_animation(animation_file_id);
CHECK(animation != nullptr);
auto file_view = td_->file_manager_->get_file_view(animation_file_id);
@ -425,13 +423,8 @@ SecretInputMedia AnimationsManager::get_secret_input_media(FileId animation_file
attributes.push_back(make_tl_object<secret_api::documentAttributeFilename>(animation->file_name));
}
if (animation->duration != 0 && animation->mime_type == "video/mp4") {
if (layer >= SecretChatActor::VIDEO_NOTES_LAYER) {
attributes.push_back(make_tl_object<secret_api::documentAttributeVideo66>(
0, false, animation->duration, animation->dimensions.width, animation->dimensions.height));
} else {
attributes.push_back(make_tl_object<secret_api::documentAttributeVideo>(
animation->duration, animation->dimensions.width, animation->dimensions.height));
}
attributes.push_back(make_tl_object<secret_api::documentAttributeVideo66>(
0, false, animation->duration, animation->dimensions.width, animation->dimensions.height));
}
if (animation->dimensions.width != 0 && animation->dimensions.height != 0) {
attributes.push_back(make_tl_object<secret_api::documentAttributeImageSize>(animation->dimensions.width,

View File

@ -45,7 +45,7 @@ class AnimationsManager : public Actor {
SecretInputMedia get_secret_input_media(FileId animation_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
const string &caption, BufferSlice thumbnail, int32 layer) const;
const string &caption, BufferSlice thumbnail) const;
FileId get_animation_thumbnail_file_id(FileId file_id) const;

View File

@ -50,7 +50,6 @@
#include "td/telegram/PollId.hpp"
#include "td/telegram/PollManager.h"
#include "td/telegram/secret_api.hpp"
#include "td/telegram/SecretChatActor.h"
#include "td/telegram/SecureValue.h"
#include "td/telegram/SecureValue.hpp"
#include "td/telegram/StickersManager.h"
@ -2061,12 +2060,12 @@ bool can_have_input_media(const Td *td, const MessageContent *content) {
SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
BufferSlice thumbnail, int32 layer) {
BufferSlice thumbnail) {
switch (content->get_type()) {
case MessageContentType::Animation: {
auto m = static_cast<const MessageAnimation *>(content);
return td->animations_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text,
std::move(thumbnail), layer);
std::move(thumbnail));
}
case MessageContentType::Audio: {
auto m = static_cast<const MessageAudio *>(content);
@ -2112,8 +2111,7 @@ SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td,
}
case MessageContentType::VideoNote: {
auto m = static_cast<const MessageVideoNote *>(content);
return td->video_notes_manager_->get_secret_input_media(m->file_id, std::move(input_file), std::move(thumbnail),
layer);
return td->video_notes_manager_->get_secret_input_media(m->file_id, std::move(input_file), std::move(thumbnail));
}
case MessageContentType::VoiceNote: {
auto m = static_cast<const MessageVoiceNote *>(content);
@ -2567,10 +2565,6 @@ Status can_send_message_content(DialogId dialog_id, const MessageContent *conten
if (!permissions.can_send_media()) {
return Status::Error(400, "Not enough rights to send video notes to the chat");
}
if (secret_chat_layer < SecretChatActor::VIDEO_NOTES_LAYER) {
return Status::Error(400, PSLICE()
<< "Video notes can't be sent to secret chat with layer " << secret_chat_layer);
}
break;
case MessageContentType::VoiceNote:
if (!permissions.can_send_media()) {

View File

@ -107,7 +107,7 @@ bool can_have_input_media(const Td *td, const MessageContent *content);
SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
BufferSlice thumbnail, int32 layer);
BufferSlice thumbnail);
tl_object_ptr<telegram_api::InputMedia> get_input_media(const MessageContent *content, Td *td,
tl_object_ptr<telegram_api::InputFile> input_file,

View File

@ -8647,11 +8647,10 @@ void MessagesManager::do_send_secret_media(DialogId dialog_id, Message *m, FileI
LOG(INFO) << "Do send secret media file " << file_id << " with thumbnail " << thumbnail_file_id
<< ", have_input_file = " << have_input_file;
auto layer = td_->contacts_manager_->get_secret_chat_layer(dialog_id.get_secret_chat_id());
on_secret_message_media_uploaded(
dialog_id, m,
get_secret_input_media(m->content.get(), td_, std::move(input_encrypted_file), std::move(thumbnail), layer),
file_id, thumbnail_file_id);
get_secret_input_media(m->content.get(), td_, std::move(input_encrypted_file), std::move(thumbnail)), file_id,
thumbnail_file_id);
}
void MessagesManager::on_upload_media_error(FileId file_id, Status status) {
@ -23422,8 +23421,7 @@ void MessagesManager::do_send_message(DialogId dialog_id, const Message *m, vect
LOG(DEBUG) << "Need to send file " << file_id << " with thumbnail " << thumbnail_file_id;
if (is_secret) {
CHECK(!is_edit);
auto layer = td_->contacts_manager_->get_secret_chat_layer(dialog_id.get_secret_chat_id());
auto secret_input_media = get_secret_input_media(content, td_, nullptr, BufferSlice(), layer);
auto secret_input_media = get_secret_input_media(content, td_, nullptr, BufferSlice());
if (secret_input_media.empty()) {
LOG(INFO) << "Ask to upload encrypted file " << file_id;
CHECK(file_view.is_encrypted_secret());
@ -23862,7 +23860,7 @@ void MessagesManager::on_text_message_ready_to_send(DialogId dialog_id, MessageI
auto layer = td_->contacts_manager_->get_secret_chat_layer(dialog_id.get_secret_chat_id());
send_closure(td_->create_net_actor<SendSecretMessageActor>(), &SendSecretMessageActor::send, dialog_id,
m->reply_to_random_id, m->ttl, message_text->text,
get_secret_input_media(content, td_, nullptr, BufferSlice(), layer),
get_secret_input_media(content, td_, nullptr, BufferSlice()),
get_input_secret_message_entities(message_text->entities, layer), m->via_bot_user_id,
m->media_album_id, m->disable_notification, random_id);
} else {

View File

@ -180,7 +180,7 @@ void SecretChatActor::replay_inbound_message(unique_ptr<log_event::InboundSecret
CHECK(!binlog_replay_finish_flag_);
CHECK(message->decrypted_message_layer); // from binlog
if (message->is_pending) { // wait for gaps?
// check_status(do_inbound_message_decrypted_unchecked(std::move(message)));
// check_status(do_inbound_message_decrypted_unchecked(std::move(message)), -1);
do_inbound_message_decrypted_pending(std::move(message));
} else { // just replay
LOG_CHECK(message->message_id > last_binlog_message_id_)
@ -208,31 +208,23 @@ void SecretChatActor::replay_outbound_message(unique_ptr<log_event::OutboundSecr
}
// NB: my_seq_no is just after message is sent, i.e. my_out_seq_no is already incremented
Result<BufferSlice> SecretChatActor::create_encrypted_message(int32 layer, int32 my_in_seq_no, int32 my_out_seq_no,
Result<BufferSlice> SecretChatActor::create_encrypted_message(int32 my_in_seq_no, int32 my_out_seq_no,
tl_object_ptr<secret_api::DecryptedMessage> &message) {
if (message->get_id() == secret_api::decryptedMessage::ID && layer < MTPROTO_2_LAYER) {
auto old = secret_api::move_object_as<secret_api::decryptedMessage>(message);
old->flags_ &= ~secret_api::decryptedMessage::GROUPED_ID_MASK;
message = secret_api::make_object<secret_api::decryptedMessage46>(
old->flags_, old->random_id_, old->ttl_, std::move(old->message_), std::move(old->media_),
std::move(old->entities_), std::move(old->via_bot_name_), old->reply_to_random_id_);
}
mtproto::AuthKey *auth_key = &pfs_state_.auth_key;
auto in_seq_no = my_in_seq_no * 2 + auth_state_.x;
auto out_seq_no = my_out_seq_no * 2 - 1 - auth_state_.x;
auto layer = current_layer();
BufferSlice random_bytes(32);
Random::secure_bytes(random_bytes.as_slice().ubegin(), random_bytes.size());
auto message_with_layer = secret_api::make_object<secret_api::decryptedMessageLayer>(
std::move(random_bytes), layer, in_seq_no, out_seq_no, std::move(message));
LOG(INFO) << to_string(message_with_layer);
LOG(INFO) << "Create message " << to_string(message_with_layer);
auto storer = create_storer(*message_with_layer);
auto new_storer = mtproto::PacketStorer<SecretImpl>(storer);
mtproto::PacketInfo info;
info.type = mtproto::PacketInfo::EndToEnd;
// Send with mtproto 2.0 if current layer is at least MTPROTO_2_LAYER
info.version = layer >= MTPROTO_2_LAYER ? 2 : 1;
info.version = 2;
info.is_creator = auth_state_.x == 0;
auto packet_writer = BufferWriter{mtproto::Transport::write(new_storer, *auth_key, &info), 0, 0};
mtproto::Transport::write(new_storer, *auth_key, &info, packet_writer.as_slice());
@ -249,63 +241,6 @@ void SecretChatActor::send_message(tl_object_ptr<secret_api::DecryptedMessage> m
send_message_impl(std::move(message), std::move(file), SendFlag::External | SendFlag::Push, std::move(promise));
}
static int32 get_min_layer(const secret_api::decryptedMessageActionTyping &message) {
switch (message.action_->get_id()) {
case secret_api::sendMessageRecordRoundAction::ID:
case secret_api::sendMessageUploadRoundAction::ID:
return SecretChatActor::VIDEO_NOTES_LAYER;
}
return 0;
}
static int32 get_min_layer(const secret_api::decryptedMessageService &message) {
switch (message.action_->get_id()) {
case secret_api::decryptedMessageActionTyping::ID:
return get_min_layer(static_cast<const secret_api::decryptedMessageActionTyping &>(*message.action_));
default:
return 0;
}
}
static int32 get_min_layer(const secret_api::DocumentAttribute &attribute) {
switch (attribute.get_id()) {
case secret_api::documentAttributeVideo66::ID:
return SecretChatActor::VIDEO_NOTES_LAYER;
default:
return 0;
}
}
static int32 get_min_layer(const secret_api::decryptedMessageMediaDocument &message) {
int32 res = 0;
for (auto &attribute : message.attributes_) {
auto attrirbute_layer = get_min_layer(*attribute);
if (attrirbute_layer > res) {
res = attrirbute_layer;
}
return res;
}
return res;
}
static int32 get_min_layer(const secret_api::decryptedMessage &message) {
if (!message.media_) {
return 0;
}
switch (message.media_->get_id()) {
case secret_api::decryptedMessageMediaDocument::ID:
return get_min_layer(static_cast<const secret_api::decryptedMessageMediaDocument &>(*message.media_));
default:
return 0;
}
}
static int32 get_min_layer(const secret_api::DecryptedMessage &message) {
switch (message.get_id()) {
case secret_api::decryptedMessageService::ID:
return get_min_layer(static_cast<const secret_api::decryptedMessageService &>(message));
case secret_api::decryptedMessage::ID:
return get_min_layer(static_cast<const secret_api::decryptedMessage &>(message));
default:
return 0;
}
}
void SecretChatActor::send_message_impl(tl_object_ptr<secret_api::DecryptedMessage> message,
tl_object_ptr<telegram_api::InputEncryptedFile> file, int32 flags,
Promise<> promise) {
@ -317,16 +252,11 @@ void SecretChatActor::send_message_impl(tl_object_ptr<secret_api::DecryptedMessa
LOG(ERROR) << "Ignore send_message: " << tag("message", to_string(message)) << tag("file", to_string(file));
return promise.set_error(Status::Error(400, "Chat is not accessible"));
}
if (get_min_layer(*message) > config_state_.his_layer) {
return promise.set_error(Status::Error(400, "Message is not supported by the other side"));
}
LOG_CHECK(binlog_replay_finish_flag_) << "Trying to send message before binlog replay is finished: "
<< to_string(*message) << to_string(file);
int64 random_id = 0;
downcast_call(*message, [&](auto &x) { random_id = x.random_id_; });
LOG(INFO) << "Send message: " << to_string(message) << to_string(file);
auto it = random_id_to_outbound_message_state_token_.find(random_id);
if (it != random_id_to_outbound_message_state_token_.end()) {
return on_outbound_outer_send_message_promise(it->second, std::move(promise));
@ -341,8 +271,7 @@ void SecretChatActor::send_message_impl(tl_object_ptr<secret_api::DecryptedMessa
binlog_event->my_out_seq_no = seq_no_state_.my_out_seq_no + 1;
binlog_event->his_in_seq_no = seq_no_state_.his_in_seq_no;
binlog_event->encrypted_message =
create_encrypted_message(current_layer(), binlog_event->my_in_seq_no, binlog_event->my_out_seq_no, message)
.move_as_ok();
create_encrypted_message(binlog_event->my_in_seq_no, binlog_event->my_out_seq_no, message).move_as_ok();
binlog_event->need_notify_user = (flags & SendFlag::Push) == 0;
binlog_event->is_external = (flags & SendFlag::External) != 0;
binlog_event->is_silent = (message->get_id() == secret_api::decryptedMessage::ID &&
@ -617,7 +546,7 @@ void SecretChatActor::run_fill_gaps() {
LOG(INFO) << "Replay pending event: " << tag("seq_no", next_seq_no);
auto message = std::move(begin->second);
pending_inbound_messages_.erase(begin);
check_status(do_inbound_message_decrypted_unchecked(std::move(message)));
check_status(do_inbound_message_decrypted_unchecked(std::move(message), -1));
CHECK(pending_inbound_messages_.find(next_seq_no) == pending_inbound_messages_.end());
} else {
break;
@ -861,7 +790,6 @@ Result<std::tuple<uint64, BufferSlice, int32>> SecretChatActor::decrypt(BufferSl
int32 mtproto_version = -1;
Result<mtproto::Transport::ReadResult> r_read_result;
for (size_t i = 0; i < versions.size(); i++) {
bool is_last = i + 1 == versions.size();
encrypted_message_copy = encrypted_message.copy();
data = encrypted_message_copy.as_slice();
CHECK(is_aligned_pointer<4>(data.data()));
@ -872,7 +800,7 @@ Result<std::tuple<uint64, BufferSlice, int32>> SecretChatActor::decrypt(BufferSl
info.version = mtproto_version;
info.is_creator = auth_state_.x == 0;
r_read_result = mtproto::Transport::read(data, *auth_key, &info);
if (!is_last && r_read_result.is_error()) {
if (i + 1 == versions.size() && r_read_result.is_error()) {
LOG(WARNING) << tag("mtproto", mtproto_version) << " decryption failed " << r_read_result.error();
continue;
}
@ -883,7 +811,7 @@ Result<std::tuple<uint64, BufferSlice, int32>> SecretChatActor::decrypt(BufferSl
case mtproto::Transport::ReadResult::Quickack:
return Status::Error("Got quickack instead of a message");
case mtproto::Transport::ReadResult::Error:
return Status::Error(PSLICE() << "Got mtproto error code instead of a message: " << read_result.error());
return Status::Error(PSLICE() << "Got MTProto error code instead of a message: " << read_result.error());
case mtproto::Transport::ReadResult::Nop:
return Status::Error("Got nop instead of a message");
case mtproto::Transport::ReadResult::Packet:
@ -938,7 +866,7 @@ Status SecretChatActor::do_inbound_message_encrypted(unique_ptr<log_event::Inbou
return Status::Error(PSLICE() << "Invalid seq_no: " << to_string(message_with_layer));
}
message->decrypted_message_layer = std::move(message_with_layer);
return do_inbound_message_decrypted_unchecked(std::move(message));
return do_inbound_message_decrypted_unchecked(std::move(message), mtproto_version);
} else {
status = Status::Error(PSLICE() << parser.get_error() << format::as_hex_dump<4>(data_buffer.as_slice()));
}
@ -958,7 +886,7 @@ Status SecretChatActor::do_inbound_message_encrypted(unique_ptr<log_event::Inbou
if (!new_parser.get_error()) {
message->decrypted_message_layer = secret_api::make_object<secret_api::decryptedMessageLayer>(
BufferSlice(), config_state_.his_layer, -1, -1, std::move(message_without_layer));
return do_inbound_message_decrypted_unchecked(std::move(message));
return do_inbound_message_decrypted_unchecked(std::move(message), mtproto_version);
}
LOG(ERROR) << "Failed to fetch update (DecryptedMessage): " << new_parser.get_error()
<< format::as_hex_dump<4>(data_buffer.as_slice());
@ -995,7 +923,8 @@ Status SecretChatActor::check_seq_no(int in_seq_no, int out_seq_no, int32 his_la
return Status::OK();
}
Status SecretChatActor::do_inbound_message_decrypted_unchecked(unique_ptr<log_event::InboundSecretMessage> message) {
Status SecretChatActor::do_inbound_message_decrypted_unchecked(unique_ptr<log_event::InboundSecretMessage> message,
int32 mtproto_version) {
SCOPE_EXIT {
CHECK(message == nullptr || !message->promise);
};
@ -1056,7 +985,8 @@ Status SecretChatActor::do_inbound_message_decrypted_unchecked(unique_ptr<log_ev
}
}
LOG(INFO) << "GOT MESSAGE " << to_string(message->decrypted_message_layer);
LOG(INFO) << "Receive message encrypted with MTProto " << mtproto_version << ": "
<< to_string(message->decrypted_message_layer);
if (status.is_error()) {
CHECK(status.code() == 2); // gap found
@ -1592,8 +1522,8 @@ Status SecretChatActor::outbound_rewrite_with_empty(uint64 state_id) {
state->message->random_id, secret_api::make_object<secret_api::decryptedMessageActionDeleteMessages>(
std::vector<int64>{static_cast<int64>(state->message->random_id)}));
TRY_RESULT(encrypted_message, create_encrypted_message(current_layer(), state->message->my_in_seq_no,
state->message->my_out_seq_no, message));
TRY_RESULT(encrypted_message,
create_encrypted_message(state->message->my_in_seq_no, state->message->my_out_seq_no, message));
state->message->encrypted_message = std::move(encrypted_message);
LOG(INFO) << tag("crc", crc64(state->message->encrypted_message.as_slice()));
state->message->is_rewritable = false;
@ -1820,7 +1750,7 @@ void SecretChatActor::on_outbound_outer_send_message_promise(uint64 state_id, Pr
}
auto *state = outbound_message_states_.get(state_id);
CHECK(state);
LOG(INFO) << "Outbound secret message [TODO] " << tag("log_event_id", state->message->log_event_id());
LOG(INFO) << "Outbound secret message " << tag("log_event_id", state->message->log_event_id());
promise.set_value(Unit()); // Seems like this message is at least stored to binlog already
if (state->send_result_) {
state->send_result_({});
@ -1918,7 +1848,6 @@ Status SecretChatActor::on_update_chat(telegram_api::encryptedChat &update) {
// NB: order is important
context_->secret_chat_db()->set_value(pfs_state_);
context_->secret_chat_db()->set_value(auth_state_);
LOG(INFO) << "OK! Ready!";
send_update_secret_chat();
send_action(secret_api::make_object<secret_api::decryptedMessageActionNotifyLayer>(MY_LAYER), SendFlag::None,
Promise<>());

View File

@ -48,10 +48,8 @@ class NetQueryCreator;
class SecretChatActor : public NetQueryCallback {
public:
// do not change DEFAULT_LAYER, unless all it's usages are fixed
enum : int32 {
DEFAULT_LAYER = 46,
VIDEO_NOTES_LAYER = 66,
DEFAULT_LAYER = 73,
MTPROTO_2_LAYER = 73,
NEW_ENTITIES_LAYER = 101,
DELETE_MESSAGES_ON_CLOSE_LAYER = 123,
@ -571,7 +569,8 @@ class SecretChatActor : public NetQueryCallback {
Result<std::tuple<uint64, BufferSlice, int32>> decrypt(BufferSlice &encrypted_message);
Status do_inbound_message_encrypted(unique_ptr<log_event::InboundSecretMessage> message);
Status do_inbound_message_decrypted_unchecked(unique_ptr<log_event::InboundSecretMessage> message);
Status do_inbound_message_decrypted_unchecked(unique_ptr<log_event::InboundSecretMessage> message,
int32 mtproto_version);
Status do_inbound_message_decrypted(unique_ptr<log_event::InboundSecretMessage> message);
void do_inbound_message_decrypted_pending(unique_ptr<log_event::InboundSecretMessage> message);
@ -620,7 +619,8 @@ class SecretChatActor : public NetQueryCallback {
tl_object_ptr<telegram_api::InputEncryptedFile> file, int32 flags, Promise<> promise);
void do_outbound_message_impl(unique_ptr<log_event::OutboundSecretMessage>, Promise<> promise);
Result<BufferSlice> create_encrypted_message(int32 layer, int32 my_in_seq_no, int32 my_out_seq_no,
Result<BufferSlice> create_encrypted_message(int32 my_in_seq_no, int32 my_out_seq_no,
tl_object_ptr<secret_api::DecryptedMessage> &message);
NetQueryPtr create_net_query(const log_event::OutboundSecretMessage &message);

View File

@ -9,7 +9,6 @@
#include "td/telegram/AuthManager.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/secret_api.h"
#include "td/telegram/SecretChatActor.h"
#include "td/telegram/Td.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -167,7 +166,7 @@ void VideoNotesManager::create_video_note(FileId file_id, string minithumbnail,
SecretInputMedia VideoNotesManager::get_secret_input_media(FileId video_note_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
BufferSlice thumbnail, int32 layer) const {
BufferSlice thumbnail) const {
const VideoNote *video_note = get_video_note(video_note_file_id);
CHECK(video_note != nullptr);
auto file_view = td_->file_manager_->get_file_view(video_note_file_id);
@ -184,7 +183,6 @@ SecretInputMedia VideoNotesManager::get_secret_input_media(FileId video_note_fil
if (video_note->thumbnail.file_id.is_valid() && thumbnail.empty()) {
return SecretInputMedia{};
}
CHECK(layer >= SecretChatActor::VIDEO_NOTES_LAYER);
vector<tl_object_ptr<secret_api::DocumentAttribute>> attributes;
attributes.push_back(make_tl_object<secret_api::documentAttributeVideo66>(
secret_api::documentAttributeVideo66::ROUND_MESSAGE_MASK, true, video_note->duration,

View File

@ -39,7 +39,7 @@ class VideoNotesManager {
SecretInputMedia get_secret_input_media(FileId video_note_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
BufferSlice thumbnail, int32 layer) const;
BufferSlice thumbnail) const;
FileId get_video_note_thumbnail_file_id(FileId file_id) const;