mirror of
https://github.com/kotatogram/kotatogram-desktop
synced 2025-08-31 14:45:14 +00:00
Add basic HTML export.
This commit is contained in:
@@ -8,18 +8,476 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "export/output/export_output_abstract.h"
|
||||
|
||||
#include "export/output/export_output_text.h"
|
||||
#include "export/output/export_output_html.h"
|
||||
#include "export/output/export_output_json.h"
|
||||
#include "export/output/export_output_stats.h"
|
||||
#include "export/output/export_output_result.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QDate>
|
||||
|
||||
namespace Export {
|
||||
namespace Output {
|
||||
|
||||
QString NormalizePath(const QString &source) {
|
||||
QDir folder(source);
|
||||
const auto path = folder.absolutePath();
|
||||
auto result = path.endsWith('/') ? path : (path + '/');
|
||||
if (!folder.exists()) {
|
||||
return result;
|
||||
}
|
||||
const auto mode = QDir::AllEntries | QDir::NoDotAndDotDot;
|
||||
const auto list = folder.entryInfoList(mode);
|
||||
if (list.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
const auto date = QDate::currentDate();
|
||||
const auto base = QString("DataExport_%1_%2_%3"
|
||||
).arg(date.day(), 2, 10, QChar('0')
|
||||
).arg(date.month(), 2, 10, QChar('0')
|
||||
).arg(date.year());
|
||||
const auto add = [&](int i) {
|
||||
return base + (i ? " (" + QString::number(i) + ')' : QString());
|
||||
};
|
||||
auto index = 0;
|
||||
while (QDir(result + add(index)).exists()) {
|
||||
++index;
|
||||
}
|
||||
result += add(index) + '/';
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractWriter> CreateWriter(Format format) {
|
||||
switch (format) {
|
||||
case Format::Html: return std::make_unique<HtmlWriter>();
|
||||
case Format::Text: return std::make_unique<TextWriter>();
|
||||
case Format::Json: return std::make_unique<JsonWriter>();
|
||||
}
|
||||
Unexpected("Format in Export::Output::CreateWriter.");
|
||||
}
|
||||
|
||||
Stats AbstractWriter::produceTestExample(const QString &path) {
|
||||
auto result = Stats();
|
||||
const auto folder = QDir(path).absolutePath();
|
||||
auto settings = Settings();
|
||||
settings.format = format();
|
||||
settings.path = (folder.endsWith('/') ? folder : (folder + '/'))
|
||||
+ "ExportExample/";
|
||||
settings.internalLinksDomain = "https://t.me/";
|
||||
settings.types = Settings::Type::AllMask;
|
||||
settings.fullChats = Settings::Type::AllMask
|
||||
& ~(Settings::Type::PublicChannels | Settings::Type::PublicGroups);
|
||||
settings.media.types = MediaSettings::Type::AllMask;
|
||||
settings.media.sizeLimit = 1024 * 1024;
|
||||
|
||||
const auto check = [](Result result) {
|
||||
Assert(result.isSuccess());
|
||||
};
|
||||
|
||||
check(start(settings, &result));
|
||||
|
||||
const auto counter = [&] {
|
||||
static auto GlobalCounter = 0;
|
||||
return ++GlobalCounter;
|
||||
};
|
||||
const auto date = [&] {
|
||||
return time(nullptr) - 86400 + counter();
|
||||
};
|
||||
const auto prevdate = [&] {
|
||||
return date() - 86400;
|
||||
};
|
||||
|
||||
auto personal = Data::PersonalInfo();
|
||||
personal.bio = "Nice text about me.";
|
||||
personal.user.info.firstName = "John";
|
||||
personal.user.info.lastName = "Preston";
|
||||
personal.user.info.phoneNumber = "447400000000";
|
||||
personal.user.info.date = date();
|
||||
personal.user.username = "preston";
|
||||
personal.user.info.userId = counter();
|
||||
personal.user.isBot = false;
|
||||
personal.user.isSelf = true;
|
||||
check(writePersonal(personal));
|
||||
|
||||
const auto generatePhoto = [&] {
|
||||
static auto index = 0;
|
||||
auto result = Data::Photo();
|
||||
result.date = date();
|
||||
result.id = counter();
|
||||
result.image.width = 512;
|
||||
result.image.height = 512;
|
||||
result.image.file.relativePath = "Files/Photo_"
|
||||
+ QString::number(++index)
|
||||
+ ".jpg";
|
||||
return result;
|
||||
};
|
||||
|
||||
auto userpics = Data::UserpicsInfo();
|
||||
userpics.count = 3;
|
||||
auto userpicsSlice1 = Data::UserpicsSlice();
|
||||
userpicsSlice1.list.push_back(generatePhoto());
|
||||
userpicsSlice1.list.push_back(generatePhoto());
|
||||
auto userpicsSlice2 = Data::UserpicsSlice();
|
||||
userpicsSlice2.list.push_back(generatePhoto());
|
||||
check(writeUserpicsStart(userpics));
|
||||
check(writeUserpicsSlice(userpicsSlice1));
|
||||
check(writeUserpicsSlice(userpicsSlice2));
|
||||
check(writeUserpicsEnd());
|
||||
|
||||
auto contacts = Data::ContactsList();
|
||||
auto topUser = Data::TopPeer();
|
||||
auto user = personal.user;
|
||||
auto peerUser = Data::Peer{ user };
|
||||
topUser.peer = peerUser;
|
||||
topUser.rating = 0.5;
|
||||
auto topChat = Data::TopPeer();
|
||||
auto chat = Data::Chat();
|
||||
chat.id = counter();
|
||||
chat.title = "Group chat";
|
||||
auto peerChat = Data::Peer{ chat };
|
||||
topChat.peer = peerChat;
|
||||
topChat.rating = 0.25;
|
||||
auto topBot = Data::TopPeer();
|
||||
auto bot = Data::User();
|
||||
bot.info.date = date();
|
||||
bot.isBot = true;
|
||||
bot.info.firstName = "Bot";
|
||||
bot.info.lastName = "Father";
|
||||
bot.info.userId = counter();
|
||||
bot.username = "botfather";
|
||||
auto peerBot = Data::Peer{ bot };
|
||||
topBot.peer = peerBot;
|
||||
topBot.rating = 0.125;
|
||||
|
||||
auto peers = std::map<Data::PeerId, Data::Peer>();
|
||||
peers.emplace(peerUser.id(), peerUser);
|
||||
peers.emplace(peerBot.id(), peerBot);
|
||||
peers.emplace(peerChat.id(), peerChat);
|
||||
|
||||
contacts.correspondents.push_back(topUser);
|
||||
contacts.correspondents.push_back(topChat);
|
||||
contacts.inlineBots.push_back(topBot);
|
||||
contacts.inlineBots.push_back(topBot);
|
||||
contacts.phoneCalls.push_back(topUser);
|
||||
contacts.list.push_back(user.info);
|
||||
contacts.list.push_back(bot.info);
|
||||
|
||||
check(writeContactsList(contacts));
|
||||
|
||||
auto sessions = Data::SessionsList();
|
||||
auto session = Data::Session();
|
||||
session.applicationName = "Telegram Desktop";
|
||||
session.applicationVersion = "1.3.8";
|
||||
session.country = "GB";
|
||||
session.created = date();
|
||||
session.deviceModel = "PC";
|
||||
session.ip = "127.0.0.1";
|
||||
session.lastActive = date();
|
||||
session.platform = "Windows";
|
||||
session.region = "London";
|
||||
session.systemVersion = "10";
|
||||
sessions.list.push_back(session);
|
||||
sessions.list.push_back(session);
|
||||
auto webSession = Data::WebSession();
|
||||
webSession.botUsername = "botfather";
|
||||
webSession.browser = "Google Chrome";
|
||||
webSession.created = date();
|
||||
webSession.domain = "telegram.org";
|
||||
webSession.ip = "127.0.0.1";
|
||||
webSession.lastActive = date();
|
||||
webSession.platform = "Windows";
|
||||
webSession.region = "London, GB";
|
||||
sessions.webList.push_back(webSession);
|
||||
sessions.webList.push_back(webSession);
|
||||
check(writeSessionsList(sessions));
|
||||
|
||||
auto sampleMessage = [&] {
|
||||
auto message = Data::Message();
|
||||
message.id = counter();
|
||||
message.date = prevdate();
|
||||
message.edited = date();
|
||||
message.forwardedFromId = user.info.userId;
|
||||
message.fromId = user.info.userId;
|
||||
message.replyToMsgId = counter();
|
||||
message.viaBotId = bot.info.userId;
|
||||
message.text.push_back(Data::TextPart{
|
||||
Data::TextPart::Type::Text,
|
||||
("Text message " + QString::number(counter())).toUtf8()
|
||||
});
|
||||
return message;
|
||||
};
|
||||
auto sliceBot1 = Data::MessagesSlice();
|
||||
sliceBot1.peers = peers;
|
||||
sliceBot1.list.push_back(sampleMessage());
|
||||
sliceBot1.list.push_back([&] {
|
||||
auto message = sampleMessage();
|
||||
message.media.content = generatePhoto();
|
||||
message.media.ttl = counter();
|
||||
return message;
|
||||
}());
|
||||
sliceBot1.list.push_back([&] {
|
||||
auto message = sampleMessage();
|
||||
auto document = Data::Document();
|
||||
document.date = prevdate();
|
||||
document.duration = counter();
|
||||
auto photo = generatePhoto();
|
||||
document.file = photo.image.file;
|
||||
document.width = photo.image.width;
|
||||
document.height = photo.image.height;
|
||||
document.id = counter();
|
||||
message.media.content = document;
|
||||
return message;
|
||||
}());
|
||||
sliceBot1.list.push_back([&] {
|
||||
auto message = sampleMessage();
|
||||
message.media.content = user.info;
|
||||
return message;
|
||||
}());
|
||||
auto sliceBot2 = Data::MessagesSlice();
|
||||
sliceBot2.peers = peers;
|
||||
sliceBot2.list.push_back([&] {
|
||||
auto message = sampleMessage();
|
||||
auto point = Data::GeoPoint();
|
||||
point.latitude = 1.5;
|
||||
point.longitude = 2.8;
|
||||
point.valid = true;
|
||||
message.media.content = point;
|
||||
message.media.ttl = counter();
|
||||
return message;
|
||||
}());
|
||||
sliceBot2.list.push_back([&] {
|
||||
auto message = sampleMessage();
|
||||
message.replyToMsgId = sliceBot1.list.back().id;
|
||||
auto venue = Data::Venue();
|
||||
venue.point.latitude = 1.5;
|
||||
venue.point.longitude = 2.8;
|
||||
venue.point.valid = true;
|
||||
venue.address = "Test address";
|
||||
venue.title = "Test venue";
|
||||
message.media.content = venue;
|
||||
return message;
|
||||
}());
|
||||
sliceBot2.list.push_back([&] {
|
||||
auto message = sampleMessage();
|
||||
auto game = Data::Game();
|
||||
game.botId = bot.info.userId;
|
||||
game.title = "Test game";
|
||||
game.description = "Test game description";
|
||||
game.id = counter();
|
||||
game.shortName = "testgame";
|
||||
message.media.content = game;
|
||||
return message;
|
||||
}());
|
||||
sliceBot2.list.push_back([&] {
|
||||
auto message = sampleMessage();
|
||||
auto invoice = Data::Invoice();
|
||||
invoice.amount = counter();
|
||||
invoice.currency = "GBP";
|
||||
invoice.title = "Huge invoice.";
|
||||
invoice.description = "So money.";
|
||||
invoice.receiptMsgId = sliceBot2.list.front().id;
|
||||
message.media.content = invoice;
|
||||
return message;
|
||||
}());
|
||||
auto serviceMessage = [&] {
|
||||
auto message = Data::Message();
|
||||
message.id = counter();
|
||||
message.date = prevdate();
|
||||
message.fromId = user.info.userId;
|
||||
return message;
|
||||
};
|
||||
auto sliceChat1 = Data::MessagesSlice();
|
||||
sliceChat1.peers = peers;
|
||||
sliceChat1.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionChatCreate();
|
||||
action.title = "Test chat";
|
||||
action.userIds.push_back(user.info.userId);
|
||||
action.userIds.push_back(bot.info.userId);
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat1.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionChatEditTitle();
|
||||
action.title = "New title";
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat1.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionChatEditPhoto();
|
||||
action.photo = generatePhoto();
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat1.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionChatDeletePhoto();
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat1.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionChatAddUser();
|
||||
action.userIds.push_back(user.info.userId);
|
||||
action.userIds.push_back(bot.info.userId);
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat1.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionChatDeleteUser();
|
||||
action.userId = bot.info.userId;
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat1.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionChatJoinedByLink();
|
||||
action.inviterId = bot.info.userId;
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat1.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionChannelCreate();
|
||||
action.title = "Channel name";
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat1.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionChatMigrateTo();
|
||||
action.channelId = chat.id;
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat1.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionChannelMigrateFrom();
|
||||
action.chatId = chat.id;
|
||||
action.title = "Supergroup now";
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
auto sliceChat2 = Data::MessagesSlice();
|
||||
sliceChat2.peers = peers;
|
||||
sliceChat2.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionPinMessage();
|
||||
message.replyToMsgId = sliceChat1.list.back().id;
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat2.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionHistoryClear();
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat2.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionGameScore();
|
||||
action.score = counter();
|
||||
action.gameId = counter();
|
||||
message.replyToMsgId = sliceChat2.list.back().id;
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat2.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionPaymentSent();
|
||||
action.amount = counter();
|
||||
action.currency = "GBP";
|
||||
message.replyToMsgId = sliceChat2.list.front().id;
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat2.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionPhoneCall();
|
||||
action.duration = counter();
|
||||
action.discardReason = Data::ActionPhoneCall::DiscardReason::Busy;
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat2.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionScreenshotTaken();
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat2.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionCustomAction();
|
||||
action.message = "Custom chat action.";
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat2.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionBotAllowed();
|
||||
action.domain = "telegram.org";
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
sliceChat2.list.push_back([&] {
|
||||
auto message = serviceMessage();
|
||||
auto action = Data::ActionSecureValuesSent();
|
||||
using Type = Data::ActionSecureValuesSent::Type;
|
||||
action.types.push_back(Type::BankStatement);
|
||||
action.types.push_back(Type::Phone);
|
||||
message.action.content = action;
|
||||
return message;
|
||||
}());
|
||||
auto dialogs = Data::DialogsInfo();
|
||||
auto dialogBot = Data::DialogInfo();
|
||||
dialogBot.messagesCountPerSplit.push_back(sliceBot1.list.size());
|
||||
dialogBot.messagesCountPerSplit.push_back(sliceBot2.list.size());
|
||||
dialogBot.type = Data::DialogInfo::Type::Bot;
|
||||
dialogBot.name = peerBot.name();
|
||||
dialogBot.onlyMyMessages = false;
|
||||
dialogBot.peerId = peerBot.id();
|
||||
dialogBot.relativePath = "Chats/C_" + QString::number(counter()) + '/';
|
||||
dialogBot.splits.push_back(0);
|
||||
dialogBot.splits.push_back(1);
|
||||
dialogBot.topMessageDate = sliceBot2.list.back().date;
|
||||
dialogBot.topMessageId = sliceBot2.list.back().id;
|
||||
auto dialogChat = Data::DialogInfo();
|
||||
dialogChat.messagesCountPerSplit.push_back(sliceChat1.list.size());
|
||||
dialogChat.messagesCountPerSplit.push_back(sliceChat2.list.size());
|
||||
dialogChat.type = Data::DialogInfo::Type::PrivateGroup;
|
||||
dialogChat.name = peerChat.name();
|
||||
dialogChat.onlyMyMessages = true;
|
||||
dialogChat.peerId = peerChat.id();
|
||||
dialogChat.relativePath = "Chats/C_" + QString::number(counter()) + '/';
|
||||
dialogChat.splits.push_back(0);
|
||||
dialogChat.splits.push_back(1);
|
||||
dialogChat.topMessageDate = sliceChat2.list.back().date;
|
||||
dialogChat.topMessageId = sliceChat2.list.back().id;
|
||||
dialogs.list.push_back(dialogBot);
|
||||
dialogs.list.push_back(dialogChat);
|
||||
|
||||
check(writeDialogsStart(dialogs));
|
||||
check(writeDialogStart(dialogBot));
|
||||
check(writeDialogSlice(sliceBot1));
|
||||
check(writeDialogSlice(sliceBot2));
|
||||
check(writeDialogEnd());
|
||||
check(writeDialogStart(dialogChat));
|
||||
check(writeDialogSlice(sliceChat1));
|
||||
check(writeDialogSlice(sliceChat2));
|
||||
check(writeDialogEnd());
|
||||
check(writeDialogsEnd());
|
||||
|
||||
check(writeLeftChannelsStart(Data::DialogsInfo()));
|
||||
check(writeLeftChannelsEnd());
|
||||
|
||||
check(finish());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Output
|
||||
} // namespace Export
|
||||
|
@@ -25,18 +25,22 @@ struct Settings;
|
||||
|
||||
namespace Output {
|
||||
|
||||
QString NormalizePath(const QString &source);
|
||||
|
||||
struct Result;
|
||||
class Stats;
|
||||
|
||||
enum class Format {
|
||||
Text,
|
||||
Json,
|
||||
Yaml,
|
||||
Html,
|
||||
Json,
|
||||
Text,
|
||||
Yaml,
|
||||
};
|
||||
|
||||
class AbstractWriter {
|
||||
public:
|
||||
[[nodiscard]] virtual Format format() = 0;
|
||||
|
||||
[[nodiscard]] virtual Result start(
|
||||
const Settings &settings,
|
||||
Stats *stats) = 0;
|
||||
@@ -80,6 +84,8 @@ public:
|
||||
|
||||
virtual ~AbstractWriter() = default;
|
||||
|
||||
Stats produceTestExample(const QString &path);
|
||||
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractWriter> CreateWriter(Format format);
|
||||
|
@@ -120,5 +120,20 @@ QString File::PrepareRelativePath(
|
||||
}
|
||||
}
|
||||
|
||||
Result File::Copy(
|
||||
const QString &source,
|
||||
const QString &path,
|
||||
Stats *stats) {
|
||||
QFile f(source);
|
||||
if (!f.exists() || !f.open(QIODevice::ReadOnly)) {
|
||||
return Result(Result::Type::FatalError, source);
|
||||
}
|
||||
const auto bytes = f.readAll();
|
||||
if (bytes.size() != f.size()) {
|
||||
return Result(Result::Type::FatalError, source);
|
||||
}
|
||||
return File(path, stats).writeBlock(bytes);
|
||||
}
|
||||
|
||||
} // namespace Output
|
||||
} // namespace File
|
||||
|
@@ -32,6 +32,11 @@ public:
|
||||
const QString &folder,
|
||||
const QString &suggested);
|
||||
|
||||
[[nodiscard]] static Result Copy(
|
||||
const QString &source,
|
||||
const QString &path,
|
||||
Stats *stats);
|
||||
|
||||
private:
|
||||
[[nodiscard]] Result reopen();
|
||||
[[nodiscard]] Result writeBlockAttempt(const QByteArray &block);
|
||||
|
1217
Telegram/SourceFiles/export/output/export_output_html.cpp
Normal file
1217
Telegram/SourceFiles/export/output/export_output_html.cpp
Normal file
File diff suppressed because it is too large
Load Diff
101
Telegram/SourceFiles/export/output/export_output_html.h
Normal file
101
Telegram/SourceFiles/export/output/export_output_html.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "export/output/export_output_abstract.h"
|
||||
#include "export/output/export_output_file.h"
|
||||
#include "export/export_settings.h"
|
||||
#include "export/data/export_data_types.h"
|
||||
|
||||
namespace Export {
|
||||
namespace Output {
|
||||
|
||||
class HtmlWriter : public AbstractWriter {
|
||||
public:
|
||||
HtmlWriter();
|
||||
|
||||
Format format() override {
|
||||
return Format::Html;
|
||||
}
|
||||
|
||||
Result start(const Settings &settings, Stats *stats) override;
|
||||
|
||||
Result writePersonal(const Data::PersonalInfo &data) override;
|
||||
|
||||
Result writeUserpicsStart(const Data::UserpicsInfo &data) override;
|
||||
Result writeUserpicsSlice(const Data::UserpicsSlice &data) override;
|
||||
Result writeUserpicsEnd() override;
|
||||
|
||||
Result writeContactsList(const Data::ContactsList &data) override;
|
||||
|
||||
Result writeSessionsList(const Data::SessionsList &data) override;
|
||||
|
||||
Result writeDialogsStart(const Data::DialogsInfo &data) override;
|
||||
Result writeDialogStart(const Data::DialogInfo &data) override;
|
||||
Result writeDialogSlice(const Data::MessagesSlice &data) override;
|
||||
Result writeDialogEnd() override;
|
||||
Result writeDialogsEnd() override;
|
||||
|
||||
Result writeLeftChannelsStart(const Data::DialogsInfo &data) override;
|
||||
Result writeLeftChannelStart(const Data::DialogInfo &data) override;
|
||||
Result writeLeftChannelSlice(const Data::MessagesSlice &data) override;
|
||||
Result writeLeftChannelEnd() override;
|
||||
Result writeLeftChannelsEnd() override;
|
||||
|
||||
Result finish() override;
|
||||
|
||||
QString mainFilePath() override;
|
||||
|
||||
~HtmlWriter();
|
||||
|
||||
private:
|
||||
class Wrap;
|
||||
|
||||
Result copyFile(
|
||||
const QString &source,
|
||||
const QString &relativePath) const;
|
||||
|
||||
QString mainFileRelativePath() const;
|
||||
QString pathWithRelativePath(const QString &path) const;
|
||||
std::unique_ptr<Wrap> fileWithRelativePath(const QString &path) const;
|
||||
|
||||
Result writeSavedContacts(const Data::ContactsList &data);
|
||||
Result writeFrequentContacts(const Data::ContactsList &data);
|
||||
|
||||
Result writeSessions(const Data::SessionsList &data);
|
||||
Result writeWebSessions(const Data::SessionsList &data);
|
||||
|
||||
Result writeChatsStart(
|
||||
const Data::DialogsInfo &data,
|
||||
const QByteArray &listName,
|
||||
const QString &fileName);
|
||||
Result writeChatStart(const Data::DialogInfo &data);
|
||||
Result writeChatSlice(const Data::MessagesSlice &data);
|
||||
Result writeChatEnd();
|
||||
Result writeChatsEnd();
|
||||
|
||||
Settings _settings;
|
||||
Stats *_stats = nullptr;
|
||||
|
||||
std::unique_ptr<Wrap> _summary;
|
||||
|
||||
int _userpicsCount = 0;
|
||||
std::unique_ptr<Wrap> _userpics;
|
||||
|
||||
int _dialogsCount = 0;
|
||||
int _dialogIndex = 0;
|
||||
Data::DialogInfo _dialog;
|
||||
|
||||
int _messagesCount = 0;
|
||||
std::unique_ptr<Wrap> _chats;
|
||||
std::unique_ptr<Wrap> _chat;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Output
|
||||
} // namespace Export
|
@@ -733,12 +733,14 @@ Result JsonWriter::writeFrequentContacts(const Data::ContactsList &data) {
|
||||
const auto type = [&] {
|
||||
if (const auto chat = top.peer.chat()) {
|
||||
return chat->username.isEmpty()
|
||||
? (chat->broadcast
|
||||
? (chat->isBroadcast
|
||||
? "private_channel"
|
||||
: "private_group")
|
||||
: (chat->broadcast
|
||||
: (chat->isSupergroup
|
||||
? "private_supergroup"
|
||||
: "private_group"))
|
||||
: (chat->isBroadcast
|
||||
? "public_channel"
|
||||
: "public_group");
|
||||
: "public_supergroup");
|
||||
}
|
||||
return "user";
|
||||
}();
|
||||
@@ -879,10 +881,12 @@ Result JsonWriter::writeChatStart(const Data::DialogInfo &data) {
|
||||
const auto TypeString = [](Type type) {
|
||||
switch (type) {
|
||||
case Type::Unknown: return "";
|
||||
case Type::Self: return "saved_messages";
|
||||
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::PrivateSupergroup: return "private_supergroup";
|
||||
case Type::PublicSupergroup: return "public_supergroup";
|
||||
case Type::PrivateChannel: return "private_channel";
|
||||
case Type::PublicChannel: return "public_channel";
|
||||
}
|
||||
@@ -891,8 +895,10 @@ Result JsonWriter::writeChatStart(const Data::DialogInfo &data) {
|
||||
|
||||
auto block = prepareArrayItemStart();
|
||||
block.append(pushNesting(Context::kObject));
|
||||
block.append(prepareObjectItemStart("name")
|
||||
+ StringAllowNull(data.name));
|
||||
if (data.type != Type::Self) {
|
||||
block.append(prepareObjectItemStart("name")
|
||||
+ StringAllowNull(data.name));
|
||||
}
|
||||
block.append(prepareObjectItemStart("type")
|
||||
+ StringAllowNull(TypeString(data.type)));
|
||||
block.append(prepareObjectItemStart("messages"));
|
||||
|
@@ -29,6 +29,10 @@ struct JsonContext {
|
||||
|
||||
class JsonWriter : public AbstractWriter {
|
||||
public:
|
||||
Format format() override {
|
||||
return Format::Json;
|
||||
}
|
||||
|
||||
Result start(const Settings &settings, Stats *stats) override;
|
||||
|
||||
Result writePersonal(const Data::PersonalInfo &data) override;
|
||||
|
@@ -10,6 +10,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Export {
|
||||
namespace Output {
|
||||
|
||||
Stats::Stats(const Stats &other)
|
||||
: _files(other._files.load())
|
||||
, _bytes(other._bytes.load()) {
|
||||
}
|
||||
|
||||
void Stats::incrementFiles() {
|
||||
++_files;
|
||||
}
|
||||
|
@@ -14,6 +14,9 @@ namespace Output {
|
||||
|
||||
class Stats {
|
||||
public:
|
||||
Stats() = default;
|
||||
Stats(const Stats &other);
|
||||
|
||||
void incrementFiles();
|
||||
void incrementBytes(int count);
|
||||
|
||||
|
@@ -580,7 +580,7 @@ Result TextWriter::writeFrequentContacts(const Data::ContactsList &data) {
|
||||
Data::Utf8String category) {
|
||||
for (const auto &top : peers) {
|
||||
const auto user = [&]() -> Data::Utf8String {
|
||||
if (!top.peer.user()) {
|
||||
if (!top.peer.user() || top.peer.user()->isSelf) {
|
||||
return Data::Utf8String();
|
||||
} else if (top.peer.name().isEmpty()) {
|
||||
return "(deleted user)";
|
||||
@@ -590,12 +590,14 @@ Result TextWriter::writeFrequentContacts(const Data::ContactsList &data) {
|
||||
const auto chatType = [&] {
|
||||
if (const auto chat = top.peer.chat()) {
|
||||
return chat->username.isEmpty()
|
||||
? (chat->broadcast
|
||||
? (chat->isBroadcast
|
||||
? "Private channel"
|
||||
: "Private group")
|
||||
: (chat->broadcast
|
||||
: (chat->isSupergroup
|
||||
? "Private supergroup"
|
||||
: "Private group"))
|
||||
: (chat->isBroadcast
|
||||
? "Public channel"
|
||||
: "Public group");
|
||||
: "Public supergroup");
|
||||
}
|
||||
return "";
|
||||
}();
|
||||
@@ -607,11 +609,18 @@ Result TextWriter::writeFrequentContacts(const Data::ContactsList &data) {
|
||||
}
|
||||
return top.peer.name();
|
||||
}();
|
||||
const auto saved = [&]() -> Data::Utf8String {
|
||||
if (!top.peer.user() || !top.peer.user()->isSelf) {
|
||||
return Data::Utf8String();
|
||||
}
|
||||
return "Saved messages";
|
||||
}();
|
||||
list.push_back(SerializeKeyValue({
|
||||
{ "Category", category },
|
||||
{ "User", top.peer.user() ? user : QByteArray() },
|
||||
{ "Chat", saved },
|
||||
{ chatType, chat },
|
||||
{ "Rating", QString::number(top.rating).toUtf8() }
|
||||
{ "Rating", Data::NumberToString(top.rating) }
|
||||
}));
|
||||
}
|
||||
};
|
||||
@@ -834,18 +843,24 @@ Result TextWriter::writeChatEnd() {
|
||||
const auto TypeString = [](Type type) {
|
||||
switch (type) {
|
||||
case Type::Unknown: return "(unknown)";
|
||||
case Type::Self:
|
||||
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::PrivateSupergroup: return "Private supergroup";
|
||||
case Type::PublicSupergroup: return "Public supergroup";
|
||||
case Type::PrivateChannel: return "Private channel";
|
||||
case Type::PublicChannel: return "Public channel";
|
||||
}
|
||||
Unexpected("Dialog type in TypeString.");
|
||||
};
|
||||
const auto NameString = [](
|
||||
const Data::Utf8String &name,
|
||||
const Data::DialogInfo &dialog,
|
||||
Type type) -> QByteArray {
|
||||
if (dialog.type == Type::Self) {
|
||||
return "Saved messages";
|
||||
}
|
||||
const auto name = dialog.name;
|
||||
if (!name.isEmpty()) {
|
||||
return name;
|
||||
}
|
||||
@@ -854,14 +869,15 @@ Result TextWriter::writeChatEnd() {
|
||||
case Type::Personal: return "(deleted user)";
|
||||
case Type::Bot: return "(deleted bot)";
|
||||
case Type::PrivateGroup:
|
||||
case Type::PublicGroup: return "(deleted group)";
|
||||
case Type::PrivateSupergroup:
|
||||
case Type::PublicSupergroup: return "(deleted group)";
|
||||
case Type::PrivateChannel:
|
||||
case Type::PublicChannel: return "(deleted channel)";
|
||||
}
|
||||
Unexpected("Dialog type in TypeString.");
|
||||
};
|
||||
return _chats->writeBlock(SerializeKeyValue({
|
||||
{ "Name", NameString(_dialog.name, _dialog.type) },
|
||||
{ "Name", NameString(_dialog, _dialog.type) },
|
||||
{ "Type", TypeString(_dialog.type) },
|
||||
{
|
||||
(_dialog.onlyMyMessages
|
||||
|
@@ -17,6 +17,10 @@ namespace Output {
|
||||
|
||||
class TextWriter : public AbstractWriter {
|
||||
public:
|
||||
Format format() override {
|
||||
return Format::Text;
|
||||
}
|
||||
|
||||
Result start(const Settings &settings, Stats *stats) override;
|
||||
|
||||
Result writePersonal(const Data::PersonalInfo &data) override;
|
||||
|
Reference in New Issue
Block a user