Add td_api::editStory.

This commit is contained in:
levlam 2023-05-30 14:41:36 +03:00
parent 81053032d8
commit 3a6494dd65
8 changed files with 283 additions and 18 deletions

View File

@ -7169,6 +7169,12 @@ readChatList chat_list:ChatList = Ok;
//@is_pinned Pass true to keep the story accessible after expiration
sendStory content:InputStoryContent caption:formattedText privacy_rules:userPrivacySettingRules is_pinned:Bool = Story;
//@description Changes content and caption of a previously sent story
//@story_id Identifier of the story to edit
//@content New content of the story; pass null to keep the current content
//@caption New story caption; pass null to keep the current caption
editStory story_id:int32 content:InputStoryContent caption:formattedText = Ok;
//@description Changes privacy rules of a previously sent story @story_id Identifier of the story @privacy_rules The new privacy rules for the story
setStoryPrivacyRules story_id:int32 privacy_rules:userPrivacySettingRules = Ok;

View File

@ -13,6 +13,7 @@
#include "td/telegram/FullMessageId.h"
#include "td/telegram/MessageContentType.h"
#include "td/telegram/PollId.h"
#include "td/telegram/StoryFullId.h"
#include "td/telegram/UserId.h"
#include "td/utils/common.h"
@ -53,6 +54,10 @@ class ChainId {
ChainId(UserId user_id) : ChainId(DialogId(user_id)) {
}
ChainId(StoryFullId story_full_id) : ChainId(story_full_id.get_dialog_id()) {
id += static_cast<uint64>(story_full_id.get_story_id().get()) << 10;
}
uint64 get() const {
return id;
}

View File

@ -3205,7 +3205,7 @@ class SendMessageQuery final : public Td::ResultHandler {
void on_error(Status status) final {
LOG(INFO) << "Receive error for SendMessage: " << status;
if (G()->close_flag() && G()->use_message_database()) {
// do not send error, message will be re-sent
// do not send error, message will be re-sent after restart
return;
}
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "SendMessageQuery");
@ -3256,7 +3256,7 @@ class StartBotQuery final : public Td::ResultHandler {
void on_error(Status status) final {
LOG(INFO) << "Receive error for StartBotQuery: " << status;
if (G()->close_flag() && G()->use_message_database()) {
// do not send error, message should be re-sent
// do not send error, message should be re-sent after restart
return;
}
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "StartBotQuery");
@ -3310,7 +3310,7 @@ class SendInlineBotResultQuery final : public Td::ResultHandler {
void on_error(Status status) final {
LOG(INFO) << "Receive error for SendInlineBotResultQuery: " << status;
if (G()->close_flag() && G()->use_message_database()) {
// do not send error, message will be re-sent
// do not send error, message will be re-sent after restart
return;
}
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "SendInlineBotResultQuery");
@ -3408,7 +3408,7 @@ class SendMultiMediaQuery final : public Td::ResultHandler {
void on_error(Status status) final {
LOG(INFO) << "Receive error for SendMultiMedia: " << status;
if (G()->close_flag() && G()->use_message_database()) {
// do not send error, message will be re-sent
// do not send error, message will be re-sent after restart
return;
}
if (!td_->auth_manager_->is_bot() && FileReferenceManager::is_file_reference_error(status)) {
@ -3508,7 +3508,7 @@ class SendMediaQuery final : public Td::ResultHandler {
void on_error(Status status) final {
LOG(INFO) << "Receive error for SendMedia: " << status;
if (G()->close_flag() && G()->use_message_database()) {
// do not send error, message will be re-sent
// do not send error, message will be re-sent after restart
return;
}
if (was_uploaded_) {
@ -3596,7 +3596,7 @@ class UploadMediaQuery final : public Td::ResultHandler {
void on_error(Status status) final {
LOG(INFO) << "Receive error for UploadMediaQuery for " << message_id_ << " in " << dialog_id_ << ": " << status;
if (G()->close_flag() && G()->use_message_database()) {
// do not send error, message will be re-sent
// do not send error, message will be re-sent after restart
return;
}
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "UploadMediaQuery");
@ -3904,7 +3904,7 @@ class ForwardMessagesQuery final : public Td::ResultHandler {
void on_error(Status status) final {
LOG(INFO) << "Receive error for forward messages: " << status;
if (G()->close_flag() && G()->use_message_database()) {
// do not send error, messages should be re-sent
// do not send error, messages will be re-sent after restart
return;
}
// no on_get_dialog_error call, because two dialogs are involved
@ -3959,7 +3959,7 @@ class SendScreenshotNotificationQuery final : public Td::ResultHandler {
void on_error(Status status) final {
LOG(INFO) << "Receive error for SendScreenshotNotificationQuery: " << status;
if (G()->close_flag() && G()->use_message_database()) {
// do not send error, messages should be re-sent
// do not send error, messages will be re-sent after restart
return;
}
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "SendScreenshotNotificationQuery");
@ -13823,7 +13823,7 @@ void MessagesManager::on_send_secret_message_error(int64 random_id, Status error
auto file_id = get_message_content_upload_file_id(m->content.get());
if (file_id.is_valid()) {
if (G()->close_flag() && G()->use_message_database()) {
// do not send error, message will be re-sent
// do not send error, message will be re-sent after restart
return;
}
if (begins_with(error.message(), "FILE_PART_") && ends_with(error.message(), "_MISSING")) {

View File

@ -187,7 +187,7 @@ class ToggleStoryPinnedQuery final : public Td::ResultHandler {
class StoryManager::SendStoryQuery final : public Td::ResultHandler {
FileId file_id_;
unique_ptr<StoryManager::PendingStory> pending_story_;
unique_ptr<PendingStory> pending_story_;
public:
void send(FileId file_id, unique_ptr<PendingStory> pending_story,
@ -239,7 +239,7 @@ class StoryManager::SendStoryQuery final : public Td::ResultHandler {
LOG(INFO) << "Receive error for SendStoryQuery: " << status;
if (G()->close_flag() && G()->use_message_database()) {
// do not send error, message will be re-sent
// do not send error, story will be re-sent after restart
return;
}
@ -253,6 +253,73 @@ class StoryManager::SendStoryQuery final : public Td::ResultHandler {
}
};
class StoryManager::EditStoryQuery final : public Td::ResultHandler {
FileId file_id_;
unique_ptr<PendingStory> pending_story_;
public:
void send(FileId file_id, unique_ptr<PendingStory> pending_story,
telegram_api::object_ptr<telegram_api::InputFile> input_file, const BeingEditedStory *edited_story) {
file_id_ = file_id;
pending_story_ = std::move(pending_story);
CHECK(pending_story_ != nullptr);
int32 flags = 0;
telegram_api::object_ptr<telegram_api::InputMedia> input_media;
const StoryContent *content = edited_story->content_.get();
if (content != nullptr) {
CHECK(input_file != nullptr);
input_media = get_story_content_input_media(td_, content, std::move(input_file));
CHECK(input_media != nullptr);
flags |= telegram_api::stories_editStory::MEDIA_MASK;
}
vector<telegram_api::object_ptr<telegram_api::MessageEntity>> entities;
if (edited_story->edit_caption_) {
flags |= telegram_api::stories_editStory::CAPTION_MASK;
flags |= telegram_api::stories_editStory::ENTITIES_MASK;
entities = get_input_message_entities(td_->contacts_manager_.get(), &edited_story->caption_, "EditStoryQuery");
}
send_query(G()->net_query_creator().create(
telegram_api::stories_editStory(flags, pending_story_->story_id_.get(), std::move(input_media),
edited_story->caption_.text, std::move(entities), Auto()),
{{StoryFullId{pending_story_->dialog_id_, pending_story_->story_id_}}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::stories_editStory>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for EditStoryQuery: " << to_string(ptr);
td_->updates_manager_->on_get_updates(
std::move(ptr), PromiseCreator::lambda([file_id = file_id_, pending_story = std::move(pending_story_)](
Result<Unit> &&result) mutable {
send_closure(G()->story_manager(), &StoryManager::on_story_edited, file_id, std::move(pending_story),
std::move(result));
}));
}
void on_error(Status status) final {
LOG(INFO) << "Receive error for EditStoryQuery: " << status;
if (G()->close_flag() && G()->use_message_database()) {
// do not send error, story will be edited after restart
return;
}
if (begins_with(status.message(), "FILE_PART_") && ends_with(status.message(), "_MISSING")) {
td_->story_manager_->on_send_story_file_part_missing(std::move(pending_story_),
to_integer<int32>(status.message().substr(10)));
return;
}
td_->story_manager_->on_story_edited(file_id_, std::move(pending_story_), std::move(status));
}
};
class StoryManager::UploadMediaCallback final : public FileManager::UploadCallback {
public:
void on_upload_ok(FileId file_id, telegram_api::object_ptr<telegram_api::InputFile> input_file) final {
@ -400,13 +467,27 @@ td_api::object_ptr<td_api::story> StoryManager::get_story_object(StoryFullId sto
privacy_rules = story->privacy_rules_.get_user_privacy_setting_rules_object(td_);
}
auto *content = story->content_.get();
auto *caption = &story->caption_;
if (is_owned && story_full_id.get_story_id().is_server()) {
auto it = being_edited_stories_.find(story_full_id);
if (it != being_edited_stories_.end()) {
if (it->second->content_ != nullptr) {
content = it->second->content_.get();
}
if (it->second->edit_caption_) {
caption = &it->second->caption_;
}
}
}
CHECK(dialog_id.get_type() == DialogType::User);
return td_api::make_object<td_api::story>(
story_full_id.get_story_id().get(),
td_->contacts_manager_->get_user_id_object(dialog_id.get_user_id(), "get_story_object"), story->date_,
story->is_pinned_, story->interaction_info_.get_story_interaction_info_object(td_), std::move(privacy_rules),
story->is_public_, story->is_for_close_friends_, get_story_content_object(td_, story->content_.get()),
get_formatted_text_object(story->caption_, true, get_story_content_duration(td_, story->content_.get())));
story->is_public_, story->is_for_close_friends_, get_story_content_object(td_, content),
get_formatted_text_object(story->caption_, true, get_story_content_duration(td_, content)));
}
td_api::object_ptr<td_api::stories> StoryManager::get_stories_object(int32 total_count,
@ -682,7 +763,12 @@ void StoryManager::on_upload_story(FileId file_id, telegram_api::object_ptr<tele
}
CHECK(input_file != nullptr);
td_->create_handler<SendStoryQuery>()->send(file_id, std::move(pending_story), std::move(input_file));
bool is_edit = pending_story->story_id_.is_server();
if (is_edit) {
do_edit_story(file_id, std::move(pending_story), std::move(input_file));
} else {
td_->create_handler<SendStoryQuery>()->send(file_id, std::move(pending_story), std::move(input_file));
}
}
void StoryManager::on_upload_story_error(FileId file_id, Status status) {
@ -701,17 +787,129 @@ void StoryManager::on_upload_story_error(FileId file_id, Status status) {
auto pending_story = std::move(it->second);
if (pending_story->log_event_id_ != 0) {
binlog_erase(G()->td_db()->get_binlog(), pending_story->log_event_id_);
}
being_uploaded_files_.erase(it);
bool is_edit = pending_story->story_id_.is_server();
if (is_edit) {
on_story_edited(file_id, std::move(pending_story), std::move(status));
} else {
if (pending_story->log_event_id_ != 0) {
binlog_erase(G()->td_db()->get_binlog(), pending_story->log_event_id_);
}
}
}
void StoryManager::on_send_story_file_part_missing(unique_ptr<PendingStory> &&pending_story, int bad_part) {
do_send_story(std::move(pending_story), {bad_part});
}
void StoryManager::edit_story(StoryId story_id, td_api::object_ptr<td_api::InputStoryContent> &&input_story_content,
td_api::object_ptr<td_api::formattedText> &&input_caption, Promise<Unit> &&promise) {
DialogId dialog_id(td_->contacts_manager_->get_my_id());
StoryFullId story_full_id{dialog_id, story_id};
const Story *story = get_story(story_full_id);
if (story == nullptr) {
return promise.set_error(Status::Error(400, "Story not found"));
}
if (!story_id.is_server()) {
return promise.set_error(Status::Error(400, "Story can't be edited"));
}
bool is_bot = td_->auth_manager_->is_bot();
unique_ptr<StoryContent> content;
bool is_caption_edited = input_caption != nullptr;
FormattedText caption;
if (input_story_content != nullptr) {
TRY_RESULT_PROMISE_ASSIGN(promise, content,
get_input_story_content(td_, std::move(input_story_content), dialog_id));
}
if (is_caption_edited) {
TRY_RESULT_PROMISE_ASSIGN(
promise, caption, get_formatted_text(td_, DialogId(), std::move(input_caption), is_bot, true, false, false));
auto *current_caption = &story->caption_;
auto it = being_edited_stories_.find(story_full_id);
if (it != being_edited_stories_.end() && it->second->edit_caption_) {
current_caption = &it->second->caption_;
}
if (*current_caption == caption) {
is_caption_edited = false;
}
}
if (content == nullptr && !is_caption_edited) {
return promise.set_value(Unit());
}
auto &edited_story = being_edited_stories_[story_full_id];
if (edited_story == nullptr) {
edited_story = make_unique<BeingEditedStory>();
}
if (content != nullptr) {
edited_story->content_ = std::move(content);
story->edit_generation_++;
}
if (is_caption_edited) {
edited_story->caption_ = std::move(caption);
edited_story->edit_caption_ = true;
story->edit_generation_++;
}
edited_story->promises_.push_back(std::move(promise));
auto new_story = make_unique<Story>();
new_story->content_ = dup_story_content(td_, edited_story->content_.get());
auto pending_story = td::make_unique<PendingStory>(dialog_id, story_id, 0 /*log_event_id*/,
std::numeric_limits<uint32>::max() - (++send_story_count_),
story->edit_generation_, std::move(new_story));
if (edited_story->content_ == nullptr) {
return do_edit_story(FileId(), std::move(pending_story), nullptr);
}
do_send_story(std::move(pending_story), {});
}
void StoryManager::do_edit_story(FileId file_id, unique_ptr<PendingStory> &&pending_story,
telegram_api::object_ptr<telegram_api::InputFile> input_file) {
StoryFullId story_full_id{pending_story->dialog_id_, pending_story->story_id_};
const Story *story = get_story(story_full_id);
auto it = being_edited_stories_.find(story_full_id);
if (story == nullptr || story->edit_generation_ != pending_story->random_id_ || it == being_edited_stories_.end()) {
LOG(INFO) << "Skip outdated edit of " << story_full_id;
if (file_id.is_valid()) {
td_->file_manager_->cancel_upload(file_id);
}
return;
}
td_->create_handler<EditStoryQuery>()->send(file_id, std::move(pending_story), std::move(input_file),
it->second.get());
}
void StoryManager::on_story_edited(FileId file_id, unique_ptr<PendingStory> pending_story, Result<Unit> result) {
G()->ignore_result_if_closing(result);
if (file_id.is_valid()) {
td_->file_manager_->delete_partial_remote_location(file_id);
}
StoryFullId story_full_id{pending_story->dialog_id_, pending_story->story_id_};
const Story *story = get_story(story_full_id);
auto it = being_edited_stories_.find(story_full_id);
if (story == nullptr || story->edit_generation_ != pending_story->random_id_ || it == being_edited_stories_.end()) {
LOG(INFO) << "Ignore outdated edit of " << story_full_id;
return;
}
if (pending_story->log_event_id_ != 0) {
binlog_erase(G()->td_db()->get_binlog(), pending_story->log_event_id_);
}
auto promises = std::move(it->second->promises_);
being_edited_stories_.erase(it);
if (result.is_ok()) {
set_promises(promises);
} else {
fail_promises(promises, result.move_as_error());
}
}
void StoryManager::set_story_privacy_rules(StoryId story_id,
td_api::object_ptr<td_api::userPrivacySettingRules> &&rules,
Promise<Unit> &&promise) {

View File

@ -41,6 +41,14 @@ class StoryManager final : public Actor {
UserPrivacySettingRules privacy_rules_;
unique_ptr<StoryContent> content_;
FormattedText caption_;
mutable int64 edit_generation_ = 0;
};
struct BeingEditedStory {
unique_ptr<StoryContent> content_;
FormattedText caption_;
bool edit_caption_ = false;
vector<Promise<Unit>> promises_;
};
struct PendingStory {
@ -71,6 +79,9 @@ class StoryManager final : public Actor {
void on_send_story_file_part_missing(unique_ptr<PendingStory> &&pending_story, int bad_part);
void edit_story(StoryId story_id, td_api::object_ptr<td_api::InputStoryContent> &&input_story_content,
td_api::object_ptr<td_api::formattedText> &&input_caption, Promise<Unit> &&promise);
void set_story_privacy_rules(StoryId story_id, td_api::object_ptr<td_api::userPrivacySettingRules> &&rules,
Promise<Unit> &&promise);
@ -99,6 +110,7 @@ class StoryManager final : public Actor {
class UploadMediaCallback;
class SendStoryQuery;
class EditStoryQuery;
void tear_down() final;
@ -135,6 +147,11 @@ class StoryManager final : public Actor {
void on_upload_story_error(FileId file_id, Status status);
void do_edit_story(FileId file_id, unique_ptr<PendingStory> &&pending_story,
telegram_api::object_ptr<telegram_api::InputFile> input_file);
void on_story_edited(FileId file_id, unique_ptr<PendingStory> pending_story, Result<Unit> result);
void on_toggle_story_is_pinned(StoryId story_id, bool is_pinned, Promise<Unit> &&promise);
std::shared_ptr<UploadMediaCallback> upload_media_callback_;
@ -143,6 +160,8 @@ class StoryManager final : public Actor {
WaitFreeHashMap<StoryFullId, unique_ptr<Story>, StoryFullIdHash> stories_;
FlatHashMap<StoryFullId, unique_ptr<BeingEditedStory>, StoryFullIdHash> being_edited_stories_;
uint32 send_story_count_ = 0;
FlatHashMap<FileId, unique_ptr<PendingStory>, FileIdHash> being_uploaded_files_;

View File

@ -5624,6 +5624,13 @@ void Td::on_request(uint64 id, td_api::sendStory &request) {
std::move(request.privacy_rules_), request.is_pinned_, std::move(promise));
}
void Td::on_request(uint64 id, td_api::editStory &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
story_manager_->edit_story(StoryId(request.story_id_), std::move(request.content_), std::move(request.caption_),
std::move(promise));
}
void Td::on_request(uint64 id, td_api::setStoryPrivacyRules &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();

View File

@ -788,6 +788,8 @@ class Td final : public Actor {
void on_request(uint64 id, td_api::sendStory &request);
void on_request(uint64 id, td_api::editStory &request);
void on_request(uint64 id, td_api::setStoryPrivacyRules &request);
void on_request(uint64 id, const td_api::toggleStoryIsPinned &request);

View File

@ -3952,6 +3952,34 @@ class CliClient final : public Actor {
td_api::make_object<td_api::inputStoryContentVideo>(as_input_file(video),
to_integers<int32>(sticker_file_ids), duration),
as_caption(caption), rules, op == "ssvp"));
} else if (op == "esc") {
StoryId story_id;
string caption;
get_args(args, story_id, caption);
send_request(td_api::make_object<td_api::editStory>(story_id, nullptr, as_caption(caption)));
} else if (op == "esp") {
StoryId story_id;
string photo;
string caption;
string sticker_file_ids;
get_args(args, story_id, photo, caption, sticker_file_ids);
send_request(
td_api::make_object<td_api::editStory>(story_id,
td_api::make_object<td_api::inputStoryContentPhoto>(
as_input_file(photo), to_integers<int32>(sticker_file_ids)),
as_caption(caption)));
} else if (op == "esv") {
StoryId story_id;
string video;
string caption;
int32 duration;
string sticker_file_ids;
get_args(args, story_id, video, caption, duration, sticker_file_ids);
send_request(td_api::make_object<td_api::editStory>(
story_id,
td_api::make_object<td_api::inputStoryContentVideo>(as_input_file(video),
to_integers<int32>(sticker_file_ids), duration),
as_caption(caption)));
} else if (op == "sspr") {
StoryId story_id;
PrivacyRules rules;