diff --git a/Telegram/Resources/scheme.tl b/Telegram/Resources/scheme.tl index 32dfaaa6775d78..a16e7d51a1eafb 100644 --- a/Telegram/Resources/scheme.tl +++ b/Telegram/Resources/scheme.tl @@ -843,8 +843,6 @@ webDocumentNoProxy#f9c8bcc6 url:string size:int mime_type:string attributes:Vect inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector = InputWebDocument; inputWebFileLocation#c239d686 url:string access_hash:long = InputWebFileLocation; -inputWebFileGeoPointLocation#66275a62 geo_point:InputGeoPoint w:int h:int zoom:int scale:int = InputWebFileLocation; -inputWebFileGeoMessageLocation#553f32eb peer:InputPeer msg_id:int w:int h:int zoom:int scale:int = InputWebFileLocation; upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile; @@ -1014,6 +1012,10 @@ account.sentEmailCode#811f854f email_pattern:string length:int = account.SentEma help.deepLinkInfoEmpty#66afa166 = help.DeepLinkInfo; help.deepLinkInfo#6a4ee832 flags:# update_app:flags.0?true message:string entities:flags.1?Vector = help.DeepLinkInfo; +savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date:int = SavedContact; + +account.takeout#4dba4501 id:long = account.Takeout; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1021,13 +1023,13 @@ invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector query:!X = X; initConnection#785188b8 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy query:!X = X; invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X; invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X; +invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X; auth.sendCode#86aef0ec flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool api_id:int api_hash:string = auth.SentCode; auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization; auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization; auth.logOut#5717da40 = Bool; auth.resetAuthorizations#9fab0d1a = Bool; -auth.sendInvites#771c1d97 phone_numbers:Vector message:string = Bool; auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization; auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization; auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool; @@ -1079,6 +1081,7 @@ account.sendVerifyPhoneCode#823380b4 flags:# allow_flashcall:flags.0?true phone_ account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool; account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode; account.verifyEmail#ecba39db email:string code:string = Bool; +account.initTakeoutSession#f05b4804 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?int = account.Takeout; users.getUsers#d91a548 id:Vector = Vector; users.getFullUser#ca30a5b1 id:InputUser = UserFull; @@ -1099,6 +1102,7 @@ contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer; contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers; contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool; contacts.resetSaved#879537f1 = Bool; +contacts.getSaved#82f1e39f = Vector; messages.getMessages#63c66506 id:Vector = messages.Messages; messages.getDialogs#191ba9c5 flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int = messages.Dialogs; diff --git a/Telegram/SourceFiles/base/bytes.h b/Telegram/SourceFiles/base/bytes.h index 411bc207758364..0150e7dc51da5d 100644 --- a/Telegram/SourceFiles/base/bytes.h +++ b/Telegram/SourceFiles/base/bytes.h @@ -134,4 +134,7 @@ vector concatenate(SpanRange args) { return result; } +// Implemented in base/openssl_help.h +void set_random(span destination); + } // namespace bytes diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index 13c14f646078e5..6a46c26795cbb8 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -826,6 +826,23 @@ ContactsList ParseContactsList(const MTPcontacts_Contacts &data) { return result; } +ContactsList ParseContactsList(const MTPVector &data) { + auto result = ContactsList(); + result.list.reserve(data.v.size()); + for (const auto &contact : data.v) { + auto info = contact.match([](const MTPDsavedPhoneContact &data) { + auto info = ContactInfo(); + info.firstName = ParseString(data.vfirst_name); + info.lastName = ParseString(data.vlast_name); + info.phoneNumber = ParseString(data.vphone); + info.date = data.vdate.v; + return info; + }); + result.list.push_back(std::move(info)); + } + return result; +} + std::vector SortedContactsIndices(const ContactsList &data) { const auto names = ranges::view::all( data.list @@ -890,14 +907,19 @@ DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data) { const auto peerId = ParsePeerId(fields.vpeer); const auto peerIt = peers.find(peerId); if (peerIt != end(peers)) { + using Type = DialogInfo::Type; const auto &peer = peerIt->second; info.type = peer.user() - ? DialogInfo::Type::Personal - : peer.chat()->broadcast - ? DialogInfo::Type::Channel - : peer.chat()->username.isEmpty() - ? DialogInfo::Type::PrivateGroup - : DialogInfo::Type::PublicGroup; + ? (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)); info.name = peer.name(); info.input = peer.input(); } @@ -940,6 +962,9 @@ Utf8String FormatDateTime( QChar dateSeparator, QChar timeSeparator, QChar separator) { + if (!date) { + return Utf8String(); + } const auto value = QDateTime::fromTime_t(date); return (QString("%1") + dateSeparator + "%2" + dateSeparator + "%3" + separator + "%4" + timeSeparator + "%5" + timeSeparator + "%6" diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index 49c13892467259..78664ef3b8b7e8 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -136,6 +136,7 @@ struct ContactInfo { Utf8String firstName; Utf8String lastName; Utf8String phoneNumber; + TimeId date = 0; Utf8String name() const; }; @@ -196,6 +197,7 @@ struct ContactsList { }; ContactsList ParseContactsList(const MTPcontacts_Contacts &data); +ContactsList ParseContactsList(const MTPVector &data); std::vector SortedContactsIndices(const ContactsList &data); struct Session { @@ -394,9 +396,11 @@ struct DialogInfo { enum class Type { Unknown, Personal, + Bot, PrivateGroup, PublicGroup, - Channel, + PrivateChannel, + PublicChannel, }; Type type = Type::Unknown; Utf8String name; diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp index 951b7cebd63b87..b47bd2aeed5fdb 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.cpp +++ b/Telegram/SourceFiles/export/export_api_wrap.cpp @@ -11,6 +11,7 @@ For license and copyright information please follow this link: #include "export/data/export_data_types.h" #include "export/output/export_output_file.h" #include "mtproto/rpc_sender.h" +#include "base/bytes.h" #include @@ -23,6 +24,7 @@ constexpr auto kFileRequestsCount = 2; constexpr auto kFileNextRequestDelay = TimeMs(20); constexpr auto kChatsSliceLimit = 100; constexpr auto kMessagesSliceLimit = 100; +constexpr auto kFileMaxSize = 1500 * 1024 * 1024; bool WillLoadFile(const Data::File &file) { return file.relativePath.isEmpty() @@ -102,23 +104,29 @@ ApiWrap::DialogsProcess::Single::Single(const Data::DialogInfo &info) template auto ApiWrap::mainRequest(Request &&request) { - return std::move(_mtp.request( - std::move(request) - ).fail([=](RPCError &&result) { + Expects(_takeoutId.has_value()); + + return std::move(_mtp.request(MTPInvokeWithTakeout( + MTP_long(*_takeoutId), + request + )).fail([=](RPCError &&result) { error(std::move(result)); }).toDC(MTP::ShiftDcId(0, MTP::kExportDcShift))); } auto ApiWrap::fileRequest(const Data::FileLocation &location, int offset) { Expects(location.dcId != 0); - - return std::move(_mtp.request(MTPupload_GetFile( - location.data, - MTP_int(offset), - MTP_int(kFileChunkSize) + Expects(_takeoutId.has_value()); + + return std::move(_mtp.request(MTPInvokeWithTakeout( + MTP_long(*_takeoutId), + MTPupload_GetFile( + location.data, + MTP_int(offset), + MTP_int(kFileChunkSize)) )).fail([=](RPCError &&result) { error(std::move(result)); - }).toDC(MTP::ShiftDcId(location.dcId, MTP::kExportDcShift))); + }).toDC(MTP::ShiftDcId(location.dcId, MTP::kExportMediaDcShift))); } ApiWrap::ApiWrap(Fn)> runner) @@ -129,10 +137,57 @@ rpl::producer ApiWrap::errors() const { return _errors.events(); } -void ApiWrap::startExport(const Settings &settings) { +void ApiWrap::startExport( + const Settings &settings, + FnMut done) { Expects(_settings == nullptr); _settings = std::make_unique(settings); + startMainSession(std::move(done)); +} + +void ApiWrap::startMainSession(FnMut done) { + auto sizeLimit = _settings->defaultMedia.sizeLimit; + auto hasFiles = _settings->defaultMedia.types != 0; + for (const auto &item : _settings->customMedia) { + sizeLimit = std::max(sizeLimit, item.second.sizeLimit); + hasFiles = hasFiles || (item.second.types != 0); + } + if (!sizeLimit) { + hasFiles = false; + } + + using Type = Settings::Type; + using Flag = MTPaccount_InitTakeoutSession::Flag; + const auto flags = Flag(0) + | (_settings->types & Type::Contacts ? Flag::f_contacts : Flag(0)) + | (hasFiles ? Flag::f_files : Flag(0)) + | (sizeLimit < kFileMaxSize ? Flag::f_file_max_size : Flag(0)) + | (_settings->types & (Type::PersonalChats | Type::BotChats) + ? Flag::f_message_users + : Flag(0)) + | (_settings->types & Type::PrivateGroups + ? (Flag::f_message_chats | Flag::f_message_megagroups) + : Flag(0)) + | (_settings->types & Type::PublicGroups + ? Flag::f_message_megagroups + : Flag(0)) + | (_settings->types & (Type::PrivateChannels | Type::PublicChannels) + ? Flag::f_message_channels + : Flag(0)); + + _mtp.request(MTPaccount_InitTakeoutSession( + MTP_flags(flags), + MTP_int(sizeLimit) + )).done([=, done = std::move(done)]( + const MTPaccount_Takeout &result) mutable { + _takeoutId = result.match([](const MTPDaccount_takeout &data) { + return data.vid.v; + }); + done(); + }).fail([=](RPCError &&result) { + error(std::move(result)); + }).toDC(MTP::ShiftDcId(0, MTP::kExportDcShift)).send(); } void ApiWrap::requestPersonalInfo(FnMut done) { @@ -266,16 +321,10 @@ void ApiWrap::finishUserpics() { } void ApiWrap::requestContacts(FnMut done) { - const auto hash = 0; - mainRequest(MTPcontacts_GetContacts( - MTP_int(hash) + mainRequest(MTPcontacts_GetSaved( )).done([=, done = std::move(done)]( - const MTPcontacts_Contacts &result) mutable { - if (result.type() == mtpc_contacts_contacts) { - done(Data::ParseContactsList(result)); - } else { - error("Bad contacts type."); - } + const MTPVector &result) mutable { + done(Data::ParseContactsList(result)); }).send(); } @@ -354,12 +403,16 @@ void ApiWrap::appendDialogsSlice(Data::DialogsInfo &&info) { switch (info.type) { case DialogType::Personal: return Settings::Type::PersonalChats; + case DialogType::Bot: + return Settings::Type::BotChats; case DialogType::PrivateGroup: return Settings::Type::PrivateGroups; case DialogType::PublicGroup: return Settings::Type::PublicGroups; - case DialogType::Channel: - return Settings::Type::MyChannels; + case DialogType::PrivateChannel: + return Settings::Type::PrivateChannels; + case DialogType::PublicChannel: + return Settings::Type::PublicChannels; } return Settings::Type(0); }(); @@ -536,6 +589,7 @@ void ApiWrap::loadFile(const Data::File &file, FnMut done) { _settings->path + relativePath); _fileProcess->relativePath = relativePath; _fileProcess->location = file.location; + _fileProcess->size = file.size; _fileProcess->done = std::move(done); if (!file.content.isEmpty()) { diff --git a/Telegram/SourceFiles/export/export_api_wrap.h b/Telegram/SourceFiles/export/export_api_wrap.h index af4380d475a87c..6db4083765a8e8 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.h +++ b/Telegram/SourceFiles/export/export_api_wrap.h @@ -31,7 +31,9 @@ class ApiWrap { rpl::producer errors() const; - void startExport(const Settings &settings); + void startExport( + const Settings &settings, + FnMut done); void requestPersonalInfo(FnMut done); @@ -54,12 +56,16 @@ class ApiWrap { ~ApiWrap(); private: + void startMainSession(FnMut done); + void handleUserpicsSlice(const MTPphotos_Photos &result); void loadUserpicsFiles(Data::UserpicsSlice &&slice); void loadNextUserpic(); void loadUserpicDone(const QString &relativePath); void finishUserpics(); + void requestSavedContacts(); + void requestDialogsSlice(); void appendDialogsSlice(Data::DialogsInfo &&info); void finishDialogsList(); @@ -88,6 +94,7 @@ class ApiWrap { void error(const QString &text); MTP::ConcurrentSender _mtp; + base::optional _takeoutId; std::unique_ptr _settings; MTPInputUser _user = MTP_inputUserSelf(); diff --git a/Telegram/SourceFiles/export/export_controller.cpp b/Telegram/SourceFiles/export/export_controller.cpp index 4caa064b1dbb70..df5f1b46589e59 100644 --- a/Telegram/SourceFiles/export/export_controller.cpp +++ b/Telegram/SourceFiles/export/export_controller.cpp @@ -157,9 +157,10 @@ void Controller::startExport(const Settings &settings) { return; } _writer = Output::CreateWriter(_settings.format); - _api.startExport(_settings); fillExportSteps(); - exportNext(); + _api.startExport(_settings, [=] { + exportNext(); + }); } bool Controller::normalizePath() { @@ -207,9 +208,11 @@ void Controller::fillExportSteps() { _steps.push_back(Step::Sessions); } const auto dialogTypes = Type::PersonalChats + | Type::BotChats | Type::PrivateGroups | Type::PublicGroups - | Type::MyChannels; + | Type::PrivateChannels + | Type::PublicChannels; if (_settings.types & dialogTypes) { _steps.push_back(Step::Dialogs); } diff --git a/Telegram/SourceFiles/export/export_pch.h b/Telegram/SourceFiles/export/export_pch.h index f5a4057528abf9..bfb80883b3605e 100644 --- a/Telegram/SourceFiles/export/export_pch.h +++ b/Telegram/SourceFiles/export/export_pch.h @@ -28,5 +28,8 @@ For license and copyright information please follow this link: #include "platform/win/windows_range_v3_helpers.h" #endif // Q_OS_WIN +#include "base/flat_map.h" +#include "base/flat_set.h" + #include "scheme.h" #include "logs.h" diff --git a/Telegram/SourceFiles/export/export_settings.h b/Telegram/SourceFiles/export/export_settings.h index ab9bf16187209f..e1a923d8058529 100644 --- a/Telegram/SourceFiles/export/export_settings.h +++ b/Telegram/SourceFiles/export/export_settings.h @@ -37,14 +37,16 @@ struct MediaSettings { struct Settings { enum class Type { - PersonalInfo = 0x01, - Userpics = 0x02, - Contacts = 0x04, - Sessions = 0x08, - PersonalChats = 0x10, - PrivateGroups = 0x20, - PublicGroups = 0x40, - MyChannels = 0x80, + PersonalInfo = 0x001, + Userpics = 0x002, + Contacts = 0x004, + Sessions = 0x008, + PersonalChats = 0x010, + BotChats = 0x020, + PrivateGroups = 0x040, + PublicGroups = 0x080, + PrivateChannels = 0x100, + PublicChannels = 0x200, }; using Types = base::flags; friend inline constexpr auto is_flag_type(Type) { return true; }; diff --git a/Telegram/SourceFiles/export/output/export_output_text.cpp b/Telegram/SourceFiles/export/output/export_output_text.cpp index c86c7701398240..73c4e64d0ae3bb 100644 --- a/Telegram/SourceFiles/export/output/export_output_text.cpp +++ b/Telegram/SourceFiles/export/output/export_output_text.cpp @@ -460,9 +460,7 @@ bool TextWriter::writeContactsList(const Data::ContactsList &data) { list.reserve(data.list.size()); for (const auto &index : Data::SortedContactsIndices(data)) { const auto &contact = data.list[index]; - if (!contact.userId) { - list.push_back("(user unavailable)" + kLineBreak); - } else if (contact.firstName.isEmpty() + if (contact.firstName.isEmpty() && contact.lastName.isEmpty() && contact.phoneNumber.isEmpty()) { list.push_back("(deleted user)" + kLineBreak); @@ -474,6 +472,7 @@ bool TextWriter::writeContactsList(const Data::ContactsList &data) { "Phone number", Data::FormatPhoneNumber(contact.phoneNumber) }, + { "Date", Data::FormatDateTime(contact.date) } })); } } @@ -539,10 +538,12 @@ bool TextWriter::writeDialogsStart(const Data::DialogsInfo &data) { const auto TypeString = [](Type type) { switch (type) { case Type::Unknown: return "(unknown)"; - case Type::Personal: return "Personal Chat"; - case Type::PrivateGroup: return "Private Group"; - case Type::PublicGroup: return "Public Group"; - case Type::Channel: return "Channel"; + case Type::Personal: return "Personal chat"; + case Type::Bot: return "Bot chat"; + case Type::PrivateGroup: return "Private group"; + case Type::PublicGroup: return "Public group"; + case Type::PrivateChannel: return "Private channel"; + case Type::PublicChannel: return "Private channel"; } Unexpected("Dialog type in TypeString."); }; @@ -555,9 +556,11 @@ bool TextWriter::writeDialogsStart(const Data::DialogsInfo &data) { switch (type) { case Type::Unknown: return "(unknown)"; case Type::Personal: return "(deleted user)"; + case Type::Bot: return "(deleted bot)"; case Type::PrivateGroup: case Type::PublicGroup: return "(deleted group)"; - case Type::Channel: return "(deleted channel)"; + case Type::PrivateChannel: + case Type::PublicChannel: return "(deleted channel)"; } Unexpected("Dialog type in TypeString."); }; diff --git a/Telegram/SourceFiles/mtproto/core_types.h b/Telegram/SourceFiles/mtproto/core_types.h index 7325393c5cf93e..d5ae244c987133 100644 --- a/Telegram/SourceFiles/mtproto/core_types.h +++ b/Telegram/SourceFiles/mtproto/core_types.h @@ -31,6 +31,7 @@ constexpr auto kConfigDcShift = 0x01; constexpr auto kLogoutDcShift = 0x02; constexpr auto kUpdaterDcShift = 0x03; constexpr auto kExportDcShift = 0x04; +constexpr auto kExportMediaDcShift = 0x05; constexpr auto kMaxMediaDcCount = 0x10; constexpr auto kBaseDownloadDcShift = 0x10; constexpr auto kBaseUploadDcShift = 0x20;