diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index edc98ba37..4b02bb460 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -1527,7 +1527,7 @@ scopeNotificationSettings mute_for:int32 sound_id:int64 show_preview:Bool use_de //@description Contains information about a message draft //@reply_to Information about the message to be replied; must be of the type inputMessageReplyToMessage; may be null if none //@date Point in time (Unix timestamp) when the draft was created -//@input_message_text Content of the message draft; must be of the type inputMessageText +//@input_message_text Content of the message draft; must be of the type inputMessageText, or inputMessageVideoNote draftMessage reply_to:InputMessageReplyTo date:int32 input_message_text:InputMessageContent = DraftMessage; @@ -8206,7 +8206,10 @@ deleteChatBackground chat_id:int53 restore_previous:Bool = Ok; //@description Changes the chat theme. Supported only in private and secret chats @chat_id Chat identifier @theme_name Name of the new chat theme; pass an empty string to return the default theme setChatTheme chat_id:int53 theme_name:string = Ok; -//@description Changes the draft message in a chat @chat_id Chat identifier @message_thread_id If not 0, a message thread identifier in which the draft was changed @draft_message New draft message; pass null to remove the draft +//@description Changes the draft message in a chat +//@chat_id Chat identifier +//@message_thread_id If not 0, a message thread identifier in which the draft was changed +//@draft_message New draft message; pass null to remove the draft. All files in draft message content must be of the type inputFileLocal. Media thumbnails and captions are ignored setChatDraftMessage chat_id:int53 message_thread_id:int53 draft_message:draftMessage = Ok; //@description Changes the notification settings of a chat. Notification settings of a chat with the current user (Saved Messages) can't be changed diff --git a/td/telegram/DraftMessage.cpp b/td/telegram/DraftMessage.cpp index ad0cdf8f6..c9f91b9d5 100644 --- a/td/telegram/DraftMessage.cpp +++ b/td/telegram/DraftMessage.cpp @@ -11,6 +11,7 @@ #include "td/telegram/DialogManager.h" #include "td/telegram/Global.h" #include "td/telegram/MessageEntity.h" +#include "td/telegram/MessageSelfDestructType.h" #include "td/telegram/MessagesManager.h" #include "td/telegram/misc.h" #include "td/telegram/Td.h" @@ -44,6 +45,7 @@ class SaveDraftMessageQuery final : public Td::ResultHandler { vector> input_message_entities; telegram_api::object_ptr media; if (draft_message != nullptr) { + CHECK(!draft_message->is_local()); input_reply_to = draft_message->message_input_reply_to_.get_input_reply_to(td_, MessageId() /*TODO*/); if (input_reply_to != nullptr) { flags |= telegram_api::messages_saveDraft::REPLY_TO_MASK; @@ -152,7 +154,131 @@ class ClearAllDraftsQuery final : public Td::ResultHandler { } }; +class DraftMessageContentVideoNote final : public DraftMessageContent { + public: + string path_; + int32 duration_ = 0; + int32 length_ = 0; + MessageSelfDestructType ttl_; + + DraftMessageContentVideoNote() = default; + + DraftMessageContentVideoNote(string &&path, int32 duration, int32 length, MessageSelfDestructType ttl) + : path_(std::move(path)), duration_(duration), length_(length), ttl_(ttl) { + } + + DraftMessageContentType get_type() const final { + return DraftMessageContentType::VideoNote; + } + + td_api::object_ptr get_input_message_content_object() const final { + return td_api::make_object(td_api::make_object(path_), + nullptr, duration_, length_, + ttl_.get_message_self_desctruct_type_object()); + } + + template + void store(StorerT &storer) const { + bool has_path = !path_.empty(); + bool has_duration = duration_ != 0; + bool has_length = length_ != 0; + bool has_ttl = ttl_.is_valid(); + BEGIN_STORE_FLAGS(); + STORE_FLAG(has_path); + STORE_FLAG(has_duration); + STORE_FLAG(has_length); + STORE_FLAG(has_ttl); + END_STORE_FLAGS(); + if (has_path) { + td::store(path_, storer); + } + if (has_duration) { + td::store(duration_, storer); + } + if (has_length) { + td::store(length_, storer); + } + if (has_ttl) { + td::store(ttl_, storer); + } + } + + template + void parse(ParserT &parser) { + bool has_path = !path_.empty(); + bool has_duration = duration_ != 0; + bool has_length = length_ != 0; + bool has_ttl = ttl_.is_valid(); + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(has_path); + PARSE_FLAG(has_duration); + PARSE_FLAG(has_length); + PARSE_FLAG(has_ttl); + END_PARSE_FLAGS(); + if (has_path) { + td::parse(path_, parser); + } + if (has_duration) { + td::parse(duration_, parser); + } + if (has_length) { + td::parse(length_, parser); + } + if (has_ttl) { + td::parse(ttl_, parser); + } + } +}; + +template +static void store(const DraftMessageContent *content, StorerT &storer) { + CHECK(content != nullptr); + + auto content_type = content->get_type(); + store(content_type, storer); + + switch (content_type) { + case DraftMessageContentType::VideoNote: { + const auto *video_note = static_cast(content); + video_note->store(storer); + break; + } + default: + UNREACHABLE(); + } +} + +void store_draft_message_content(const DraftMessageContent *content, LogEventStorerCalcLength &storer) { + store(content, storer); +} + +void store_draft_message_content(const DraftMessageContent *content, LogEventStorerUnsafe &storer) { + store(content, storer); +} + +void parse_draft_message_content(unique_ptr &content, LogEventParser &parser) { + DraftMessageContentType type; + parse(type, parser); + switch (type) { + case DraftMessageContentType::VideoNote: { + unique_ptr video_note; + parse(video_note, parser); + content = std::move(video_note); + break; + } + default: + parser.set_error("Wrong draft content type"); + } +} + +DraftMessage::DraftMessage() = default; + +DraftMessage::~DraftMessage() = default; + bool DraftMessage::need_update_to(const DraftMessage &other, bool from_update) const { + if (is_local()) { + return !from_update || other.is_local(); + } if (message_input_reply_to_ == other.message_input_reply_to_ && input_message_text_ == other.input_message_text_) { return date_ < other.date_; } else { @@ -166,8 +292,14 @@ void DraftMessage::add_dependencies(Dependencies &dependencies) const { } td_api::object_ptr DraftMessage::get_draft_message_object(Td *td) const { + td_api::object_ptr input_message_content; + if (local_content_ != nullptr) { + input_message_content = local_content_->get_input_message_content_object(); + } else { + input_message_content = input_message_text_.get_input_message_text_object(); + } return td_api::make_object(message_input_reply_to_.get_input_message_reply_to_object(td), date_, - input_message_text_.get_input_message_text_object()); + std::move(input_message_content)); } DraftMessage::DraftMessage(Td *td, telegram_api::object_ptr &&draft_message) { @@ -218,15 +350,32 @@ Result> DraftMessage::get_draft_message( auto input_message_content = std::move(draft_message->input_message_text_); if (input_message_content != nullptr) { - if (input_message_content->get_id() != td_api::inputMessageText::ID) { - return Status::Error(400, "Input message content type must be InputMessageText"); + switch (input_message_content->get_id()) { + case td_api::inputMessageText::ID: { + TRY_RESULT(input_message_text, + process_input_message_text(td, dialog_id, std::move(input_message_content), false, true)); + result->input_message_text_ = std::move(input_message_text); + break; + } + case td_api::inputMessageVideoNote::ID: { + auto video_note = td_api::move_object_as(input_message_content); + if (video_note->video_note_ == nullptr || video_note->video_note_->get_id() != td_api::inputFileLocal::ID) { + return Status::Error(400, "Invalid video message file specified"); + } + TRY_RESULT(ttl, + MessageSelfDestructType::get_message_self_destruct_type(std::move(video_note->self_destruct_type_))); + result->local_content_ = td::make_unique( + std::move(static_cast(video_note->video_note_.get())->path_), + video_note->duration_, video_note->length_, ttl); + break; + } + default: + return Status::Error(400, "Input message content type must be InputMessageText"); } - TRY_RESULT(input_message_text, - process_input_message_text(td, dialog_id, std::move(input_message_content), false, true)); - result->input_message_text_ = std::move(input_message_text); } - if (!result->message_input_reply_to_.is_valid() && result->input_message_text_.is_empty()) { + if (!result->message_input_reply_to_.is_valid() && result->input_message_text_.is_empty() && + result->local_content_ == nullptr) { return nullptr; } @@ -234,6 +383,13 @@ Result> DraftMessage::get_draft_message( return std::move(result); } +bool is_local_draft_message(const unique_ptr &draft_message) { + if (draft_message == nullptr) { + return false; + } + return draft_message->is_local(); +} + bool need_update_draft_message(const unique_ptr &old_draft_message, const unique_ptr &new_draft_message, bool from_update) { if (new_draft_message == nullptr) { diff --git a/td/telegram/DraftMessage.h b/td/telegram/DraftMessage.h index b48313eec..bb3bf18c2 100644 --- a/td/telegram/DraftMessage.h +++ b/td/telegram/DraftMessage.h @@ -8,6 +8,7 @@ #include "td/telegram/DialogId.h" #include "td/telegram/InputMessageText.h" +#include "td/telegram/logevent/LogEvent.h" #include "td/telegram/MessageId.h" #include "td/telegram/MessageInputReplyTo.h" #include "td/telegram/td_api.h" @@ -22,21 +23,48 @@ namespace td { class Dependencies; class Td; +enum class DraftMessageContentType : int32 { VideoNote }; + +class DraftMessageContent { + public: + DraftMessageContent() = default; + DraftMessageContent(const DraftMessageContent &) = default; + DraftMessageContent &operator=(const DraftMessageContent &) = default; + DraftMessageContent(DraftMessageContent &&) = default; + DraftMessageContent &operator=(DraftMessageContent &&) = default; + + virtual DraftMessageContentType get_type() const = 0; + + virtual td_api::object_ptr get_input_message_content_object() const = 0; + + virtual ~DraftMessageContent() = default; +}; + class DraftMessage { int32 date_ = 0; MessageInputReplyTo message_input_reply_to_; InputMessageText input_message_text_; + unique_ptr local_content_; friend class SaveDraftMessageQuery; public: - DraftMessage() = default; + DraftMessage(); DraftMessage(Td *td, telegram_api::object_ptr &&draft_message); + DraftMessage(const DraftMessage &) = delete; + DraftMessage &operator=(const DraftMessage &) = delete; + DraftMessage(DraftMessage &&) = delete; + DraftMessage &operator=(DraftMessage &&) = delete; + ~DraftMessage(); int32 get_date() const { return date_; } + bool is_local() const { + return local_content_ != nullptr; + } + bool need_update_to(const DraftMessage &other, bool from_update) const; void add_dependencies(Dependencies &dependencies) const; @@ -53,6 +81,14 @@ class DraftMessage { void parse(ParserT &parser); }; +void store_draft_message_content(const DraftMessageContent *content, LogEventStorerCalcLength &storer); + +void store_draft_message_content(const DraftMessageContent *content, LogEventStorerUnsafe &storer); + +void parse_draft_message_content(unique_ptr &content, LogEventParser &parser); + +bool is_local_draft_message(const unique_ptr &draft_message); + bool need_update_draft_message(const unique_ptr &old_draft_message, const unique_ptr &new_draft_message, bool from_update); diff --git a/td/telegram/DraftMessage.hpp b/td/telegram/DraftMessage.hpp index c39181a12..c4636b9c5 100644 --- a/td/telegram/DraftMessage.hpp +++ b/td/telegram/DraftMessage.hpp @@ -18,11 +18,13 @@ namespace td { template void DraftMessage::store(StorerT &storer) const { - bool has_message_input_reply_to = !message_input_reply_to_.is_empty(); bool has_input_message_text = !input_message_text_.is_empty(); + bool has_message_input_reply_to = !message_input_reply_to_.is_empty(); + bool has_local_content = local_content_ != nullptr; BEGIN_STORE_FLAGS(); STORE_FLAG(has_input_message_text); STORE_FLAG(has_message_input_reply_to); + STORE_FLAG(has_local_content); END_STORE_FLAGS(); td::store(date_, storer); if (has_input_message_text) { @@ -31,23 +33,26 @@ void DraftMessage::store(StorerT &storer) const { if (has_message_input_reply_to) { td::store(message_input_reply_to_, storer); } + if (has_local_content) { + store_draft_message_content(local_content_.get(), storer); + } } template void DraftMessage::parse(ParserT &parser) { bool has_legacy_reply_to_message_id; - bool has_input_message_text; - bool has_message_input_reply_to; + bool has_input_message_text = true; + bool has_message_input_reply_to = false; + bool has_local_content = false; if (parser.version() >= static_cast(Version::SupportRepliesInOtherChats)) { has_legacy_reply_to_message_id = false; BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_input_message_text); PARSE_FLAG(has_message_input_reply_to); + PARSE_FLAG(has_local_content); END_PARSE_FLAGS(); } else { has_legacy_reply_to_message_id = true; - has_input_message_text = true; - has_message_input_reply_to = false; } td::parse(date_, parser); if (has_legacy_reply_to_message_id) { @@ -61,6 +66,9 @@ void DraftMessage::parse(ParserT &parser) { if (has_message_input_reply_to) { td::parse(message_input_reply_to_, parser); } + if (has_local_content) { + parse_draft_message_content(local_content_, parser); + } } } // namespace td diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 5155e37e5..107b5abd0 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -18089,7 +18089,7 @@ Status MessagesManager::set_dialog_draft_message(DialogId dialog_id, MessageId t } if (update_dialog_draft_message(d, std::move(new_draft_message), false, true)) { - if (dialog_id.get_type() != DialogType::SecretChat) { + if (dialog_id.get_type() != DialogType::SecretChat && !is_local_draft_message(d->draft_message)) { if (G()->use_message_database()) { SaveDialogDraftMessageOnServerLogEvent log_event; log_event.dialog_id_ = dialog_id; diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 98aa00885..a25afe111 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -4296,6 +4296,16 @@ class CliClient final : public Actor { } send_request( td_api::make_object(chat_id, message_thread_id_, std::move(draft_message))); + } else if (op == "scdmvn") { + ChatId chat_id; + string video_path; + get_args(args, chat_id, video_path); + send_request(td_api::make_object( + chat_id, message_thread_id_, + td_api::make_object( + nullptr, 0, + td_api::make_object(as_input_file(video_path), nullptr, 10, 5, + get_message_self_destruct_type())))); } else if (op == "cadm") { send_request(td_api::make_object()); } else if (op == "tchpc") {