Merge old group with supergroup history in export.

This commit is contained in:
John Preston 2019-04-09 18:55:21 +04:00
parent 1598165e2b
commit 8f80c19ae1
5 changed files with 190 additions and 38 deletions

View File

@ -36,6 +36,7 @@ namespace {
constexpr auto kUserPeerIdShift = (1ULL << 32);
constexpr auto kChatPeerIdShift = (2ULL << 32);
constexpr auto kMaxImageSize = 10000;
constexpr auto kMigratedMessagesIdShift = -1'000'000'000;
QString PrepareFileNameDatePart(TimeId date) {
return date
@ -712,6 +713,12 @@ Chat ParseChat(const MTPChat &data) {
result.id = data.vid().v;
result.title = ParseString(data.vtitle());
result.input = MTP_inputPeerChat(MTP_int(result.id));
if (const auto migratedTo = data.vmigrated_to()) {
result.migratedToChannelId = migratedTo->match(
[](const MTPDinputChannel &data) {
return data.vchannel_id().v;
}, [](auto&&) { return 0; });
}
}, [&](const MTPDchatEmpty &data) {
result.id = data.vid().v;
result.input = MTP_inputPeerChat(MTP_int(result.id));
@ -1477,6 +1484,9 @@ DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data) {
? peer.user()->info.lastName
: Utf8String();
info.input = peer.input();
info.migratedToChannelId = peer.chat()
? peer.chat()->migratedToChannelId
: 0;
}
info.topMessageId = fields.vtop_message().v;
const auto shift = IsChatPeerId(info.peerId)
@ -1515,6 +1525,7 @@ DialogInfo DialogInfoFromChat(const Chat &data) {
result.topMessageDate = 0;
result.topMessageId = 0;
result.type = DialogTypeFromChat(data);
result.migratedToChannelId = data.migratedToChannelId;
return result;
}
@ -1590,6 +1601,34 @@ DialogsInfo ParseDialogsInfo(
return result;
}
bool AddMigrateFromSlice(
DialogInfo &to,
const DialogInfo &from,
int splitIndex,
int splitsCount) {
Expects(splitIndex < splitsCount);
const auto good = to.migratedFromInput.match([](
const MTPDinputPeerEmpty &) {
return true;
}, [&](const MTPDinputPeerChat & data) {
return (ChatPeerId(data.vchat_id().v) == from.peerId);
}, [](auto&&) { return false; });
if (!good) {
return false;
}
Assert(from.splits.size() == from.messagesCountPerSplit.size());
for (auto i = 0, count = int(from.splits.size()); i != count; ++i) {
Assert(from.splits[i] < splitsCount);
to.splits.push_back(from.splits[i] - splitsCount);
to.messagesCountPerSplit.push_back(from.messagesCountPerSplit[i]);
}
to.migratedFromInput = from.input;
to.splits.push_back(splitIndex - splitsCount);
to.messagesCountPerSplit.push_back(0);
return true;
}
void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings) {
auto &chats = info.chats;
auto &left = info.left;
@ -1619,7 +1658,7 @@ void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings) {
}();
dialog.onlyMyMessages = ((settings.fullChats & setting) != setting);
ranges::reverse(dialog.splits);
ranges::sort(dialog.splits);
}
for (auto &dialog : left) {
Assert(!settings.onlySinglePeer());
@ -1647,6 +1686,16 @@ MessagesSlice ParseMessagesSlice(
return result;
}
MessagesSlice AdjustMigrateMessageIds(MessagesSlice slice) {
for (auto &message : slice.list) {
message.id += kMigratedMessagesIdShift;
if (message.replyToMsgId) {
message.replyToMsgId += kMigratedMessagesIdShift;
}
}
return slice;
}
TimeId SingleMessageDate(const MTPmessages_Messages &data) {
return data.match([&](const MTPDmessages_messagesNotModified &data) {
return 0;

View File

@ -220,6 +220,7 @@ std::map<int32, User> ParseUsersList(const MTPVector<MTPUser> &data);
struct Chat {
int32 id = 0;
int32 migratedToChannelId = 0;
Utf8String title;
Utf8String username;
bool isBroadcast = false;
@ -564,6 +565,9 @@ struct DialogInfo {
TimeId topMessageDate = 0;
PeerId peerId = 0;
MTPInputPeer migratedFromInput = MTP_inputPeerEmpty();
int32 migratedToChannelId = 0;
// User messages splits which contained that dialog.
std::vector<int> splits;
@ -594,6 +598,11 @@ DialogsInfo ParseDialogsInfo(
const MTPInputPeer &singlePeer,
const MTPmessages_Chats &data);
DialogsInfo ParseLeftChannelsInfo(const MTPmessages_Chats &data);
bool AddMigrateFromSlice(
DialogInfo &to,
const DialogInfo &from,
int splitIndex,
int splitsCount);
void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings);
struct MessagesSlice {
@ -607,6 +616,7 @@ MessagesSlice ParseMessagesSlice(
const MTPVector<MTPUser> &users,
const MTPVector<MTPChat> &chats,
const QString &mediaFolder);
MessagesSlice AdjustMigrateMessageIds(MessagesSlice slice);
bool SingleMessageBefore(
const MTPmessages_Messages &data,

View File

@ -1047,34 +1047,9 @@ void ApiWrap::cancelExportFast() {
}
void ApiWrap::requestSinglePeerDialog() {
const auto isChannelType = [](Data::DialogInfo::Type type) {
using Type = Data::DialogInfo::Type;
return (type == Type::PrivateSupergroup)
|| (type == Type::PublicSupergroup)
|| (type == Type::PrivateChannel)
|| (type == Type::PublicChannel);
};
auto doneSinglePeer = [=](const auto &result) {
auto info = Data::ParseDialogsInfo(_settings->singlePeer, result);
_dialogsProcess->processedCount += info.chats.size();
appendDialogsSlice(std::move(info));
const auto last = _dialogsProcess->splitIndexPlusOne - 1;
for (auto &info : _dialogsProcess->info.chats) {
if (isChannelType(info.type)) {
continue;
}
for (auto i = last; i != 0; --i) {
info.splits.push_back(i - 1);
info.messagesCountPerSplit.push_back(0);
}
}
if (!_dialogsProcess->progress(_dialogsProcess->processedCount)) {
return;
}
finishDialogsList();
appendSinglePeerDialogs(
Data::ParseDialogsInfo(_settings->singlePeer, result));
};
const auto requestUser = [&](const MTPInputUser &data) {
mainRequest(MTPusers_GetUsers(
@ -1104,6 +1079,76 @@ void ApiWrap::requestSinglePeerDialog() {
});
}
mtpRequestId ApiWrap::requestSinglePeerMigrated(
const Data::DialogInfo &info) {
const auto input = info.input.match([&](
const MTPDinputPeerChannel & data) {
return MTP_inputChannel(
data.vchannel_id(),
data.vaccess_hash());
}, [](auto&&) -> MTPinputChannel {
Unexpected("Peer type in a supergroup.");
});
return mainRequest(MTPchannels_GetFullChannel(
input
)).done([=](const MTPmessages_ChatFull &result) {
auto info = result.match([&](
const MTPDmessages_chatFull &data) {
const auto migratedChatId = data.vfull_chat().match([&](
const MTPDchannelFull &data) {
return data.vmigrated_from_chat_id().value_or_empty();
}, [](auto &&other) {
return 0;
});
return migratedChatId
? Data::ParseDialogsInfo(
MTP_inputPeerChat(MTP_int(migratedChatId)),
MTP_messages_chats(data.vchats()))
: Data::DialogsInfo();
});
appendSinglePeerDialogs(std::move(info));
}).send();
}
void ApiWrap::appendSinglePeerDialogs(Data::DialogsInfo &&info) {
const auto isSupergroupType = [](Data::DialogInfo::Type type) {
using Type = Data::DialogInfo::Type;
return (type == Type::PrivateSupergroup)
|| (type == Type::PublicSupergroup);
};
const auto isChannelType = [](Data::DialogInfo::Type type) {
using Type = Data::DialogInfo::Type;
return (type == Type::PrivateChannel)
|| (type == Type::PublicChannel);
};
auto migratedRequestId = mtpRequestId(0);
const auto last = _dialogsProcess->splitIndexPlusOne - 1;
for (auto &info : info.chats) {
if (isSupergroupType(info.type) && !migratedRequestId) {
migratedRequestId = requestSinglePeerMigrated(info);
continue;
} else if (isChannelType(info.type)) {
continue;
}
for (auto i = last; i != 0; --i) {
info.splits.push_back(i - 1);
info.messagesCountPerSplit.push_back(0);
}
}
if (!migratedRequestId) {
_dialogsProcess->processedCount += info.chats.size();
}
appendDialogsSlice(std::move(info));
if (migratedRequestId
|| !_dialogsProcess->progress(_dialogsProcess->processedCount)) {
return;
}
finishDialogsList();
}
void ApiWrap::requestDialogsSlice() {
Expects(_dialogsProcess != nullptr);
@ -1261,15 +1306,41 @@ void ApiWrap::appendChatsSlice(
Expects(_settings != nullptr);
const auto types = _settings->types;
const auto goodByTypes = [&](const Data::DialogInfo &info) {
return ((types & SettingsFromDialogsType(info.type)) != 0);
};
auto filtered = ranges::view::all(
from
) | ranges::view::filter([&](const Data::DialogInfo &info) {
return (types & SettingsFromDialogsType(info.type)) != 0;
if (goodByTypes(info)) {
return true;
} else if (info.migratedToChannelId
&& (((types & Settings::Type::PublicGroups) != 0)
|| ((types & Settings::Type::PrivateGroups) != 0))) {
return true;
}
return false;
});
to.reserve(to.size() + from.size());
for (auto &info : filtered) {
const auto nextIndex = to.size();
const auto [i, ok] = process.indexByPeer.emplace(info.peerId, nextIndex);
if (info.migratedToChannelId) {
const auto toPeerId = Data::ChatPeerId(info.migratedToChannelId);
const auto i = process.indexByPeer.find(toPeerId);
if (i != process.indexByPeer.end()
&& Data::AddMigrateFromSlice(
to[i->second],
info,
splitIndex,
int(_splits.size()))) {
continue;
} else if (!goodByTypes(info)) {
continue;
}
}
const auto [i, ok] = process.indexByPeer.emplace(
info.peerId,
nextIndex);
if (ok) {
to.push_back(std::move(info));
}
@ -1325,10 +1396,17 @@ void ApiWrap::requestChatMessages(
base::take(_chatProcess->requestDone)(std::move(result));
};
const auto splitsCount = int(_splits.size());
const auto realPeerInput = (splitIndex >= 0)
? _chatProcess->info.input
: _chatProcess->info.migratedFromInput;
const auto realSplitIndex = (splitIndex >= 0)
? splitIndex
: (splitsCount + splitIndex);
if (_chatProcess->info.onlyMyMessages) {
splitRequest(splitIndex, MTPmessages_Search(
splitRequest(realSplitIndex, MTPmessages_Search(
MTP_flags(MTPmessages_Search::Flag::f_from_id),
_chatProcess->info.input,
realPeerInput,
MTP_string(), // query
_user,
MTP_inputMessagesFilterEmpty(),
@ -1342,8 +1420,8 @@ void ApiWrap::requestChatMessages(
MTP_int(0) // hash
)).done(doneHandler).send();
} else {
splitRequest(splitIndex, MTPmessages_GetHistory(
_chatProcess->info.input,
splitRequest(realSplitIndex, MTPmessages_GetHistory(
realPeerInput,
MTP_int(offsetId),
MTP_int(0), // offset_date
MTP_int(addOffset),
@ -1355,7 +1433,7 @@ void ApiWrap::requestChatMessages(
Expects(_chatProcess != nullptr);
if (error.type() == qstr("CHANNEL_PRIVATE")) {
if (_chatProcess->info.input.type() == mtpc_inputPeerChannel
if (realPeerInput.type() == mtpc_inputPeerChannel
&& !_chatProcess->info.onlyMyMessages) {
// Perhaps we just left / were kicked from channel.
@ -1399,10 +1477,16 @@ Data::FileOrigin ApiWrap::currentFileMessageOrigin() const {
Expects(_chatProcess != nullptr);
Expects(_chatProcess->slice.has_value());
const auto splitIndex = _chatProcess->info.splits[
_chatProcess->localSplitIndex];
auto result = Data::FileOrigin();
result.messageId = currentFileMessage()->id;
result.peer = _chatProcess->info.input;
result.split = _chatProcess->info.splits[_chatProcess->localSplitIndex];
result.split = (splitIndex >= 0)
? splitIndex
: (int(_splits.size()) + splitIndex);
result.peer = (splitIndex >= 0)
? _chatProcess->info.input
: _chatProcess->info.migratedFromInput;
return result;
}
@ -1452,6 +1536,11 @@ void ApiWrap::finishMessagesSlice() {
auto slice = *base::take(_chatProcess->slice);
if (!slice.list.empty()) {
_chatProcess->largestIdPlusOne = slice.list.back().id + 1;
const auto splitIndex = _chatProcess->info.splits[
_chatProcess->localSplitIndex];
if (splitIndex < 0) {
slice = AdjustMigrateMessageIds(std::move(slice));
}
if (!_chatProcess->handleSlice(std::move(slice))) {
return;
}

View File

@ -126,6 +126,8 @@ private:
void appendDialogsSlice(Data::DialogsInfo &&info);
void finishDialogsList();
void requestSinglePeerDialog();
mtpRequestId requestSinglePeerMigrated(const Data::DialogInfo &info);
void appendSinglePeerDialogs(Data::DialogsInfo &&info);
void requestLeftChannelsIfNeeded();
void requestLeftChannelsList(

View File

@ -352,7 +352,9 @@ void ControllerObject::initialized(const ApiWrap::StartInfo &info) {
void ControllerObject::collectDialogsList() {
setState(stateDialogsList(0));
_api.requestDialogsList([=](int count) {
setState(stateDialogsList(count));
if (count > 0) {
setState(stateDialogsList(count - 1));
}
return true;
}, [=](Data::DialogsInfo &&result) {
_dialogsInfo = std::move(result);