diff --git a/Telegram/Resources/scheme.tl b/Telegram/Resources/scheme.tl index 9faad2bb7dc498..3f77507615a84d 100644 --- a/Telegram/Resources/scheme.tl +++ b/Telegram/Resources/scheme.tl @@ -1266,7 +1266,7 @@ channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector = Bool; channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool; channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates; -channels.getLeftChannels#90920196 = messages.Chats; +channels.getLeftChannels#8341ecc0 offset:int = messages.Chats; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index f957b69be838fb..4b8f504454eadb 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -944,30 +944,23 @@ DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data) { return result; } -void InsertLeftDialog( - DialogsInfo &info, - const Chat &chat, - Message &&message) { - const auto projection = [](const DialogInfo &dialog) { - return std::make_tuple( - dialog.topMessageDate, - dialog.topMessageId, - BarePeerId(dialog.peerId)); - }; - const auto i = ranges::lower_bound( - info.list, - std::make_tuple(message.date, message.id, chat.id), - ranges::ordered_less{}, - projection); - - auto insert = DialogInfo(); - insert.input = chat.input; - insert.name = chat.title; - insert.peerId = ChatPeerId(chat.id); - insert.topMessageDate = message.date; - insert.topMessageId = message.id; - insert.type = DialogTypeFromChat(chat); - info.list.insert(i, std::move(insert)); +DialogsInfo ParseLeftChannelsInfo(const MTPmessages_Chats &data) { + auto result = DialogsInfo(); + data.match([&](const auto &data) { //MTPDmessages_chats &data) { + result.list.reserve(data.vchats.v.size()); + for (const auto &single : data.vchats.v) { + const auto chat = ParseChat(single); + auto info = DialogInfo(); + info.input = chat.input; + info.name = chat.title; + info.peerId = ChatPeerId(chat.id); + info.topMessageDate = 0; + info.topMessageId = 0; + info.type = DialogTypeFromChat(chat); + result.list.push_back(std::move(info)); + } + }); + return result; } void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings) { @@ -995,6 +988,17 @@ void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings) { } } +void FinalizeLeftChannelsInfo(DialogsInfo &info, const Settings &settings) { + auto &list = info.list; + const auto digits = Data::NumberToString(list.size() - 1).size(); + auto index = 0; + for (auto &dialog : list) { + const auto number = Data::NumberToString(++index, digits, '0'); + dialog.relativePath = "Chats/left_" + number + '/'; + dialog.onlyMyMessages = true; + } +} + MessagesSlice ParseMessagesSlice( const MTPVector &data, const MTPVector &users, diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index f005a5ed261b5d..93d85cf713eabc 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -433,12 +433,12 @@ struct DialogsInfo { std::vector list; }; +DialogInfo::Type DialogTypeFromChat(const Chat &chat); + DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data); -void InsertLeftDialog( - DialogsInfo &info, - const Chat &chat, - Message &&message); +DialogsInfo ParseLeftChannelsInfo(const MTPmessages_Chats &data); void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings); +void FinalizeLeftChannelsInfo(DialogsInfo &info, const Settings &settings); struct MessagesSlice { std::vector list; diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp index c80df6eb05693e..73234fe6b65964 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.cpp +++ b/Telegram/SourceFiles/export/export_api_wrap.cpp @@ -63,6 +63,25 @@ LocationKey ComputeLocationKey(const Data::FileLocation &value) { return result; } +Settings::Type SettingsFromDialogsType(Data::DialogInfo::Type type) { + using DialogType = Data::DialogInfo::Type; + switch (type) { + case DialogType::Personal: + return Settings::Type::PersonalChats; + case DialogType::Bot: + return Settings::Type::BotChats; + case DialogType::PrivateGroup: + return Settings::Type::PrivateGroups; + case DialogType::PublicGroup: + return Settings::Type::PublicGroups; + case DialogType::PrivateChannel: + return Settings::Type::PrivateChannels; + case DialogType::PublicChannel: + return Settings::Type::PublicChannels; + } + return Settings::Type(0); +} + } // namespace class ApiWrap::LoadedFileCache { @@ -81,6 +100,19 @@ class ApiWrap::LoadedFileCache { }; +struct ApiWrap::StartProcess { + FnMut done; + + enum class Step { + UserpicsCount, + DialogsCount, + LeftChannelsCount, + }; + std::deque steps; + StartInfo info; + +}; + struct ApiWrap::UserpicsProcess { FnMut start; Fn handleSlice; @@ -112,30 +144,36 @@ struct ApiWrap::FileProcess { }; -struct ApiWrap::DialogsProcess { +struct ApiWrap::LeftChannelsProcess { + FnMut done; + Data::DialogsInfo info; - std::map left; - FnMut start; - Fn startOne; - Fn sliceOne; - Fn finishOne; - FnMut finish; + rpl::variable count; + int fullCount = 0; + bool finished = false; + +}; + +struct ApiWrap::DialogsProcess { + FnMut done; + + Data::DialogsInfo info; Data::TimeId offsetDate = 0; int32 offsetId = 0; MTPInputPeer offsetPeer = MTP_inputPeerEmpty(); - struct Single; - std::unique_ptr single; - int singleIndex = -1; + rpl::variable count; }; -struct ApiWrap::DialogsProcess::Single { - Single(const Data::DialogInfo &info); - +struct ApiWrap::ChatProcess { Data::DialogInfo info; + + Fn handleSlice; + FnMut done; + int32 offsetId = 1; base::optional slice; @@ -173,10 +211,6 @@ base::optional ApiWrap::LoadedFileCache::find( ApiWrap::FileProcess::FileProcess(const QString &path) : file(path) { } -ApiWrap::DialogsProcess::Single::Single(const Data::DialogInfo &info) -: info(info) { -} - template auto ApiWrap::mainRequest(Request &&request) { Expects(_takeoutId.has_value()); @@ -215,11 +249,158 @@ rpl::producer ApiWrap::errors() const { void ApiWrap::startExport( const Settings &settings, - FnMut done) { + FnMut done) { Expects(_settings == nullptr); + Expects(_startProcess == nullptr); _settings = std::make_unique(settings); - startMainSession(std::move(done)); + _startProcess = std::make_unique(); + _startProcess->done = std::move(done); + + using Step = StartProcess::Step; + if (_settings->types & Settings::Type::Userpics) { + _startProcess->steps.push_back(Step::UserpicsCount); + } else if (_settings->types & Settings::Type::AnyChatsMask) { + _startProcess->steps.push_back(Step::DialogsCount); + } else if (_settings->types & Settings::Type::GroupsChannelsMask) { + _startProcess->steps.push_back(Step::LeftChannelsCount); + } + startMainSession([=] { + sendNextStartRequest(); + }); +} + +void ApiWrap::sendNextStartRequest() { + Expects(_startProcess != nullptr); + + auto &steps = _startProcess->steps; + if (steps.empty()) { + finishStartProcess(); + return; + } + const auto step = steps.front(); + steps.pop_front(); + switch (step) { + case StartProcess::Step::UserpicsCount: + return requestUserpicsCount(); + case StartProcess::Step::DialogsCount: + return requestDialogsCount(); + case StartProcess::Step::LeftChannelsCount: + return requestLeftChannelsCount(); + } + Unexpected("Step in ApiWrap::sendNextStartRequest."); +} + +void ApiWrap::requestUserpicsCount() { + Expects(_startProcess != nullptr); + + mainRequest(MTPphotos_GetUserPhotos( + _user, + MTP_int(0), // offset + MTP_long(0), // max_id + MTP_int(0) // limit + )).done([=](const MTPphotos_Photos &result) { + Expects(_settings != nullptr); + Expects(_startProcess != nullptr); + + _startProcess->info.userpicsCount = result.match( + [](const MTPDphotos_photos &data) { + return int(data.vphotos.v.size()); + }, [](const MTPDphotos_photosSlice &data) { + return data.vcount.v; + }); + + sendNextStartRequest(); + }).send(); +} + +void ApiWrap::requestDialogsCount() { + Expects(_startProcess != nullptr); + + mainRequest(MTPmessages_GetDialogs( + MTP_flags(0), + MTP_int(0), // offset_date + MTP_int(0), // offset_id + MTP_inputPeerEmpty(), // offset_peer + MTP_int(1) + )).done([=](const MTPmessages_Dialogs &result) { + Expects(_settings != nullptr); + Expects(_startProcess != nullptr); + + _startProcess->info.dialogsCount = result.match( + [](const MTPDmessages_dialogs &data) { + return int(data.vdialogs.v.size()); + }, [](const MTPDmessages_dialogsSlice &data) { + return data.vcount.v; + }); + + sendNextStartRequest(); + }).send(); +} + +void ApiWrap::requestLeftChannelsCount() { + Expects(_startProcess != nullptr); + Expects(_leftChannelsProcess == nullptr); + + _leftChannelsProcess = std::make_unique(); + requestLeftChannelsSliceGeneric([=] { + Expects(_startProcess != nullptr); + Expects(_leftChannelsProcess != nullptr); + + _startProcess->info.leftChannelsCount + = _leftChannelsProcess->fullCount; + sendNextStartRequest(); + }); +} + +void ApiWrap::finishStartProcess() { + Expects(_startProcess != nullptr); + + const auto process = base::take(_startProcess); + process->done(process->info); +} + +void ApiWrap::requestLeftChannelsList( + FnMut done) { + Expects(_leftChannelsProcess != nullptr); + + _leftChannelsProcess->done = std::move(done); + requestLeftChannelsSlice(); +} + +void ApiWrap::requestLeftChannelsSlice() { + requestLeftChannelsSliceGeneric([=] { + Expects(_leftChannelsProcess != nullptr); + + if (_leftChannelsProcess->finished) { + const auto process = base::take(_leftChannelsProcess); + Data::FinalizeLeftChannelsInfo(process->info, *_settings); + process->done(std::move(process->info)); + } else { + requestLeftChannelsSlice(); + } + }); +} + +rpl::producer ApiWrap::leftChannelsLoadedCount() const { + Expects(_leftChannelsProcess != nullptr); + + return _leftChannelsProcess->count.value(); +} + +void ApiWrap::requestDialogsList(FnMut done) { + Expects(_dialogsProcess == nullptr); + + _dialogsProcess = std::make_unique(); + _dialogsProcess->done = std::move(done); + + requestDialogsSlice(); +} + +rpl::producer ApiWrap::dialogsLoadedCount() const { + Expects(_dialogsProcess != nullptr); + + return _dialogsProcess->count.value(); } void ApiWrap::startMainSession(FnMut done) { @@ -287,8 +468,8 @@ void ApiWrap::requestUserpics( mainRequest(MTPphotos_GetUserPhotos( _user, - MTP_int(0), - MTP_long(0), + MTP_int(0), // offset + MTP_long(0), // max_id MTP_int(kUserpicsSliceLimit) )).done([=](const MTPphotos_Photos &result) mutable { Expects(_userpicsProcess != nullptr); @@ -405,22 +586,18 @@ void ApiWrap::requestSessions(FnMut done) { }).send(); } -void ApiWrap::requestDialogs( - FnMut start, - Fn startOne, - Fn sliceOne, - Fn finishOne, - FnMut finish) { - Expects(_dialogsProcess == nullptr); +void ApiWrap::requestMessages( + const Data::DialogInfo &info, + Fn slice, + FnMut done) { + Expects(_chatProcess == nullptr); - _dialogsProcess = std::make_unique(); - _dialogsProcess->start = std::move(start); - _dialogsProcess->startOne = std::move(startOne); - _dialogsProcess->sliceOne = std::move(sliceOne); - _dialogsProcess->finishOne = std::move(finishOne); - _dialogsProcess->finish = std::move(finish); + _chatProcess = std::make_unique(); + _chatProcess->info = info; + _chatProcess->handleSlice = std::move(slice); + _chatProcess->done = std::move(done); - requestDialogsSlice(); + requestMessagesSlice(); } void ApiWrap::requestDialogsSlice() { @@ -432,7 +609,7 @@ void ApiWrap::requestDialogsSlice() { MTP_int(_dialogsProcess->offsetId), _dialogsProcess->offsetPeer, MTP_int(kChatsSliceLimit) - )).done([=](const MTPmessages_Dialogs &result) mutable { + )).done([=](const MTPmessages_Dialogs &result) { const auto finished = result.match( [](const MTPDmessages_dialogs &data) { return true; @@ -442,7 +619,7 @@ void ApiWrap::requestDialogsSlice() { auto info = Data::ParseDialogsInfo(result); if (finished || info.list.empty()) { - requestLeftChannels(); + finishDialogsList(); } else { const auto &last = info.list.back(); _dialogsProcess->offsetId = last.topMessageId; @@ -458,139 +635,86 @@ void ApiWrap::requestDialogsSlice() { void ApiWrap::appendDialogsSlice(Data::DialogsInfo &&info) { Expects(_dialogsProcess != nullptr); - Expects(_settings != nullptr); - const auto types = _settings->types; - auto filtered = ranges::view::all( - info.list - ) | ranges::view::filter([&](const Data::DialogInfo &info) { - const auto bit = [&] { - using DialogType = Data::DialogInfo::Type; - switch (info.type) { - case DialogType::Personal: - return Settings::Type::PersonalChats; - case DialogType::Bot: - return Settings::Type::BotChats; - case DialogType::PrivateGroup: - return Settings::Type::PrivateGroups; - case DialogType::PublicGroup: - return Settings::Type::PublicGroups; - case DialogType::PrivateChannel: - return Settings::Type::PrivateChannels; - case DialogType::PublicChannel: - return Settings::Type::PublicChannels; - } - return Settings::Type(0); - }(); - return (types & bit) != 0; - }); - auto &list = _dialogsProcess->info.list; - list.reserve(list.size()); - for (auto &info : filtered) { - list.push_back(std::move(info)); - } + appendChatsSlice(_dialogsProcess->info, std::move(info)); } -void ApiWrap::requestLeftChannels() { +void ApiWrap::finishDialogsList() { Expects(_dialogsProcess != nullptr); + const auto process = base::take(_dialogsProcess); + + ranges::reverse(process->info.list); + Data::FinalizeDialogsInfo(process->info, *_settings); + + process->done(std::move(process->info)); +} + +void ApiWrap::requestLeftChannelsSliceGeneric(FnMut done) { + Expects(_leftChannelsProcess != nullptr); + mainRequest(MTPchannels_GetLeftChannels( - )).done([=](const MTPmessages_Chats &result) mutable { - Expects(_dialogsProcess != nullptr); + MTP_int(_leftChannelsProcess->info.list.size()) + )).done([=, done = std::move(done)]( + const MTPmessages_Chats &result) mutable { + Expects(_leftChannelsProcess != nullptr); - const auto finished = result.match( + appendLeftChannelsSlice(Data::ParseLeftChannelsInfo(result)); + + const auto process = _leftChannelsProcess.get(); + process->fullCount = result.match( + [](const MTPDmessages_chats &data) { + return int(data.vchats.v.size()); + }, [](const MTPDmessages_chatsSlice &data) { + return data.vcount.v; + }); + + process->finished = result.match( [](const MTPDmessages_chats &data) { return true; }, [](const MTPDmessages_chatsSlice &data) { return data.vchats.v.isEmpty(); }); - _dialogsProcess->left = Data::ParseChatsList(*result.match( - [](const auto &data) { - return &data.vchats; - })); - requestLeftDialog(); - }).send(); -} - -void ApiWrap::requestLeftDialog() { - Expects(_dialogsProcess != nullptr); + process->count = process->info.list.size(); - auto &left = _dialogsProcess->left; - if (true || left.empty()) { // #TODO export - finishDialogsList(); - return; - } - const auto key = std::move(*left.begin()); - left.erase(key.first); - - mainRequest(MTPmessages_Search( - MTP_flags(MTPmessages_Search::Flag::f_from_id), - key.second.input, - MTP_string(""), // query - _user, - MTP_inputMessagesFilterEmpty(), - MTP_int(0), // min_date - MTP_int(0), // max_date - MTP_int(0), // offset_id - MTP_int(0), // add_offset - MTP_int(1), // limit - MTP_int(0), // max_id - MTP_int(0), // min_id - MTP_int(0) // hash - )).done([=](const MTPmessages_Messages &result) { - Expects(_dialogsProcess != nullptr); - - result.match([=](const MTPDmessages_messagesNotModified &data) { - error("Unexpected messagesNotModified received."); - }, [=](const auto &data) { - auto messages = Data::ParseMessagesList( - data.vmessages, - QString()); - if (!messages.empty() && messages.begin()->second.date > 0) { - Data::InsertLeftDialog( - _dialogsProcess->info, - key.second, - std::move(messages.begin()->second)); - } - requestLeftDialog(); - }); + done(); }).send(); } -void ApiWrap::finishDialogsList() { - Expects(_dialogsProcess != nullptr); - - ranges::reverse(_dialogsProcess->info.list); - Data::FinalizeDialogsInfo(_dialogsProcess->info, *_settings); +void ApiWrap::appendLeftChannelsSlice(Data::DialogsInfo &&info) { + Expects(_leftChannelsProcess != nullptr); - _dialogsProcess->start(_dialogsProcess->info); - requestNextDialog(); + appendChatsSlice(_leftChannelsProcess->info, std::move(info)); } -void ApiWrap::requestNextDialog() { - Expects(_dialogsProcess != nullptr); - Expects(_dialogsProcess->single == nullptr); +void ApiWrap::appendChatsSlice( + Data::DialogsInfo &to, + Data::DialogsInfo &&info) { + Expects(_settings != nullptr); - const auto index = ++_dialogsProcess->singleIndex; - if (index < _dialogsProcess->info.list.size()) { - const auto &one = _dialogsProcess->info.list[index]; - _dialogsProcess->single = std::make_unique(one); - _dialogsProcess->startOne(one); - requestMessagesSlice(); - return; + const auto types = _settings->types; + auto filtered = ranges::view::all( + info.list + ) | ranges::view::filter([&](const Data::DialogInfo &info) { + return (types & SettingsFromDialogsType(info.type)) != 0; + }); + auto &list = to.list; + if (list.empty()) { + list = filtered | ranges::to_vector; + } else { + list.reserve(list.size() + info.list.size()); + for (auto &info : filtered) { + list.push_back(std::move(info)); + } } - finishDialogs(); } void ApiWrap::requestMessagesSlice() { - Expects(_dialogsProcess != nullptr); - Expects(_dialogsProcess->single != nullptr); - - const auto process = _dialogsProcess->single.get(); + Expects(_chatProcess != nullptr); // #TODO export - if (process->info.input.match([](const MTPDinputPeerUser &value) { + if (_chatProcess->info.input.match([](const MTPDinputPeerUser &value) { return !value.vaccess_hash.v; }, [](const auto &data) { return false; })) { finishMessages(); @@ -598,33 +722,31 @@ void ApiWrap::requestMessagesSlice() { } auto handleResult = [=](const MTPmessages_Messages &result) mutable { - Expects(_dialogsProcess != nullptr); - Expects(_dialogsProcess->single != nullptr); + Expects(_chatProcess != nullptr); - const auto process = _dialogsProcess->single.get(); result.match([&](const MTPDmessages_messagesNotModified &data) { error("Unexpected messagesNotModified received."); }, [&](const auto &data) { if constexpr (MTPDmessages_messages::Is()) { - process->lastSlice = true; + _chatProcess->lastSlice = true; } loadMessagesFiles(Data::ParseMessagesSlice( data.vmessages, data.vusers, data.vchats, - process->info.relativePath)); + _chatProcess->info.relativePath)); }); }; - if (process->info.onlyMyMessages) { + if (_chatProcess->info.onlyMyMessages) { mainRequest(MTPmessages_Search( MTP_flags(MTPmessages_Search::Flag::f_from_id), - process->info.input, + _chatProcess->info.input, MTP_string(""), // query _user, MTP_inputMessagesFilterEmpty(), MTP_int(0), // min_date MTP_int(0), // max_date - MTP_int(process->offsetId), + MTP_int(_chatProcess->offsetId), MTP_int(-kMessagesSliceLimit), MTP_int(kMessagesSliceLimit), MTP_int(0), // max_id @@ -633,8 +755,8 @@ void ApiWrap::requestMessagesSlice() { )).done(std::move(handleResult)).send(); } else { mainRequest(MTPmessages_GetHistory( - process->info.input, - MTP_int(process->offsetId), + _chatProcess->info.input, + MTP_int(_chatProcess->offsetId), MTP_int(0), // offset_date MTP_int(-kMessagesSliceLimit), MTP_int(kMessagesSliceLimit), @@ -646,29 +768,25 @@ void ApiWrap::requestMessagesSlice() { } void ApiWrap::loadMessagesFiles(Data::MessagesSlice &&slice) { - Expects(_dialogsProcess != nullptr); - Expects(_dialogsProcess->single != nullptr); - Expects(!_dialogsProcess->single->slice.has_value()); + Expects(_chatProcess != nullptr); + Expects(!_chatProcess->slice.has_value()); - const auto process = _dialogsProcess->single.get(); if (slice.list.empty()) { - process->lastSlice = true; + _chatProcess->lastSlice = true; } - process->slice = std::move(slice); - process->fileIndex = -1; + _chatProcess->slice = std::move(slice); + _chatProcess->fileIndex = -1; loadNextMessageFile(); } void ApiWrap::loadNextMessageFile() { - Expects(_dialogsProcess != nullptr); - Expects(_dialogsProcess->single != nullptr); - Expects(_dialogsProcess->single->slice.has_value()); + Expects(_chatProcess != nullptr); + Expects(_chatProcess->slice.has_value()); - const auto process = _dialogsProcess->single.get(); - auto &list = process->slice->list; + auto &list = _chatProcess->slice->list; while (true) { - const auto index = ++process->fileIndex; + const auto index = ++_chatProcess->fileIndex; if (index >= list.size()) { break; } @@ -684,17 +802,15 @@ void ApiWrap::loadNextMessageFile() { } void ApiWrap::finishMessagesSlice() { - Expects(_dialogsProcess != nullptr); - Expects(_dialogsProcess->single != nullptr); - Expects(_dialogsProcess->single->slice.has_value()); + Expects(_chatProcess != nullptr); + Expects(_chatProcess->slice.has_value()); - const auto process = _dialogsProcess->single.get(); - auto slice = *base::take(process->slice); + auto slice = *base::take(_chatProcess->slice); if (!slice.list.empty()) { - process->offsetId = slice.list.back().id + 1; - _dialogsProcess->sliceOne(std::move(slice)); + _chatProcess->offsetId = slice.list.back().id + 1; + _chatProcess->handleSlice(std::move(slice)); } - if (process->lastSlice) { + if (_chatProcess->lastSlice) { finishMessages(); } else { requestMessagesSlice(); @@ -702,35 +818,22 @@ void ApiWrap::finishMessagesSlice() { } void ApiWrap::loadMessageFileDone(const QString &relativePath) { - Expects(_dialogsProcess != nullptr); - Expects(_dialogsProcess->single != nullptr); - Expects(_dialogsProcess->single->slice.has_value()); - Expects((_dialogsProcess->single->fileIndex >= 0) - && (_dialogsProcess->single->fileIndex - < _dialogsProcess->single->slice->list.size())); - - const auto process = _dialogsProcess->single.get(); - const auto index = process->fileIndex; - process->slice->list[index].file().relativePath = relativePath; + Expects(_chatProcess != nullptr); + Expects(_chatProcess->slice.has_value()); + Expects((_chatProcess->fileIndex >= 0) + && (_chatProcess->fileIndex < _chatProcess->slice->list.size())); + + const auto index = _chatProcess->fileIndex; + _chatProcess->slice->list[index].file().relativePath = relativePath; loadNextMessageFile(); } void ApiWrap::finishMessages() { - Expects(_dialogsProcess != nullptr); - Expects(_dialogsProcess->single != nullptr); - Expects(!_dialogsProcess->single->slice.has_value()); - - _dialogsProcess->single = nullptr; - _dialogsProcess->finishOne(); - - requestNextDialog(); -} - -void ApiWrap::finishDialogs() { - Expects(_dialogsProcess != nullptr); - Expects(_dialogsProcess->single == nullptr); + Expects(_chatProcess != nullptr); + Expects(!_chatProcess->slice.has_value()); - base::take(_dialogsProcess)->finish(); + const auto process = base::take(_chatProcess); + process->done(); } bool ApiWrap::processFileLoad( @@ -800,7 +903,9 @@ bool ApiWrap::writePreloadedFile(Data::File &file) { return false; } -void ApiWrap::loadFile(const Data::File &file, FnMut done) { +void ApiWrap::loadFile( + const Data::File &file, + FnMut done) { Expects(_fileProcess == nullptr); Expects(file.location.dcId != 0); diff --git a/Telegram/SourceFiles/export/export_api_wrap.h b/Telegram/SourceFiles/export/export_api_wrap.h index 9b9a4b0f2baa28..e101e420b898c3 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.h +++ b/Telegram/SourceFiles/export/export_api_wrap.h @@ -33,9 +33,20 @@ class ApiWrap { rpl::producer errors() const; + struct StartInfo { + int userpicsCount = 0; + int dialogsCount = 0; + int leftChannelsCount = 0; + }; void startExport( const Settings &settings, - FnMut done); + FnMut done); + + void requestLeftChannelsList(FnMut done); + rpl::producer leftChannelsLoadedCount() const; + + void requestDialogsList(FnMut done); + rpl::producer dialogsLoadedCount() const; void requestPersonalInfo(FnMut done); @@ -48,22 +59,28 @@ class ApiWrap { void requestSessions(FnMut done); - void requestDialogs( - FnMut start, - Fn startOne, - Fn sliceOne, - Fn finishOne, - FnMut finish); + void requestMessages( + const Data::DialogInfo &info, + Fn slice, + FnMut done); ~ApiWrap(); private: + class LoadedFileCache; + struct StartProcess; struct UserpicsProcess; struct FileProcess; + struct LeftChannelsProcess; struct DialogsProcess; - class LoadedFileCache; + struct ChatProcess; void startMainSession(FnMut done); + void sendNextStartRequest(); + void requestUserpicsCount(); + void requestDialogsCount(); + void requestLeftChannelsCount(); + void finishStartProcess(); void handleUserpicsSlice(const MTPphotos_Photos &result); void loadUserpicsFiles(Data::UserpicsSlice &&slice); @@ -73,18 +90,20 @@ class ApiWrap { void requestDialogsSlice(); void appendDialogsSlice(Data::DialogsInfo &&info); - void requestLeftChannels(); - void requestLeftDialog(); void finishDialogsList(); - void requestNextDialog(); + void requestLeftChannelsSliceGeneric(FnMut done); + void requestLeftChannelsSlice(); + void appendLeftChannelsSlice(Data::DialogsInfo &&info); + + void appendChatsSlice(Data::DialogsInfo &to, Data::DialogsInfo &&info); + void requestMessagesSlice(); void loadMessagesFiles(Data::MessagesSlice &&slice); void loadNextMessageFile(); void loadMessageFileDone(const QString &relativePath); void finishMessagesSlice(); void finishMessages(); - void finishDialogs(); bool processFileLoad( Data::File &file, @@ -93,7 +112,9 @@ class ApiWrap { std::unique_ptr prepareFileProcess( const Data::File &file) const; bool writePreloadedFile(Data::File &file); - void loadFile(const Data::File &file, FnMut done); + void loadFile( + const Data::File &file, + FnMut done); void loadFilePart(); void filePartDone(int offset, const MTPupload_File &result); @@ -113,10 +134,13 @@ class ApiWrap { std::unique_ptr _settings; MTPInputUser _user = MTP_inputUserSelf(); + std::unique_ptr _startProcess; std::unique_ptr _fileCache; std::unique_ptr _userpicsProcess; std::unique_ptr _fileProcess; + std::unique_ptr _leftChannelsProcess; std::unique_ptr _dialogsProcess; + std::unique_ptr _chatProcess; rpl::event_stream _errors; diff --git a/Telegram/SourceFiles/export/export_controller.cpp b/Telegram/SourceFiles/export/export_controller.cpp index df5f1b46589e59..c590b1375be2e5 100644 --- a/Telegram/SourceFiles/export/export_controller.cpp +++ b/Telegram/SourceFiles/export/export_controller.cpp @@ -14,6 +14,8 @@ For license and copyright information please follow this link: namespace Export { +auto kNullStateCallback = [](ProcessingState&) {}; + class Controller { public: Controller(crl::weak_on_queue weak); @@ -21,11 +23,11 @@ class Controller { rpl::producer state() const; // Password step. - void submitPassword(const QString &password); - void requestPasswordRecover(); - rpl::producer passwordUpdate() const; - void reloadPasswordState(); - void cancelUnconfirmedPassword(); + //void submitPassword(const QString &password); + //void requestPasswordRecover(); + //rpl::producer passwordUpdate() const; + //void reloadPasswordState(); + //void cancelUnconfirmedPassword(); // Processing step. void startExport(const Settings &settings); @@ -37,30 +39,56 @@ class Controller { void ioError(const QString &path); void setFinishedState(); - void requestPasswordState(); - void passwordStateDone(const MTPaccount_Password &password); + //void requestPasswordState(); + //void passwordStateDone(const MTPaccount_Password &password); void fillExportSteps(); + void fillSubstepsInSteps(const ApiWrap::StartInfo &info); void exportNext(); + void initialize(); + void collectLeftChannels(); + void collectDialogsList(); void exportPersonalInfo(); void exportUserpics(); void exportContacts(); void exportSessions(); void exportDialogs(); + void exportNextDialog(); + void exportLeftChannels(); + void exportNextLeftChannel(); + + template + ProcessingState prepareState( + Step step, + Callback &&callback = kNullStateCallback) const; + ProcessingState stateInitializing() const; + ProcessingState stateLeftChannelsList(int processed) const; + ProcessingState stateDialogsList(int processed) const; + ProcessingState statePersonalInfo() const; + ProcessingState stateUserpics() const; + ProcessingState stateContacts() const; + ProcessingState stateSessions() const; + ProcessingState stateLeftChannels() const; + ProcessingState stateDialogs() const; + + int substepsInStep(Step step) const; bool normalizePath(); ApiWrap _api; Settings _settings; + Data::DialogsInfo _leftChannelsInfo; + Data::DialogsInfo _dialogsInfo; + int _leftChannelIndex = -1; + int _dialogIndex = -1; // rpl::variable fails to compile in MSVC :( State _state; rpl::event_stream _stateChanges; - - mtpRequestId _passwordRequestId = 0; + std::vector _substepsInStep; std::unique_ptr _writer; - std::vector _steps; + std::vector _steps; int _stepIndex = -1; rpl::lifetime _lifetime; @@ -102,49 +130,49 @@ void Controller::ioError(const QString &path) { setState(ErrorState{ ErrorState::Type::IO, base::none, path }); } -void Controller::submitPassword(const QString &password) { - -} - -void Controller::requestPasswordRecover() { - -} - -rpl::producer Controller::passwordUpdate() const { - return rpl::never(); -} - -void Controller::reloadPasswordState() { - //_mtp.request(base::take(_passwordRequestId)).cancel(); - requestPasswordState(); -} - -void Controller::requestPasswordState() { - if (_passwordRequestId) { - return; - } - //_passwordRequestId = _mtp.request(MTPaccount_GetPassword( - //)).done([=](const MTPaccount_Password &result) { - // _passwordRequestId = 0; - // passwordStateDone(result); - //}).fail([=](const RPCError &error) { - // apiError(error); - //}).send(); -} - -void Controller::passwordStateDone(const MTPaccount_Password &result) { - auto state = PasswordCheckState(); - state.checked = false; - state.requesting = false; - state.hasPassword; - state.hint; - state.unconfirmedPattern; - setState(std::move(state)); -} - -void Controller::cancelUnconfirmedPassword() { - -} +//void Controller::submitPassword(const QString &password) { +// +//} +// +//void Controller::requestPasswordRecover() { +// +//} +// +//rpl::producer Controller::passwordUpdate() const { +// return rpl::never(); +//} +// +//void Controller::reloadPasswordState() { +// //_mtp.request(base::take(_passwordRequestId)).cancel(); +// requestPasswordState(); +//} +// +//void Controller::requestPasswordState() { +// if (_passwordRequestId) { +// return; +// } +// //_passwordRequestId = _mtp.request(MTPaccount_GetPassword( +// //)).done([=](const MTPaccount_Password &result) { +// // _passwordRequestId = 0; +// // passwordStateDone(result); +// //}).fail([=](const RPCError &error) { +// // apiError(error); +// //}).send(); +//} +// +//void Controller::passwordStateDone(const MTPaccount_Password &result) { +// auto state = PasswordCheckState(); +// state.checked = false; +// state.requesting = false; +// state.hasPassword; +// state.hint; +// state.unconfirmedPattern; +// setState(std::move(state)); +//} +// +//void Controller::cancelUnconfirmedPassword() { +// +//} void Controller::startExport(const Settings &settings) { if (!_settings.path.isEmpty()) { @@ -158,9 +186,7 @@ void Controller::startExport(const Settings &settings) { } _writer = Output::CreateWriter(_settings.format); fillExportSteps(); - _api.startExport(_settings, [=] { - exportNext(); - }); + exportNext(); } bool Controller::normalizePath() { @@ -195,6 +221,13 @@ bool Controller::normalizePath() { void Controller::fillExportSteps() { using Type = Settings::Type; + _steps.push_back(Step::Initializing); + if (_settings.types & Type::GroupsChannelsMask) { + _steps.push_back(Step::LeftChannels); + } + if (_settings.types & Type::AnyChatsMask) { + _steps.push_back(Step::DialogsList); + } if (_settings.types & Type::PersonalInfo) { _steps.push_back(Step::PersonalInfo); } @@ -207,17 +240,46 @@ void Controller::fillExportSteps() { if (_settings.types & Type::Sessions) { _steps.push_back(Step::Sessions); } - const auto dialogTypes = Type::PersonalChats - | Type::BotChats - | Type::PrivateGroups - | Type::PublicGroups - | Type::PrivateChannels - | Type::PublicChannels; - if (_settings.types & dialogTypes) { + if (_settings.types & Type::AnyChatsMask) { _steps.push_back(Step::Dialogs); } } +void Controller::fillSubstepsInSteps(const ApiWrap::StartInfo &info) { + const auto push = [&](Step step, int count) { + const auto index = static_cast(step); + if (index >= _substepsInStep.size()) { + _substepsInStep.resize(index + 1, 0); + } + _substepsInStep[index] = count; + }; + push(Step::Initializing, 1); + if (_settings.types & Settings::Type::GroupsChannelsMask) { + push(Step::LeftChannelsList, 1); + } + if (_settings.types & Settings::Type::AnyChatsMask) { + push(Step::DialogsList, 1); + } + if (_settings.types & Settings::Type::PersonalInfo) { + push(Step::PersonalInfo, 1); + } + if (_settings.types & Settings::Type::Userpics) { + push(Step::Userpics, info.userpicsCount); + } + if (_settings.types & Settings::Type::Contacts) { + push(Step::Contacts, 1); + } + if (_settings.types & Settings::Type::Sessions) { + push(Step::Sessions, 1); + } + if (_settings.types & Settings::Type::GroupsChannelsMask) { + push(Step::LeftChannels, info.leftChannelsCount); + } + if (_settings.types & Settings::Type::AnyChatsMask) { + push(Step::Dialogs, info.dialogsCount); + } +} + void Controller::exportNext() { if (!++_stepIndex) { _writer->start(_settings); @@ -229,15 +291,52 @@ void Controller::exportNext() { } const auto step = _steps[_stepIndex]; switch (step) { + case Step::Initializing: return initialize(); + case Step::LeftChannelsList: return collectLeftChannels(); + case Step::DialogsList: return collectDialogsList(); case Step::PersonalInfo: return exportPersonalInfo(); case Step::Userpics: return exportUserpics(); case Step::Contacts: return exportContacts(); case Step::Sessions: return exportSessions(); + case Step::LeftChannels: return exportLeftChannels(); case Step::Dialogs: return exportDialogs(); } Unexpected("Step in Controller::exportNext."); } +void Controller::initialize() { + setState(stateInitializing()); + + _api.startExport(_settings, [=](ApiWrap::StartInfo info) { + fillSubstepsInSteps(info); + exportNext(); + }); +} + +void Controller::collectLeftChannels() { + _api.requestLeftChannelsList([=](Data::DialogsInfo &&result) { + _leftChannelsInfo = std::move(result); + exportNext(); + }); + + _api.leftChannelsLoadedCount( + ) | rpl::start_with_next([=](int count) { + setState(stateLeftChannelsList(count)); + }, _lifetime); +} + +void Controller::collectDialogsList() { + _api.requestDialogsList([=](Data::DialogsInfo &&result) { + _dialogsInfo = std::move(result); + exportNext(); + }); + + _api.dialogsLoadedCount( + ) | rpl::start_with_next([=](int count) { + setState(stateDialogsList(count)); + }, _lifetime); +} + void Controller::exportPersonalInfo() { _api.requestPersonalInfo([=](Data::PersonalInfo &&result) { _writer->writePersonal(result); @@ -271,63 +370,166 @@ void Controller::exportSessions() { } void Controller::exportDialogs() { - _api.requestDialogs([=](const Data::DialogsInfo &result) { - _writer->writeDialogsStart(result); - }, [=](const Data::DialogInfo &result) { - _writer->writeDialogStart(result); - }, [=](Data::MessagesSlice &&result) { - _writer->writeMessagesSlice(result); - }, [=] { - _writer->writeDialogEnd(); - }, [=] { - _writer->writeDialogsEnd(); - exportNext(); - }); + _writer->writeDialogsStart(_dialogsInfo); + + exportNextDialog(); } -void Controller::setFinishedState() { - setState(FinishedState{ _writer->mainFilePath() }); +void Controller::exportNextDialog() { + const auto index = ++_dialogIndex; + if (index < _dialogsInfo.list.size()) { + const auto &info = _dialogsInfo.list[index]; + _writer->writeDialogStart(info); + + _api.requestMessages(info, [=](Data::MessagesSlice &&result) { + _writer->writeDialogSlice(result); + }, [=] { + _writer->writeDialogEnd(); + exportNextDialog(); + }); + return; + } + _writer->writeDialogsEnd(); + exportNext(); } -ControllerWrap::ControllerWrap() { +void Controller::exportLeftChannels() { + _writer->writeLeftChannelsStart(_leftChannelsInfo); + + exportNextLeftChannel(); } -rpl::producer ControllerWrap::state() const { - return _wrapped.producer_on_main([=](const Controller &controller) { - return controller.state(); +void Controller::exportNextLeftChannel() { + const auto index = ++_leftChannelIndex; + if (index < _leftChannelsInfo.list.size()) { + const auto &chat = _leftChannelsInfo.list[index]; + _writer->writeLeftChannelStart(chat); + + _api.requestMessages(chat, [=](Data::MessagesSlice &&result) { + _writer->writeLeftChannelSlice(result); + }, [=] { + _writer->writeLeftChannelEnd(); + exportNextLeftChannel(); + }); + return; + } + _writer->writeLeftChannelsEnd(); + exportNext(); +} + +template +ProcessingState Controller::prepareState( + Step step, + Callback &&callback) const { + auto result = ProcessingState(); + callback(result); + result.step = step; + result.substepsInStep = _substepsInStep; + return result; +} + +ProcessingState Controller::stateInitializing() const { + return ProcessingState(); +} + +ProcessingState Controller::stateLeftChannelsList(int processed) const { + const auto step = Step::LeftChannelsList; + return prepareState(step, [&](ProcessingState &result) { + result.entityIndex = processed; + result.entityCount = std::max(processed, substepsInStep(step)); }); } -void ControllerWrap::submitPassword(const QString &password) { - _wrapped.with([=](Controller &controller) { - controller.submitPassword(password); +ProcessingState Controller::stateDialogsList(int processed) const { + const auto step = Step::DialogsList; + return prepareState(step, [&](ProcessingState &result) { + result.entityIndex = processed; + result.entityCount = std::max(processed, substepsInStep(step)); }); } +ProcessingState Controller::statePersonalInfo() const { + return prepareState(Step::PersonalInfo); +} + +ProcessingState Controller::stateUserpics() const { + return prepareState(Step::Userpics, [&](ProcessingState &result) { -void ControllerWrap::requestPasswordRecover() { - _wrapped.with([=](Controller &controller) { - controller.requestPasswordRecover(); }); } -rpl::producer ControllerWrap::passwordUpdate() const { - return _wrapped.producer_on_main([=](const Controller &controller) { - return controller.passwordUpdate(); +ProcessingState Controller::stateContacts() const { + return prepareState(Step::Contacts); +} + +ProcessingState Controller::stateSessions() const { + return prepareState(Step::Sessions); +} + +ProcessingState Controller::stateLeftChannels() const { + const auto step = Step::LeftChannels; + return prepareState(step, [&](ProcessingState &result) { + //result.entityIndex = processed; + //result.entityCount = std::max(processed, substepsInStep(step)); }); } -void ControllerWrap::reloadPasswordState() { - _wrapped.with([=](Controller &controller) { - controller.reloadPasswordState(); +ProcessingState Controller::stateDialogs() const { + const auto step = Step::Dialogs; + return prepareState(step, [&](ProcessingState &result) { + //result.entityIndex = processed; + //result.entityCount = std::max(processed, substepsInStep(step)); }); } -void ControllerWrap::cancelUnconfirmedPassword() { - _wrapped.with([=](Controller &controller) { - controller.cancelUnconfirmedPassword(); +int Controller::substepsInStep(Step step) const { + Expects(_substepsInStep.size() > static_cast(step)); + + return _substepsInStep[static_cast(step)]; +} + +void Controller::setFinishedState() { + setState(FinishedState{ _writer->mainFilePath() }); +} + +ControllerWrap::ControllerWrap() { +} + +rpl::producer ControllerWrap::state() const { + return _wrapped.producer_on_main([=](const Controller &controller) { + return controller.state(); }); } +//void ControllerWrap::submitPassword(const QString &password) { +// _wrapped.with([=](Controller &controller) { +// controller.submitPassword(password); +// }); +//} +// +//void ControllerWrap::requestPasswordRecover() { +// _wrapped.with([=](Controller &controller) { +// controller.requestPasswordRecover(); +// }); +//} +// +//rpl::producer ControllerWrap::passwordUpdate() const { +// return _wrapped.producer_on_main([=](const Controller &controller) { +// return controller.passwordUpdate(); +// }); +//} +// +//void ControllerWrap::reloadPasswordState() { +// _wrapped.with([=](Controller &controller) { +// controller.reloadPasswordState(); +// }); +//} +// +//void ControllerWrap::cancelUnconfirmedPassword() { +// _wrapped.with([=](Controller &controller) { +// controller.cancelUnconfirmedPassword(); +// }); +//} + void ControllerWrap::startExport(const Settings &settings) { LOG(("Export Info: Started export to '%1'.").arg(settings.path)); diff --git a/Telegram/SourceFiles/export/export_controller.h b/Telegram/SourceFiles/export/export_controller.h index 73ab039ce4a5b9..22b89295833df3 100644 --- a/Telegram/SourceFiles/export/export_controller.h +++ b/Telegram/SourceFiles/export/export_controller.h @@ -27,20 +27,30 @@ struct PasswordCheckState { struct ProcessingState { enum class Step { + Initializing, + LeftChannelsList, + DialogsList, PersonalInfo, Userpics, Contacts, Sessions, + LeftChannels, Dialogs, }; enum class Item { Other, Photo, Video, + VoiceMessage, + VideoMessage, + Sticker, + GIF, File, }; - Step step = Step::PersonalInfo; + Step step = Step::Initializing; + + std::vector substepsInStep; int entityIndex = 0; int entityCount = 1; @@ -50,9 +60,11 @@ struct ProcessingState { int itemCount = 0; Item itemType = Item::Other; QString itemName; + QString itemId; int bytesLoaded = 0; int bytesCount = 0; + QString objectId; }; @@ -79,16 +91,16 @@ using State = base::optional_variant< ErrorState, FinishedState>; -struct PasswordUpdate { - enum class Type { - CheckSucceed, - WrongPassword, - FloodLimit, - RecoverUnavailable, - }; - Type type = Type::WrongPassword; - -}; +//struct PasswordUpdate { +// enum class Type { +// CheckSucceed, +// WrongPassword, +// FloodLimit, +// RecoverUnavailable, +// }; +// Type type = Type::WrongPassword; +// +//}; class ControllerWrap { public: @@ -97,11 +109,11 @@ class ControllerWrap { rpl::producer state() const; // Password step. - void submitPassword(const QString &password); - void requestPasswordRecover(); - rpl::producer passwordUpdate() const; - void reloadPasswordState(); - void cancelUnconfirmedPassword(); + //void submitPassword(const QString &password); + //void requestPasswordRecover(); + //rpl::producer passwordUpdate() const; + //void reloadPasswordState(); + //void cancelUnconfirmedPassword(); // Processing step. void startExport(const Settings &settings); diff --git a/Telegram/SourceFiles/export/export_settings.h b/Telegram/SourceFiles/export/export_settings.h index 91dcf0e2049cb2..618ab4de4cc7c1 100644 --- a/Telegram/SourceFiles/export/export_settings.h +++ b/Telegram/SourceFiles/export/export_settings.h @@ -39,16 +39,21 @@ struct MediaSettings { struct Settings { enum class Type { - PersonalInfo = 0x001, - Userpics = 0x002, - Contacts = 0x004, - Sessions = 0x008, - PersonalChats = 0x010, - BotChats = 0x020, - PrivateGroups = 0x040, - PublicGroups = 0x080, - PrivateChannels = 0x100, - PublicChannels = 0x200, + PersonalInfo = 0x001, + Userpics = 0x002, + Contacts = 0x004, + Sessions = 0x008, + PersonalChats = 0x010, + BotChats = 0x020, + PrivateGroups = 0x040, + PublicGroups = 0x080, + PrivateChannels = 0x100, + PublicChannels = 0x200, + + GroupsMask = PrivateGroups | PublicGroups, + ChannelsMask = PrivateChannels | PublicChannels, + GroupsChannelsMask = GroupsMask | ChannelsMask, + AnyChatsMask = PersonalChats | BotChats | GroupsChannelsMask, }; using Types = base::flags; friend inline constexpr auto is_flag_type(Type) { return true; }; diff --git a/Telegram/SourceFiles/export/output/export_output_abstract.h b/Telegram/SourceFiles/export/output/export_output_abstract.h index 3d0bfd65b46c49..fbd2ba696b36dc 100644 --- a/Telegram/SourceFiles/export/output/export_output_abstract.h +++ b/Telegram/SourceFiles/export/output/export_output_abstract.h @@ -47,10 +47,16 @@ class AbstractWriter { virtual bool writeDialogsStart(const Data::DialogsInfo &data) = 0; virtual bool writeDialogStart(const Data::DialogInfo &data) = 0; - virtual bool writeMessagesSlice(const Data::MessagesSlice &data) = 0; + virtual bool writeDialogSlice(const Data::MessagesSlice &data) = 0; virtual bool writeDialogEnd() = 0; virtual bool writeDialogsEnd() = 0; + virtual bool writeLeftChannelsStart(const Data::DialogsInfo &data) = 0; + virtual bool writeLeftChannelStart(const Data::DialogInfo &data) = 0; + virtual bool writeLeftChannelSlice(const Data::MessagesSlice &data) = 0; + virtual bool writeLeftChannelEnd() = 0; + virtual bool writeLeftChannelsEnd() = 0; + virtual bool finish() = 0; virtual QString mainFilePath() = 0; diff --git a/Telegram/SourceFiles/export/output/export_output_file.cpp b/Telegram/SourceFiles/export/output/export_output_file.cpp index 539c8ab275929b..e415b765245422 100644 --- a/Telegram/SourceFiles/export/output/export_output_file.cpp +++ b/Telegram/SourceFiles/export/output/export_output_file.cpp @@ -18,6 +18,10 @@ namespace Output { File::File(const QString &path) : _path(path) { } +int File::size() const { + return _offset; +} + bool File::empty() const { return !_offset; } diff --git a/Telegram/SourceFiles/export/output/export_output_file.h b/Telegram/SourceFiles/export/output/export_output_file.h index 680b25e2d29cc3..32907682ee97ec 100644 --- a/Telegram/SourceFiles/export/output/export_output_file.h +++ b/Telegram/SourceFiles/export/output/export_output_file.h @@ -20,6 +20,7 @@ class File { public: File(const QString &path); + int size() const; bool empty() const; enum class Result { diff --git a/Telegram/SourceFiles/export/output/export_output_text.cpp b/Telegram/SourceFiles/export/output/export_output_text.cpp index 23d2db12c574e6..03ccec333bcc12 100644 --- a/Telegram/SourceFiles/export/output/export_output_text.cpp +++ b/Telegram/SourceFiles/export/output/export_output_text.cpp @@ -582,6 +582,49 @@ bool TextWriter::writeSessionsList(const Data::SessionsList &data) { } bool TextWriter::writeDialogsStart(const Data::DialogsInfo &data) { + return writeChatsStart(data, "Chats", "chats.txt"); +} + +bool TextWriter::writeDialogStart(const Data::DialogInfo &data) { + return writeChatStart(data); +} + +bool TextWriter::writeDialogSlice(const Data::MessagesSlice &data) { + return writeChatSlice(data); +} + +bool TextWriter::writeDialogEnd() { + return writeChatEnd(); +} + +bool TextWriter::writeDialogsEnd() { + return true; +} + +bool TextWriter::writeLeftChannelsStart(const Data::DialogsInfo &data) { + return writeChatsStart(data, "Left chats", "left_chats.txt"); +} + +bool TextWriter::writeLeftChannelStart(const Data::DialogInfo &data) { + return writeChatStart(data); +} + +bool TextWriter::writeLeftChannelSlice(const Data::MessagesSlice &data) { + return writeChatSlice(data); +} + +bool TextWriter::writeLeftChannelEnd() { + return writeChatEnd(); +} + +bool TextWriter::writeLeftChannelsEnd() { + return true; +} + +bool TextWriter::writeChatsStart( + const Data::DialogsInfo &data, + const QByteArray &listName, + const QString &fileName) { Expects(_result != nullptr); if (data.list.empty()) { @@ -620,7 +663,7 @@ bool TextWriter::writeDialogsStart(const Data::DialogsInfo &data) { } Unexpected("Dialog type in TypeString."); }; - const auto file = fileWithRelativePath("chats.txt"); + const auto file = fileWithRelativePath(fileName); auto list = std::vector(); list.reserve(data.list.size()); auto index = 0; @@ -637,27 +680,28 @@ bool TextWriter::writeDialogsStart(const Data::DialogsInfo &data) { return false; } - const auto header = "Chats " - "(" + Data::NumberToString(data.list.size()) + ") - chats.txt" + const auto header = listName + " " + "(" + Data::NumberToString(data.list.size()) + ") - " + + fileName.toUtf8() + kLineBreak + kLineBreak; return _result->writeBlock(header) == File::Result::Success; } -bool TextWriter::writeDialogStart(const Data::DialogInfo &data) { - Expects(_dialog == nullptr); +bool TextWriter::writeChatStart(const Data::DialogInfo &data) { + Expects(_chat == nullptr); Expects(_dialogIndex < _dialogsCount); const auto digits = Data::NumberToString(_dialogsCount - 1).size(); const auto number = Data::NumberToString(++_dialogIndex, digits, '0'); - _dialog = fileWithRelativePath(data.relativePath + "messages.txt"); + _chat = fileWithRelativePath(data.relativePath + "messages.txt"); _dialogEmpty = true; _dialogOnlyMy = data.onlyMyMessages; return true; } -bool TextWriter::writeMessagesSlice(const Data::MessagesSlice &data) { - Expects(_dialog != nullptr); +bool TextWriter::writeChatSlice(const Data::MessagesSlice &data) { + Expects(_chat != nullptr); Expects(!data.list.empty()); _dialogEmpty = false; @@ -670,25 +714,21 @@ bool TextWriter::writeMessagesSlice(const Data::MessagesSlice &data) { data.peers, _settings.internalLinksDomain)); } - const auto full = _dialog->empty() + const auto full = _chat->empty() ? JoinList(kLineBreak, list) : kLineBreak + JoinList(kLineBreak, list); - return _dialog->writeBlock(full) == File::Result::Success; + return _chat->writeBlock(full) == File::Result::Success; } -bool TextWriter::writeDialogEnd() { - Expects(_dialog != nullptr); +bool TextWriter::writeChatEnd() { + Expects(_chat != nullptr); if (_dialogEmpty) { - _dialog->writeBlock(_dialogOnlyMy + _chat->writeBlock(_dialogOnlyMy ? "No outgoing messages in this chat." : "No messages in this chat."); } - _dialog = nullptr; - return true; -} - -bool TextWriter::writeDialogsEnd() { + _chat = nullptr; return true; } diff --git a/Telegram/SourceFiles/export/output/export_output_text.h b/Telegram/SourceFiles/export/output/export_output_text.h index d6f20ccdf70a76..cdb994eaf3ae5b 100644 --- a/Telegram/SourceFiles/export/output/export_output_text.h +++ b/Telegram/SourceFiles/export/output/export_output_text.h @@ -30,10 +30,16 @@ class TextWriter : public AbstractWriter { bool writeDialogsStart(const Data::DialogsInfo &data) override; bool writeDialogStart(const Data::DialogInfo &data) override; - bool writeMessagesSlice(const Data::MessagesSlice &data) override; + bool writeDialogSlice(const Data::MessagesSlice &data) override; bool writeDialogEnd() override; bool writeDialogsEnd() override; + bool writeLeftChannelsStart(const Data::DialogsInfo &data) override; + bool writeLeftChannelStart(const Data::DialogInfo &data) override; + bool writeLeftChannelSlice(const Data::MessagesSlice &data) override; + bool writeLeftChannelEnd() override; + bool writeLeftChannelsEnd() override; + bool finish() override; QString mainFilePath() override; @@ -43,6 +49,14 @@ class TextWriter : public AbstractWriter { QString pathWithRelativePath(const QString &path) const; std::unique_ptr fileWithRelativePath(const QString &path) const; + bool writeChatsStart( + const Data::DialogsInfo &data, + const QByteArray &listName, + const QString &fileName); + bool writeChatStart(const Data::DialogInfo &data); + bool writeChatSlice(const Data::MessagesSlice &data); + bool writeChatEnd(); + Settings _settings; std::unique_ptr _result; @@ -52,7 +66,12 @@ class TextWriter : public AbstractWriter { int _dialogIndex = 0; bool _dialogOnlyMy = false; bool _dialogEmpty = true; - std::unique_ptr _dialog; + + int _leftChannelsCount = 0; + int _leftChannelIndex = 0; + bool _leftChannelEmpty = true; + + std::unique_ptr _chat; };