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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue