Export top peers as frequent contacts.

This commit is contained in:
John Preston 2018-06-19 21:40:16 +01:00
parent 4115d3d13d
commit 4156beaa3c
6 changed files with 215 additions and 10 deletions

View File

@ -517,6 +517,25 @@ std::map<PeerId, Peer> ParsePeersLists(
return result;
}
User EmptyUser(int32 userId) {
return ParseUser(MTP_userEmpty(MTP_int(userId)));
}
Chat EmptyChat(int32 chatId) {
return ParseChat(MTP_chatEmpty(MTP_int(chatId)));
}
Peer EmptyPeer(PeerId peerId) {
if (UserPeerId(BarePeerId(peerId)) == peerId) {
auto user = ParseUser(MTP_userEmpty(MTP_int(BarePeerId(peerId))));
return Peer{ EmptyUser(BarePeerId(peerId)) };
} else if (ChatPeerId(BarePeerId(peerId)) == peerId) {
auto chat = ParseChat(MTP_chatEmpty(MTP_int(BarePeerId(peerId))));
return Peer{ EmptyChat(BarePeerId(peerId)) };
}
Unexpected("PeerId in EmptyPeer.");
}
File &Media::file() {
return content.match([](Photo &data) -> File& {
return data.image.file;
@ -835,7 +854,7 @@ ContactsList ParseContactsList(const MTPcontacts_Contacts &data) {
if (const auto i = map.find(userId); i != end(map)) {
result.list.push_back(i->second.info);
} else {
result.list.push_back(ContactInfo());
result.list.push_back(EmptyUser(userId).info);
}
}
return result;
@ -875,6 +894,52 @@ std::vector<int> SortedContactsIndices(const ContactsList &data) {
return indices;
}
bool AppendTopPeers(ContactsList &to, const MTPcontacts_TopPeers &data) {
return data.match([](const MTPDcontacts_topPeersNotModified &data) {
return false;
}, [&](const MTPDcontacts_topPeers &data) {
const auto peers = ParsePeersLists(data.vusers, data.vchats);
const auto append = [&](
std::vector<TopPeer> &to,
const MTPVector<MTPTopPeer> &list) {
for (const auto &topPeer : list.v) {
to.push_back(topPeer.match([&](const MTPDtopPeer &data) {
const auto peerId = ParsePeerId(data.vpeer);
auto peer = [&] {
const auto i = peers.find(peerId);
return (i != peers.end())
? i->second
: EmptyPeer(peerId);
}();
return TopPeer{
Peer{ std::move(peer) },
data.vrating.v
};
}));
}
};
for (const auto &list : data.vcategories.v) {
const auto appended = list.match(
[&](const MTPDtopPeerCategoryPeers &data) {
const auto category = data.vcategory.type();
if (category == mtpc_topPeerCategoryCorrespondents) {
append(to.correspondents, data.vpeers);
return true;
} else if (category == mtpc_topPeerCategoryBotsInline) {
append(to.inlineBots, data.vpeers);
return true;
} else {
return false;
}
});
if (!appended) {
return false;
}
}
return true;
});
}
Session ParseSession(const MTPAuthorization &data) {
Expects(data.type() == mtpc_authorization);

View File

@ -205,13 +205,21 @@ struct PersonalInfo {
PersonalInfo ParsePersonalInfo(const MTPUserFull &data);
struct TopPeer {
Peer peer;
float64 rating = 0.;
};
struct ContactsList {
std::vector<ContactInfo> list;
std::vector<TopPeer> correspondents;
std::vector<TopPeer> inlineBots;
};
ContactsList ParseContactsList(const MTPcontacts_Contacts &data);
ContactsList ParseContactsList(const MTPVector<MTPSavedContact> &data);
std::vector<int> SortedContactsIndices(const ContactsList &data);
bool AppendTopPeers(ContactsList &to, const MTPcontacts_TopPeers &data);
struct Session {
Utf8String platform;

View File

@ -26,6 +26,7 @@ constexpr auto kFileRequestsCount = 2;
constexpr auto kFileNextRequestDelay = TimeMs(20);
constexpr auto kChatsSliceLimit = 100;
constexpr auto kMessagesSliceLimit = 100;
constexpr auto kTopPeerSliceLimit = 100;
constexpr auto kFileMaxSize = 1500 * 1024 * 1024;
constexpr auto kLocationCacheSize = 100'000;
@ -111,7 +112,14 @@ struct ApiWrap::StartProcess {
};
std::deque<Step> steps;
StartInfo info;
};
struct ApiWrap::ContactsProcess {
FnMut<void(Data::ContactsList&&)> done;
Data::ContactsList result;
int topPeersOffset = 0;
};
struct ApiWrap::UserpicsProcess {
@ -124,7 +132,6 @@ struct ApiWrap::UserpicsProcess {
uint64 maxId = 0;
bool lastSlice = false;
int fileIndex = -1;
};
struct ApiWrap::FileProcess {
@ -145,7 +152,6 @@ struct ApiWrap::FileProcess {
QByteArray bytes;
};
std::deque<Request> requests;
};
struct ApiWrap::FileProgress {
@ -162,7 +168,6 @@ struct ApiWrap::LeftChannelsProcess {
int fullCount = 0;
int offset = 0;
bool finished = false;
};
struct ApiWrap::DialogsProcess {
@ -174,7 +179,6 @@ struct ApiWrap::DialogsProcess {
Data::TimeId offsetDate = 0;
int32 offsetId = 0;
MTPInputPeer offsetPeer = MTP_inputPeerEmpty();
};
struct ApiWrap::ChatProcess {
@ -190,7 +194,6 @@ struct ApiWrap::ChatProcess {
base::optional<Data::MessagesSlice> slice;
bool lastSlice = false;
int fileIndex = -1;
};
ApiWrap::LoadedFileCache::LoadedFileCache(int limit) : _limit(limit) {
@ -605,10 +608,60 @@ void ApiWrap::finishUserpics() {
}
void ApiWrap::requestContacts(FnMut<void(Data::ContactsList&&)> done) {
Expects(_contactsProcess == nullptr);
_contactsProcess = std::make_unique<ContactsProcess>();
_contactsProcess->done = std::move(done);
mainRequest(MTPcontacts_GetSaved(
)).done([=, done = std::move(done)](
const MTPVector<MTPSavedContact> &result) mutable {
done(Data::ParseContactsList(result));
)).done([=](const MTPVector<MTPSavedContact> &result) {
_contactsProcess->result = Data::ParseContactsList(result);
requestTopPeersSlice();
}).send();
}
void ApiWrap::requestTopPeersSlice() {
Expects(_contactsProcess != nullptr);
using Flag = MTPcontacts_GetTopPeers::Flag;
mainRequest(MTPcontacts_GetTopPeers(
MTP_flags(Flag::f_correspondents | Flag::f_bots_inline),
MTP_int(_contactsProcess->topPeersOffset),
MTP_int(kTopPeerSliceLimit),
MTP_int(0) // hash
)).done([=](const MTPcontacts_TopPeers &result) {
Expects(_contactsProcess != nullptr);
if (!Data::AppendTopPeers(_contactsProcess->result, result)) {
error("Unexpected data in ApiWrap::requestTopPeersSlice.");
return;
}
const auto offset = _contactsProcess->topPeersOffset;
const auto loaded = result.match(
[](const MTPDcontacts_topPeersNotModified &data) {
return true;
}, [&](const MTPDcontacts_topPeers &data) {
for (const auto &category : data.vcategories.v) {
const auto loaded = category.match(
[&](const MTPDtopPeerCategoryPeers &data) {
return offset + data.vpeers.v.size() >= data.vcount.v;
});
if (!loaded) {
return false;
}
}
return true;
});
if (loaded) {
auto process = base::take(_contactsProcess);
process->done(std::move(process->result));
} else {
_contactsProcess->topPeersOffset = std::max(
_contactsProcess->result.correspondents.size(),
_contactsProcess->result.inlineBots.size());
requestTopPeersSlice();
}
}).send();
}

View File

@ -84,6 +84,7 @@ public:
private:
class LoadedFileCache;
struct StartProcess;
struct ContactsProcess;
struct UserpicsProcess;
struct FileProcess;
struct FileProgress;
@ -98,6 +99,8 @@ private:
void requestLeftChannelsCount();
void finishStartProcess();
void requestTopPeersSlice();
void handleUserpicsSlice(const MTPphotos_Photos &result);
void loadUserpicsFiles(Data::UserpicsSlice &&slice);
void loadNextUserpic();
@ -158,6 +161,7 @@ private:
std::unique_ptr<StartProcess> _startProcess;
std::unique_ptr<LoadedFileCache> _fileCache;
std::unique_ptr<ContactsProcess> _contactsProcess;
std::unique_ptr<UserpicsProcess> _userpicsProcess;
std::unique_ptr<FileProcess> _fileProcess;
std::unique_ptr<LeftChannelsProcess> _leftChannelsProcess;

View File

@ -504,6 +504,15 @@ Result TextWriter::writeUserpicsEnd() {
Result TextWriter::writeContactsList(const Data::ContactsList &data) {
Expects(_summary != nullptr);
if (const auto result = writeSavedContacts(data); !result) {
return result;
} else if (const auto result = writeFrequentContacts(data); !result) {
return result;
}
return Result::Success();
}
Result TextWriter::writeSavedContacts(const Data::ContactsList &data) {
if (data.list.empty()) {
return Result::Success();
}
@ -511,7 +520,7 @@ Result TextWriter::writeContactsList(const Data::ContactsList &data) {
const auto file = fileWithRelativePath("contacts.txt");
auto list = std::vector<QByteArray>();
list.reserve(data.list.size());
for (const auto &index : Data::SortedContactsIndices(data)) {
for (const auto index : Data::SortedContactsIndices(data)) {
const auto &contact = data.list[index];
if (contact.firstName.isEmpty()
&& contact.lastName.isEmpty()
@ -541,6 +550,69 @@ Result TextWriter::writeContactsList(const Data::ContactsList &data) {
return _summary->writeBlock(header);
}
Result TextWriter::writeFrequentContacts(const Data::ContactsList &data) {
const auto size = data.correspondents.size() + data.inlineBots.size();
if (data.correspondents.empty() && data.inlineBots.empty()) {
return Result::Success();
}
const auto file = fileWithRelativePath("frequent.txt");
auto list = std::vector<QByteArray>();
list.reserve(size);
const auto writeList = [&](
const std::vector<Data::TopPeer> &peers,
Data::Utf8String category) {
for (const auto &top : peers) {
const auto user = [&]() -> Data::Utf8String {
if (!top.peer.user()) {
return Data::Utf8String();
} else if (top.peer.name().isEmpty()) {
return "(deleted user)";
}
return top.peer.name();
}();
const auto chatType = [&] {
if (const auto chat = top.peer.chat()) {
return chat->username.isEmpty()
? (chat->broadcast
? "Private channel"
: "Private group")
: (chat->broadcast
? "Public channel"
: "Public group");
}
return "";
}();
const auto chat = [&]() -> Data::Utf8String {
if (!top.peer.chat()) {
return Data::Utf8String();
} else if (top.peer.name().isEmpty()) {
return "(deleted chat)";
}
return top.peer.name();
}();
list.push_back(SerializeKeyValue({
{ "Category", category },
{ "User", top.peer.user() ? user : QByteArray() },
{ chatType, chat },
{ "Rating", QString::number(top.rating).toUtf8() }
}));
}
};
writeList(data.correspondents, "Correspondents");
writeList(data.inlineBots, "Inline bots");
const auto full = JoinList(kLineBreak, list);
if (const auto result = file->writeBlock(full); !result) {
return result;
}
const auto header = "Frequent contacts "
"(" + Data::NumberToString(size) + ") - frequent.txt"
+ kLineBreak
+ kLineBreak;
return _summary->writeBlock(header);
}
Result TextWriter::writeSessionsList(const Data::SessionsList &data) {
Expects(_summary != nullptr);

View File

@ -49,6 +49,9 @@ private:
QString pathWithRelativePath(const QString &path) const;
std::unique_ptr<File> fileWithRelativePath(const QString &path) const;
Result writeSavedContacts(const Data::ContactsList &data);
Result writeFrequentContacts(const Data::ContactsList &data);
Result writeChatsStart(
const Data::DialogsInfo &data,
const QByteArray &listName,