Added initial ability to export topic history.

This commit is contained in:
23rd
2025-11-25 06:19:34 +03:00
parent 4eee00d95e
commit 57411b962f
12 changed files with 608 additions and 11 deletions
@@ -37,6 +37,12 @@ public:
crl::weak_on_queue<ControllerObject> weak,
QPointer<MTP::Instance> mtproto,
const MTPInputPeer &peer);
ControllerObject(
crl::weak_on_queue<ControllerObject> weak,
QPointer<MTP::Instance> mtproto,
const MTPInputPeer &peer,
int32 topicRootId,
uint64 peerId);
rpl::producer<State> state() const;
@@ -82,6 +88,7 @@ private:
void exportOtherData();
void exportDialogs();
void exportNextDialog();
void exportTopic();
template <typename Callback = const decltype(kNullStateCallback) &>
ProcessingState prepareState(
@@ -97,6 +104,7 @@ private:
ProcessingState stateSessions() const;
ProcessingState stateOtherData() const;
ProcessingState stateDialogs(const DownloadProgress &progress) const;
ProcessingState stateTopic(const DownloadProgress &progress) const;
void fillMessagesState(
ProcessingState &result,
const Data::DialogsInfo &info,
@@ -139,6 +147,9 @@ private:
std::vector<Step> _steps;
int _stepIndex = -1;
int32 _topicRootId = 0;
uint64 _topicPeerId = 0;
rpl::lifetime _lifetime;
};
@@ -167,6 +178,34 @@ ControllerObject::ControllerObject(
setState(std::move(state));
}
ControllerObject::ControllerObject(
crl::weak_on_queue<ControllerObject> weak,
QPointer<MTP::Instance> mtproto,
const MTPInputPeer &peer,
int32 topicRootId,
uint64 peerId)
: _api(mtproto, weak.runner())
, _state(PasswordCheckState{})
, _topicRootId(topicRootId)
, _topicPeerId(peerId) {
_api.errors(
) | rpl::start_with_next([=](const MTP::Error &error) {
setState(ApiErrorState{ error });
}, _lifetime);
_api.ioErrors(
) | rpl::start_with_next([=](const Output::Result &result) {
ioCatchError(result);
}, _lifetime);
//requestPasswordState();
auto state = PasswordCheckState();
state.checked = false;
state.requesting = false;
state.singlePeer = peer;
setState(std::move(state));
}
rpl::producer<State> ControllerObject::state() const {
return rpl::single(
_state
@@ -257,6 +296,8 @@ void ControllerObject::startExport(
}
_settings = NormalizeSettings(settings);
_environment = environment;
_settings.singleTopicRootId = _topicRootId;
_settings.singleTopicPeerId = _topicPeerId;
_settings.path = Output::NormalizePath(_settings);
_writer = Output::CreateWriter(_settings.format);
@@ -274,6 +315,10 @@ void ControllerObject::skipFile(uint64 randomId) {
void ControllerObject::fillExportSteps() {
using Type = Settings::Type;
_steps.push_back(Step::Initializing);
if (_settings.onlySingleTopic()) {
_steps.push_back(Step::Topic);
return;
}
if (_settings.types & Type::AnyChatsMask) {
_steps.push_back(Step::DialogsList);
}
@@ -340,6 +385,9 @@ void ControllerObject::fillSubstepsInSteps(const ApiWrap::StartInfo &info) {
if (_settings.types & Settings::Type::AnyChatsMask) {
push(Step::Dialogs, info.dialogsCount);
}
if (_settings.onlySingleTopic()) {
push(Step::Topic, 1);
}
_substepsInStep = std::move(result);
_substepsTotal = ranges::accumulate(_substepsInStep, 0);
}
@@ -372,6 +420,7 @@ void ControllerObject::exportNext() {
case Step::Sessions: return exportSessions();
case Step::OtherData: return exportOtherData();
case Step::Dialogs: return exportDialogs();
case Step::Topic: return exportTopic();
}
Unexpected("Step in ControllerObject::exportNext.");
}
@@ -708,6 +757,70 @@ int ControllerObject::substepsInStep(Step step) const {
return _substepsInStep[static_cast<int>(step)];
}
void ControllerObject::exportTopic() {
auto topicInfo = Data::DialogInfo();
topicInfo.type = Data::DialogInfo::Type::PublicSupergroup;
topicInfo.name = "Topic";
topicInfo.peerId = PeerId(_topicPeerId);
topicInfo.relativePath = QString();
if (ioCatchError(_writer->writeDialogStart(topicInfo))) {
return;
}
_api.requestTopicMessages(
PeerId(_topicPeerId),
_settings.singlePeer,
_topicRootId,
[=](int count) {
_messagesWritten = 0;
_messagesCount = count;
setState(stateTopic(DownloadProgress()));
return true;
},
[=](DownloadProgress progress) {
setState(stateTopic(progress));
return true;
},
[=](Data::MessagesSlice &&slice) {
if (ioCatchError(_writer->writeDialogSlice(slice))) {
return false;
}
_messagesWritten += slice.list.size();
setState(stateTopic(DownloadProgress()));
return true;
},
[=] {
if (ioCatchError(_writer->writeDialogEnd())) {
return;
}
if (ioCatchError(_writer->finish())) {
return;
}
_api.finishExport([=] {
setFinishedState();
});
});
}
ProcessingState ControllerObject::stateTopic(
const DownloadProgress &progress) const {
return prepareState(Step::Topic, [&](ProcessingState &result) {
result.entityType = ProcessingState::EntityType::Topic;
result.entityIndex = 0;
result.entityCount = 1;
result.itemIndex = _messagesWritten + progress.itemIndex;
result.itemCount = std::max(_messagesCount, result.itemIndex);
result.bytesRandomId = progress.randomId;
if (!progress.path.isEmpty()) {
const auto last = progress.path.lastIndexOf('/');
result.bytesName = progress.path.mid(last + 1);
}
result.bytesLoaded = progress.ready;
result.bytesCount = progress.total;
});
}
void ControllerObject::setFinishedState() {
setState(FinishedState{
_writer->mainFilePath(),
@@ -721,6 +834,18 @@ Controller::Controller(
: _wrapped(std::move(mtproto), peer) {
}
Controller::Controller(
QPointer<MTP::Instance> mtproto,
const MTPInputPeer &peer,
int32 topicRootId,
uint64 peerId)
: _wrapped(
std::move(mtproto),
peer,
static_cast<int32>(topicRootId),
static_cast<uint64>(peerId)) {
}
rpl::producer<State> Controller::state() const {
return _wrapped.producer_on_main([=](const Implementation &unwrapped) {
return unwrapped.state();