Skip to content

Commit

Permalink
Export my messages from left channels.
Browse files Browse the repository at this point in the history
  • Loading branch information
john-preston committed Jun 17, 2018
1 parent e8d619c commit 1bfe409
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 67 deletions.
1 change: 1 addition & 0 deletions Telegram/Resources/scheme.tl
Original file line number Diff line number Diff line change
Expand Up @@ -1266,6 +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;

bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
Expand Down
83 changes: 71 additions & 12 deletions Telegram/SourceFiles/export/data/export_data_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ For license and copyright information please follow this link:
*/
#include "export/data/export_data_types.h"

#include "export/export_settings.h"
#include "core/mime_type.h"

#include <QtCore/QDateTime>
Expand Down Expand Up @@ -891,6 +892,21 @@ SessionsList ParseSessionsList(const MTPaccount_Authorizations &data) {
return result;
}

DialogInfo::Type DialogTypeFromChat(const Chat &chat) {
using Type = DialogInfo::Type;
return chat.username.isEmpty()
? (chat.broadcast
? Type::PrivateChannel
: Type::PrivateGroup)
: (chat.broadcast
? Type::PublicChannel
: Type::PublicGroup);
}

DialogInfo::Type DialogTypeFromUser(const User &user) {
return user.isBot ? DialogInfo::Type::Bot : DialogInfo::Type::Personal;
}

DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data) {
auto result = DialogsInfo();
const auto folder = QString();
Expand All @@ -905,22 +921,14 @@ DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data) {
const auto &fields = dialog.c_dialog();

auto info = DialogInfo();
const auto peerId = ParsePeerId(fields.vpeer);
const auto peerIt = peers.find(peerId);
info.peerId = ParsePeerId(fields.vpeer);
const auto peerIt = peers.find(info.peerId);
if (peerIt != end(peers)) {
using Type = DialogInfo::Type;
const auto &peer = peerIt->second;
info.type = peer.user()
? (peer.user()->isBot
? Type::Bot
: Type::Personal)
: (peer.chat()->broadcast
? (peer.chat()->username.isEmpty()
? Type::PrivateChannel
: Type::PublicChannel)
: (peer.chat()->username.isEmpty()
? Type::PrivateGroup
: Type::PublicGroup));
? DialogTypeFromUser(*peer.user())
: DialogTypeFromChat(*peer.chat());
info.name = peer.name();
info.input = peer.input();
}
Expand All @@ -936,6 +944,57 @@ 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));
}

void FinalizeDialogsInfo(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/chat_" + number + '/';

using DialogType = DialogInfo::Type;
using Type = Settings::Type;
const auto setting = [&] {
switch (dialog.type) {
case DialogType::Personal: return Type::PersonalChats;
case DialogType::Bot: return Type::BotChats;
case DialogType::PrivateGroup: return Type::PrivateGroups;
case DialogType::PrivateChannel: return Type::PrivateChannels;
case DialogType::PublicGroup: return Type::PublicGroups;
case DialogType::PublicChannel: return Type::PublicChannels;
}
Unexpected("Type in ApiWrap::onlyMyMessages.");
}();
dialog.onlyMyMessages = ((settings.fullChats & setting) != setting);
}
}

MessagesSlice ParseMessagesSlice(
const MTPVector<MTPMessage> &data,
const MTPVector<MTPUser> &users,
Expand Down
9 changes: 9 additions & 0 deletions Telegram/SourceFiles/export/data/export_data_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ For license and copyright information please follow this link:
#include <vector>

namespace Export {
struct Settings;
namespace Data {

using TimeId = int32;
Expand Down Expand Up @@ -420,7 +421,10 @@ struct DialogInfo {
MTPInputPeer input = MTP_inputPeerEmpty();
int32 topMessageId = 0;
TimeId topMessageDate = 0;
PeerId peerId = 0;

// Filled after the whole dialogs list is accumulated.
bool onlyMyMessages = false;
QString relativePath;

};
Expand All @@ -430,6 +434,11 @@ struct DialogsInfo {
};

DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data);
void InsertLeftDialog(
DialogsInfo &info,
const Chat &chat,
Message &&message);
void FinalizeDialogsInfo(DialogsInfo &info, const Settings &settings);

struct MessagesSlice {
std::vector<Message> list;
Expand Down
151 changes: 101 additions & 50 deletions Telegram/SourceFiles/export/export_api_wrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ struct ApiWrap::FileProcess {

struct ApiWrap::DialogsProcess {
Data::DialogsInfo info;
std::map<int32, Data::Chat> left;

FnMut<void(const Data::DialogsInfo&)> start;
Fn<void(const Data::DialogInfo&)> startOne;
Expand Down Expand Up @@ -350,8 +351,9 @@ void ApiWrap::loadNextUserpic() {
? base::none
: base::make_optional(list.back().id);

_userpicsProcess->handleSlice(*base::take(_userpicsProcess->slice));

if (!list.empty()) {
_userpicsProcess->handleSlice(*base::take(_userpicsProcess->slice));
}
if (_userpicsProcess->lastSlice) {
finishUserpics();
return;
Expand Down Expand Up @@ -431,19 +433,16 @@ void ApiWrap::requestDialogsSlice() {
_dialogsProcess->offsetPeer,
MTP_int(kChatsSliceLimit)
)).done([=](const MTPmessages_Dialogs &result) mutable {
const auto finished = [&] {
switch (result.type()) {
case mtpc_messages_dialogs: return true;
case mtpc_messages_dialogsSlice: {
const auto &data = result.c_messages_dialogsSlice();
return data.vdialogs.v.isEmpty();
} break;
default: Unexpected("Type in ApiWrap::requestChatsSlice.");
}
}();
const auto finished = result.match(
[](const MTPDmessages_dialogs &data) {
return true;
}, [](const MTPDmessages_dialogsSlice &data) {
return data.vdialogs.v.isEmpty();
});

auto info = Data::ParseDialogsInfo(result);
if (finished || info.list.empty()) {
finishDialogsList();
requestLeftChannels();
} else {
const auto &last = info.list.back();
_dialogsProcess->offsetId = last.topMessageId;
Expand Down Expand Up @@ -492,26 +491,81 @@ void ApiWrap::appendDialogsSlice(Data::DialogsInfo &&info) {
}
}

void ApiWrap::finishDialogsList() {
void ApiWrap::requestLeftChannels() {
Expects(_dialogsProcess != nullptr);

ranges::reverse(_dialogsProcess->info.list);
fillDialogsPaths();
mainRequest(MTPchannels_GetLeftChannels(
)).done([=](const MTPmessages_Chats &result) mutable {
Expects(_dialogsProcess != nullptr);

_dialogsProcess->start(_dialogsProcess->info);
requestNextDialog();
const auto 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();
}).send();
}

void ApiWrap::fillDialogsPaths() {
void ApiWrap::requestLeftDialog() {
Expects(_dialogsProcess != nullptr);

auto &list = _dialogsProcess->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/chat_" + number + '/';
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();
}

void ApiWrap::finishDialogsList() {
Expects(_dialogsProcess != nullptr);

ranges::reverse(_dialogsProcess->info.list);
Data::FinalizeDialogsInfo(_dialogsProcess->info, *_settings);

_dialogsProcess->start(_dialogsProcess->info);
requestNextDialog();
}

void ApiWrap::requestNextDialog() {
Expand All @@ -534,6 +588,15 @@ void ApiWrap::requestMessagesSlice() {
Expects(_dialogsProcess->single != nullptr);

const auto process = _dialogsProcess->single.get();

// #TODO export
if (process->info.input.match([](const MTPDinputPeerUser &value) {
return !value.vaccess_hash.v;
}, [](const auto &data) { return false; })) {
finishMessages();
return;
}

auto handleResult = [=](const MTPmessages_Messages &result) mutable {
Expects(_dialogsProcess != nullptr);
Expects(_dialogsProcess->single != nullptr);
Expand All @@ -552,7 +615,7 @@ void ApiWrap::requestMessagesSlice() {
process->info.relativePath));
});
};
if (onlyMyMessages()) {
if (process->info.onlyMyMessages) {
mainRequest(MTPmessages_Search(
MTP_flags(MTPmessages_Search::Flag::f_from_id),
process->info.input,
Expand Down Expand Up @@ -582,26 +645,6 @@ void ApiWrap::requestMessagesSlice() {
}
}

bool ApiWrap::onlyMyMessages() const {
Expects(_dialogsProcess != nullptr);
Expects(_dialogsProcess->single != nullptr);

const auto process = _dialogsProcess->single.get();
using Type = Data::DialogInfo::Type;
const auto setting = [&] {
switch (process->info.type) {
case Type::Personal: return Settings::Type::PersonalChats;
case Type::Bot: return Settings::Type::BotChats;
case Type::PrivateGroup: return Settings::Type::PrivateGroups;
case Type::PrivateChannel: return Settings::Type::PrivateChannels;
case Type::PublicGroup: return Settings::Type::PublicGroups;
case Type::PublicChannel: return Settings::Type::PublicChannels;
}
Unexpected("Type in ApiWrap::onlyMyMessages.");
}();
return (_settings->fullChats & setting) != setting;
}

void ApiWrap::loadMessagesFiles(Data::MessagesSlice &&slice) {
Expects(_dialogsProcess != nullptr);
Expects(_dialogsProcess->single != nullptr);
Expand Down Expand Up @@ -637,12 +680,20 @@ void ApiWrap::loadNextMessageFile() {
return;
}
}
finishMessagesSlice();
}

if (!list.empty()) {
process->offsetId = list.back().id + 1;
}
_dialogsProcess->sliceOne(*base::take(process->slice));
void ApiWrap::finishMessagesSlice() {
Expects(_dialogsProcess != nullptr);
Expects(_dialogsProcess->single != nullptr);
Expects(_dialogsProcess->single->slice.has_value());

const auto process = _dialogsProcess->single.get();
auto slice = *base::take(process->slice);
if (!slice.list.empty()) {
process->offsetId = slice.list.back().id + 1;
_dialogsProcess->sliceOne(std::move(slice));
}
if (process->lastSlice) {
finishMessages();
} else {
Expand Down
Loading

0 comments on commit 1bfe409

Please sign in to comment.