| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "export/export_api_wrap.h"
- #include "export/export_settings.h"
- #include "export/data/export_data_types.h"
- #include "export/output/export_output_result.h"
- #include "export/output/export_output_file.h"
- #include "mtproto/mtproto_response.h"
- #include "base/bytes.h"
- #include "base/options.h"
- #include "base/random.h"
- #include <set>
- #include <deque>
- namespace Export {
- namespace {
- constexpr auto kUserpicsSliceLimit = 100;
- constexpr auto kFileChunkSize = 128 * 1024;
- constexpr auto kFileRequestsCount = 2;
- //constexpr auto kFileNextRequestDelay = crl::time(20);
- constexpr auto kChatsSliceLimit = 100;
- constexpr auto kMessagesSliceLimit = 100;
- constexpr auto kTopPeerSliceLimit = 100;
- constexpr auto kFileMaxSize = 4000 * int64(1024 * 1024);
- constexpr auto kLocationCacheSize = 100'000;
- constexpr auto kMaxEmojiPerRequest = 100;
- constexpr auto kStoriesSliceLimit = 100;
- struct LocationKey {
- uint64 type;
- uint64 id;
- inline bool operator<(const LocationKey &other) const {
- return std::tie(type, id) < std::tie(other.type, other.id);
- }
- };
- LocationKey ComputeLocationKey(const Data::FileLocation &value) {
- auto result = LocationKey();
- result.type = value.dcId;
- value.data.match([&](const MTPDinputDocumentFileLocation &data) {
- const auto letter = data.vthumb_size().v.isEmpty()
- ? char(0)
- : data.vthumb_size().v[0];
- result.type |= (2ULL << 24);
- result.type |= (uint64(uint32(letter)) << 16);
- result.id = data.vid().v;
- }, [&](const MTPDinputPhotoFileLocation &data) {
- const auto letter = data.vthumb_size().v.isEmpty()
- ? char(0)
- : data.vthumb_size().v[0];
- result.type |= (6ULL << 24);
- result.type |= (uint64(uint32(letter)) << 16);
- result.id = data.vid().v;
- }, [&](const MTPDinputTakeoutFileLocation &data) {
- result.type |= (5ULL << 24);
- }, [](const auto &data) {
- Unexpected("File location type in Export::ComputeLocationKey.");
- });
- return result;
- }
- Settings::Type SettingsFromDialogsType(Data::DialogInfo::Type type) {
- using DialogType = Data::DialogInfo::Type;
- switch (type) {
- case DialogType::Self:
- case DialogType::Personal:
- return Settings::Type::PersonalChats;
- case DialogType::Bot:
- return Settings::Type::BotChats;
- case DialogType::PrivateGroup:
- case DialogType::PrivateSupergroup:
- return Settings::Type::PrivateGroups;
- case DialogType::PublicSupergroup:
- return Settings::Type::PublicGroups;
- case DialogType::PrivateChannel:
- return Settings::Type::PrivateChannels;
- case DialogType::PublicChannel:
- return Settings::Type::PublicChannels;
- }
- return Settings::Type(0);
- }
- } // namespace
- class ApiWrap::LoadedFileCache {
- public:
- using Location = Data::FileLocation;
- LoadedFileCache(int limit);
- void save(const Location &location, const QString &relativePath);
- std::optional<QString> find(const Location &location) const;
- private:
- int _limit = 0;
- std::map<LocationKey, QString> _map;
- std::deque<LocationKey> _list;
- };
- struct ApiWrap::StartProcess {
- FnMut<void(StartInfo)> done;
- enum class Step {
- UserpicsCount,
- StoriesCount,
- SplitRanges,
- DialogsCount,
- LeftChannelsCount,
- };
- std::deque<Step> steps;
- int splitIndex = 0;
- StartInfo info;
- };
- struct ApiWrap::ContactsProcess {
- FnMut<void(Data::ContactsList&&)> done;
- Data::ContactsList result;
- int topPeersOffset = 0;
- };
- struct ApiWrap::UserpicsProcess {
- FnMut<bool(Data::UserpicsInfo&&)> start;
- Fn<bool(DownloadProgress)> fileProgress;
- Fn<bool(Data::UserpicsSlice&&)> handleSlice;
- FnMut<void()> finish;
- int processed = 0;
- std::optional<Data::UserpicsSlice> slice;
- uint64 maxId = 0;
- bool lastSlice = false;
- int fileIndex = 0;
- };
- struct ApiWrap::StoriesProcess {
- FnMut<bool(Data::StoriesInfo&&)> start;
- Fn<bool(DownloadProgress)> fileProgress;
- Fn<bool(Data::StoriesSlice&&)> handleSlice;
- FnMut<void()> finish;
- int processed = 0;
- std::optional<Data::StoriesSlice> slice;
- int offsetId = 0;
- bool lastSlice = false;
- int fileIndex = 0;
- };
- struct ApiWrap::OtherDataProcess {
- Data::File file;
- FnMut<void(Data::File&&)> done;
- };
- struct ApiWrap::FileProcess {
- FileProcess(const QString &path, Output::Stats *stats);
- Output::File file;
- QString relativePath;
- Fn<bool(FileProgress)> progress;
- FnMut<void(const QString &relativePath)> done;
- uint64 randomId = 0;
- Data::FileLocation location;
- Data::FileOrigin origin;
- int64 offset = 0;
- int64 size = 0;
- struct Request {
- int64 offset = 0;
- QByteArray bytes;
- };
- std::deque<Request> requests;
- mtpRequestId requestId = 0;
- };
- struct ApiWrap::FileProgress {
- int64 ready = 0;
- int64 total = 0;
- };
- struct ApiWrap::ChatsProcess {
- Fn<bool(int count)> progress;
- FnMut<void(Data::DialogsInfo&&)> done;
- Data::DialogsInfo info;
- int processedCount = 0;
- std::map<PeerId, int> indexByPeer;
- };
- struct ApiWrap::LeftChannelsProcess : ChatsProcess {
- int fullCount = 0;
- int offset = 0;
- bool finished = false;
- };
- struct ApiWrap::DialogsProcess : ChatsProcess {
- int splitIndexPlusOne = 0;
- TimeId offsetDate = 0;
- int32 offsetId = 0;
- MTPInputPeer offsetPeer = MTP_inputPeerEmpty();
- };
- struct ApiWrap::ChatProcess {
- Data::DialogInfo info;
- FnMut<bool(const Data::DialogInfo &)> start;
- Fn<bool(DownloadProgress)> fileProgress;
- Fn<bool(Data::MessagesSlice&&)> handleSlice;
- FnMut<void()> done;
- FnMut<void(MTPmessages_Messages&&)> requestDone;
- int localSplitIndex = 0;
- int32 largestIdPlusOne = 1;
- Data::ParseMediaContext context;
- std::optional<Data::MessagesSlice> slice;
- bool lastSlice = false;
- int fileIndex = 0;
- };
- template <typename Request>
- class ApiWrap::RequestBuilder {
- public:
- using Original = MTP::ConcurrentSender::SpecificRequestBuilder<Request>;
- using Response = typename Request::ResponseType;
- RequestBuilder(
- Original &&builder,
- Fn<void(const MTP::Error&)> commonFailHandler);
- [[nodiscard]] RequestBuilder &done(FnMut<void()> &&handler);
- [[nodiscard]] RequestBuilder &done(
- FnMut<void(Response &&)> &&handler);
- [[nodiscard]] RequestBuilder &fail(
- Fn<bool(const MTP::Error&)> &&handler);
- mtpRequestId send();
- private:
- Original _builder;
- Fn<void(const MTP::Error&)> _commonFailHandler;
- };
- template <typename Request>
- ApiWrap::RequestBuilder<Request>::RequestBuilder(
- Original &&builder,
- Fn<void(const MTP::Error&)> commonFailHandler)
- : _builder(std::move(builder))
- , _commonFailHandler(std::move(commonFailHandler)) {
- }
- template <typename Request>
- auto ApiWrap::RequestBuilder<Request>::done(
- FnMut<void()> &&handler
- ) -> RequestBuilder& {
- if (handler) {
- [[maybe_unused]] auto &silence_warning = _builder.done(std::move(handler));
- }
- return *this;
- }
- template <typename Request>
- auto ApiWrap::RequestBuilder<Request>::done(
- FnMut<void(Response &&)> &&handler
- ) -> RequestBuilder& {
- if (handler) {
- [[maybe_unused]] auto &silence_warning = _builder.done(std::move(handler));
- }
- return *this;
- }
- template <typename Request>
- auto ApiWrap::RequestBuilder<Request>::fail(
- Fn<bool(const MTP::Error &)> &&handler
- ) -> RequestBuilder& {
- if (handler) {
- [[maybe_unused]] auto &silence_warning = _builder.fail([
- common = base::take(_commonFailHandler),
- specific = std::move(handler)
- ](const MTP::Error &error) {
- if (!specific(error)) {
- common(error);
- }
- });
- }
- return *this;
- }
- template <typename Request>
- mtpRequestId ApiWrap::RequestBuilder<Request>::send() {
- return _commonFailHandler
- ? _builder.fail(base::take(_commonFailHandler)).send()
- : _builder.send();
- }
- ApiWrap::LoadedFileCache::LoadedFileCache(int limit) : _limit(limit) {
- Expects(limit >= 0);
- }
- void ApiWrap::LoadedFileCache::save(
- const Location &location,
- const QString &relativePath) {
- if (!location) {
- return;
- }
- const auto key = ComputeLocationKey(location);
- _map[key] = relativePath;
- _list.push_back(key);
- if (_list.size() > _limit) {
- const auto key = _list.front();
- _list.pop_front();
- _map.erase(key);
- }
- }
- std::optional<QString> ApiWrap::LoadedFileCache::find(
- const Location &location) const {
- if (!location) {
- return std::nullopt;
- }
- const auto key = ComputeLocationKey(location);
- if (const auto i = _map.find(key); i != end(_map)) {
- return i->second;
- }
- return std::nullopt;
- }
- ApiWrap::FileProcess::FileProcess(const QString &path, Output::Stats *stats)
- : file(path, stats) {
- }
- template <typename Request>
- auto ApiWrap::mainRequest(Request &&request) {
- Expects(_takeoutId.has_value());
- auto original = std::move(_mtp.request(MTPInvokeWithTakeout<Request>(
- MTP_long(*_takeoutId),
- std::forward<Request>(request)
- )).toDC(MTP::ShiftDcId(0, MTP::kExportDcShift)));
- return RequestBuilder<MTPInvokeWithTakeout<Request>>(
- std::move(original),
- [=](const MTP::Error &result) { error(result); });
- }
- template <typename Request>
- auto ApiWrap::splitRequest(int index, Request &&request) {
- Expects(index < _splits.size());
- //if (index == _splits.size() - 1) {
- // return mainRequest(std::forward<Request>(request));
- //}
- return mainRequest(MTPInvokeWithMessagesRange<Request>(
- _splits[index],
- std::forward<Request>(request)));
- }
- auto ApiWrap::fileRequest(const Data::FileLocation &location, int64 offset) {
- Expects(location.dcId != 0
- || location.data.type() == mtpc_inputTakeoutFileLocation);
- Expects(_takeoutId.has_value());
- Expects(_fileProcess->requestId == 0);
- return std::move(_mtp.request(MTPInvokeWithTakeout<MTPupload_GetFile>(
- MTP_long(*_takeoutId),
- MTPupload_GetFile(
- MTP_flags(0),
- location.data,
- MTP_long(offset),
- MTP_int(kFileChunkSize))
- )).fail([=](const MTP::Error &result) {
- _fileProcess->requestId = 0;
- if (result.type() == u"TAKEOUT_FILE_EMPTY"_q
- && _otherDataProcess != nullptr) {
- filePartDone(
- 0,
- MTP_upload_file(
- MTP_storage_filePartial(),
- MTP_int(0),
- MTP_bytes()));
- } else if (result.type() == u"LOCATION_INVALID"_q
- || result.type() == u"VERSION_INVALID"_q
- || result.type() == u"LOCATION_NOT_AVAILABLE"_q) {
- filePartUnavailable();
- } else if (result.code() == 400
- && result.type().startsWith(u"FILE_REFERENCE_"_q)) {
- filePartRefreshReference(offset);
- } else {
- error(std::move(result));
- }
- }).toDC(MTP::ShiftDcId(location.dcId, MTP::kExportMediaDcShift)));
- }
- ApiWrap::ApiWrap(QPointer<MTP::Instance> weak, Fn<void(FnMut<void()>)> runner)
- : _mtp(weak, std::move(runner))
- , _fileCache(std::make_unique<LoadedFileCache>(kLocationCacheSize)) {
- }
- rpl::producer<MTP::Error> ApiWrap::errors() const {
- return _errors.events();
- }
- rpl::producer<Output::Result> ApiWrap::ioErrors() const {
- return _ioErrors.events();
- }
- void ApiWrap::startExport(
- const Settings &settings,
- Output::Stats *stats,
- FnMut<void(StartInfo)> done) {
- Expects(_settings == nullptr);
- Expects(_startProcess == nullptr);
- _settings = std::make_unique<Settings>(settings);
- _stats = stats;
- _startProcess = std::make_unique<StartProcess>();
- _startProcess->done = std::move(done);
- using Step = StartProcess::Step;
- if (_settings->types & Settings::Type::Userpics) {
- _startProcess->steps.push_back(Step::UserpicsCount);
- }
- if (_settings->types & Settings::Type::Stories) {
- _startProcess->steps.push_back(Step::StoriesCount);
- }
- if (_settings->types & Settings::Type::AnyChatsMask) {
- _startProcess->steps.push_back(Step::SplitRanges);
- _startProcess->steps.push_back(Step::DialogsCount);
- }
- if (_settings->types & Settings::Type::GroupsChannelsMask) {
- if (!_settings->onlySinglePeer()) {
- _startProcess->steps.push_back(Step::LeftChannelsCount);
- }
- }
- startMainSession([=] {
- sendNextStartRequest();
- });
- }
- void ApiWrap::sendNextStartRequest() {
- Expects(_startProcess != nullptr);
- auto &steps = _startProcess->steps;
- if (steps.empty()) {
- finishStartProcess();
- return;
- }
- using Step = StartProcess::Step;
- const auto step = steps.front();
- steps.pop_front();
- switch (step) {
- case Step::UserpicsCount:
- return requestUserpicsCount();
- case Step::StoriesCount:
- return requestStoriesCount();
- case Step::SplitRanges:
- return requestSplitRanges();
- case Step::DialogsCount:
- return requestDialogsCount();
- case Step::LeftChannelsCount:
- return requestLeftChannelsCount();
- }
- Unexpected("Step in ApiWrap::sendNextStartRequest.");
- }
- void ApiWrap::requestUserpicsCount() {
- Expects(_startProcess != nullptr);
- mainRequest(MTPphotos_GetUserPhotos(
- _user,
- MTP_int(0), // offset
- MTP_long(0), // max_id
- MTP_int(0) // limit
- )).done([=](const MTPphotos_Photos &result) {
- Expects(_settings != nullptr);
- Expects(_startProcess != nullptr);
- _startProcess->info.userpicsCount = result.match(
- [](const MTPDphotos_photos &data) {
- return int(data.vphotos().v.size());
- }, [](const MTPDphotos_photosSlice &data) {
- return data.vcount().v;
- });
- sendNextStartRequest();
- }).send();
- }
- void ApiWrap::requestStoriesCount() {
- Expects(_startProcess != nullptr);
- mainRequest(MTPstories_GetStoriesArchive(
- MTP_inputPeerSelf(),
- MTP_int(0), // offset_id
- MTP_int(0) // limit
- )).done([=](const MTPstories_Stories &result) {
- Expects(_settings != nullptr);
- Expects(_startProcess != nullptr);
- _startProcess->info.storiesCount = result.data().vcount().v;
- sendNextStartRequest();
- }).send();
- }
- void ApiWrap::requestSplitRanges() {
- Expects(_startProcess != nullptr);
- mainRequest(MTPmessages_GetSplitRanges(
- )).done([=](const MTPVector<MTPMessageRange> &result) {
- _splits = result.v;
- if (_splits.empty()) {
- _splits.push_back(MTP_messageRange(
- MTP_int(1),
- MTP_int(std::numeric_limits<int>::max())));
- }
- _startProcess->splitIndex = useOnlyLastSplit()
- ? (_splits.size() - 1)
- : 0;
- sendNextStartRequest();
- }).send();
- }
- void ApiWrap::requestDialogsCount() {
- Expects(_startProcess != nullptr);
- if (_settings->onlySinglePeer()) {
- _startProcess->info.dialogsCount
- = (_settings->singlePeer.type() == mtpc_inputPeerChannel
- ? 1
- : _splits.size());
- sendNextStartRequest();
- return;
- }
- const auto offsetDate = 0;
- const auto offsetId = 0;
- const auto offsetPeer = MTP_inputPeerEmpty();
- const auto limit = 1;
- const auto hash = uint64(0);
- splitRequest(_startProcess->splitIndex, MTPmessages_GetDialogs(
- MTP_flags(0),
- MTPint(), // folder_id
- MTP_int(offsetDate),
- MTP_int(offsetId),
- offsetPeer,
- MTP_int(limit),
- MTP_long(hash)
- )).done([=](const MTPmessages_Dialogs &result) {
- Expects(_settings != nullptr);
- Expects(_startProcess != nullptr);
- const auto count = result.match(
- [](const MTPDmessages_dialogs &data) {
- return int(data.vdialogs().v.size());
- }, [](const MTPDmessages_dialogsSlice &data) {
- return data.vcount().v;
- }, [](const MTPDmessages_dialogsNotModified &data) {
- return -1;
- });
- if (count < 0) {
- error("Unexpected dialogsNotModified received.");
- return;
- }
- _startProcess->info.dialogsCount += count;
- if (++_startProcess->splitIndex >= _splits.size()) {
- sendNextStartRequest();
- } else {
- requestDialogsCount();
- }
- }).send();
- }
- void ApiWrap::requestLeftChannelsCount() {
- Expects(_startProcess != nullptr);
- Expects(_leftChannelsProcess == nullptr);
- _leftChannelsProcess = std::make_unique<LeftChannelsProcess>();
- requestLeftChannelsSliceGeneric([=] {
- Expects(_startProcess != nullptr);
- Expects(_leftChannelsProcess != nullptr);
- _startProcess->info.dialogsCount
- += _leftChannelsProcess->fullCount;
- sendNextStartRequest();
- });
- }
- void ApiWrap::finishStartProcess() {
- Expects(_startProcess != nullptr);
- const auto process = base::take(_startProcess);
- process->done(process->info);
- }
- bool ApiWrap::useOnlyLastSplit() const {
- return !(_settings->types & Settings::Type::NonChannelChatsMask);
- }
- void ApiWrap::requestLeftChannelsList(
- Fn<bool(int count)> progress,
- FnMut<void(Data::DialogsInfo&&)> done) {
- Expects(_leftChannelsProcess != nullptr);
- _leftChannelsProcess->progress = std::move(progress);
- _leftChannelsProcess->done = std::move(done);
- requestLeftChannelsSlice();
- }
- void ApiWrap::requestLeftChannelsSlice() {
- requestLeftChannelsSliceGeneric([=] {
- Expects(_leftChannelsProcess != nullptr);
- if (_leftChannelsProcess->finished) {
- const auto process = base::take(_leftChannelsProcess);
- process->done(std::move(process->info));
- } else {
- requestLeftChannelsSlice();
- }
- });
- }
- void ApiWrap::requestDialogsList(
- Fn<bool(int count)> progress,
- FnMut<void(Data::DialogsInfo&&)> done) {
- Expects(_dialogsProcess == nullptr);
- _dialogsProcess = std::make_unique<DialogsProcess>();
- _dialogsProcess->splitIndexPlusOne = _splits.size();
- _dialogsProcess->progress = std::move(progress);
- _dialogsProcess->done = std::move(done);
- requestDialogsSlice();
- }
- void ApiWrap::startMainSession(FnMut<void()> done) {
- using Type = Settings::Type;
- const auto sizeLimit = _settings->media.sizeLimit;
- const auto hasFiles = ((_settings->media.types != 0) && (sizeLimit > 0))
- || (_settings->types & Type::Userpics)
- || (_settings->types & Type::Stories);
- 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))
- | ((hasFiles && 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(MTPusers_GetUsers(
- MTP_vector<MTPInputUser>(1, MTP_inputUserSelf())
- )).done([=, done = std::move(done)](
- const MTPVector<MTPUser> &result) mutable {
- for (const auto &user : result.v) {
- user.match([&](const MTPDuser &data) {
- if (data.is_self()) {
- _selfId.emplace(data.vid());
- }
- }, [&](const MTPDuserEmpty&) {
- });
- }
- if (!_selfId) {
- error("Could not retrieve selfId.");
- return;
- }
- _mtp.request(MTPaccount_InitTakeoutSession(
- MTP_flags(flags),
- MTP_long(sizeLimit)
- )).done([=, done = std::move(done)](
- const MTPaccount_Takeout &result) mutable {
- _takeoutId = result.match([](const MTPDaccount_takeout &data) {
- return data.vid().v;
- });
- done();
- }).fail([=](const MTP::Error &result) {
- error(result);
- }).toDC(MTP::ShiftDcId(0, MTP::kExportDcShift)).send();
- }).fail([=](const MTP::Error &result) {
- error(result);
- }).send();
- }
- void ApiWrap::requestPersonalInfo(FnMut<void(Data::PersonalInfo&&)> done) {
- mainRequest(MTPusers_GetFullUser(
- _user
- )).done([=, done = std::move(done)](const MTPusers_UserFull &result) mutable {
- result.match([&](const MTPDusers_userFull &data) {
- if (!data.vusers().v.empty()) {
- done(Data::ParsePersonalInfo(data));
- } else {
- error("Bad user type.");
- }
- });
- }).send();
- }
- void ApiWrap::requestOtherData(
- const QString &suggestedPath,
- FnMut<void(Data::File&&)> done) {
- Expects(_otherDataProcess == nullptr);
- _otherDataProcess = std::make_unique<OtherDataProcess>();
- _otherDataProcess->done = std::move(done);
- _otherDataProcess->file.location.data = MTP_inputTakeoutFileLocation();
- _otherDataProcess->file.suggestedPath = suggestedPath;
- loadFile(
- _otherDataProcess->file,
- Data::FileOrigin(),
- [](FileProgress progress) { return true; },
- [=](const QString &result) { otherDataDone(result); });
- }
- void ApiWrap::otherDataDone(const QString &relativePath) {
- Expects(_otherDataProcess != nullptr);
- const auto process = base::take(_otherDataProcess);
- process->file.relativePath = relativePath;
- if (relativePath.isEmpty()) {
- process->file.skipReason = Data::File::SkipReason::Unavailable;
- }
- process->done(std::move(process->file));
- }
- void ApiWrap::requestUserpics(
- FnMut<bool(Data::UserpicsInfo&&)> start,
- Fn<bool(DownloadProgress)> progress,
- Fn<bool(Data::UserpicsSlice&&)> slice,
- FnMut<void()> finish) {
- Expects(_userpicsProcess == nullptr);
- _userpicsProcess = std::make_unique<UserpicsProcess>();
- _userpicsProcess->start = std::move(start);
- _userpicsProcess->fileProgress = std::move(progress);
- _userpicsProcess->handleSlice = std::move(slice);
- _userpicsProcess->finish = std::move(finish);
- mainRequest(MTPphotos_GetUserPhotos(
- _user,
- MTP_int(0), // offset
- MTP_long(_userpicsProcess->maxId),
- MTP_int(kUserpicsSliceLimit)
- )).done([=](const MTPphotos_Photos &result) mutable {
- Expects(_userpicsProcess != nullptr);
- auto startInfo = result.match(
- [](const MTPDphotos_photos &data) {
- return Data::UserpicsInfo{ int(data.vphotos().v.size()) };
- }, [](const MTPDphotos_photosSlice &data) {
- return Data::UserpicsInfo{ data.vcount().v };
- });
- if (!_userpicsProcess->start(std::move(startInfo))) {
- return;
- }
- handleUserpicsSlice(result);
- }).send();
- }
- void ApiWrap::handleUserpicsSlice(const MTPphotos_Photos &result) {
- Expects(_userpicsProcess != nullptr);
- result.match([&](const auto &data) {
- if constexpr (MTPDphotos_photos::Is<decltype(data)>()) {
- _userpicsProcess->lastSlice = true;
- }
- loadUserpicsFiles(Data::ParseUserpicsSlice(
- data.vphotos(),
- _userpicsProcess->processed));
- });
- }
- void ApiWrap::loadUserpicsFiles(Data::UserpicsSlice &&slice) {
- Expects(_userpicsProcess != nullptr);
- Expects(!_userpicsProcess->slice.has_value());
- if (slice.list.empty()) {
- _userpicsProcess->lastSlice = true;
- }
- _userpicsProcess->slice = std::move(slice);
- _userpicsProcess->fileIndex = 0;
- loadNextUserpic();
- }
- void ApiWrap::loadNextUserpic() {
- Expects(_userpicsProcess != nullptr);
- Expects(_userpicsProcess->slice.has_value());
- for (auto &list = _userpicsProcess->slice->list
- ; _userpicsProcess->fileIndex < list.size()
- ; ++_userpicsProcess->fileIndex) {
- const auto ready = processFileLoad(
- list[_userpicsProcess->fileIndex].image.file,
- Data::FileOrigin(),
- [=](FileProgress value) { return loadUserpicProgress(value); },
- [=](const QString &path) { loadUserpicDone(path); });
- if (!ready) {
- return;
- }
- }
- finishUserpicsSlice();
- }
- void ApiWrap::finishUserpicsSlice() {
- Expects(_userpicsProcess != nullptr);
- Expects(_userpicsProcess->slice.has_value());
- auto slice = *base::take(_userpicsProcess->slice);
- if (!slice.list.empty()) {
- _userpicsProcess->processed += slice.list.size();
- _userpicsProcess->maxId = slice.list.back().id;
- if (!_userpicsProcess->handleSlice(std::move(slice))) {
- return;
- }
- }
- if (_userpicsProcess->lastSlice) {
- finishUserpics();
- return;
- }
- mainRequest(MTPphotos_GetUserPhotos(
- _user,
- MTP_int(0), // offset
- MTP_long(_userpicsProcess->maxId),
- MTP_int(kUserpicsSliceLimit)
- )).done([=](const MTPphotos_Photos &result) {
- handleUserpicsSlice(result);
- }).send();
- }
- bool ApiWrap::loadUserpicProgress(FileProgress progress) {
- Expects(_fileProcess != nullptr);
- Expects(_userpicsProcess != nullptr);
- Expects(_userpicsProcess->slice.has_value());
- Expects((_userpicsProcess->fileIndex >= 0)
- && (_userpicsProcess->fileIndex
- < _userpicsProcess->slice->list.size()));
- return _userpicsProcess->fileProgress(DownloadProgress{
- _fileProcess->randomId,
- _fileProcess->relativePath,
- _userpicsProcess->fileIndex,
- progress.ready,
- progress.total });
- }
- void ApiWrap::loadUserpicDone(const QString &relativePath) {
- Expects(_userpicsProcess != nullptr);
- Expects(_userpicsProcess->slice.has_value());
- Expects((_userpicsProcess->fileIndex >= 0)
- && (_userpicsProcess->fileIndex
- < _userpicsProcess->slice->list.size()));
- const auto index = _userpicsProcess->fileIndex;
- auto &file = _userpicsProcess->slice->list[index].image.file;
- file.relativePath = relativePath;
- if (relativePath.isEmpty()) {
- file.skipReason = Data::File::SkipReason::Unavailable;
- }
- loadNextUserpic();
- }
- void ApiWrap::finishUserpics() {
- Expects(_userpicsProcess != nullptr);
- base::take(_userpicsProcess)->finish();
- }
- void ApiWrap::requestStories(
- FnMut<bool(Data::StoriesInfo&&)> start,
- Fn<bool(DownloadProgress)> progress,
- Fn<bool(Data::StoriesSlice&&)> slice,
- FnMut<void()> finish) {
- Expects(_storiesProcess == nullptr);
- _storiesProcess = std::make_unique<StoriesProcess>();
- _storiesProcess->start = std::move(start);
- _storiesProcess->fileProgress = std::move(progress);
- _storiesProcess->handleSlice = std::move(slice);
- _storiesProcess->finish = std::move(finish);
- mainRequest(MTPstories_GetStoriesArchive(
- MTP_inputPeerSelf(),
- MTP_int(_storiesProcess->offsetId),
- MTP_int(kStoriesSliceLimit)
- )).done([=](const MTPstories_Stories &result) mutable {
- Expects(_storiesProcess != nullptr);
- auto startInfo = Data::StoriesInfo{ result.data().vcount().v };
- if (!_storiesProcess->start(std::move(startInfo))) {
- return;
- }
- handleStoriesSlice(result);
- }).send();
- }
- void ApiWrap::handleStoriesSlice(const MTPstories_Stories &result) {
- Expects(_storiesProcess != nullptr);
- loadStoriesFiles(Data::ParseStoriesSlice(
- result.data().vstories(),
- _storiesProcess->processed));
- }
- void ApiWrap::loadStoriesFiles(Data::StoriesSlice &&slice) {
- Expects(_storiesProcess != nullptr);
- Expects(!_storiesProcess->slice.has_value());
- if (!slice.lastId) {
- _storiesProcess->lastSlice = true;
- }
- _storiesProcess->slice = std::move(slice);
- _storiesProcess->fileIndex = 0;
- loadNextStory();
- }
- void ApiWrap::loadNextStory() {
- Expects(_storiesProcess != nullptr);
- Expects(_storiesProcess->slice.has_value());
- for (auto &list = _storiesProcess->slice->list
- ; _storiesProcess->fileIndex < list.size()
- ; ++_storiesProcess->fileIndex) {
- auto &story = list[_storiesProcess->fileIndex];
- const auto origin = Data::FileOrigin{ .storyId = story.id };
- const auto ready = processFileLoad(
- story.file(),
- origin,
- [=](FileProgress value) { return loadStoryProgress(value); },
- [=](const QString &path) { loadStoryDone(path); });
- if (!ready) {
- return;
- }
- const auto thumbProgress = [=](FileProgress value) {
- return loadStoryThumbProgress(value);
- };
- const auto thumbReady = processFileLoad(
- story.thumb().file,
- origin,
- thumbProgress,
- [=](const QString &path) { loadStoryThumbDone(path); },
- nullptr,
- &story);
- if (!thumbReady) {
- return;
- }
- }
- finishStoriesSlice();
- }
- void ApiWrap::finishStoriesSlice() {
- Expects(_storiesProcess != nullptr);
- Expects(_storiesProcess->slice.has_value());
- auto slice = *base::take(_storiesProcess->slice);
- if (slice.lastId) {
- _storiesProcess->processed += slice.list.size();
- _storiesProcess->offsetId = slice.lastId;
- if (!_storiesProcess->handleSlice(std::move(slice))) {
- return;
- }
- }
- if (_storiesProcess->lastSlice) {
- finishStories();
- return;
- }
- mainRequest(MTPstories_GetStoriesArchive(
- MTP_inputPeerSelf(),
- MTP_int(_storiesProcess->offsetId),
- MTP_int(kStoriesSliceLimit)
- )).done([=](const MTPstories_Stories &result) {
- handleStoriesSlice(result);
- }).send();
- }
- bool ApiWrap::loadStoryProgress(FileProgress progress) {
- Expects(_fileProcess != nullptr);
- Expects(_storiesProcess != nullptr);
- Expects(_storiesProcess->slice.has_value());
- Expects((_storiesProcess->fileIndex >= 0)
- && (_storiesProcess->fileIndex
- < _storiesProcess->slice->list.size()));
- return _storiesProcess->fileProgress(DownloadProgress{
- _fileProcess->randomId,
- _fileProcess->relativePath,
- _storiesProcess->fileIndex,
- progress.ready,
- progress.total });
- }
- void ApiWrap::loadStoryDone(const QString &relativePath) {
- Expects(_storiesProcess != nullptr);
- Expects(_storiesProcess->slice.has_value());
- Expects((_storiesProcess->fileIndex >= 0)
- && (_storiesProcess->fileIndex
- < _storiesProcess->slice->list.size()));
- const auto index = _storiesProcess->fileIndex;
- auto &file = _storiesProcess->slice->list[index].file();
- file.relativePath = relativePath;
- if (relativePath.isEmpty()) {
- file.skipReason = Data::File::SkipReason::Unavailable;
- }
- loadNextStory();
- }
- bool ApiWrap::loadStoryThumbProgress(FileProgress progress) {
- return loadStoryProgress(progress);
- }
- void ApiWrap::loadStoryThumbDone(const QString &relativePath) {
- Expects(_storiesProcess != nullptr);
- Expects(_storiesProcess->slice.has_value());
- Expects((_storiesProcess->fileIndex >= 0)
- && (_storiesProcess->fileIndex
- < _storiesProcess->slice->list.size()));
- const auto index = _storiesProcess->fileIndex;
- auto &file = _storiesProcess->slice->list[index].thumb().file;
- file.relativePath = relativePath;
- if (relativePath.isEmpty()) {
- file.skipReason = Data::File::SkipReason::Unavailable;
- }
- loadNextStory();
- }
- void ApiWrap::finishStories() {
- Expects(_storiesProcess != nullptr);
- base::take(_storiesProcess)->finish();
- }
- 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([=](const MTPVector<MTPSavedContact> &result) {
- _contactsProcess->result = Data::ParseContactsList(result);
- const auto resolve = [=](int index, const auto &resolveNext) -> void {
- if (index == _contactsProcess->result.list.size()) {
- return requestTopPeersSlice();
- }
- const auto &contact = _contactsProcess->result.list[index];
- mainRequest(MTPcontacts_ResolvePhone(
- MTP_string(qs(contact.phoneNumber))
- )).done([=](const MTPcontacts_ResolvedPeer &result) {
- auto &contact = _contactsProcess->result.list[index];
- contact.userId = result.data().vpeer().match([&](
- const MTPDpeerUser &user) {
- return UserId(user.vuser_id());
- }, [](const auto &) {
- return UserId();
- });
- resolveNext(index + 1, resolveNext);
- }).fail([=](const MTP::Error &) {
- resolveNext(index + 1, resolveNext);
- return true;
- }).send();
- };
- if (base::options::lookup<bool>("show-peer-id-below-about").value()) {
- resolve(0, resolve);
- } else {
- 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
- | Flag::f_phone_calls),
- MTP_int(_contactsProcess->topPeersOffset),
- MTP_int(kTopPeerSliceLimit),
- MTP_long(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_topPeersDisabled &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(std::max(
- _contactsProcess->result.correspondents.size(),
- _contactsProcess->result.inlineBots.size()),
- _contactsProcess->result.phoneCalls.size());
- requestTopPeersSlice();
- }
- }).send();
- }
- void ApiWrap::requestSessions(FnMut<void(Data::SessionsList&&)> done) {
- mainRequest(MTPaccount_GetAuthorizations(
- )).done([=, done = std::move(done)](
- const MTPaccount_Authorizations &result) mutable {
- auto list = Data::ParseSessionsList(result);
- mainRequest(MTPaccount_GetWebAuthorizations(
- )).done([=, done = std::move(done), list = std::move(list)](
- const MTPaccount_WebAuthorizations &result) mutable {
- list.webList = Data::ParseWebSessionsList(result).webList;
- done(std::move(list));
- }).send();
- }).send();
- }
- void ApiWrap::requestMessages(
- const Data::DialogInfo &info,
- FnMut<bool(const Data::DialogInfo &)> start,
- Fn<bool(DownloadProgress)> progress,
- Fn<bool(Data::MessagesSlice&&)> slice,
- FnMut<void()> done) {
- Expects(_chatProcess == nullptr);
- Expects(_selfId.has_value());
- _chatProcess = std::make_unique<ChatProcess>();
- _chatProcess->context.selfPeerId = peerFromUser(*_selfId);
- _chatProcess->info = info;
- _chatProcess->start = std::move(start);
- _chatProcess->fileProgress = std::move(progress);
- _chatProcess->handleSlice = std::move(slice);
- _chatProcess->done = std::move(done);
- requestMessagesCount(0);
- }
- void ApiWrap::requestMessagesCount(int localSplitIndex) {
- Expects(_chatProcess != nullptr);
- Expects(localSplitIndex < _chatProcess->info.splits.size());
- requestChatMessages(
- _chatProcess->info.splits[localSplitIndex],
- 0, // offset_id
- 0, // add_offset
- 1, // limit
- [=](const MTPmessages_Messages &result) {
- Expects(_chatProcess != nullptr);
- const auto count = result.match(
- [](const MTPDmessages_messages &data) {
- return int(data.vmessages().v.size());
- }, [](const MTPDmessages_messagesSlice &data) {
- return data.vcount().v;
- }, [](const MTPDmessages_channelMessages &data) {
- return data.vcount().v;
- }, [](const MTPDmessages_messagesNotModified &data) {
- return -1;
- });
- if (count < 0) {
- error("Unexpected messagesNotModified received.");
- return;
- }
- const auto skipSplit = !Data::SingleMessageAfter(
- result,
- _settings->singlePeerFrom);
- if (skipSplit) {
- // No messages from the requested range, skip this split.
- messagesCountLoaded(localSplitIndex, 0);
- return;
- }
- checkFirstMessageDate(localSplitIndex, count);
- });
- }
- void ApiWrap::checkFirstMessageDate(int localSplitIndex, int count) {
- Expects(_chatProcess != nullptr);
- Expects(localSplitIndex < _chatProcess->info.splits.size());
- if (_settings->singlePeerTill <= 0) {
- messagesCountLoaded(localSplitIndex, count);
- return;
- }
- // Request first message in this split to check if its' date < till.
- requestChatMessages(
- _chatProcess->info.splits[localSplitIndex],
- 1, // offset_id
- -1, // add_offset
- 1, // limit
- [=](const MTPmessages_Messages &result) {
- Expects(_chatProcess != nullptr);
- const auto skipSplit = !Data::SingleMessageBefore(
- result,
- _settings->singlePeerTill);
- messagesCountLoaded(localSplitIndex, skipSplit ? 0 : count);
- });
- }
- void ApiWrap::messagesCountLoaded(int localSplitIndex, int count) {
- Expects(_chatProcess != nullptr);
- Expects(localSplitIndex < _chatProcess->info.splits.size());
- _chatProcess->info.messagesCountPerSplit[localSplitIndex] = count;
- if (localSplitIndex + 1 < _chatProcess->info.splits.size()) {
- requestMessagesCount(localSplitIndex + 1);
- } else if (_chatProcess->start(_chatProcess->info)) {
- requestMessagesSlice();
- }
- }
- void ApiWrap::finishExport(FnMut<void()> done) {
- const auto guard = gsl::finally([&] { _takeoutId = std::nullopt; });
- mainRequest(MTPaccount_FinishTakeoutSession(
- MTP_flags(MTPaccount_FinishTakeoutSession::Flag::f_success)
- )).done(std::move(done)).send();
- }
- void ApiWrap::skipFile(uint64 randomId) {
- if (!_fileProcess || _fileProcess->randomId != randomId) {
- return;
- }
- LOG(("Export Info: File skipped."));
- Assert(!_fileProcess->requests.empty());
- Assert(_fileProcess->requestId != 0);
- _mtp.request(base::take(_fileProcess->requestId)).cancel();
- base::take(_fileProcess)->done(QString());
- }
- void ApiWrap::cancelExportFast() {
- if (_takeoutId.has_value()) {
- const auto requestId = mainRequest(MTPaccount_FinishTakeoutSession(
- MTP_flags(0)
- )).send();
- _mtp.request(requestId).detach();
- }
- }
- void ApiWrap::requestSinglePeerDialog() {
- auto doneSinglePeer = [=](const auto &result) {
- appendSinglePeerDialogs(
- Data::ParseDialogsInfo(_settings->singlePeer, result));
- };
- const auto requestUser = [&](const MTPInputUser &data) {
- mainRequest(MTPusers_GetUsers(
- MTP_vector<MTPInputUser>(1, data)
- )).done(std::move(doneSinglePeer)).send();
- };
- _settings->singlePeer.match([&](const MTPDinputPeerUser &data) {
- requestUser(MTP_inputUser(data.vuser_id(), data.vaccess_hash()));
- }, [&](const MTPDinputPeerChat &data) {
- mainRequest(MTPmessages_GetChats(
- MTP_vector<MTPlong>(1, data.vchat_id())
- )).done(std::move(doneSinglePeer)).send();
- }, [&](const MTPDinputPeerChannel &data) {
- mainRequest(MTPchannels_GetChannels(
- MTP_vector<MTPInputChannel>(
- 1,
- MTP_inputChannel(data.vchannel_id(), data.vaccess_hash()))
- )).done(std::move(doneSinglePeer)).send();
- }, [&](const MTPDinputPeerSelf &data) {
- requestUser(MTP_inputUserSelf());
- }, [&](const MTPDinputPeerUserFromMessage &data) {
- Unexpected("From message peer in ApiWrap::requestSinglePeerDialog.");
- }, [&](const MTPDinputPeerChannelFromMessage &data) {
- Unexpected("From message peer in ApiWrap::requestSinglePeerDialog.");
- }, [](const MTPDinputPeerEmpty &data) {
- Unexpected("Empty peer in ApiWrap::requestSinglePeerDialog.");
- });
- }
- mtpRequestId ApiWrap::requestSinglePeerMigrated(
- const Data::DialogInfo &info) {
- const auto input = info.input.match([&](
- const MTPDinputPeerChannel & data) {
- return MTP_inputChannel(
- data.vchannel_id(),
- data.vaccess_hash());
- }, [](auto&&) -> MTPinputChannel {
- Unexpected("Peer type in a supergroup.");
- });
- return mainRequest(MTPchannels_GetFullChannel(
- input
- )).done([=](const MTPmessages_ChatFull &result) {
- auto info = result.match([&](
- const MTPDmessages_chatFull &data) {
- const auto migratedChatId = data.vfull_chat().match([&](
- const MTPDchannelFull &data) {
- return data.vmigrated_from_chat_id().value_or_empty();
- }, [](auto &&other) -> BareId {
- return 0;
- });
- return migratedChatId
- ? Data::ParseDialogsInfo(
- MTP_inputPeerChat(MTP_long(migratedChatId)),
- MTP_messages_chats(data.vchats()))
- : Data::DialogsInfo();
- });
- appendSinglePeerDialogs(std::move(info));
- }).send();
- }
- void ApiWrap::appendSinglePeerDialogs(Data::DialogsInfo &&info) {
- const auto isSupergroupType = [](Data::DialogInfo::Type type) {
- using Type = Data::DialogInfo::Type;
- return (type == Type::PrivateSupergroup)
- || (type == Type::PublicSupergroup);
- };
- const auto isChannelType = [](Data::DialogInfo::Type type) {
- using Type = Data::DialogInfo::Type;
- return (type == Type::PrivateChannel)
- || (type == Type::PublicChannel);
- };
- auto migratedRequestId = mtpRequestId(0);
- const auto last = _dialogsProcess->splitIndexPlusOne - 1;
- for (auto &info : info.chats) {
- if (isSupergroupType(info.type) && !migratedRequestId) {
- migratedRequestId = requestSinglePeerMigrated(info);
- continue;
- } else if (isChannelType(info.type)) {
- continue;
- }
- for (auto i = last; i != 0; --i) {
- info.splits.push_back(i - 1);
- info.messagesCountPerSplit.push_back(0);
- }
- }
- if (!migratedRequestId) {
- _dialogsProcess->processedCount += info.chats.size();
- }
- appendDialogsSlice(std::move(info));
- if (migratedRequestId
- || !_dialogsProcess->progress(_dialogsProcess->processedCount)) {
- return;
- }
- finishDialogsList();
- }
- void ApiWrap::requestDialogsSlice() {
- Expects(_dialogsProcess != nullptr);
- if (_settings->onlySinglePeer()) {
- requestSinglePeerDialog();
- return;
- }
- const auto splitIndex = _dialogsProcess->splitIndexPlusOne - 1;
- const auto hash = uint64(0);
- splitRequest(splitIndex, MTPmessages_GetDialogs(
- MTP_flags(0),
- MTPint(), // folder_id
- MTP_int(_dialogsProcess->offsetDate),
- MTP_int(_dialogsProcess->offsetId),
- _dialogsProcess->offsetPeer,
- MTP_int(kChatsSliceLimit),
- MTP_long(hash)
- )).done([=](const MTPmessages_Dialogs &result) {
- if (result.type() == mtpc_messages_dialogsNotModified) {
- error("Unexpected dialogsNotModified received.");
- return;
- }
- auto finished = result.match(
- [](const MTPDmessages_dialogs &data) {
- return true;
- }, [](const MTPDmessages_dialogsSlice &data) {
- return data.vdialogs().v.isEmpty();
- }, [](const MTPDmessages_dialogsNotModified &data) {
- return true;
- });
- auto info = Data::ParseDialogsInfo(result);
- _dialogsProcess->processedCount += info.chats.size();
- const auto last = info.chats.empty()
- ? Data::DialogInfo()
- : info.chats.back();
- appendDialogsSlice(std::move(info));
- if (!_dialogsProcess->progress(_dialogsProcess->processedCount)) {
- return;
- }
- if (!finished && last.topMessageDate > 0) {
- _dialogsProcess->offsetId = last.topMessageId;
- _dialogsProcess->offsetDate = last.topMessageDate;
- _dialogsProcess->offsetPeer = last.input;
- } else if (!useOnlyLastSplit()
- && --_dialogsProcess->splitIndexPlusOne > 0) {
- _dialogsProcess->offsetId = 0;
- _dialogsProcess->offsetDate = 0;
- _dialogsProcess->offsetPeer = MTP_inputPeerEmpty();
- } else {
- requestLeftChannelsIfNeeded();
- return;
- }
- requestDialogsSlice();
- }).send();
- }
- void ApiWrap::appendDialogsSlice(Data::DialogsInfo &&info) {
- Expects(_dialogsProcess != nullptr);
- Expects(_dialogsProcess->splitIndexPlusOne <= _splits.size());
- appendChatsSlice(
- *_dialogsProcess,
- _dialogsProcess->info.chats,
- std::move(info.chats),
- _dialogsProcess->splitIndexPlusOne - 1);
- }
- void ApiWrap::requestLeftChannelsIfNeeded() {
- if (_settings->types & Settings::Type::GroupsChannelsMask) {
- requestLeftChannelsList([=](int count) {
- Expects(_dialogsProcess != nullptr);
- return _dialogsProcess->progress(
- _dialogsProcess->processedCount + count);
- }, [=](Data::DialogsInfo &&result) {
- Expects(_dialogsProcess != nullptr);
- _dialogsProcess->info.left = std::move(result.left);
- finishDialogsList();
- });
- } else {
- finishDialogsList();
- }
- }
- void ApiWrap::finishDialogsList() {
- Expects(_dialogsProcess != nullptr);
- const auto process = base::take(_dialogsProcess);
- Data::FinalizeDialogsInfo(process->info, *_settings);
- process->done(std::move(process->info));
- }
- void ApiWrap::requestLeftChannelsSliceGeneric(FnMut<void()> done) {
- Expects(_leftChannelsProcess != nullptr);
- mainRequest(MTPchannels_GetLeftChannels(
- MTP_int(_leftChannelsProcess->offset)
- )).done([=, done = std::move(done)](
- const MTPmessages_Chats &result) mutable {
- Expects(_leftChannelsProcess != nullptr);
- appendLeftChannelsSlice(Data::ParseLeftChannelsInfo(result));
- const auto process = _leftChannelsProcess.get();
- process->offset += result.match(
- [](const auto &data) {
- return int(data.vchats().v.size());
- });
- process->fullCount = result.match(
- [](const MTPDmessages_chats &data) {
- return int(data.vchats().v.size());
- }, [](const MTPDmessages_chatsSlice &data) {
- return data.vcount().v;
- });
- process->finished = result.match(
- [](const MTPDmessages_chats &data) {
- return true;
- }, [](const MTPDmessages_chatsSlice &data) {
- return data.vchats().v.isEmpty();
- });
- if (process->progress) {
- if (!process->progress(process->info.left.size())) {
- return;
- }
- }
- done();
- }).send();
- }
- void ApiWrap::appendLeftChannelsSlice(Data::DialogsInfo &&info) {
- Expects(_leftChannelsProcess != nullptr);
- Expects(!_splits.empty());
- appendChatsSlice(
- *_leftChannelsProcess,
- _leftChannelsProcess->info.left,
- std::move(info.left),
- _splits.size() - 1);
- }
- void ApiWrap::appendChatsSlice(
- ChatsProcess &process,
- std::vector<Data::DialogInfo> &to,
- std::vector<Data::DialogInfo> &&from,
- int splitIndex) {
- Expects(_settings != nullptr);
- const auto types = _settings->types;
- const auto goodByTypes = [&](const Data::DialogInfo &info) {
- return ((types & SettingsFromDialogsType(info.type)) != 0);
- };
- auto filtered = ranges::views::all(
- from
- ) | ranges::views::filter([&](const Data::DialogInfo &info) {
- if (goodByTypes(info)) {
- return true;
- } else if (info.migratedToChannelId
- && (((types & Settings::Type::PublicGroups) != 0)
- || ((types & Settings::Type::PrivateGroups) != 0))) {
- return true;
- }
- return false;
- });
- to.reserve(to.size() + from.size());
- for (auto &info : filtered) {
- const auto nextIndex = to.size();
- if (info.migratedToChannelId) {
- const auto toPeerId = PeerId(info.migratedToChannelId);
- const auto i = process.indexByPeer.find(toPeerId);
- if (i != process.indexByPeer.end()
- && Data::AddMigrateFromSlice(
- to[i->second],
- info,
- splitIndex,
- int(_splits.size()))) {
- continue;
- } else if (!goodByTypes(info)) {
- continue;
- }
- }
- const auto &[i, ok] = process.indexByPeer.emplace(
- info.peerId,
- nextIndex);
- if (ok) {
- to.push_back(std::move(info));
- }
- to[i->second].splits.push_back(splitIndex);
- to[i->second].messagesCountPerSplit.push_back(0);
- }
- }
- void ApiWrap::requestMessagesSlice() {
- Expects(_chatProcess != nullptr);
- const auto count = _chatProcess->info.messagesCountPerSplit[
- _chatProcess->localSplitIndex];
- if (!count) {
- loadMessagesFiles({});
- return;
- }
- requestChatMessages(
- _chatProcess->info.splits[_chatProcess->localSplitIndex],
- _chatProcess->largestIdPlusOne,
- -kMessagesSliceLimit,
- kMessagesSliceLimit,
- [=](const MTPmessages_Messages &result) {
- Expects(_chatProcess != nullptr);
- result.match([&](const MTPDmessages_messagesNotModified &data) {
- error("Unexpected messagesNotModified received.");
- }, [&](const auto &data) {
- if constexpr (MTPDmessages_messages::Is<decltype(data)>()) {
- _chatProcess->lastSlice = true;
- }
- loadMessagesFiles(Data::ParseMessagesSlice(
- _chatProcess->context,
- data.vmessages(),
- data.vusers(),
- data.vchats(),
- _chatProcess->info.relativePath));
- });
- });
- }
- void ApiWrap::requestChatMessages(
- int splitIndex,
- int offsetId,
- int addOffset,
- int limit,
- FnMut<void(MTPmessages_Messages&&)> done) {
- Expects(_chatProcess != nullptr);
- _chatProcess->requestDone = std::move(done);
- const auto doneHandler = [=](MTPmessages_Messages &&result) {
- Expects(_chatProcess != nullptr);
- base::take(_chatProcess->requestDone)(std::move(result));
- };
- const auto splitsCount = int(_splits.size());
- const auto realPeerInput = (splitIndex >= 0)
- ? _chatProcess->info.input
- : _chatProcess->info.migratedFromInput;
- const auto realSplitIndex = (splitIndex >= 0)
- ? splitIndex
- : (splitsCount + splitIndex);
- if (_chatProcess->info.onlyMyMessages) {
- splitRequest(realSplitIndex, MTPmessages_Search(
- MTP_flags(MTPmessages_Search::Flag::f_from_id),
- realPeerInput,
- MTP_string(), // query
- MTP_inputPeerSelf(),
- MTPInputPeer(), // saved_peer_id
- MTPVector<MTPReaction>(), // saved_reaction
- MTPint(), // top_msg_id
- MTP_inputMessagesFilterEmpty(),
- MTP_int(0), // min_date
- MTP_int(0), // max_date
- MTP_int(offsetId),
- MTP_int(addOffset),
- MTP_int(limit),
- MTP_int(0), // max_id
- MTP_int(0), // min_id
- MTP_long(0) // hash
- )).done(doneHandler).send();
- } else {
- splitRequest(realSplitIndex, MTPmessages_GetHistory(
- realPeerInput,
- MTP_int(offsetId),
- MTP_int(0), // offset_date
- MTP_int(addOffset),
- MTP_int(limit),
- MTP_int(0), // max_id
- MTP_int(0), // min_id
- MTP_long(0) // hash
- )).fail([=](const MTP::Error &error) {
- Expects(_chatProcess != nullptr);
- if (error.type() == u"CHANNEL_PRIVATE"_q) {
- if (realPeerInput.type() == mtpc_inputPeerChannel
- && !_chatProcess->info.onlyMyMessages) {
- // Perhaps we just left / were kicked from channel.
- // Just switch to only my messages.
- _chatProcess->info.onlyMyMessages = true;
- requestChatMessages(
- splitIndex,
- offsetId,
- addOffset,
- limit,
- base::take(_chatProcess->requestDone));
- return true;
- }
- }
- return false;
- }).done(doneHandler).send();
- }
- }
- void ApiWrap::loadMessagesFiles(Data::MessagesSlice &&slice) {
- Expects(_chatProcess != nullptr);
- Expects(!_chatProcess->slice.has_value());
- collectMessagesCustomEmoji(slice);
- if (slice.list.empty()) {
- _chatProcess->lastSlice = true;
- }
- _chatProcess->slice = std::move(slice);
- _chatProcess->fileIndex = 0;
- resolveCustomEmoji();
- }
- void ApiWrap::collectMessagesCustomEmoji(const Data::MessagesSlice &slice) {
- for (const auto &message : slice.list) {
- for (const auto &part : message.text) {
- if (part.type == Data::TextPart::Type::CustomEmoji) {
- if (const auto id = part.additional.toULongLong()) {
- if (!_resolvedCustomEmoji.contains(id)) {
- _unresolvedCustomEmoji.emplace(id);
- }
- }
- }
- }
- for (const auto &reaction : message.reactions) {
- if (reaction.type == Data::Reaction::Type::CustomEmoji) {
- if (const auto id = reaction.documentId.toULongLong()) {
- if (!_resolvedCustomEmoji.contains(id)) {
- _unresolvedCustomEmoji.emplace(id);
- }
- }
- }
- }
- }
- }
- void ApiWrap::resolveCustomEmoji() {
- if (_unresolvedCustomEmoji.empty()) {
- loadNextMessageFile();
- return;
- }
- const auto count = std::min(
- int(_unresolvedCustomEmoji.size()),
- kMaxEmojiPerRequest);
- auto v = QVector<MTPlong>();
- v.reserve(count);
- const auto till = end(_unresolvedCustomEmoji);
- const auto from = end(_unresolvedCustomEmoji) - count;
- for (auto i = from; i != till; ++i) {
- v.push_back(MTP_long(*i));
- }
- _unresolvedCustomEmoji.erase(from, till);
- const auto finalize = [=] {
- for (const auto &id : v) {
- if (_resolvedCustomEmoji.contains(id.v)) {
- continue;
- }
- _resolvedCustomEmoji.emplace(
- id.v,
- Data::Document{
- .file = {
- .skipReason = Data::File::SkipReason::Unavailable,
- },
- });
- }
- resolveCustomEmoji();
- };
- mainRequest(MTPmessages_GetCustomEmojiDocuments(
- MTP_vector<MTPlong>(v)
- )).fail([=](const MTP::Error &error) {
- LOG(("Export Error: Failed to get documents for emoji."));
- finalize();
- return true;
- }).done([=](const MTPVector<MTPDocument> &result) {
- for (const auto &entry : result.v) {
- auto document = Data::ParseDocument(
- _chatProcess->context,
- entry,
- _chatProcess->info.relativePath,
- TimeId());
- _resolvedCustomEmoji.emplace(document.id, std::move(document));
- }
- finalize();
- }).send();
- }
- Data::Message *ApiWrap::currentFileMessage() const {
- Expects(_chatProcess != nullptr);
- Expects(_chatProcess->slice.has_value());
- return &_chatProcess->slice->list[_chatProcess->fileIndex];
- }
- Data::FileOrigin ApiWrap::currentFileMessageOrigin() const {
- Expects(_chatProcess != nullptr);
- Expects(_chatProcess->slice.has_value());
- const auto splitIndex = _chatProcess->info.splits[
- _chatProcess->localSplitIndex];
- auto result = Data::FileOrigin();
- result.messageId = currentFileMessage()->id;
- result.split = (splitIndex >= 0)
- ? splitIndex
- : (int(_splits.size()) + splitIndex);
- result.peer = (splitIndex >= 0)
- ? _chatProcess->info.input
- : _chatProcess->info.migratedFromInput;
- return result;
- }
- std::optional<QByteArray> ApiWrap::getCustomEmoji(QByteArray &data) {
- if (const auto id = data.toULongLong()) {
- const auto i = _resolvedCustomEmoji.find(id);
- if (i == end(_resolvedCustomEmoji)) {
- return Data::TextPart::UnavailableEmoji();
- }
- auto &file = i->second.file;
- const auto fileProgress = [=](FileProgress value) {
- return loadMessageEmojiProgress(value);
- };
- const auto ready = processFileLoad(
- file,
- { .customEmojiId = id },
- fileProgress,
- [=](const QString &path) { loadMessageEmojiDone(id, path); });
- if (!ready) {
- return std::nullopt;
- }
- using SkipReason = Data::File::SkipReason;
- if (file.skipReason == SkipReason::Unavailable) {
- return Data::TextPart::UnavailableEmoji();
- } else if (file.skipReason == SkipReason::FileType
- || file.skipReason == SkipReason::FileSize) {
- return QByteArray();
- } else {
- return file.relativePath.toUtf8();
- }
- }
- return data;
- }
- bool ApiWrap::messageCustomEmojiReady(Data::Message &message) {
- for (auto &part : message.text) {
- if (part.type == Data::TextPart::Type::CustomEmoji) {
- auto data = getCustomEmoji(part.additional);
- if (data.has_value()) {
- part.additional = base::take(*data);
- } else {
- return false;
- }
- }
- }
- for (auto &reaction : message.reactions) {
- if (reaction.type == Data::Reaction::Type::CustomEmoji) {
- auto data = getCustomEmoji(reaction.documentId);
- if (data.has_value()) {
- reaction.documentId = base::take(*data);
- } else {
- return false;
- }
- }
- }
- return true;
- }
- void ApiWrap::loadNextMessageFile() {
- Expects(_chatProcess != nullptr);
- Expects(_chatProcess->slice.has_value());
- for (auto &list = _chatProcess->slice->list
- ; _chatProcess->fileIndex < list.size()
- ; ++_chatProcess->fileIndex) {
- auto &message = list[_chatProcess->fileIndex];
- if (Data::SkipMessageByDate(message, *_settings)) {
- continue;
- }
- if (!messageCustomEmojiReady(message)) {
- return;
- }
- const auto fileProgress = [=](FileProgress value) {
- return loadMessageFileProgress(value);
- };
- const auto ready = processFileLoad(
- list[_chatProcess->fileIndex].file(),
- currentFileMessageOrigin(),
- fileProgress,
- [=](const QString &path) { loadMessageFileDone(path); },
- currentFileMessage());
- if (!ready) {
- return;
- }
- const auto thumbProgress = [=](FileProgress value) {
- return loadMessageThumbProgress(value);
- };
- const auto thumbReady = processFileLoad(
- list[_chatProcess->fileIndex].thumb().file,
- currentFileMessageOrigin(),
- thumbProgress,
- [=](const QString &path) { loadMessageThumbDone(path); },
- currentFileMessage());
- if (!thumbReady) {
- return;
- }
- }
- finishMessagesSlice();
- }
- void ApiWrap::finishMessagesSlice() {
- Expects(_chatProcess != nullptr);
- Expects(_chatProcess->slice.has_value());
- auto slice = *base::take(_chatProcess->slice);
- if (!slice.list.empty()) {
- _chatProcess->largestIdPlusOne = slice.list.back().id + 1;
- const auto splitIndex = _chatProcess->info.splits[
- _chatProcess->localSplitIndex];
- if (splitIndex < 0) {
- slice = AdjustMigrateMessageIds(std::move(slice));
- }
- if (!_chatProcess->handleSlice(std::move(slice))) {
- return;
- }
- }
- if (_chatProcess->lastSlice
- && (++_chatProcess->localSplitIndex
- < _chatProcess->info.splits.size())) {
- _chatProcess->lastSlice = false;
- _chatProcess->largestIdPlusOne = 1;
- }
- if (!_chatProcess->lastSlice) {
- requestMessagesSlice();
- } else {
- finishMessages();
- }
- }
- bool ApiWrap::loadMessageFileProgress(FileProgress progress) {
- Expects(_fileProcess != nullptr);
- Expects(_chatProcess != nullptr);
- Expects(_chatProcess->slice.has_value());
- Expects((_chatProcess->fileIndex >= 0)
- && (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
- return _chatProcess->fileProgress(DownloadProgress{
- .randomId = _fileProcess->randomId,
- .path = _fileProcess->relativePath,
- .itemIndex = _chatProcess->fileIndex,
- .ready = progress.ready,
- .total = progress.total });
- }
- void ApiWrap::loadMessageFileDone(const QString &relativePath) {
- Expects(_chatProcess != nullptr);
- Expects(_chatProcess->slice.has_value());
- Expects((_chatProcess->fileIndex >= 0)
- && (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
- const auto index = _chatProcess->fileIndex;
- auto &file = _chatProcess->slice->list[index].file();
- file.relativePath = relativePath;
- if (relativePath.isEmpty()) {
- file.skipReason = Data::File::SkipReason::Unavailable;
- }
- loadNextMessageFile();
- }
- bool ApiWrap::loadMessageThumbProgress(FileProgress progress) {
- return loadMessageFileProgress(progress);
- }
- void ApiWrap::loadMessageThumbDone(const QString &relativePath) {
- Expects(_chatProcess != nullptr);
- Expects(_chatProcess->slice.has_value());
- Expects((_chatProcess->fileIndex >= 0)
- && (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
- const auto index = _chatProcess->fileIndex;
- auto &file = _chatProcess->slice->list[index].thumb().file;
- file.relativePath = relativePath;
- if (relativePath.isEmpty()) {
- file.skipReason = Data::File::SkipReason::Unavailable;
- }
- loadNextMessageFile();
- }
- bool ApiWrap::loadMessageEmojiProgress(FileProgress progress) {
- return loadMessageFileProgress(progress);
- }
- void ApiWrap::loadMessageEmojiDone(uint64 id, const QString &relativePath) {
- const auto i = _resolvedCustomEmoji.find(id);
- if (i != end(_resolvedCustomEmoji)) {
- i->second.file.relativePath = relativePath;
- if (relativePath.isEmpty()) {
- i->second.file.skipReason = Data::File::SkipReason::Unavailable;
- }
- }
- loadNextMessageFile();
- }
- void ApiWrap::finishMessages() {
- Expects(_chatProcess != nullptr);
- Expects(!_chatProcess->slice.has_value());
- const auto process = base::take(_chatProcess);
- process->done();
- }
- bool ApiWrap::processFileLoad(
- Data::File &file,
- const Data::FileOrigin &origin,
- Fn<bool(FileProgress)> progress,
- FnMut<void(QString)> done,
- Data::Message *message,
- Data::Story *story) {
- using SkipReason = Data::File::SkipReason;
- if (!file.relativePath.isEmpty()
- || file.skipReason != SkipReason::None) {
- return true;
- } else if (!file.location && file.content.isEmpty()) {
- file.skipReason = SkipReason::Unavailable;
- return true;
- } else if (writePreloadedFile(file, origin)) {
- return !file.relativePath.isEmpty();
- }
- using Type = MediaSettings::Type;
- const auto media = message
- ? &message->media
- : story
- ? &story->media
- : nullptr;
- const auto type = media ? v::match(media->content, [&](
- const Data::Document &data) {
- if (data.isSticker) {
- return Type::Sticker;
- } else if (data.isVideoMessage) {
- return Type::VideoMessage;
- } else if (data.isVoiceMessage) {
- return Type::VoiceMessage;
- } else if (data.isAnimated) {
- return Type::GIF;
- } else if (data.isVideoFile) {
- return Type::Video;
- } else {
- return Type::File;
- }
- }, [](const auto &data) {
- return Type::Photo;
- }) : Type(0);
- const auto fullSize = message
- ? message->file().size
- : story
- ? story->file().size
- : file.size;
- if (message && Data::SkipMessageByDate(*message, *_settings)) {
- file.skipReason = SkipReason::DateLimits;
- return true;
- } else if (!story && (_settings->media.types & type) != type) {
- file.skipReason = SkipReason::FileType;
- return true;
- } else if (!story && fullSize >= _settings->media.sizeLimit) {
- // Don't load thumbs for large files that we skip.
- file.skipReason = SkipReason::FileSize;
- return true;
- }
- loadFile(file, origin, std::move(progress), std::move(done));
- return false;
- }
- bool ApiWrap::writePreloadedFile(
- Data::File &file,
- const Data::FileOrigin &origin) {
- Expects(_settings != nullptr);
- using namespace Output;
- if (const auto path = _fileCache->find(file.location)) {
- file.relativePath = *path;
- return true;
- } else if (!file.content.isEmpty()) {
- const auto process = prepareFileProcess(file, origin);
- if (const auto result = process->file.writeBlock(file.content)) {
- file.relativePath = process->relativePath;
- _fileCache->save(file.location, file.relativePath);
- } else {
- ioError(result);
- }
- return true;
- }
- return false;
- }
- void ApiWrap::loadFile(
- const Data::File &file,
- const Data::FileOrigin &origin,
- Fn<bool(FileProgress)> progress,
- FnMut<void(QString)> done) {
- Expects(_fileProcess == nullptr);
- Expects(file.location.dcId != 0
- || file.location.data.type() == mtpc_inputTakeoutFileLocation);
- _fileProcess = prepareFileProcess(file, origin);
- _fileProcess->progress = std::move(progress);
- _fileProcess->done = std::move(done);
- if (_fileProcess->progress) {
- const auto progress = FileProgress{
- _fileProcess->file.size(),
- _fileProcess->size
- };
- if (!_fileProcess->progress(progress)) {
- return;
- }
- }
- loadFilePart();
- Ensures(_fileProcess->requestId != 0);
- }
- auto ApiWrap::prepareFileProcess(
- const Data::File &file,
- const Data::FileOrigin &origin) const
- -> std::unique_ptr<FileProcess> {
- Expects(_settings != nullptr);
- const auto relativePath = Output::File::PrepareRelativePath(
- _settings->path,
- file.suggestedPath);
- auto result = std::make_unique<FileProcess>(
- _settings->path + relativePath,
- _stats);
- result->relativePath = relativePath;
- result->location = file.location;
- result->size = file.size;
- result->origin = origin;
- result->randomId = base::RandomValue<uint64>();
- return result;
- }
- void ApiWrap::loadFilePart() {
- if (!_fileProcess
- || _fileProcess->requestId
- || _fileProcess->requests.size() >= kFileRequestsCount
- || (_fileProcess->size > 0
- && _fileProcess->offset >= _fileProcess->size)) {
- return;
- }
- const auto offset = _fileProcess->offset;
- _fileProcess->requests.push_back({ offset });
- _fileProcess->requestId = fileRequest(
- _fileProcess->location,
- _fileProcess->offset
- ).done([=](const MTPupload_File &result) {
- _fileProcess->requestId = 0;
- filePartDone(offset, result);
- }).send();
- _fileProcess->offset += kFileChunkSize;
- if (_fileProcess->size > 0
- && _fileProcess->requests.size() < kFileRequestsCount) {
- // Only one request at a time supported right now.
- //const auto runner = _runner;
- //crl::on_main([=] {
- // QTimer::singleShot(kFileNextRequestDelay, [=] {
- // runner([=] {
- // loadFilePart();
- // });
- // });
- //});
- }
- }
- void ApiWrap::filePartDone(int64 offset, const MTPupload_File &result) {
- Expects(_fileProcess != nullptr);
- Expects(!_fileProcess->requests.empty());
- if (result.type() == mtpc_upload_fileCdnRedirect) {
- error("Cdn redirect is not supported.");
- return;
- }
- const auto &data = result.c_upload_file();
- if (data.vbytes().v.isEmpty()) {
- if (_fileProcess->size > 0) {
- error("Empty bytes received in file part.");
- return;
- }
- const auto result = _fileProcess->file.writeBlock({});
- if (!result) {
- ioError(result);
- return;
- }
- } else {
- using Request = FileProcess::Request;
- auto &requests = _fileProcess->requests;
- const auto i = ranges::find(
- requests,
- offset,
- [](const Request &request) { return request.offset; });
- Assert(i != end(requests));
- i->bytes = data.vbytes().v;
- auto &file = _fileProcess->file;
- while (!requests.empty() && !requests.front().bytes.isEmpty()) {
- const auto &bytes = requests.front().bytes;
- if (const auto result = file.writeBlock(bytes); !result) {
- ioError(result);
- return;
- }
- requests.pop_front();
- }
- if (_fileProcess->progress) {
- _fileProcess->progress(FileProgress{
- file.size(),
- _fileProcess->size });
- }
- if (!requests.empty()
- || !_fileProcess->size
- || _fileProcess->size > _fileProcess->offset) {
- loadFilePart();
- return;
- }
- }
- auto process = base::take(_fileProcess);
- const auto relativePath = process->relativePath;
- _fileCache->save(process->location, relativePath);
- process->done(process->relativePath);
- }
- void ApiWrap::filePartRefreshReference(int64 offset) {
- Expects(_fileProcess != nullptr);
- Expects(_fileProcess->requestId == 0);
- const auto &origin = _fileProcess->origin;
- if (origin.storyId) {
- _fileProcess->requestId = mainRequest(MTPstories_GetStoriesByID(
- MTP_inputPeerSelf(),
- MTP_vector<MTPint>(1, MTP_int(origin.storyId))
- )).fail([=](const MTP::Error &error) {
- _fileProcess->requestId = 0;
- filePartUnavailable();
- return true;
- }).done([=](const MTPstories_Stories &result) {
- _fileProcess->requestId = 0;
- filePartExtractReference(offset, result);
- }).send();
- return;
- } else if (!origin.messageId) {
- error("FILE_REFERENCE error for non-message file.");
- return;
- }
- if (origin.peer.type() == mtpc_inputPeerChannel
- || origin.peer.type() == mtpc_inputPeerChannelFromMessage) {
- const auto channel = (origin.peer.type() == mtpc_inputPeerChannel)
- ? MTP_inputChannel(
- origin.peer.c_inputPeerChannel().vchannel_id(),
- origin.peer.c_inputPeerChannel().vaccess_hash())
- : MTP_inputChannelFromMessage(
- origin.peer.c_inputPeerChannelFromMessage().vpeer(),
- origin.peer.c_inputPeerChannelFromMessage().vmsg_id(),
- origin.peer.c_inputPeerChannelFromMessage().vchannel_id());
- _fileProcess->requestId = mainRequest(MTPchannels_GetMessages(
- channel,
- MTP_vector<MTPInputMessage>(
- 1,
- MTP_inputMessageID(MTP_int(origin.messageId)))
- )).fail([=](const MTP::Error &error) {
- _fileProcess->requestId = 0;
- filePartUnavailable();
- return true;
- }).done([=](const MTPmessages_Messages &result) {
- _fileProcess->requestId = 0;
- filePartExtractReference(offset, result);
- }).send();
- } else {
- _fileProcess->requestId = splitRequest(
- origin.split,
- MTPmessages_GetMessages(
- MTP_vector<MTPInputMessage>(
- 1,
- MTP_inputMessageID(MTP_int(origin.messageId)))
- )
- ).fail([=](const MTP::Error &error) {
- _fileProcess->requestId = 0;
- filePartUnavailable();
- return true;
- }).done([=](const MTPmessages_Messages &result) {
- _fileProcess->requestId = 0;
- filePartExtractReference(offset, result);
- }).send();
- }
- }
- void ApiWrap::filePartExtractReference(
- int64 offset,
- const MTPmessages_Messages &result) {
- Expects(_fileProcess != nullptr);
- Expects(_fileProcess->requestId == 0);
- result.match([&](const MTPDmessages_messagesNotModified &data) {
- error("Unexpected messagesNotModified received.");
- }, [&](const auto &data) {
- Expects(_selfId.has_value());
- auto context = Data::ParseMediaContext();
- context.selfPeerId = peerFromUser(*_selfId);
- const auto messages = Data::ParseMessagesSlice(
- context,
- data.vmessages(),
- data.vusers(),
- data.vchats(),
- _chatProcess->info.relativePath);
- for (const auto &message : messages.list) {
- if (message.id == _fileProcess->origin.messageId) {
- const auto refresh1 = Data::RefreshFileReference(
- _fileProcess->location,
- message.file().location);
- const auto refresh2 = Data::RefreshFileReference(
- _fileProcess->location,
- message.thumb().file.location);
- if (refresh1 || refresh2) {
- _fileProcess->requestId = fileRequest(
- _fileProcess->location,
- offset
- ).done([=](const MTPupload_File &result) {
- _fileProcess->requestId = 0;
- filePartDone(offset, result);
- }).send();
- return;
- }
- }
- }
- filePartUnavailable();
- });
- }
- void ApiWrap::filePartExtractReference(
- int64 offset,
- const MTPstories_Stories &result) {
- Expects(_fileProcess != nullptr);
- Expects(_fileProcess->requestId == 0);
- const auto stories = Data::ParseStoriesSlice(
- result.data().vstories(),
- 0);
- for (const auto &story : stories.list) {
- if (story.id == _fileProcess->origin.storyId) {
- const auto refresh1 = Data::RefreshFileReference(
- _fileProcess->location,
- story.file().location);
- const auto refresh2 = Data::RefreshFileReference(
- _fileProcess->location,
- story.thumb().file.location);
- if (refresh1 || refresh2) {
- _fileProcess->requestId = fileRequest(
- _fileProcess->location,
- offset
- ).done([=](const MTPupload_File &result) {
- _fileProcess->requestId = 0;
- filePartDone(offset, result);
- }).send();
- return;
- }
- }
- }
- filePartUnavailable();
- }
- void ApiWrap::filePartUnavailable() {
- Expects(_fileProcess != nullptr);
- Expects(!_fileProcess->requests.empty());
- LOG(("Export Error: File unavailable."));
- base::take(_fileProcess)->done(QString());
- }
- void ApiWrap::error(const MTP::Error &error) {
- _errors.fire_copy(error);
- }
- void ApiWrap::error(const QString &text) {
- error(MTP::Error(
- MTP_rpc_error(MTP_int(0), MTP_string("API_ERROR: " + text))));
- }
- void ApiWrap::ioError(const Output::Result &result) {
- _ioErrors.fire_copy(result);
- }
- ApiWrap::~ApiWrap() = default;
- } // namespace Export
|