Better steps division in export.

This commit is contained in:
John Preston 2018-06-18 22:52:13 +01:00
parent c7aa5ed544
commit 7d4e23448e
13 changed files with 815 additions and 393 deletions

View File

@ -1266,7 +1266,7 @@ channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet =
channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool; channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;
channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool; channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool;
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates; 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.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;

View File

@ -944,30 +944,23 @@ DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data) {
return result; return result;
} }
void InsertLeftDialog( DialogsInfo ParseLeftChannelsInfo(const MTPmessages_Chats &data) {
DialogsInfo &info, auto result = DialogsInfo();
const Chat &chat, data.match([&](const auto &data) { //MTPDmessages_chats &data) {
Message &&message) { result.list.reserve(data.vchats.v.size());
const auto projection = [](const DialogInfo &dialog) { for (const auto &single : data.vchats.v) {
return std::make_tuple( const auto chat = ParseChat(single);
dialog.topMessageDate, auto info = DialogInfo();
dialog.topMessageId, info.input = chat.input;
BarePeerId(dialog.peerId)); info.name = chat.title;
}; info.peerId = ChatPeerId(chat.id);
const auto i = ranges::lower_bound( info.topMessageDate = 0;
info.list, info.topMessageId = 0;
std::make_tuple(message.date, message.id, chat.id), info.type = DialogTypeFromChat(chat);
ranges::ordered_less{}, result.list.push_back(std::move(info));
projection); }
});
auto insert = DialogInfo(); return result;
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));
} }
void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings) { 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( MessagesSlice ParseMessagesSlice(
const MTPVector<MTPMessage> &data, const MTPVector<MTPMessage> &data,
const MTPVector<MTPUser> &users, const MTPVector<MTPUser> &users,

View File

@ -433,12 +433,12 @@ struct DialogsInfo {
std::vector<DialogInfo> list; std::vector<DialogInfo> list;
}; };
DialogInfo::Type DialogTypeFromChat(const Chat &chat);
DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data); DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data);
void InsertLeftDialog( DialogsInfo ParseLeftChannelsInfo(const MTPmessages_Chats &data);
DialogsInfo &info,
const Chat &chat,
Message &&message);
void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings); void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings);
void FinalizeLeftChannelsInfo(DialogsInfo &info, const Settings &settings);
struct MessagesSlice { struct MessagesSlice {
std::vector<Message> list; std::vector<Message> list;

View File

@ -63,6 +63,25 @@ LocationKey ComputeLocationKey(const Data::FileLocation &value) {
return result; 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 } // namespace
class ApiWrap::LoadedFileCache { class ApiWrap::LoadedFileCache {
@ -81,6 +100,19 @@ private:
}; };
struct ApiWrap::StartProcess {
FnMut<void(StartInfo)> done;
enum class Step {
UserpicsCount,
DialogsCount,
LeftChannelsCount,
};
std::deque<Step> steps;
StartInfo info;
};
struct ApiWrap::UserpicsProcess { struct ApiWrap::UserpicsProcess {
FnMut<void(Data::UserpicsInfo&&)> start; FnMut<void(Data::UserpicsInfo&&)> start;
Fn<void(Data::UserpicsSlice&&)> handleSlice; Fn<void(Data::UserpicsSlice&&)> handleSlice;
@ -112,30 +144,36 @@ struct ApiWrap::FileProcess {
}; };
struct ApiWrap::DialogsProcess { struct ApiWrap::LeftChannelsProcess {
Data::DialogsInfo info; FnMut<void(Data::DialogsInfo&&)> done;
std::map<int32, Data::Chat> left;
FnMut<void(const Data::DialogsInfo&)> start; Data::DialogsInfo info;
Fn<void(const Data::DialogInfo&)> startOne;
Fn<void(Data::MessagesSlice&&)> sliceOne; rpl::variable<int> count;
Fn<void()> finishOne; int fullCount = 0;
FnMut<void()> finish; bool finished = false;
};
struct ApiWrap::DialogsProcess {
FnMut<void(Data::DialogsInfo&&)> done;
Data::DialogsInfo info;
Data::TimeId offsetDate = 0; Data::TimeId offsetDate = 0;
int32 offsetId = 0; int32 offsetId = 0;
MTPInputPeer offsetPeer = MTP_inputPeerEmpty(); MTPInputPeer offsetPeer = MTP_inputPeerEmpty();
struct Single; rpl::variable<int> count;
std::unique_ptr<Single> single;
int singleIndex = -1;
}; };
struct ApiWrap::DialogsProcess::Single { struct ApiWrap::ChatProcess {
Single(const Data::DialogInfo &info);
Data::DialogInfo info; Data::DialogInfo info;
Fn<void(Data::MessagesSlice&&)> handleSlice;
FnMut<void()> done;
int32 offsetId = 1; int32 offsetId = 1;
base::optional<Data::MessagesSlice> slice; base::optional<Data::MessagesSlice> slice;
@ -173,10 +211,6 @@ base::optional<QString> ApiWrap::LoadedFileCache::find(
ApiWrap::FileProcess::FileProcess(const QString &path) : file(path) { ApiWrap::FileProcess::FileProcess(const QString &path) : file(path) {
} }
ApiWrap::DialogsProcess::Single::Single(const Data::DialogInfo &info)
: info(info) {
}
template <typename Request> template <typename Request>
auto ApiWrap::mainRequest(Request &&request) { auto ApiWrap::mainRequest(Request &&request) {
Expects(_takeoutId.has_value()); Expects(_takeoutId.has_value());
@ -215,11 +249,158 @@ rpl::producer<RPCError> ApiWrap::errors() const {
void ApiWrap::startExport( void ApiWrap::startExport(
const Settings &settings, const Settings &settings,
FnMut<void()> done) { FnMut<void(StartInfo)> done) {
Expects(_settings == nullptr); Expects(_settings == nullptr);
Expects(_startProcess == nullptr);
_settings = std::make_unique<Settings>(settings); _settings = std::make_unique<Settings>(settings);
startMainSession(std::move(done)); _startProcess = std::make_unique<StartProcess>();
_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<LeftChannelsProcess>();
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<void(Data::DialogsInfo&&)> 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<int> ApiWrap::leftChannelsLoadedCount() const {
Expects(_leftChannelsProcess != nullptr);
return _leftChannelsProcess->count.value();
}
void ApiWrap::requestDialogsList(FnMut<void(Data::DialogsInfo&&)> done) {
Expects(_dialogsProcess == nullptr);
_dialogsProcess = std::make_unique<DialogsProcess>();
_dialogsProcess->done = std::move(done);
requestDialogsSlice();
}
rpl::producer<int> ApiWrap::dialogsLoadedCount() const {
Expects(_dialogsProcess != nullptr);
return _dialogsProcess->count.value();
} }
void ApiWrap::startMainSession(FnMut<void()> done) { void ApiWrap::startMainSession(FnMut<void()> done) {
@ -287,8 +468,8 @@ void ApiWrap::requestUserpics(
mainRequest(MTPphotos_GetUserPhotos( mainRequest(MTPphotos_GetUserPhotos(
_user, _user,
MTP_int(0), MTP_int(0), // offset
MTP_long(0), MTP_long(0), // max_id
MTP_int(kUserpicsSliceLimit) MTP_int(kUserpicsSliceLimit)
)).done([=](const MTPphotos_Photos &result) mutable { )).done([=](const MTPphotos_Photos &result) mutable {
Expects(_userpicsProcess != nullptr); Expects(_userpicsProcess != nullptr);
@ -405,22 +586,18 @@ void ApiWrap::requestSessions(FnMut<void(Data::SessionsList&&)> done) {
}).send(); }).send();
} }
void ApiWrap::requestDialogs( void ApiWrap::requestMessages(
FnMut<void(const Data::DialogsInfo&)> start, const Data::DialogInfo &info,
Fn<void(const Data::DialogInfo&)> startOne, Fn<void(Data::MessagesSlice&&)> slice,
Fn<void(Data::MessagesSlice&&)> sliceOne, FnMut<void()> done) {
Fn<void()> finishOne, Expects(_chatProcess == nullptr);
FnMut<void()> finish) {
Expects(_dialogsProcess == nullptr);
_dialogsProcess = std::make_unique<DialogsProcess>(); _chatProcess = std::make_unique<ChatProcess>();
_dialogsProcess->start = std::move(start); _chatProcess->info = info;
_dialogsProcess->startOne = std::move(startOne); _chatProcess->handleSlice = std::move(slice);
_dialogsProcess->sliceOne = std::move(sliceOne); _chatProcess->done = std::move(done);
_dialogsProcess->finishOne = std::move(finishOne);
_dialogsProcess->finish = std::move(finish);
requestDialogsSlice(); requestMessagesSlice();
} }
void ApiWrap::requestDialogsSlice() { void ApiWrap::requestDialogsSlice() {
@ -432,7 +609,7 @@ void ApiWrap::requestDialogsSlice() {
MTP_int(_dialogsProcess->offsetId), MTP_int(_dialogsProcess->offsetId),
_dialogsProcess->offsetPeer, _dialogsProcess->offsetPeer,
MTP_int(kChatsSliceLimit) MTP_int(kChatsSliceLimit)
)).done([=](const MTPmessages_Dialogs &result) mutable { )).done([=](const MTPmessages_Dialogs &result) {
const auto finished = result.match( const auto finished = result.match(
[](const MTPDmessages_dialogs &data) { [](const MTPDmessages_dialogs &data) {
return true; return true;
@ -442,7 +619,7 @@ void ApiWrap::requestDialogsSlice() {
auto info = Data::ParseDialogsInfo(result); auto info = Data::ParseDialogsInfo(result);
if (finished || info.list.empty()) { if (finished || info.list.empty()) {
requestLeftChannels(); finishDialogsList();
} else { } else {
const auto &last = info.list.back(); const auto &last = info.list.back();
_dialogsProcess->offsetId = last.topMessageId; _dialogsProcess->offsetId = last.topMessageId;
@ -458,139 +635,86 @@ void ApiWrap::requestDialogsSlice() {
void ApiWrap::appendDialogsSlice(Data::DialogsInfo &&info) { void ApiWrap::appendDialogsSlice(Data::DialogsInfo &&info) {
Expects(_dialogsProcess != nullptr); Expects(_dialogsProcess != nullptr);
Expects(_settings != nullptr);
const auto types = _settings->types; appendChatsSlice(_dialogsProcess->info, std::move(info));
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));
}
} }
void ApiWrap::requestLeftChannels() { void ApiWrap::finishDialogsList() {
Expects(_dialogsProcess != nullptr); Expects(_dialogsProcess != nullptr);
mainRequest(MTPchannels_GetLeftChannels( const auto process = base::take(_dialogsProcess);
)).done([=](const MTPmessages_Chats &result) mutable {
Expects(_dialogsProcess != nullptr);
const auto finished = result.match( ranges::reverse(process->info.list);
Data::FinalizeDialogsInfo(process->info, *_settings);
process->done(std::move(process->info));
}
void ApiWrap::requestLeftChannelsSliceGeneric(FnMut<void()> done) {
Expects(_leftChannelsProcess != nullptr);
mainRequest(MTPchannels_GetLeftChannels(
MTP_int(_leftChannelsProcess->info.list.size())
)).done([=, done = std::move(done)](
const MTPmessages_Chats &result) mutable {
Expects(_leftChannelsProcess != nullptr);
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) { [](const MTPDmessages_chats &data) {
return true; return true;
}, [](const MTPDmessages_chatsSlice &data) { }, [](const MTPDmessages_chatsSlice &data) {
return data.vchats.v.isEmpty(); return data.vchats.v.isEmpty();
}); });
_dialogsProcess->left = Data::ParseChatsList(*result.match( process->count = process->info.list.size();
[](const auto &data) {
return &data.vchats; done();
}));
requestLeftDialog();
}).send(); }).send();
} }
void ApiWrap::requestLeftDialog() { void ApiWrap::appendLeftChannelsSlice(Data::DialogsInfo &&info) {
Expects(_dialogsProcess != nullptr); Expects(_leftChannelsProcess != nullptr);
auto &left = _dialogsProcess->left; appendChatsSlice(_leftChannelsProcess->info, std::move(info));
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();
});
}).send();
} }
void ApiWrap::finishDialogsList() { void ApiWrap::appendChatsSlice(
Expects(_dialogsProcess != nullptr); Data::DialogsInfo &to,
Data::DialogsInfo &&info) {
Expects(_settings != nullptr);
ranges::reverse(_dialogsProcess->info.list); const auto types = _settings->types;
Data::FinalizeDialogsInfo(_dialogsProcess->info, *_settings); auto filtered = ranges::view::all(
info.list
_dialogsProcess->start(_dialogsProcess->info); ) | ranges::view::filter([&](const Data::DialogInfo &info) {
requestNextDialog(); return (types & SettingsFromDialogsType(info.type)) != 0;
} });
auto &list = to.list;
void ApiWrap::requestNextDialog() { if (list.empty()) {
Expects(_dialogsProcess != nullptr); list = filtered | ranges::to_vector;
Expects(_dialogsProcess->single == nullptr); } else {
list.reserve(list.size() + info.list.size());
const auto index = ++_dialogsProcess->singleIndex; for (auto &info : filtered) {
if (index < _dialogsProcess->info.list.size()) { list.push_back(std::move(info));
const auto &one = _dialogsProcess->info.list[index]; }
_dialogsProcess->single = std::make_unique<DialogsProcess::Single>(one);
_dialogsProcess->startOne(one);
requestMessagesSlice();
return;
} }
finishDialogs();
} }
void ApiWrap::requestMessagesSlice() { void ApiWrap::requestMessagesSlice() {
Expects(_dialogsProcess != nullptr); Expects(_chatProcess != nullptr);
Expects(_dialogsProcess->single != nullptr);
const auto process = _dialogsProcess->single.get();
// #TODO export // #TODO export
if (process->info.input.match([](const MTPDinputPeerUser &value) { if (_chatProcess->info.input.match([](const MTPDinputPeerUser &value) {
return !value.vaccess_hash.v; return !value.vaccess_hash.v;
}, [](const auto &data) { return false; })) { }, [](const auto &data) { return false; })) {
finishMessages(); finishMessages();
@ -598,33 +722,31 @@ void ApiWrap::requestMessagesSlice() {
} }
auto handleResult = [=](const MTPmessages_Messages &result) mutable { auto handleResult = [=](const MTPmessages_Messages &result) mutable {
Expects(_dialogsProcess != nullptr); Expects(_chatProcess != nullptr);
Expects(_dialogsProcess->single != nullptr);
const auto process = _dialogsProcess->single.get();
result.match([&](const MTPDmessages_messagesNotModified &data) { result.match([&](const MTPDmessages_messagesNotModified &data) {
error("Unexpected messagesNotModified received."); error("Unexpected messagesNotModified received.");
}, [&](const auto &data) { }, [&](const auto &data) {
if constexpr (MTPDmessages_messages::Is<decltype(data)>()) { if constexpr (MTPDmessages_messages::Is<decltype(data)>()) {
process->lastSlice = true; _chatProcess->lastSlice = true;
} }
loadMessagesFiles(Data::ParseMessagesSlice( loadMessagesFiles(Data::ParseMessagesSlice(
data.vmessages, data.vmessages,
data.vusers, data.vusers,
data.vchats, data.vchats,
process->info.relativePath)); _chatProcess->info.relativePath));
}); });
}; };
if (process->info.onlyMyMessages) { if (_chatProcess->info.onlyMyMessages) {
mainRequest(MTPmessages_Search( mainRequest(MTPmessages_Search(
MTP_flags(MTPmessages_Search::Flag::f_from_id), MTP_flags(MTPmessages_Search::Flag::f_from_id),
process->info.input, _chatProcess->info.input,
MTP_string(""), // query MTP_string(""), // query
_user, _user,
MTP_inputMessagesFilterEmpty(), MTP_inputMessagesFilterEmpty(),
MTP_int(0), // min_date MTP_int(0), // min_date
MTP_int(0), // max_date MTP_int(0), // max_date
MTP_int(process->offsetId), MTP_int(_chatProcess->offsetId),
MTP_int(-kMessagesSliceLimit), MTP_int(-kMessagesSliceLimit),
MTP_int(kMessagesSliceLimit), MTP_int(kMessagesSliceLimit),
MTP_int(0), // max_id MTP_int(0), // max_id
@ -633,8 +755,8 @@ void ApiWrap::requestMessagesSlice() {
)).done(std::move(handleResult)).send(); )).done(std::move(handleResult)).send();
} else { } else {
mainRequest(MTPmessages_GetHistory( mainRequest(MTPmessages_GetHistory(
process->info.input, _chatProcess->info.input,
MTP_int(process->offsetId), MTP_int(_chatProcess->offsetId),
MTP_int(0), // offset_date MTP_int(0), // offset_date
MTP_int(-kMessagesSliceLimit), MTP_int(-kMessagesSliceLimit),
MTP_int(kMessagesSliceLimit), MTP_int(kMessagesSliceLimit),
@ -646,29 +768,25 @@ void ApiWrap::requestMessagesSlice() {
} }
void ApiWrap::loadMessagesFiles(Data::MessagesSlice &&slice) { void ApiWrap::loadMessagesFiles(Data::MessagesSlice &&slice) {
Expects(_dialogsProcess != nullptr); Expects(_chatProcess != nullptr);
Expects(_dialogsProcess->single != nullptr); Expects(!_chatProcess->slice.has_value());
Expects(!_dialogsProcess->single->slice.has_value());
const auto process = _dialogsProcess->single.get();
if (slice.list.empty()) { if (slice.list.empty()) {
process->lastSlice = true; _chatProcess->lastSlice = true;
} }
process->slice = std::move(slice); _chatProcess->slice = std::move(slice);
process->fileIndex = -1; _chatProcess->fileIndex = -1;
loadNextMessageFile(); loadNextMessageFile();
} }
void ApiWrap::loadNextMessageFile() { void ApiWrap::loadNextMessageFile() {
Expects(_dialogsProcess != nullptr); Expects(_chatProcess != nullptr);
Expects(_dialogsProcess->single != nullptr); Expects(_chatProcess->slice.has_value());
Expects(_dialogsProcess->single->slice.has_value());
const auto process = _dialogsProcess->single.get(); auto &list = _chatProcess->slice->list;
auto &list = process->slice->list;
while (true) { while (true) {
const auto index = ++process->fileIndex; const auto index = ++_chatProcess->fileIndex;
if (index >= list.size()) { if (index >= list.size()) {
break; break;
} }
@ -684,17 +802,15 @@ void ApiWrap::loadNextMessageFile() {
} }
void ApiWrap::finishMessagesSlice() { void ApiWrap::finishMessagesSlice() {
Expects(_dialogsProcess != nullptr); Expects(_chatProcess != nullptr);
Expects(_dialogsProcess->single != nullptr); Expects(_chatProcess->slice.has_value());
Expects(_dialogsProcess->single->slice.has_value());
const auto process = _dialogsProcess->single.get(); auto slice = *base::take(_chatProcess->slice);
auto slice = *base::take(process->slice);
if (!slice.list.empty()) { if (!slice.list.empty()) {
process->offsetId = slice.list.back().id + 1; _chatProcess->offsetId = slice.list.back().id + 1;
_dialogsProcess->sliceOne(std::move(slice)); _chatProcess->handleSlice(std::move(slice));
} }
if (process->lastSlice) { if (_chatProcess->lastSlice) {
finishMessages(); finishMessages();
} else { } else {
requestMessagesSlice(); requestMessagesSlice();
@ -702,35 +818,22 @@ void ApiWrap::finishMessagesSlice() {
} }
void ApiWrap::loadMessageFileDone(const QString &relativePath) { void ApiWrap::loadMessageFileDone(const QString &relativePath) {
Expects(_dialogsProcess != nullptr); Expects(_chatProcess != nullptr);
Expects(_dialogsProcess->single != nullptr); Expects(_chatProcess->slice.has_value());
Expects(_dialogsProcess->single->slice.has_value()); Expects((_chatProcess->fileIndex >= 0)
Expects((_dialogsProcess->single->fileIndex >= 0) && (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
&& (_dialogsProcess->single->fileIndex
< _dialogsProcess->single->slice->list.size()));
const auto process = _dialogsProcess->single.get(); const auto index = _chatProcess->fileIndex;
const auto index = process->fileIndex; _chatProcess->slice->list[index].file().relativePath = relativePath;
process->slice->list[index].file().relativePath = relativePath;
loadNextMessageFile(); loadNextMessageFile();
} }
void ApiWrap::finishMessages() { void ApiWrap::finishMessages() {
Expects(_dialogsProcess != nullptr); Expects(_chatProcess != nullptr);
Expects(_dialogsProcess->single != nullptr); Expects(!_chatProcess->slice.has_value());
Expects(!_dialogsProcess->single->slice.has_value());
_dialogsProcess->single = nullptr; const auto process = base::take(_chatProcess);
_dialogsProcess->finishOne(); process->done();
requestNextDialog();
}
void ApiWrap::finishDialogs() {
Expects(_dialogsProcess != nullptr);
Expects(_dialogsProcess->single == nullptr);
base::take(_dialogsProcess)->finish();
} }
bool ApiWrap::processFileLoad( bool ApiWrap::processFileLoad(
@ -800,7 +903,9 @@ bool ApiWrap::writePreloadedFile(Data::File &file) {
return false; return false;
} }
void ApiWrap::loadFile(const Data::File &file, FnMut<void(QString)> done) { void ApiWrap::loadFile(
const Data::File &file,
FnMut<void(QString)> done) {
Expects(_fileProcess == nullptr); Expects(_fileProcess == nullptr);
Expects(file.location.dcId != 0); Expects(file.location.dcId != 0);

View File

@ -33,9 +33,20 @@ public:
rpl::producer<RPCError> errors() const; rpl::producer<RPCError> errors() const;
struct StartInfo {
int userpicsCount = 0;
int dialogsCount = 0;
int leftChannelsCount = 0;
};
void startExport( void startExport(
const Settings &settings, const Settings &settings,
FnMut<void()> done); FnMut<void(StartInfo)> done);
void requestLeftChannelsList(FnMut<void(Data::DialogsInfo&&)> done);
rpl::producer<int> leftChannelsLoadedCount() const;
void requestDialogsList(FnMut<void(Data::DialogsInfo&&)> done);
rpl::producer<int> dialogsLoadedCount() const;
void requestPersonalInfo(FnMut<void(Data::PersonalInfo&&)> done); void requestPersonalInfo(FnMut<void(Data::PersonalInfo&&)> done);
@ -48,22 +59,28 @@ public:
void requestSessions(FnMut<void(Data::SessionsList&&)> done); void requestSessions(FnMut<void(Data::SessionsList&&)> done);
void requestDialogs( void requestMessages(
FnMut<void(const Data::DialogsInfo&)> start, const Data::DialogInfo &info,
Fn<void(const Data::DialogInfo&)> startOne, Fn<void(Data::MessagesSlice&&)> slice,
Fn<void(Data::MessagesSlice&&)> sliceOne, FnMut<void()> done);
Fn<void()> finishOne,
FnMut<void()> finish);
~ApiWrap(); ~ApiWrap();
private: private:
class LoadedFileCache;
struct StartProcess;
struct UserpicsProcess; struct UserpicsProcess;
struct FileProcess; struct FileProcess;
struct LeftChannelsProcess;
struct DialogsProcess; struct DialogsProcess;
class LoadedFileCache; struct ChatProcess;
void startMainSession(FnMut<void()> done); void startMainSession(FnMut<void()> done);
void sendNextStartRequest();
void requestUserpicsCount();
void requestDialogsCount();
void requestLeftChannelsCount();
void finishStartProcess();
void handleUserpicsSlice(const MTPphotos_Photos &result); void handleUserpicsSlice(const MTPphotos_Photos &result);
void loadUserpicsFiles(Data::UserpicsSlice &&slice); void loadUserpicsFiles(Data::UserpicsSlice &&slice);
@ -73,18 +90,20 @@ private:
void requestDialogsSlice(); void requestDialogsSlice();
void appendDialogsSlice(Data::DialogsInfo &&info); void appendDialogsSlice(Data::DialogsInfo &&info);
void requestLeftChannels();
void requestLeftDialog();
void finishDialogsList(); void finishDialogsList();
void requestNextDialog(); void requestLeftChannelsSliceGeneric(FnMut<void()> done);
void requestLeftChannelsSlice();
void appendLeftChannelsSlice(Data::DialogsInfo &&info);
void appendChatsSlice(Data::DialogsInfo &to, Data::DialogsInfo &&info);
void requestMessagesSlice(); void requestMessagesSlice();
void loadMessagesFiles(Data::MessagesSlice &&slice); void loadMessagesFiles(Data::MessagesSlice &&slice);
void loadNextMessageFile(); void loadNextMessageFile();
void loadMessageFileDone(const QString &relativePath); void loadMessageFileDone(const QString &relativePath);
void finishMessagesSlice(); void finishMessagesSlice();
void finishMessages(); void finishMessages();
void finishDialogs();
bool processFileLoad( bool processFileLoad(
Data::File &file, Data::File &file,
@ -93,7 +112,9 @@ private:
std::unique_ptr<FileProcess> prepareFileProcess( std::unique_ptr<FileProcess> prepareFileProcess(
const Data::File &file) const; const Data::File &file) const;
bool writePreloadedFile(Data::File &file); bool writePreloadedFile(Data::File &file);
void loadFile(const Data::File &file, FnMut<void(QString)> done); void loadFile(
const Data::File &file,
FnMut<void(QString)> done);
void loadFilePart(); void loadFilePart();
void filePartDone(int offset, const MTPupload_File &result); void filePartDone(int offset, const MTPupload_File &result);
@ -113,10 +134,13 @@ private:
std::unique_ptr<Settings> _settings; std::unique_ptr<Settings> _settings;
MTPInputUser _user = MTP_inputUserSelf(); MTPInputUser _user = MTP_inputUserSelf();
std::unique_ptr<StartProcess> _startProcess;
std::unique_ptr<LoadedFileCache> _fileCache; std::unique_ptr<LoadedFileCache> _fileCache;
std::unique_ptr<UserpicsProcess> _userpicsProcess; std::unique_ptr<UserpicsProcess> _userpicsProcess;
std::unique_ptr<FileProcess> _fileProcess; std::unique_ptr<FileProcess> _fileProcess;
std::unique_ptr<LeftChannelsProcess> _leftChannelsProcess;
std::unique_ptr<DialogsProcess> _dialogsProcess; std::unique_ptr<DialogsProcess> _dialogsProcess;
std::unique_ptr<ChatProcess> _chatProcess;
rpl::event_stream<RPCError> _errors; rpl::event_stream<RPCError> _errors;

View File

@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Export { namespace Export {
auto kNullStateCallback = [](ProcessingState&) {};
class Controller { class Controller {
public: public:
Controller(crl::weak_on_queue<Controller> weak); Controller(crl::weak_on_queue<Controller> weak);
@ -21,11 +23,11 @@ public:
rpl::producer<State> state() const; rpl::producer<State> state() const;
// Password step. // Password step.
void submitPassword(const QString &password); //void submitPassword(const QString &password);
void requestPasswordRecover(); //void requestPasswordRecover();
rpl::producer<PasswordUpdate> passwordUpdate() const; //rpl::producer<PasswordUpdate> passwordUpdate() const;
void reloadPasswordState(); //void reloadPasswordState();
void cancelUnconfirmedPassword(); //void cancelUnconfirmedPassword();
// Processing step. // Processing step.
void startExport(const Settings &settings); void startExport(const Settings &settings);
@ -37,30 +39,56 @@ private:
void ioError(const QString &path); void ioError(const QString &path);
void setFinishedState(); void setFinishedState();
void requestPasswordState(); //void requestPasswordState();
void passwordStateDone(const MTPaccount_Password &password); //void passwordStateDone(const MTPaccount_Password &password);
void fillExportSteps(); void fillExportSteps();
void fillSubstepsInSteps(const ApiWrap::StartInfo &info);
void exportNext(); void exportNext();
void initialize();
void collectLeftChannels();
void collectDialogsList();
void exportPersonalInfo(); void exportPersonalInfo();
void exportUserpics(); void exportUserpics();
void exportContacts(); void exportContacts();
void exportSessions(); void exportSessions();
void exportDialogs(); void exportDialogs();
void exportNextDialog();
void exportLeftChannels();
void exportNextLeftChannel();
template <typename Callback = const decltype(kNullStateCallback) &>
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(); bool normalizePath();
ApiWrap _api; ApiWrap _api;
Settings _settings; Settings _settings;
Data::DialogsInfo _leftChannelsInfo;
Data::DialogsInfo _dialogsInfo;
int _leftChannelIndex = -1;
int _dialogIndex = -1;
// rpl::variable<State> fails to compile in MSVC :( // rpl::variable<State> fails to compile in MSVC :(
State _state; State _state;
rpl::event_stream<State> _stateChanges; rpl::event_stream<State> _stateChanges;
std::vector<int> _substepsInStep;
mtpRequestId _passwordRequestId = 0;
std::unique_ptr<Output::AbstractWriter> _writer; std::unique_ptr<Output::AbstractWriter> _writer;
std::vector<ProcessingState::Step> _steps; std::vector<Step> _steps;
int _stepIndex = -1; int _stepIndex = -1;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;
@ -102,49 +130,49 @@ void Controller::ioError(const QString &path) {
setState(ErrorState{ ErrorState::Type::IO, base::none, path }); setState(ErrorState{ ErrorState::Type::IO, base::none, path });
} }
void Controller::submitPassword(const QString &password) { //void Controller::submitPassword(const QString &password) {
//
} //}
//
void Controller::requestPasswordRecover() { //void Controller::requestPasswordRecover() {
//
} //}
//
rpl::producer<PasswordUpdate> Controller::passwordUpdate() const { //rpl::producer<PasswordUpdate> Controller::passwordUpdate() const {
return rpl::never<PasswordUpdate>(); // return rpl::never<PasswordUpdate>();
} //}
//
void Controller::reloadPasswordState() { //void Controller::reloadPasswordState() {
//_mtp.request(base::take(_passwordRequestId)).cancel(); // //_mtp.request(base::take(_passwordRequestId)).cancel();
requestPasswordState(); // requestPasswordState();
} //}
//
void Controller::requestPasswordState() { //void Controller::requestPasswordState() {
if (_passwordRequestId) { // if (_passwordRequestId) {
return; // return;
} // }
//_passwordRequestId = _mtp.request(MTPaccount_GetPassword( // //_passwordRequestId = _mtp.request(MTPaccount_GetPassword(
//)).done([=](const MTPaccount_Password &result) { // //)).done([=](const MTPaccount_Password &result) {
// _passwordRequestId = 0; // // _passwordRequestId = 0;
// passwordStateDone(result); // // passwordStateDone(result);
//}).fail([=](const RPCError &error) { // //}).fail([=](const RPCError &error) {
// apiError(error); // // apiError(error);
//}).send(); // //}).send();
} //}
//
void Controller::passwordStateDone(const MTPaccount_Password &result) { //void Controller::passwordStateDone(const MTPaccount_Password &result) {
auto state = PasswordCheckState(); // auto state = PasswordCheckState();
state.checked = false; // state.checked = false;
state.requesting = false; // state.requesting = false;
state.hasPassword; // state.hasPassword;
state.hint; // state.hint;
state.unconfirmedPattern; // state.unconfirmedPattern;
setState(std::move(state)); // setState(std::move(state));
} //}
//
void Controller::cancelUnconfirmedPassword() { //void Controller::cancelUnconfirmedPassword() {
//
} //}
void Controller::startExport(const Settings &settings) { void Controller::startExport(const Settings &settings) {
if (!_settings.path.isEmpty()) { if (!_settings.path.isEmpty()) {
@ -158,9 +186,7 @@ void Controller::startExport(const Settings &settings) {
} }
_writer = Output::CreateWriter(_settings.format); _writer = Output::CreateWriter(_settings.format);
fillExportSteps(); fillExportSteps();
_api.startExport(_settings, [=] { exportNext();
exportNext();
});
} }
bool Controller::normalizePath() { bool Controller::normalizePath() {
@ -195,6 +221,13 @@ bool Controller::normalizePath() {
void Controller::fillExportSteps() { void Controller::fillExportSteps() {
using Type = Settings::Type; 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) { if (_settings.types & Type::PersonalInfo) {
_steps.push_back(Step::PersonalInfo); _steps.push_back(Step::PersonalInfo);
} }
@ -207,17 +240,46 @@ void Controller::fillExportSteps() {
if (_settings.types & Type::Sessions) { if (_settings.types & Type::Sessions) {
_steps.push_back(Step::Sessions); _steps.push_back(Step::Sessions);
} }
const auto dialogTypes = Type::PersonalChats if (_settings.types & Type::AnyChatsMask) {
| Type::BotChats
| Type::PrivateGroups
| Type::PublicGroups
| Type::PrivateChannels
| Type::PublicChannels;
if (_settings.types & dialogTypes) {
_steps.push_back(Step::Dialogs); _steps.push_back(Step::Dialogs);
} }
} }
void Controller::fillSubstepsInSteps(const ApiWrap::StartInfo &info) {
const auto push = [&](Step step, int count) {
const auto index = static_cast<int>(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() { void Controller::exportNext() {
if (!++_stepIndex) { if (!++_stepIndex) {
_writer->start(_settings); _writer->start(_settings);
@ -229,15 +291,52 @@ void Controller::exportNext() {
} }
const auto step = _steps[_stepIndex]; const auto step = _steps[_stepIndex];
switch (step) { switch (step) {
case Step::Initializing: return initialize();
case Step::LeftChannelsList: return collectLeftChannels();
case Step::DialogsList: return collectDialogsList();
case Step::PersonalInfo: return exportPersonalInfo(); case Step::PersonalInfo: return exportPersonalInfo();
case Step::Userpics: return exportUserpics(); case Step::Userpics: return exportUserpics();
case Step::Contacts: return exportContacts(); case Step::Contacts: return exportContacts();
case Step::Sessions: return exportSessions(); case Step::Sessions: return exportSessions();
case Step::LeftChannels: return exportLeftChannels();
case Step::Dialogs: return exportDialogs(); case Step::Dialogs: return exportDialogs();
} }
Unexpected("Step in Controller::exportNext."); 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() { void Controller::exportPersonalInfo() {
_api.requestPersonalInfo([=](Data::PersonalInfo &&result) { _api.requestPersonalInfo([=](Data::PersonalInfo &&result) {
_writer->writePersonal(result); _writer->writePersonal(result);
@ -271,20 +370,123 @@ void Controller::exportSessions() {
} }
void Controller::exportDialogs() { void Controller::exportDialogs() {
_api.requestDialogs([=](const Data::DialogsInfo &result) { _writer->writeDialogsStart(_dialogsInfo);
_writer->writeDialogsStart(result);
}, [=](const Data::DialogInfo &result) { exportNextDialog();
_writer->writeDialogStart(result); }
}, [=](Data::MessagesSlice &&result) {
_writer->writeMessagesSlice(result); void Controller::exportNextDialog() {
}, [=] { const auto index = ++_dialogIndex;
_writer->writeDialogEnd(); if (index < _dialogsInfo.list.size()) {
}, [=] { const auto &info = _dialogsInfo.list[index];
_writer->writeDialogsEnd(); _writer->writeDialogStart(info);
exportNext();
_api.requestMessages(info, [=](Data::MessagesSlice &&result) {
_writer->writeDialogSlice(result);
}, [=] {
_writer->writeDialogEnd();
exportNextDialog();
});
return;
}
_writer->writeDialogsEnd();
exportNext();
}
void Controller::exportLeftChannels() {
_writer->writeLeftChannelsStart(_leftChannelsInfo);
exportNextLeftChannel();
}
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 <typename Callback>
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));
}); });
} }
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) {
});
}
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));
});
}
ProcessingState Controller::stateDialogs() const {
const auto step = Step::Dialogs;
return prepareState(step, [&](ProcessingState &result) {
//result.entityIndex = processed;
//result.entityCount = std::max(processed, substepsInStep(step));
});
}
int Controller::substepsInStep(Step step) const {
Expects(_substepsInStep.size() > static_cast<int>(step));
return _substepsInStep[static_cast<int>(step)];
}
void Controller::setFinishedState() { void Controller::setFinishedState() {
setState(FinishedState{ _writer->mainFilePath() }); setState(FinishedState{ _writer->mainFilePath() });
} }
@ -298,35 +500,35 @@ rpl::producer<State> ControllerWrap::state() const {
}); });
} }
void ControllerWrap::submitPassword(const QString &password) { //void ControllerWrap::submitPassword(const QString &password) {
_wrapped.with([=](Controller &controller) { // _wrapped.with([=](Controller &controller) {
controller.submitPassword(password); // controller.submitPassword(password);
}); // });
} //}
//
void ControllerWrap::requestPasswordRecover() { //void ControllerWrap::requestPasswordRecover() {
_wrapped.with([=](Controller &controller) { // _wrapped.with([=](Controller &controller) {
controller.requestPasswordRecover(); // controller.requestPasswordRecover();
}); // });
} //}
//
rpl::producer<PasswordUpdate> ControllerWrap::passwordUpdate() const { //rpl::producer<PasswordUpdate> ControllerWrap::passwordUpdate() const {
return _wrapped.producer_on_main([=](const Controller &controller) { // return _wrapped.producer_on_main([=](const Controller &controller) {
return controller.passwordUpdate(); // return controller.passwordUpdate();
}); // });
} //}
//
void ControllerWrap::reloadPasswordState() { //void ControllerWrap::reloadPasswordState() {
_wrapped.with([=](Controller &controller) { // _wrapped.with([=](Controller &controller) {
controller.reloadPasswordState(); // controller.reloadPasswordState();
}); // });
} //}
//
void ControllerWrap::cancelUnconfirmedPassword() { //void ControllerWrap::cancelUnconfirmedPassword() {
_wrapped.with([=](Controller &controller) { // _wrapped.with([=](Controller &controller) {
controller.cancelUnconfirmedPassword(); // controller.cancelUnconfirmedPassword();
}); // });
} //}
void ControllerWrap::startExport(const Settings &settings) { void ControllerWrap::startExport(const Settings &settings) {
LOG(("Export Info: Started export to '%1'.").arg(settings.path)); LOG(("Export Info: Started export to '%1'.").arg(settings.path));

View File

@ -27,20 +27,30 @@ struct PasswordCheckState {
struct ProcessingState { struct ProcessingState {
enum class Step { enum class Step {
Initializing,
LeftChannelsList,
DialogsList,
PersonalInfo, PersonalInfo,
Userpics, Userpics,
Contacts, Contacts,
Sessions, Sessions,
LeftChannels,
Dialogs, Dialogs,
}; };
enum class Item { enum class Item {
Other, Other,
Photo, Photo,
Video, Video,
VoiceMessage,
VideoMessage,
Sticker,
GIF,
File, File,
}; };
Step step = Step::PersonalInfo; Step step = Step::Initializing;
std::vector<int> substepsInStep;
int entityIndex = 0; int entityIndex = 0;
int entityCount = 1; int entityCount = 1;
@ -50,9 +60,11 @@ struct ProcessingState {
int itemCount = 0; int itemCount = 0;
Item itemType = Item::Other; Item itemType = Item::Other;
QString itemName; QString itemName;
QString itemId;
int bytesLoaded = 0; int bytesLoaded = 0;
int bytesCount = 0; int bytesCount = 0;
QString objectId;
}; };
@ -79,16 +91,16 @@ using State = base::optional_variant<
ErrorState, ErrorState,
FinishedState>; FinishedState>;
struct PasswordUpdate { //struct PasswordUpdate {
enum class Type { // enum class Type {
CheckSucceed, // CheckSucceed,
WrongPassword, // WrongPassword,
FloodLimit, // FloodLimit,
RecoverUnavailable, // RecoverUnavailable,
}; // };
Type type = Type::WrongPassword; // Type type = Type::WrongPassword;
//
}; //};
class ControllerWrap { class ControllerWrap {
public: public:
@ -97,11 +109,11 @@ public:
rpl::producer<State> state() const; rpl::producer<State> state() const;
// Password step. // Password step.
void submitPassword(const QString &password); //void submitPassword(const QString &password);
void requestPasswordRecover(); //void requestPasswordRecover();
rpl::producer<PasswordUpdate> passwordUpdate() const; //rpl::producer<PasswordUpdate> passwordUpdate() const;
void reloadPasswordState(); //void reloadPasswordState();
void cancelUnconfirmedPassword(); //void cancelUnconfirmedPassword();
// Processing step. // Processing step.
void startExport(const Settings &settings); void startExport(const Settings &settings);

View File

@ -39,16 +39,21 @@ struct MediaSettings {
struct Settings { struct Settings {
enum class Type { enum class Type {
PersonalInfo = 0x001, PersonalInfo = 0x001,
Userpics = 0x002, Userpics = 0x002,
Contacts = 0x004, Contacts = 0x004,
Sessions = 0x008, Sessions = 0x008,
PersonalChats = 0x010, PersonalChats = 0x010,
BotChats = 0x020, BotChats = 0x020,
PrivateGroups = 0x040, PrivateGroups = 0x040,
PublicGroups = 0x080, PublicGroups = 0x080,
PrivateChannels = 0x100, PrivateChannels = 0x100,
PublicChannels = 0x200, PublicChannels = 0x200,
GroupsMask = PrivateGroups | PublicGroups,
ChannelsMask = PrivateChannels | PublicChannels,
GroupsChannelsMask = GroupsMask | ChannelsMask,
AnyChatsMask = PersonalChats | BotChats | GroupsChannelsMask,
}; };
using Types = base::flags<Type>; using Types = base::flags<Type>;
friend inline constexpr auto is_flag_type(Type) { return true; }; friend inline constexpr auto is_flag_type(Type) { return true; };

View File

@ -47,10 +47,16 @@ public:
virtual bool writeDialogsStart(const Data::DialogsInfo &data) = 0; virtual bool writeDialogsStart(const Data::DialogsInfo &data) = 0;
virtual bool writeDialogStart(const Data::DialogInfo &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 writeDialogEnd() = 0;
virtual bool writeDialogsEnd() = 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 bool finish() = 0;
virtual QString mainFilePath() = 0; virtual QString mainFilePath() = 0;

View File

@ -18,6 +18,10 @@ namespace Output {
File::File(const QString &path) : _path(path) { File::File(const QString &path) : _path(path) {
} }
int File::size() const {
return _offset;
}
bool File::empty() const { bool File::empty() const {
return !_offset; return !_offset;
} }

View File

@ -20,6 +20,7 @@ class File {
public: public:
File(const QString &path); File(const QString &path);
int size() const;
bool empty() const; bool empty() const;
enum class Result { enum class Result {

View File

@ -582,6 +582,49 @@ bool TextWriter::writeSessionsList(const Data::SessionsList &data) {
} }
bool TextWriter::writeDialogsStart(const Data::DialogsInfo &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); Expects(_result != nullptr);
if (data.list.empty()) { if (data.list.empty()) {
@ -620,7 +663,7 @@ bool TextWriter::writeDialogsStart(const Data::DialogsInfo &data) {
} }
Unexpected("Dialog type in TypeString."); Unexpected("Dialog type in TypeString.");
}; };
const auto file = fileWithRelativePath("chats.txt"); const auto file = fileWithRelativePath(fileName);
auto list = std::vector<QByteArray>(); auto list = std::vector<QByteArray>();
list.reserve(data.list.size()); list.reserve(data.list.size());
auto index = 0; auto index = 0;
@ -637,27 +680,28 @@ bool TextWriter::writeDialogsStart(const Data::DialogsInfo &data) {
return false; return false;
} }
const auto header = "Chats " const auto header = listName + " "
"(" + Data::NumberToString(data.list.size()) + ") - chats.txt" "(" + Data::NumberToString(data.list.size()) + ") - "
+ fileName.toUtf8()
+ kLineBreak + kLineBreak
+ kLineBreak; + kLineBreak;
return _result->writeBlock(header) == File::Result::Success; return _result->writeBlock(header) == File::Result::Success;
} }
bool TextWriter::writeDialogStart(const Data::DialogInfo &data) { bool TextWriter::writeChatStart(const Data::DialogInfo &data) {
Expects(_dialog == nullptr); Expects(_chat == nullptr);
Expects(_dialogIndex < _dialogsCount); Expects(_dialogIndex < _dialogsCount);
const auto digits = Data::NumberToString(_dialogsCount - 1).size(); const auto digits = Data::NumberToString(_dialogsCount - 1).size();
const auto number = Data::NumberToString(++_dialogIndex, digits, '0'); const auto number = Data::NumberToString(++_dialogIndex, digits, '0');
_dialog = fileWithRelativePath(data.relativePath + "messages.txt"); _chat = fileWithRelativePath(data.relativePath + "messages.txt");
_dialogEmpty = true; _dialogEmpty = true;
_dialogOnlyMy = data.onlyMyMessages; _dialogOnlyMy = data.onlyMyMessages;
return true; return true;
} }
bool TextWriter::writeMessagesSlice(const Data::MessagesSlice &data) { bool TextWriter::writeChatSlice(const Data::MessagesSlice &data) {
Expects(_dialog != nullptr); Expects(_chat != nullptr);
Expects(!data.list.empty()); Expects(!data.list.empty());
_dialogEmpty = false; _dialogEmpty = false;
@ -670,25 +714,21 @@ bool TextWriter::writeMessagesSlice(const Data::MessagesSlice &data) {
data.peers, data.peers,
_settings.internalLinksDomain)); _settings.internalLinksDomain));
} }
const auto full = _dialog->empty() const auto full = _chat->empty()
? JoinList(kLineBreak, list) ? JoinList(kLineBreak, list)
: kLineBreak + JoinList(kLineBreak, list); : kLineBreak + JoinList(kLineBreak, list);
return _dialog->writeBlock(full) == File::Result::Success; return _chat->writeBlock(full) == File::Result::Success;
} }
bool TextWriter::writeDialogEnd() { bool TextWriter::writeChatEnd() {
Expects(_dialog != nullptr); Expects(_chat != nullptr);
if (_dialogEmpty) { if (_dialogEmpty) {
_dialog->writeBlock(_dialogOnlyMy _chat->writeBlock(_dialogOnlyMy
? "No outgoing messages in this chat." ? "No outgoing messages in this chat."
: "No messages in this chat."); : "No messages in this chat.");
} }
_dialog = nullptr; _chat = nullptr;
return true;
}
bool TextWriter::writeDialogsEnd() {
return true; return true;
} }

View File

@ -30,10 +30,16 @@ public:
bool writeDialogsStart(const Data::DialogsInfo &data) override; bool writeDialogsStart(const Data::DialogsInfo &data) override;
bool writeDialogStart(const Data::DialogInfo &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 writeDialogEnd() override;
bool writeDialogsEnd() 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; bool finish() override;
QString mainFilePath() override; QString mainFilePath() override;
@ -43,6 +49,14 @@ private:
QString pathWithRelativePath(const QString &path) const; QString pathWithRelativePath(const QString &path) const;
std::unique_ptr<File> fileWithRelativePath(const QString &path) const; std::unique_ptr<File> 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; Settings _settings;
std::unique_ptr<File> _result; std::unique_ptr<File> _result;
@ -52,7 +66,12 @@ private:
int _dialogIndex = 0; int _dialogIndex = 0;
bool _dialogOnlyMy = false; bool _dialogOnlyMy = false;
bool _dialogEmpty = true; bool _dialogEmpty = true;
std::unique_ptr<File> _dialog;
int _leftChannelsCount = 0;
int _leftChannelIndex = 0;
bool _leftChannelEmpty = true;
std::unique_ptr<File> _chat;
}; };