mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 10:11:41 -05:00
Export top peers as frequent contacts.
This commit is contained in:
parent
4115d3d13d
commit
4156beaa3c
6 changed files with 215 additions and 10 deletions
|
@ -517,6 +517,25 @@ std::map<PeerId, Peer> ParsePeersLists(
|
||||||
return result;
|
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() {
|
File &Media::file() {
|
||||||
return content.match([](Photo &data) -> File& {
|
return content.match([](Photo &data) -> File& {
|
||||||
return data.image.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)) {
|
if (const auto i = map.find(userId); i != end(map)) {
|
||||||
result.list.push_back(i->second.info);
|
result.list.push_back(i->second.info);
|
||||||
} else {
|
} else {
|
||||||
result.list.push_back(ContactInfo());
|
result.list.push_back(EmptyUser(userId).info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -875,6 +894,52 @@ std::vector<int> SortedContactsIndices(const ContactsList &data) {
|
||||||
return indices;
|
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) {
|
Session ParseSession(const MTPAuthorization &data) {
|
||||||
Expects(data.type() == mtpc_authorization);
|
Expects(data.type() == mtpc_authorization);
|
||||||
|
|
||||||
|
|
|
@ -205,13 +205,21 @@ struct PersonalInfo {
|
||||||
|
|
||||||
PersonalInfo ParsePersonalInfo(const MTPUserFull &data);
|
PersonalInfo ParsePersonalInfo(const MTPUserFull &data);
|
||||||
|
|
||||||
|
struct TopPeer {
|
||||||
|
Peer peer;
|
||||||
|
float64 rating = 0.;
|
||||||
|
};
|
||||||
|
|
||||||
struct ContactsList {
|
struct ContactsList {
|
||||||
std::vector<ContactInfo> list;
|
std::vector<ContactInfo> list;
|
||||||
|
std::vector<TopPeer> correspondents;
|
||||||
|
std::vector<TopPeer> inlineBots;
|
||||||
};
|
};
|
||||||
|
|
||||||
ContactsList ParseContactsList(const MTPcontacts_Contacts &data);
|
ContactsList ParseContactsList(const MTPcontacts_Contacts &data);
|
||||||
ContactsList ParseContactsList(const MTPVector<MTPSavedContact> &data);
|
ContactsList ParseContactsList(const MTPVector<MTPSavedContact> &data);
|
||||||
std::vector<int> SortedContactsIndices(const ContactsList &data);
|
std::vector<int> SortedContactsIndices(const ContactsList &data);
|
||||||
|
bool AppendTopPeers(ContactsList &to, const MTPcontacts_TopPeers &data);
|
||||||
|
|
||||||
struct Session {
|
struct Session {
|
||||||
Utf8String platform;
|
Utf8String platform;
|
||||||
|
|
|
@ -26,6 +26,7 @@ constexpr auto kFileRequestsCount = 2;
|
||||||
constexpr auto kFileNextRequestDelay = TimeMs(20);
|
constexpr auto kFileNextRequestDelay = TimeMs(20);
|
||||||
constexpr auto kChatsSliceLimit = 100;
|
constexpr auto kChatsSliceLimit = 100;
|
||||||
constexpr auto kMessagesSliceLimit = 100;
|
constexpr auto kMessagesSliceLimit = 100;
|
||||||
|
constexpr auto kTopPeerSliceLimit = 100;
|
||||||
constexpr auto kFileMaxSize = 1500 * 1024 * 1024;
|
constexpr auto kFileMaxSize = 1500 * 1024 * 1024;
|
||||||
constexpr auto kLocationCacheSize = 100'000;
|
constexpr auto kLocationCacheSize = 100'000;
|
||||||
|
|
||||||
|
@ -111,7 +112,14 @@ struct ApiWrap::StartProcess {
|
||||||
};
|
};
|
||||||
std::deque<Step> steps;
|
std::deque<Step> steps;
|
||||||
StartInfo info;
|
StartInfo info;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ApiWrap::ContactsProcess {
|
||||||
|
FnMut<void(Data::ContactsList&&)> done;
|
||||||
|
|
||||||
|
Data::ContactsList result;
|
||||||
|
|
||||||
|
int topPeersOffset = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ApiWrap::UserpicsProcess {
|
struct ApiWrap::UserpicsProcess {
|
||||||
|
@ -124,7 +132,6 @@ struct ApiWrap::UserpicsProcess {
|
||||||
uint64 maxId = 0;
|
uint64 maxId = 0;
|
||||||
bool lastSlice = false;
|
bool lastSlice = false;
|
||||||
int fileIndex = -1;
|
int fileIndex = -1;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ApiWrap::FileProcess {
|
struct ApiWrap::FileProcess {
|
||||||
|
@ -145,7 +152,6 @@ struct ApiWrap::FileProcess {
|
||||||
QByteArray bytes;
|
QByteArray bytes;
|
||||||
};
|
};
|
||||||
std::deque<Request> requests;
|
std::deque<Request> requests;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ApiWrap::FileProgress {
|
struct ApiWrap::FileProgress {
|
||||||
|
@ -162,7 +168,6 @@ struct ApiWrap::LeftChannelsProcess {
|
||||||
int fullCount = 0;
|
int fullCount = 0;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
bool finished = false;
|
bool finished = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ApiWrap::DialogsProcess {
|
struct ApiWrap::DialogsProcess {
|
||||||
|
@ -174,7 +179,6 @@ struct ApiWrap::DialogsProcess {
|
||||||
Data::TimeId offsetDate = 0;
|
Data::TimeId offsetDate = 0;
|
||||||
int32 offsetId = 0;
|
int32 offsetId = 0;
|
||||||
MTPInputPeer offsetPeer = MTP_inputPeerEmpty();
|
MTPInputPeer offsetPeer = MTP_inputPeerEmpty();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ApiWrap::ChatProcess {
|
struct ApiWrap::ChatProcess {
|
||||||
|
@ -190,7 +194,6 @@ struct ApiWrap::ChatProcess {
|
||||||
base::optional<Data::MessagesSlice> slice;
|
base::optional<Data::MessagesSlice> slice;
|
||||||
bool lastSlice = false;
|
bool lastSlice = false;
|
||||||
int fileIndex = -1;
|
int fileIndex = -1;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ApiWrap::LoadedFileCache::LoadedFileCache(int limit) : _limit(limit) {
|
ApiWrap::LoadedFileCache::LoadedFileCache(int limit) : _limit(limit) {
|
||||||
|
@ -605,10 +608,60 @@ void ApiWrap::finishUserpics() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::requestContacts(FnMut<void(Data::ContactsList&&)> done) {
|
void ApiWrap::requestContacts(FnMut<void(Data::ContactsList&&)> done) {
|
||||||
|
Expects(_contactsProcess == nullptr);
|
||||||
|
|
||||||
|
_contactsProcess = std::make_unique<ContactsProcess>();
|
||||||
|
_contactsProcess->done = std::move(done);
|
||||||
mainRequest(MTPcontacts_GetSaved(
|
mainRequest(MTPcontacts_GetSaved(
|
||||||
)).done([=, done = std::move(done)](
|
)).done([=](const MTPVector<MTPSavedContact> &result) {
|
||||||
const MTPVector<MTPSavedContact> &result) mutable {
|
_contactsProcess->result = Data::ParseContactsList(result);
|
||||||
done(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();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,7 @@ public:
|
||||||
private:
|
private:
|
||||||
class LoadedFileCache;
|
class LoadedFileCache;
|
||||||
struct StartProcess;
|
struct StartProcess;
|
||||||
|
struct ContactsProcess;
|
||||||
struct UserpicsProcess;
|
struct UserpicsProcess;
|
||||||
struct FileProcess;
|
struct FileProcess;
|
||||||
struct FileProgress;
|
struct FileProgress;
|
||||||
|
@ -98,6 +99,8 @@ private:
|
||||||
void requestLeftChannelsCount();
|
void requestLeftChannelsCount();
|
||||||
void finishStartProcess();
|
void finishStartProcess();
|
||||||
|
|
||||||
|
void requestTopPeersSlice();
|
||||||
|
|
||||||
void handleUserpicsSlice(const MTPphotos_Photos &result);
|
void handleUserpicsSlice(const MTPphotos_Photos &result);
|
||||||
void loadUserpicsFiles(Data::UserpicsSlice &&slice);
|
void loadUserpicsFiles(Data::UserpicsSlice &&slice);
|
||||||
void loadNextUserpic();
|
void loadNextUserpic();
|
||||||
|
@ -158,6 +161,7 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<StartProcess> _startProcess;
|
std::unique_ptr<StartProcess> _startProcess;
|
||||||
std::unique_ptr<LoadedFileCache> _fileCache;
|
std::unique_ptr<LoadedFileCache> _fileCache;
|
||||||
|
std::unique_ptr<ContactsProcess> _contactsProcess;
|
||||||
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<LeftChannelsProcess> _leftChannelsProcess;
|
||||||
|
|
|
@ -504,6 +504,15 @@ Result TextWriter::writeUserpicsEnd() {
|
||||||
Result TextWriter::writeContactsList(const Data::ContactsList &data) {
|
Result TextWriter::writeContactsList(const Data::ContactsList &data) {
|
||||||
Expects(_summary != nullptr);
|
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()) {
|
if (data.list.empty()) {
|
||||||
return Result::Success();
|
return Result::Success();
|
||||||
}
|
}
|
||||||
|
@ -511,7 +520,7 @@ Result TextWriter::writeContactsList(const Data::ContactsList &data) {
|
||||||
const auto file = fileWithRelativePath("contacts.txt");
|
const auto file = fileWithRelativePath("contacts.txt");
|
||||||
auto list = std::vector<QByteArray>();
|
auto list = std::vector<QByteArray>();
|
||||||
list.reserve(data.list.size());
|
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];
|
const auto &contact = data.list[index];
|
||||||
if (contact.firstName.isEmpty()
|
if (contact.firstName.isEmpty()
|
||||||
&& contact.lastName.isEmpty()
|
&& contact.lastName.isEmpty()
|
||||||
|
@ -541,6 +550,69 @@ Result TextWriter::writeContactsList(const Data::ContactsList &data) {
|
||||||
return _summary->writeBlock(header);
|
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) {
|
Result TextWriter::writeSessionsList(const Data::SessionsList &data) {
|
||||||
Expects(_summary != nullptr);
|
Expects(_summary != nullptr);
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,9 @@ 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;
|
||||||
|
|
||||||
|
Result writeSavedContacts(const Data::ContactsList &data);
|
||||||
|
Result writeFrequentContacts(const Data::ContactsList &data);
|
||||||
|
|
||||||
Result writeChatsStart(
|
Result writeChatsStart(
|
||||||
const Data::DialogsInfo &data,
|
const Data::DialogsInfo &data,
|
||||||
const QByteArray &listName,
|
const QByteArray &listName,
|
||||||
|
|
Loading…
Add table
Reference in a new issue