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.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;

View File

@ -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<MTPMessage> &data,
const MTPVector<MTPUser> &users,

View File

@ -433,12 +433,12 @@ struct DialogsInfo {
std::vector<DialogInfo> 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<Message> list;

View File

@ -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 @@ private:
};
struct ApiWrap::StartProcess {
FnMut<void(StartInfo)> done;
enum class Step {
UserpicsCount,
DialogsCount,
LeftChannelsCount,
};
std::deque<Step> steps;
StartInfo info;
};
struct ApiWrap::UserpicsProcess {
FnMut<void(Data::UserpicsInfo&&)> start;
Fn<void(Data::UserpicsSlice&&)> handleSlice;
@ -112,30 +144,36 @@ struct ApiWrap::FileProcess {
};
struct ApiWrap::DialogsProcess {
Data::DialogsInfo info;
std::map<int32, Data::Chat> left;
struct ApiWrap::LeftChannelsProcess {
FnMut<void(Data::DialogsInfo&&)> done;
FnMut<void(const Data::DialogsInfo&)> start;
Fn<void(const Data::DialogInfo&)> startOne;
Fn<void(Data::MessagesSlice&&)> sliceOne;
Fn<void()> finishOne;
FnMut<void()> finish;
Data::DialogsInfo info;
rpl::variable<int> count;
int fullCount = 0;
bool finished = false;
};
struct ApiWrap::DialogsProcess {
FnMut<void(Data::DialogsInfo&&)> done;
Data::DialogsInfo info;
Data::TimeId offsetDate = 0;
int32 offsetId = 0;
MTPInputPeer offsetPeer = MTP_inputPeerEmpty();
struct Single;
std::unique_ptr<Single> single;
int singleIndex = -1;
rpl::variable<int> count;
};
struct ApiWrap::DialogsProcess::Single {
Single(const Data::DialogInfo &info);
struct ApiWrap::ChatProcess {
Data::DialogInfo info;
Fn<void(Data::MessagesSlice&&)> handleSlice;
FnMut<void()> done;
int32 offsetId = 1;
base::optional<Data::MessagesSlice> slice;
@ -173,10 +211,6 @@ base::optional<QString> ApiWrap::LoadedFileCache::find(
ApiWrap::FileProcess::FileProcess(const QString &path) : file(path) {
}
ApiWrap::DialogsProcess::Single::Single(const Data::DialogInfo &info)
: info(info) {
}
template <typename Request>
auto ApiWrap::mainRequest(Request &&request) {
Expects(_takeoutId.has_value());
@ -215,11 +249,158 @@ rpl::producer<RPCError> ApiWrap::errors() const {
void ApiWrap::startExport(
const Settings &settings,
FnMut<void()> done) {
FnMut<void(StartInfo)> done) {
Expects(_settings == nullptr);
Expects(_startProcess == nullptr);
_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) {
@ -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<void(Data::SessionsList&&)> done) {
}).send();
}
void ApiWrap::requestDialogs(
FnMut<void(const Data::DialogsInfo&)> start,
Fn<void(const Data::DialogInfo&)> startOne,
Fn<void(Data::MessagesSlice&&)> sliceOne,
Fn<void()> finishOne,
FnMut<void()> finish) {
Expects(_dialogsProcess == nullptr);
void ApiWrap::requestMessages(
const Data::DialogInfo &info,
Fn<void(Data::MessagesSlice&&)> slice,
FnMut<void()> done) {
Expects(_chatProcess == nullptr);
_dialogsProcess = std::make_unique<DialogsProcess>();
_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>();
_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);
mainRequest(MTPchannels_GetLeftChannels(
)).done([=](const MTPmessages_Chats &result) mutable {
Expects(_dialogsProcess != nullptr);
const auto process = base::take(_dialogsProcess);
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) {
return true;
}, [](const MTPDmessages_chatsSlice &data) {
return data.vchats.v.isEmpty();
});
_dialogsProcess->left = Data::ParseChatsList(*result.match(
[](const auto &data) {
return &data.vchats;
}));
requestLeftDialog();
process->count = process->info.list.size();
done();
}).send();
}
void ApiWrap::requestLeftDialog() {
Expects(_dialogsProcess != nullptr);
void ApiWrap::appendLeftChannelsSlice(Data::DialogsInfo &&info) {
Expects(_leftChannelsProcess != nullptr);
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();
});
}).send();
appendChatsSlice(_leftChannelsProcess->info, std::move(info));
}
void ApiWrap::finishDialogsList() {
Expects(_dialogsProcess != nullptr);
void ApiWrap::appendChatsSlice(
Data::DialogsInfo &to,
Data::DialogsInfo &&info) {
Expects(_settings != nullptr);
ranges::reverse(_dialogsProcess->info.list);
Data::FinalizeDialogsInfo(_dialogsProcess->info, *_settings);
_dialogsProcess->start(_dialogsProcess->info);
requestNextDialog();
}
void ApiWrap::requestNextDialog() {
Expects(_dialogsProcess != nullptr);
Expects(_dialogsProcess->single == nullptr);
const auto index = ++_dialogsProcess->singleIndex;
if (index < _dialogsProcess->info.list.size()) {
const auto &one = _dialogsProcess->info.list[index];
_dialogsProcess->single = std::make_unique<DialogsProcess::Single>(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<decltype(data)>()) {
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()));
Expects(_chatProcess != nullptr);
Expects(_chatProcess->slice.has_value());
Expects((_chatProcess->fileIndex >= 0)
&& (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
const auto process = _dialogsProcess->single.get();
const auto index = process->fileIndex;
process->slice->list[index].file().relativePath = relativePath;
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());
Expects(_chatProcess != nullptr);
Expects(!_chatProcess->slice.has_value());
_dialogsProcess->single = nullptr;
_dialogsProcess->finishOne();
requestNextDialog();
}
void ApiWrap::finishDialogs() {
Expects(_dialogsProcess != nullptr);
Expects(_dialogsProcess->single == nullptr);
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<void(QString)> done) {
void ApiWrap::loadFile(
const Data::File &file,
FnMut<void(QString)> done) {
Expects(_fileProcess == nullptr);
Expects(file.location.dcId != 0);

View File

@ -33,9 +33,20 @@ public:
rpl::producer<RPCError> errors() const;
struct StartInfo {
int userpicsCount = 0;
int dialogsCount = 0;
int leftChannelsCount = 0;
};
void startExport(
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);
@ -48,22 +59,28 @@ public:
void requestSessions(FnMut<void(Data::SessionsList&&)> done);
void requestDialogs(
FnMut<void(const Data::DialogsInfo&)> start,
Fn<void(const Data::DialogInfo&)> startOne,
Fn<void(Data::MessagesSlice&&)> sliceOne,
Fn<void()> finishOne,
FnMut<void()> finish);
void requestMessages(
const Data::DialogInfo &info,
Fn<void(Data::MessagesSlice&&)> slice,
FnMut<void()> done);
~ApiWrap();
private:
class LoadedFileCache;
struct StartProcess;
struct UserpicsProcess;
struct FileProcess;
struct LeftChannelsProcess;
struct DialogsProcess;
class LoadedFileCache;
struct ChatProcess;
void startMainSession(FnMut<void()> 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 @@ private:
void requestDialogsSlice();
void appendDialogsSlice(Data::DialogsInfo &&info);
void requestLeftChannels();
void requestLeftDialog();
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 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 @@ private:
std::unique_ptr<FileProcess> prepareFileProcess(
const Data::File &file) const;
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 filePartDone(int offset, const MTPupload_File &result);
@ -113,10 +134,13 @@ private:
std::unique_ptr<Settings> _settings;
MTPInputUser _user = MTP_inputUserSelf();
std::unique_ptr<StartProcess> _startProcess;
std::unique_ptr<LoadedFileCache> _fileCache;
std::unique_ptr<UserpicsProcess> _userpicsProcess;
std::unique_ptr<FileProcess> _fileProcess;
std::unique_ptr<LeftChannelsProcess> _leftChannelsProcess;
std::unique_ptr<DialogsProcess> _dialogsProcess;
std::unique_ptr<ChatProcess> _chatProcess;
rpl::event_stream<RPCError> _errors;

View File

@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Export {
auto kNullStateCallback = [](ProcessingState&) {};
class Controller {
public:
Controller(crl::weak_on_queue<Controller> weak);
@ -21,11 +23,11 @@ public:
rpl::producer<State> state() const;
// Password step.
void submitPassword(const QString &password);
void requestPasswordRecover();
rpl::producer<PasswordUpdate> passwordUpdate() const;
void reloadPasswordState();
void cancelUnconfirmedPassword();
//void submitPassword(const QString &password);
//void requestPasswordRecover();
//rpl::producer<PasswordUpdate> passwordUpdate() const;
//void reloadPasswordState();
//void cancelUnconfirmedPassword();
// Processing step.
void startExport(const Settings &settings);
@ -37,30 +39,56 @@ private:
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 <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();
ApiWrap _api;
Settings _settings;
Data::DialogsInfo _leftChannelsInfo;
Data::DialogsInfo _dialogsInfo;
int _leftChannelIndex = -1;
int _dialogIndex = -1;
// rpl::variable<State> fails to compile in MSVC :(
State _state;
rpl::event_stream<State> _stateChanges;
mtpRequestId _passwordRequestId = 0;
std::vector<int> _substepsInStep;
std::unique_ptr<Output::AbstractWriter> _writer;
std::vector<ProcessingState::Step> _steps;
std::vector<Step> _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<PasswordUpdate> Controller::passwordUpdate() const {
return rpl::never<PasswordUpdate>();
}
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<PasswordUpdate> Controller::passwordUpdate() const {
// return rpl::never<PasswordUpdate>();
//}
//
//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<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() {
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,20 +370,123 @@ 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::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();
}
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() {
setState(FinishedState{ _writer->mainFilePath() });
}
@ -298,35 +500,35 @@ rpl::producer<State> ControllerWrap::state() const {
});
}
void ControllerWrap::submitPassword(const QString &password) {
_wrapped.with([=](Controller &controller) {
controller.submitPassword(password);
});
}
void ControllerWrap::requestPasswordRecover() {
_wrapped.with([=](Controller &controller) {
controller.requestPasswordRecover();
});
}
rpl::producer<PasswordUpdate> 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::submitPassword(const QString &password) {
// _wrapped.with([=](Controller &controller) {
// controller.submitPassword(password);
// });
//}
//
//void ControllerWrap::requestPasswordRecover() {
// _wrapped.with([=](Controller &controller) {
// controller.requestPasswordRecover();
// });
//}
//
//rpl::producer<PasswordUpdate> 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));

View File

@ -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<int> 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 @@ public:
rpl::producer<State> state() const;
// Password step.
void submitPassword(const QString &password);
void requestPasswordRecover();
rpl::producer<PasswordUpdate> passwordUpdate() const;
void reloadPasswordState();
void cancelUnconfirmedPassword();
//void submitPassword(const QString &password);
//void requestPasswordRecover();
//rpl::producer<PasswordUpdate> passwordUpdate() const;
//void reloadPasswordState();
//void cancelUnconfirmedPassword();
// Processing step.
void startExport(const Settings &settings);

View File

@ -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<Type>;
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 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;

View File

@ -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;
}

View File

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

View File

@ -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<QByteArray>();
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;
}

View File

@ -30,10 +30,16 @@ public:
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 @@ private:
QString pathWithRelativePath(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;
std::unique_ptr<File> _result;
@ -52,7 +66,12 @@ private:
int _dialogIndex = 0;
bool _dialogOnlyMy = false;
bool _dialogEmpty = true;
std::unique_ptr<File> _dialog;
int _leftChannelsCount = 0;
int _leftChannelIndex = 0;
bool _leftChannelEmpty = true;
std::unique_ptr<File> _chat;
};