export_output_json.cpp 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "export/output/export_output_json.h"
  8. #include "export/output/export_output_result.h"
  9. #include "export/data/export_data_types.h"
  10. #include "core/utils.h"
  11. #include <QtCore/QDateTime>
  12. #include <QtCore/QJsonDocument>
  13. #include <QtCore/QJsonObject>
  14. #include <QtCore/QJsonArray>
  15. #include <QtCore/QJsonValue>
  16. namespace Export {
  17. namespace Output {
  18. namespace {
  19. using Context = details::JsonContext;
  20. QByteArray SerializeString(const QByteArray &value) {
  21. const auto size = value.size();
  22. const auto begin = value.data();
  23. const auto end = begin + size;
  24. auto result = QByteArray();
  25. result.reserve(2 + size * 4);
  26. result.append('"');
  27. for (auto p = begin; p != end; ++p) {
  28. const auto ch = *p;
  29. if (ch == '\n') {
  30. result.append("\\n", 2);
  31. } else if (ch == '\r') {
  32. result.append("\\r", 2);
  33. } else if (ch == '\t') {
  34. result.append("\\t", 2);
  35. } else if (ch == '"') {
  36. result.append("\\\"", 2);
  37. } else if (ch == '\\') {
  38. result.append("\\\\", 2);
  39. } else if (ch >= 0 && ch < 32) {
  40. result.append("\\x", 2).append('0' + (ch >> 4));
  41. const auto left = (ch & 0x0F);
  42. if (left >= 10) {
  43. result.append('A' + (left - 10));
  44. } else {
  45. result.append('0' + left);
  46. }
  47. } else if (ch == char(0xE2)
  48. && (p + 2 < end)
  49. && *(p + 1) == char(0x80)) {
  50. if (*(p + 2) == char(0xA8)) { // Line separator.
  51. result.append("\\u2028", 6);
  52. } else if (*(p + 2) == char(0xA9)) { // Paragraph separator.
  53. result.append("\\u2029", 6);
  54. } else {
  55. result.append(ch);
  56. }
  57. } else {
  58. result.append(ch);
  59. }
  60. }
  61. result.append('"');
  62. return result;
  63. }
  64. QByteArray SerializeDate(TimeId date) {
  65. return SerializeString(
  66. QDateTime::fromSecsSinceEpoch(date).toString(Qt::ISODate).toUtf8());
  67. }
  68. QByteArray SerializeDateRaw(TimeId date) {
  69. return SerializeString(QString::number(date).toUtf8());
  70. }
  71. QByteArray StringAllowEmpty(const Data::Utf8String &data) {
  72. return data.isEmpty() ? data : SerializeString(data);
  73. }
  74. QByteArray StringAllowNull(const Data::Utf8String &data) {
  75. return data.isEmpty() ? QByteArray("null") : SerializeString(data);
  76. }
  77. QByteArray Indentation(int size) {
  78. return QByteArray(size, ' ');
  79. }
  80. QByteArray Indentation(const Context &context) {
  81. return Indentation(context.nesting.size());
  82. }
  83. QByteArray SerializeObject(
  84. Context &context,
  85. const std::vector<std::pair<QByteArray, QByteArray>> &values) {
  86. const auto indent = Indentation(context);
  87. context.nesting.push_back(Context::kObject);
  88. const auto guard = gsl::finally([&] { context.nesting.pop_back(); });
  89. const auto next = '\n' + Indentation(context);
  90. auto first = true;
  91. auto result = QByteArray();
  92. result.append('{');
  93. for (const auto &[key, value] : values) {
  94. if (value.isEmpty()) {
  95. continue;
  96. }
  97. if (first) {
  98. first = false;
  99. } else {
  100. result.append(',');
  101. }
  102. result.append(next).append(SerializeString(key)).append(": ", 2);
  103. result.append(value);
  104. }
  105. result.append('\n').append(indent).append("}");
  106. return result;
  107. }
  108. QByteArray SerializeArray(
  109. Context &context,
  110. const std::vector<QByteArray> &values) {
  111. const auto indent = Indentation(context.nesting.size());
  112. const auto next = '\n' + Indentation(context.nesting.size() + 1);
  113. auto first = true;
  114. auto result = QByteArray();
  115. result.append('[');
  116. for (const auto &value : values) {
  117. if (first) {
  118. first = false;
  119. } else {
  120. result.append(',');
  121. }
  122. result.append(next).append(value);
  123. }
  124. result.append('\n').append(indent).append("]");
  125. return result;
  126. }
  127. QByteArray SerializeText(
  128. Context &context,
  129. const std::vector<Data::TextPart> &data,
  130. bool serializeToObjects = false) {
  131. using Type = Data::TextPart::Type;
  132. if (data.empty()) {
  133. return serializeToObjects ? QByteArray("[]") : SerializeString("");
  134. }
  135. context.nesting.push_back(Context::kArray);
  136. const auto text = ranges::views::all(
  137. data
  138. ) | ranges::views::transform([&](const Data::TextPart &part) {
  139. if ((part.type == Type::Text) && !serializeToObjects) {
  140. return SerializeString(part.text);
  141. }
  142. const auto typeString = [&] {
  143. switch (part.type) {
  144. case Type::Unknown: return "unknown";
  145. case Type::Mention: return "mention";
  146. case Type::Hashtag: return "hashtag";
  147. case Type::BotCommand: return "bot_command";
  148. case Type::Url: return "link";
  149. case Type::Email: return "email";
  150. case Type::Bold: return "bold";
  151. case Type::Italic: return "italic";
  152. case Type::Code: return "code";
  153. case Type::Pre: return "pre";
  154. case Type::Text: return "plain";
  155. case Type::TextUrl: return "text_link";
  156. case Type::MentionName: return "mention_name";
  157. case Type::Phone: return "phone";
  158. case Type::Cashtag: return "cashtag";
  159. case Type::Underline: return "underline";
  160. case Type::Strike: return "strikethrough";
  161. case Type::Blockquote: return "blockquote";
  162. case Type::BankCard: return "bank_card";
  163. case Type::Spoiler: return "spoiler";
  164. case Type::CustomEmoji: return "custom_emoji";
  165. }
  166. Unexpected("Type in SerializeText.");
  167. }();
  168. const auto additionalName = (part.type == Type::MentionName)
  169. ? "user_id"
  170. : (part.type == Type::CustomEmoji)
  171. ? "document_id"
  172. : (part.type == Type::Pre)
  173. ? "language"
  174. : (part.type == Type::TextUrl)
  175. ? "href"
  176. : (part.type == Type::Blockquote)
  177. ? "collapsed"
  178. : "none";
  179. const auto additionalValue = (part.type == Type::MentionName)
  180. ? part.additional
  181. : (part.type == Type::Pre
  182. || part.type == Type::TextUrl
  183. || part.type == Type::CustomEmoji)
  184. ? SerializeString(part.additional)
  185. : (part.type == Type::Blockquote)
  186. ? (part.additional.isEmpty() ? "false" : "true")
  187. : QByteArray();
  188. return SerializeObject(context, {
  189. { "type", SerializeString(typeString) },
  190. { "text", SerializeString(part.text) },
  191. { additionalName, additionalValue },
  192. });
  193. }) | ranges::to_vector;
  194. context.nesting.pop_back();
  195. if (!serializeToObjects) {
  196. if (data.size() == 1 && data[0].type == Data::TextPart::Type::Text) {
  197. return text[0];
  198. }
  199. }
  200. return SerializeArray(context, text);
  201. }
  202. Data::Utf8String FormatUsername(const Data::Utf8String &username) {
  203. return username.isEmpty() ? username : ('@' + username);
  204. }
  205. QByteArray FormatFilePath(const Data::File &file) {
  206. return file.relativePath.toUtf8();
  207. }
  208. QByteArray SerializeMessage(
  209. Context &context,
  210. const Data::Message &message,
  211. const std::map<PeerId, Data::Peer> &peers,
  212. const QString &internalLinksDomain) {
  213. using namespace Data;
  214. if (v::is<UnsupportedMedia>(message.media.content)) {
  215. return SerializeObject(context, {
  216. { "id", Data::NumberToString(message.id) },
  217. { "type", SerializeString("unsupported") }
  218. });
  219. }
  220. const auto peer = [&](PeerId peerId) -> const Peer& {
  221. if (const auto i = peers.find(peerId); i != end(peers)) {
  222. return i->second;
  223. }
  224. static auto empty = Peer{ User() };
  225. return empty;
  226. };
  227. const auto user = [&](UserId userId) -> const User& {
  228. if (const auto result = peer(userId).user()) {
  229. return *result;
  230. }
  231. static auto empty = User();
  232. return empty;
  233. };
  234. auto values = std::vector<std::pair<QByteArray, QByteArray>>{
  235. { "id", NumberToString(message.id) },
  236. {
  237. "type",
  238. SerializeString(!v::is_null(message.action.content)
  239. ? "service"
  240. : "message")
  241. },
  242. { "date", SerializeDate(message.date) },
  243. { "date_unixtime", SerializeDateRaw(message.date) },
  244. };
  245. context.nesting.push_back(Context::kObject);
  246. const auto serialized = [&] {
  247. context.nesting.pop_back();
  248. return SerializeObject(context, values);
  249. };
  250. const auto pushBare = [&](
  251. const QByteArray &key,
  252. const QByteArray &value) {
  253. if (!value.isEmpty()) {
  254. values.emplace_back(key, value);
  255. }
  256. };
  257. if (message.edited) {
  258. pushBare("edited", SerializeDate(message.edited));
  259. pushBare("edited_unixtime", SerializeDateRaw(message.edited));
  260. }
  261. const auto wrapPeerId = [&](PeerId peerId) {
  262. if (const auto chat = peerToChat(peerId)) {
  263. return SerializeString("chat"
  264. + Data::NumberToString(chat.bare));
  265. } else if (const auto channel = peerToChannel(peerId)) {
  266. return SerializeString("channel"
  267. + Data::NumberToString(channel.bare));
  268. }
  269. return SerializeString("user"
  270. + Data::NumberToString(peerToUser(peerId).bare));
  271. };
  272. const auto push = [&](const QByteArray &key, const auto &value) {
  273. using V = std::decay_t<decltype(value)>;
  274. if constexpr (std::is_same_v<V, bool>) {
  275. pushBare(key, value ? "true" : "false");
  276. } else if constexpr (std::is_arithmetic_v<V>) {
  277. pushBare(key, Data::NumberToString(value));
  278. } else if constexpr (std::is_same_v<V, PeerId>) {
  279. pushBare(key, wrapPeerId(value));
  280. } else {
  281. const auto wrapped = QByteArray(value);
  282. if (!wrapped.isEmpty()) {
  283. pushBare(key, SerializeString(wrapped));
  284. }
  285. }
  286. };
  287. const auto wrapPeerName = [&](PeerId peerId) {
  288. return StringAllowNull(peer(peerId).name());
  289. };
  290. const auto wrapUserName = [&](UserId userId) {
  291. return StringAllowNull(user(userId).name());
  292. };
  293. const auto pushFrom = [&](const QByteArray &label = "from") {
  294. if (message.fromId) {
  295. pushBare(label, wrapPeerName(message.fromId));
  296. push(label + "_id", message.fromId);
  297. }
  298. };
  299. const auto pushReplyToMsgId = [&](
  300. const QByteArray &label = "reply_to_message_id") {
  301. if (message.replyToMsgId) {
  302. push(label, message.replyToMsgId);
  303. if (message.replyToPeerId) {
  304. push("reply_to_peer_id", message.replyToPeerId);
  305. }
  306. }
  307. };
  308. const auto pushUserNames = [&](
  309. const std::vector<UserId> &data,
  310. const QByteArray &label = "members") {
  311. auto list = std::vector<QByteArray>();
  312. for (const auto &userId : data) {
  313. list.push_back(wrapUserName(userId));
  314. }
  315. pushBare(label, SerializeArray(context, list));
  316. };
  317. const auto pushActor = [&] {
  318. pushFrom("actor");
  319. };
  320. const auto pushAction = [&](const QByteArray &action) {
  321. push("action", action);
  322. };
  323. const auto pushTTL = [&](
  324. const QByteArray &label = "self_destruct_period_seconds") {
  325. if (const auto ttl = message.media.ttl) {
  326. push(label, ttl);
  327. }
  328. };
  329. using SkipReason = Data::File::SkipReason;
  330. const auto pushPath = [&](
  331. const Data::File &file,
  332. const QByteArray &label,
  333. const QByteArray &name = QByteArray()) {
  334. Expects(!file.relativePath.isEmpty()
  335. || file.skipReason != SkipReason::None);
  336. push(label, [&]() -> QByteArray {
  337. const auto pre = name.isEmpty() ? QByteArray() : name + ' ';
  338. switch (file.skipReason) {
  339. case SkipReason::Unavailable:
  340. return pre + "(File unavailable, please try again later)";
  341. case SkipReason::FileSize:
  342. return pre + "(File exceeds maximum size. "
  343. "Change data exporting settings to download.)";
  344. case SkipReason::FileType:
  345. return pre + "(File not included. "
  346. "Change data exporting settings to download.)";
  347. case SkipReason::None: return FormatFilePath(file);
  348. }
  349. Unexpected("Skip reason while writing file path.");
  350. }());
  351. };
  352. const auto pushPhoto = [&](const Image &image) {
  353. pushPath(image.file, "photo");
  354. push("photo_file_size", image.file.size);
  355. if (image.width && image.height) {
  356. push("width", image.width);
  357. push("height", image.height);
  358. }
  359. };
  360. const auto pushSpoiler = [&](const auto &media) {
  361. if (media.spoilered) {
  362. push("media_spoiler", true);
  363. }
  364. };
  365. v::match(message.action.content, [&](const ActionChatCreate &data) {
  366. pushActor();
  367. pushAction("create_group");
  368. push("title", data.title);
  369. pushUserNames(data.userIds);
  370. }, [&](const ActionChatEditTitle &data) {
  371. pushActor();
  372. pushAction("edit_group_title");
  373. push("title", data.title);
  374. }, [&](const ActionChatEditPhoto &data) {
  375. pushActor();
  376. pushAction("edit_group_photo");
  377. pushPhoto(data.photo.image);
  378. pushSpoiler(data.photo);
  379. }, [&](const ActionChatDeletePhoto &data) {
  380. pushActor();
  381. pushAction("delete_group_photo");
  382. }, [&](const ActionChatAddUser &data) {
  383. pushActor();
  384. pushAction("invite_members");
  385. pushUserNames(data.userIds);
  386. }, [&](const ActionChatDeleteUser &data) {
  387. pushActor();
  388. pushAction("remove_members");
  389. pushUserNames({ data.userId });
  390. }, [&](const ActionChatJoinedByLink &data) {
  391. pushActor();
  392. pushAction("join_group_by_link");
  393. pushBare("inviter", wrapUserName(data.inviterId));
  394. }, [&](const ActionChannelCreate &data) {
  395. pushActor();
  396. pushAction("create_channel");
  397. push("title", data.title);
  398. }, [&](const ActionChatMigrateTo &data) {
  399. pushActor();
  400. pushAction("migrate_to_supergroup");
  401. }, [&](const ActionChannelMigrateFrom &data) {
  402. pushActor();
  403. pushAction("migrate_from_group");
  404. push("title", data.title);
  405. }, [&](const ActionPinMessage &data) {
  406. pushActor();
  407. pushAction("pin_message");
  408. pushReplyToMsgId("message_id");
  409. }, [&](const ActionHistoryClear &data) {
  410. pushActor();
  411. pushAction("clear_history");
  412. }, [&](const ActionGameScore &data) {
  413. pushActor();
  414. pushAction("score_in_game");
  415. pushReplyToMsgId("game_message_id");
  416. push("score", data.score);
  417. }, [&](const ActionPaymentSent &data) {
  418. pushAction("send_payment");
  419. push("amount", data.amount);
  420. push("currency", data.currency);
  421. pushReplyToMsgId("invoice_message_id");
  422. if (data.recurringUsed) {
  423. push("recurring", "used");
  424. } else if (data.recurringInit) {
  425. push("recurring", "init");
  426. }
  427. }, [&](const ActionPhoneCall &data) {
  428. pushActor();
  429. pushAction("phone_call");
  430. if (data.duration) {
  431. push("duration_seconds", data.duration);
  432. }
  433. using Reason = ActionPhoneCall::DiscardReason;
  434. push("discard_reason", [&] {
  435. switch (data.discardReason) {
  436. case Reason::Busy: return "busy";
  437. case Reason::Disconnect: return "disconnect";
  438. case Reason::Hangup: return "hangup";
  439. case Reason::Missed: return "missed";
  440. }
  441. return "";
  442. }());
  443. }, [&](const ActionScreenshotTaken &data) {
  444. pushActor();
  445. pushAction("take_screenshot");
  446. }, [&](const ActionCustomAction &data) {
  447. pushActor();
  448. push("information_text", data.message);
  449. }, [&](const ActionBotAllowed &data) {
  450. if (data.attachMenu) {
  451. pushAction("attach_menu_bot_allowed");
  452. } else if (data.fromRequest) {
  453. pushAction("web_app_bot_allowed");
  454. } else if (data.appId) {
  455. pushAction("allow_sending_messages");
  456. push("reason_app_id", data.appId);
  457. push("reason_app_name", data.app);
  458. } else {
  459. pushAction("allow_sending_messages");
  460. push("reason_domain", data.domain);
  461. }
  462. }, [&](const ActionSecureValuesSent &data) {
  463. pushAction("send_passport_values");
  464. auto list = std::vector<QByteArray>();
  465. for (const auto type : data.types) {
  466. list.push_back(SerializeString([&] {
  467. using Type = ActionSecureValuesSent::Type;
  468. switch (type) {
  469. case Type::PersonalDetails: return "personal_details";
  470. case Type::Passport: return "passport";
  471. case Type::DriverLicense: return "driver_license";
  472. case Type::IdentityCard: return "identity_card";
  473. case Type::InternalPassport: return "internal_passport";
  474. case Type::Address: return "address_information";
  475. case Type::UtilityBill: return "utility_bill";
  476. case Type::BankStatement: return "bank_statement";
  477. case Type::RentalAgreement: return "rental_agreement";
  478. case Type::PassportRegistration:
  479. return "passport_registration";
  480. case Type::TemporaryRegistration:
  481. return "temporary_registration";
  482. case Type::Phone: return "phone_number";
  483. case Type::Email: return "email";
  484. }
  485. return "";
  486. }()));
  487. }
  488. pushBare("values", SerializeArray(context, list));
  489. }, [&](const ActionContactSignUp &data) {
  490. pushActor();
  491. pushAction("joined_telegram");
  492. }, [&](const ActionGeoProximityReached &data) {
  493. pushAction("proximity_reached");
  494. if (data.fromId) {
  495. pushBare("from", wrapPeerName(data.fromId));
  496. push("from_id", data.fromId);
  497. }
  498. if (data.toId) {
  499. pushBare("to", wrapPeerName(data.toId));
  500. push("to_id", data.toId);
  501. }
  502. push("distance", data.distance);
  503. }, [&](const ActionPhoneNumberRequest &data) {
  504. pushActor();
  505. pushAction("requested_phone_number");
  506. }, [&](const ActionGroupCall &data) {
  507. pushActor();
  508. pushAction("group_call");
  509. if (data.duration) {
  510. push("duration", data.duration);
  511. }
  512. }, [&](const ActionInviteToGroupCall &data) {
  513. pushActor();
  514. pushAction("invite_to_group_call");
  515. pushUserNames(data.userIds);
  516. }, [&](const ActionSetMessagesTTL &data) {
  517. pushActor();
  518. pushAction("set_messages_ttl");
  519. push("period", data.period);
  520. }, [&](const ActionGroupCallScheduled &data) {
  521. pushActor();
  522. pushAction("group_call_scheduled");
  523. push("schedule_date", data.date);
  524. }, [&](const ActionSetChatTheme &data) {
  525. pushActor();
  526. pushAction("edit_chat_theme");
  527. if (!data.emoji.isEmpty()) {
  528. push("emoticon", data.emoji.toUtf8());
  529. }
  530. }, [&](const ActionChatJoinedByRequest &data) {
  531. pushActor();
  532. pushAction("join_group_by_request");
  533. }, [&](const ActionWebViewDataSent &data) {
  534. pushAction("send_webview_data");
  535. push("text", data.text);
  536. }, [&](const ActionGiftPremium &data) {
  537. pushActor();
  538. pushAction("send_premium_gift");
  539. if (!data.cost.isEmpty()) {
  540. push("cost", data.cost);
  541. }
  542. if (data.months) {
  543. push("months", data.months);
  544. }
  545. }, [&](const ActionTopicCreate &data) {
  546. pushActor();
  547. pushAction("topic_created");
  548. push("title", data.title);
  549. }, [&](const ActionTopicEdit &data) {
  550. pushActor();
  551. pushAction("topic_edit");
  552. if (!data.title.isEmpty()) {
  553. push("new_title", data.title);
  554. }
  555. if (data.iconEmojiId) {
  556. push("new_icon_emoji_id", *data.iconEmojiId);
  557. }
  558. }, [&](const ActionSuggestProfilePhoto &data) {
  559. pushActor();
  560. pushAction("suggest_profile_photo");
  561. pushPhoto(data.photo.image);
  562. pushSpoiler(data.photo);
  563. }, [&](const ActionRequestedPeer &data) {
  564. pushActor();
  565. pushAction("requested_peer");
  566. push("button_id", data.buttonId);
  567. auto values = std::vector<QByteArray>();
  568. for (const auto &one : data.peers) {
  569. values.push_back(Data::NumberToString(one.value));
  570. }
  571. push("peers", SerializeArray(context, values));
  572. }, [&](const ActionGiftCode &data) {
  573. pushAction("gift_code_prize");
  574. push("gift_code", data.code);
  575. if (data.boostPeerId) {
  576. push("boost_peer_id", data.boostPeerId);
  577. }
  578. push("months", data.months);
  579. push("unclaimed", data.unclaimed);
  580. push("via_giveaway", data.viaGiveaway);
  581. }, [&](const ActionGiveawayLaunch &data) {
  582. pushAction("giveaway_launch");
  583. }, [&](const ActionGiveawayResults &data) {
  584. pushAction("giveaway_results");
  585. push("winners", data.winners);
  586. push("unclaimed", data.unclaimed);
  587. push("stars", data.credits);
  588. }, [&](const ActionSetChatWallPaper &data) {
  589. pushActor();
  590. pushAction(data.same
  591. ? "set_same_chat_wallpaper"
  592. : "set_chat_wallpaper");
  593. pushReplyToMsgId("message_id");
  594. }, [&](const ActionBoostApply &data) {
  595. pushActor();
  596. pushAction("boost_apply");
  597. push("boosts", data.boosts);
  598. }, [&](const ActionPaymentRefunded &data) {
  599. pushAction("refunded_payment");
  600. push("amount", data.amount);
  601. push("currency", data.currency);
  602. pushBare("peer_name", wrapPeerName(data.peerId));
  603. push("peer_id", data.peerId);
  604. push("charge_id", data.transactionId);
  605. }, [&](const ActionGiftStars &data) {
  606. pushActor();
  607. pushAction("send_stars_gift");
  608. if (!data.cost.isEmpty()) {
  609. push("cost", data.cost);
  610. }
  611. if (data.credits) {
  612. push("stars", data.credits);
  613. }
  614. }, [&](const ActionPrizeStars &data) {
  615. pushActor();
  616. pushAction("stars_prize");
  617. push("boost_peer_id", data.peerId);
  618. pushBare("boost_peer_name", wrapPeerName(data.peerId));
  619. push("stars", data.amount);
  620. push("is_unclaimed", data.isUnclaimed);
  621. push("giveaway_msg_id", data.giveawayMsgId);
  622. push("transaction_id", data.transactionId);
  623. }, [&](const ActionStarGift &data) {
  624. pushActor();
  625. pushAction("send_star_gift");
  626. push("gift_id", data.giftId);
  627. push("stars", data.stars);
  628. push("is_limited", data.limited);
  629. push("is_anonymous", data.anonymous);
  630. pushBare("gift_text", SerializeText(context, data.text));
  631. }, [](v::null_t) {});
  632. if (v::is_null(message.action.content)) {
  633. pushFrom();
  634. push("author", message.signature);
  635. if (message.forwardedFromId) {
  636. pushBare(
  637. "forwarded_from",
  638. wrapPeerName(message.forwardedFromId));
  639. } else if (!message.forwardedFromName.isEmpty()) {
  640. pushBare(
  641. "forwarded_from",
  642. StringAllowNull(message.forwardedFromName));
  643. }
  644. if (message.savedFromChatId) {
  645. pushBare("saved_from", wrapPeerName(message.savedFromChatId));
  646. }
  647. pushReplyToMsgId();
  648. if (message.viaBotId) {
  649. const auto username = FormatUsername(
  650. user(message.viaBotId).username);
  651. if (!username.isEmpty()) {
  652. push("via_bot", username);
  653. }
  654. }
  655. }
  656. v::match(message.media.content, [&](const Photo &photo) {
  657. pushPhoto(photo.image);
  658. pushSpoiler(photo);
  659. pushTTL();
  660. }, [&](const Document &data) {
  661. pushPath(data.file, "file");
  662. push("file_name", data.name);
  663. push("file_size", data.file.size);
  664. if (data.thumb.width > 0) {
  665. pushPath(data.thumb.file, "thumbnail");
  666. push("thumbnail_file_size", data.thumb.file.size);
  667. }
  668. const auto pushType = [&](const QByteArray &value) {
  669. push("media_type", value);
  670. };
  671. if (data.isSticker) {
  672. pushType("sticker");
  673. push("sticker_emoji", data.stickerEmoji);
  674. } else if (data.isVideoMessage) {
  675. pushType("video_message");
  676. } else if (data.isVoiceMessage) {
  677. pushType("voice_message");
  678. } else if (data.isAnimated) {
  679. pushType("animation");
  680. } else if (data.isVideoFile) {
  681. pushType("video_file");
  682. } else if (data.isAudioFile) {
  683. pushType("audio_file");
  684. push("performer", data.songPerformer);
  685. push("title", data.songTitle);
  686. }
  687. push("mime_type", data.mime);
  688. if (data.duration) {
  689. push("duration_seconds", data.duration);
  690. }
  691. if (data.width && data.height) {
  692. push("width", data.width);
  693. push("height", data.height);
  694. }
  695. pushSpoiler(data);
  696. pushTTL();
  697. }, [&](const SharedContact &data) {
  698. pushBare("contact_information", SerializeObject(context, {
  699. { "first_name", SerializeString(data.info.firstName) },
  700. { "last_name", SerializeString(data.info.lastName) },
  701. {
  702. "phone_number",
  703. SerializeString(FormatPhoneNumber(data.info.phoneNumber))
  704. }
  705. }));
  706. if (!data.vcard.content.isEmpty()) {
  707. pushPath(data.vcard, "contact_vcard");
  708. push("contact_vcard_file_size", data.vcard.size);
  709. }
  710. }, [&](const GeoPoint &data) {
  711. pushBare(
  712. "location_information",
  713. data.valid ? SerializeObject(context, {
  714. { "latitude", NumberToString(data.latitude) },
  715. { "longitude", NumberToString(data.longitude) },
  716. }) : QByteArray("null"));
  717. pushTTL("live_location_period_seconds");
  718. }, [&](const Venue &data) {
  719. push("place_name", data.title);
  720. push("address", data.address);
  721. if (data.point.valid) {
  722. pushBare("location_information", SerializeObject(context, {
  723. { "latitude", NumberToString(data.point.latitude) },
  724. { "longitude", NumberToString(data.point.longitude) },
  725. }));
  726. }
  727. }, [&](const Game &data) {
  728. push("game_title", data.title);
  729. push("game_description", data.description);
  730. if (data.botId != 0 && !data.shortName.isEmpty()) {
  731. const auto bot = user(data.botId);
  732. if (bot.isBot && !bot.username.isEmpty()) {
  733. push("game_link", internalLinksDomain.toUtf8()
  734. + bot.username
  735. + "?game="
  736. + data.shortName);
  737. }
  738. }
  739. }, [&](const Invoice &data) {
  740. push("invoice_information", SerializeObject(context, {
  741. { "title", SerializeString(data.title) },
  742. { "description", SerializeString(data.description) },
  743. { "amount", NumberToString(data.amount) },
  744. { "currency", SerializeString(data.currency) },
  745. { "receipt_message_id", (data.receiptMsgId
  746. ? NumberToString(data.receiptMsgId)
  747. : QByteArray()) }
  748. }));
  749. }, [&](const Poll &data) {
  750. context.nesting.push_back(Context::kObject);
  751. const auto answers = ranges::views::all(
  752. data.answers
  753. ) | ranges::views::transform([&](const Poll::Answer &answer) {
  754. context.nesting.push_back(Context::kArray);
  755. auto result = SerializeObject(context, {
  756. { "text", SerializeString(answer.text) },
  757. { "voters", NumberToString(answer.votes) },
  758. { "chosen", answer.my ? "true" : "false" },
  759. });
  760. context.nesting.pop_back();
  761. return result;
  762. }) | ranges::to_vector;
  763. const auto serialized = SerializeArray(context, answers);
  764. context.nesting.pop_back();
  765. pushBare("poll", SerializeObject(context, {
  766. { "question", SerializeString(data.question) },
  767. { "closed", data.closed ? "true" : "false" },
  768. { "total_voters", NumberToString(data.totalVotes) },
  769. { "answers", serialized }
  770. }));
  771. }, [&](const GiveawayStart &data) {
  772. context.nesting.push_back(Context::kArray);
  773. const auto channels = ranges::views::all(
  774. data.channels
  775. ) | ranges::views::transform([&](ChannelId id) {
  776. return NumberToString(id.bare);
  777. }) | ranges::to_vector;
  778. const auto serialized = SerializeArray(context, channels);
  779. context.nesting.pop_back();
  780. context.nesting.push_back(Context::kArray);
  781. const auto countries = ranges::views::all(
  782. data.countries
  783. ) | ranges::views::transform([&](const QString &code) {
  784. return SerializeString(code.toUtf8());
  785. }) | ranges::to_vector;
  786. const auto serializedCountries = SerializeArray(context, countries);
  787. context.nesting.pop_back();
  788. const auto additionalPrize = data.additionalPrize.toUtf8();
  789. pushBare("giveaway_information", SerializeObject(context, {
  790. { "quantity", NumberToString(data.quantity) },
  791. { "months", NumberToString(data.months) },
  792. { "until_date", SerializeDate(data.untilDate) },
  793. { "channels", serialized },
  794. { "countries", serializedCountries },
  795. { "additional_prize", SerializeString(additionalPrize) },
  796. { "stars", NumberToString(data.credits) },
  797. { "is_only_new_subscribers", (!data.all) ? "true" : "false" },
  798. }));
  799. }, [&](const GiveawayResults &data) {
  800. context.nesting.push_back(Context::kArray);
  801. const auto winners = ranges::views::all(
  802. data.winners
  803. ) | ranges::views::transform([&](PeerId id) {
  804. return NumberToString(id.value);
  805. }) | ranges::to_vector;
  806. const auto serialized = SerializeArray(context, winners);
  807. context.nesting.pop_back();
  808. const auto additionalPrize = data.additionalPrize.toUtf8();
  809. const auto peersCount = data.additionalPeersCount;
  810. pushBare("giveaway_results", SerializeObject(context, {
  811. { "channel", NumberToString(data.channel.bare) },
  812. { "winners", serialized },
  813. { "additional_prize", SerializeString(additionalPrize) },
  814. { "until_date", SerializeDate(data.untilDate) },
  815. { "launch_message_id", NumberToString(data.launchId) },
  816. { "additional_peers_count", NumberToString(peersCount) },
  817. { "winners_count", NumberToString(data.winnersCount) },
  818. { "unclaimed_count", NumberToString(data.unclaimedCount) },
  819. { "months", NumberToString(data.months) },
  820. { "stars", NumberToString(data.credits) },
  821. { "is_refunded", data.refunded ? "true" : "false" },
  822. { "is_only_new_subscribers", (!data.all) ? "true" : "false" },
  823. }));
  824. }, [&](const PaidMedia &data) {
  825. push("paid_stars_amount", data.stars);
  826. }, [](const UnsupportedMedia &data) {
  827. Unexpected("Unsupported message.");
  828. }, [](v::null_t) {});
  829. pushBare("text", SerializeText(context, message.text));
  830. pushBare("text_entities", SerializeText(context, message.text, true));
  831. if (!message.inlineButtonRows.empty()) {
  832. const auto serializeRow = [&](
  833. const std::vector<HistoryMessageMarkupButton> &row) {
  834. context.nesting.push_back(Context::kArray);
  835. const auto buttons = ranges::views::all(
  836. row
  837. ) | ranges::views::transform([&](
  838. const HistoryMessageMarkupButton &entry) {
  839. auto pairs = std::vector<std::pair<QByteArray, QByteArray>>();
  840. pairs.push_back({
  841. "type",
  842. SerializeString(
  843. HistoryMessageMarkupButton::TypeToString(entry)),
  844. });
  845. if (!entry.text.isEmpty()) {
  846. pairs.push_back({
  847. "text",
  848. SerializeString(entry.text.toUtf8()),
  849. });
  850. }
  851. if (!entry.data.isEmpty()) {
  852. using Type = HistoryMessageMarkupButton::Type;
  853. const auto isCallback = (entry.type == Type::Callback)
  854. || (entry.type == Type::CallbackWithPassword);
  855. const auto data = isCallback
  856. ? entry.data.toBase64(QByteArray::Base64UrlEncoding
  857. | QByteArray::OmitTrailingEquals)
  858. : entry.data;
  859. if (isCallback) {
  860. pairs.push_back({
  861. "dataBase64",
  862. SerializeString(data),
  863. });
  864. pairs.push_back({ "data", SerializeString({}) });
  865. } else {
  866. pairs.push_back({ "data", SerializeString(data) });
  867. }
  868. }
  869. if (!entry.forwardText.isEmpty()) {
  870. pairs.push_back({
  871. "forward_text",
  872. SerializeString(entry.forwardText.toUtf8()),
  873. });
  874. }
  875. if (entry.buttonId) {
  876. pairs.push_back({
  877. "button_id",
  878. NumberToString(entry.buttonId),
  879. });
  880. }
  881. return SerializeObject(context, pairs);
  882. }) | ranges::to_vector;
  883. context.nesting.pop_back();
  884. return SerializeArray(context, buttons);
  885. };
  886. context.nesting.push_back(Context::kArray);
  887. const auto rows = ranges::views::all(
  888. message.inlineButtonRows
  889. ) | ranges::views::transform(serializeRow) | ranges::to_vector;
  890. context.nesting.pop_back();
  891. pushBare("inline_bot_buttons", SerializeArray(context, rows));
  892. }
  893. if (!message.reactions.empty()) {
  894. const auto serializeReaction = [&](const Reaction &reaction) {
  895. context.nesting.push_back(Context::kObject);
  896. const auto guard = gsl::finally([&] {
  897. context.nesting.pop_back();
  898. });
  899. auto pairs = std::vector<std::pair<QByteArray, QByteArray>>();
  900. pairs.push_back({
  901. "type",
  902. SerializeString(Reaction::TypeToString(reaction)),
  903. });
  904. pairs.push_back({
  905. "count",
  906. NumberToString(reaction.count),
  907. });
  908. switch (reaction.type) {
  909. case Reaction::Type::Emoji:
  910. pairs.push_back({
  911. "emoji",
  912. SerializeString(reaction.emoji.toUtf8()),
  913. });
  914. break;
  915. case Reaction::Type::CustomEmoji:
  916. pairs.push_back({
  917. "document_id",
  918. SerializeString(reaction.documentId),
  919. });
  920. break;
  921. }
  922. if (!reaction.recent.empty()) {
  923. context.nesting.push_back(Context::kArray);
  924. const auto recents = ranges::views::all(
  925. reaction.recent
  926. ) | ranges::views::transform([&](
  927. const Reaction::Recent &recent) {
  928. context.nesting.push_back(Context::kArray);
  929. const auto guard = gsl::finally([&] {
  930. context.nesting.pop_back();
  931. });
  932. return SerializeObject(context, {
  933. { "from", wrapPeerName(recent.peerId) },
  934. { "from_id", wrapPeerId(recent.peerId) },
  935. { "date", SerializeDate(recent.date) },
  936. });
  937. }) | ranges::to_vector;
  938. pairs.push_back({"recent", SerializeArray(context, recents)});
  939. context.nesting.pop_back();
  940. }
  941. return SerializeObject(context, pairs);
  942. };
  943. context.nesting.push_back(Context::kArray);
  944. const auto reactions = ranges::views::all(
  945. message.reactions
  946. ) | ranges::views::transform(serializeReaction) | ranges::to_vector;
  947. pushBare("reactions", SerializeArray(context, reactions));
  948. context.nesting.pop_back();
  949. }
  950. return serialized();
  951. }
  952. } // namespace
  953. Result JsonWriter::start(
  954. const Settings &settings,
  955. const Environment &environment,
  956. Stats *stats) {
  957. Expects(_output == nullptr);
  958. Expects(settings.path.endsWith('/'));
  959. _settings = base::duplicate(settings);
  960. _environment = environment;
  961. _stats = stats;
  962. _output = fileWithRelativePath(mainFileRelativePath());
  963. if (_settings.onlySinglePeer()) {
  964. return Result::Success();
  965. }
  966. auto block = pushNesting(Context::kObject);
  967. block.append(prepareObjectItemStart("about"));
  968. block.append(SerializeString(_environment.aboutTelegram));
  969. return _output->writeBlock(block);
  970. }
  971. QByteArray JsonWriter::pushNesting(Context::Type type) {
  972. Expects(_output != nullptr);
  973. _context.nesting.push_back(type);
  974. _currentNestingHadItem = false;
  975. return (type == Context::kObject ? "{" : "[");
  976. }
  977. QByteArray JsonWriter::prepareObjectItemStart(const QByteArray &key) {
  978. const auto guard = gsl::finally([&] { _currentNestingHadItem = true; });
  979. return (_currentNestingHadItem ? ",\n" : "\n")
  980. + Indentation(_context)
  981. + SerializeString(key)
  982. + ": ";
  983. }
  984. QByteArray JsonWriter::prepareArrayItemStart() {
  985. const auto guard = gsl::finally([&] { _currentNestingHadItem = true; });
  986. return (_currentNestingHadItem ? ",\n" : "\n") + Indentation(_context);
  987. }
  988. QByteArray JsonWriter::popNesting() {
  989. Expects(_output != nullptr);
  990. Expects(!_context.nesting.empty());
  991. const auto type = Context::Type(_context.nesting.back());
  992. _context.nesting.pop_back();
  993. _currentNestingHadItem = true;
  994. return '\n'
  995. + Indentation(_context)
  996. + (type == Context::kObject ? '}' : ']');
  997. }
  998. Result JsonWriter::writePersonal(const Data::PersonalInfo &data) {
  999. Expects(_output != nullptr);
  1000. const auto &info = data.user.info;
  1001. return _output->writeBlock(
  1002. prepareObjectItemStart("personal_information")
  1003. + SerializeObject(_context, {
  1004. { "user_id", Data::NumberToString(data.user.bareId) },
  1005. { "first_name", SerializeString(info.firstName) },
  1006. { "last_name", SerializeString(info.lastName) },
  1007. {
  1008. "phone_number",
  1009. SerializeString(Data::FormatPhoneNumber(info.phoneNumber))
  1010. },
  1011. {
  1012. "username",
  1013. (!data.user.username.isEmpty()
  1014. ? SerializeString(FormatUsername(data.user.username))
  1015. : QByteArray())
  1016. },
  1017. {
  1018. "bio",
  1019. (!data.bio.isEmpty()
  1020. ? SerializeString(data.bio)
  1021. : QByteArray())
  1022. },
  1023. }));
  1024. }
  1025. Result JsonWriter::writeUserpicsStart(const Data::UserpicsInfo &data) {
  1026. Expects(_output != nullptr);
  1027. auto block = prepareObjectItemStart("profile_pictures");
  1028. return _output->writeBlock(block + pushNesting(Context::kArray));
  1029. }
  1030. Result JsonWriter::writeUserpicsSlice(const Data::UserpicsSlice &data) {
  1031. Expects(_output != nullptr);
  1032. Expects(!data.list.empty());
  1033. auto block = QByteArray();
  1034. for (const auto &userpic : data.list) {
  1035. using SkipReason = Data::File::SkipReason;
  1036. const auto &file = userpic.image.file;
  1037. Assert(!file.relativePath.isEmpty()
  1038. || file.skipReason != SkipReason::None);
  1039. const auto path = [&]() -> Data::Utf8String {
  1040. switch (file.skipReason) {
  1041. case SkipReason::Unavailable:
  1042. return "(Photo unavailable, please try again later)";
  1043. case SkipReason::FileSize:
  1044. return "(Photo exceeds maximum size. "
  1045. "Change data exporting settings to download.)";
  1046. case SkipReason::FileType:
  1047. return "(Photo not included. "
  1048. "Change data exporting settings to download.)";
  1049. case SkipReason::None: return FormatFilePath(file);
  1050. }
  1051. Unexpected("Skip reason while writing photo path.");
  1052. }();
  1053. block.append(prepareArrayItemStart());
  1054. block.append(SerializeObject(_context, {
  1055. {
  1056. "date",
  1057. userpic.date ? SerializeDate(userpic.date) : QByteArray()
  1058. },
  1059. {
  1060. "date_unixtime",
  1061. userpic.date ? SerializeDateRaw(userpic.date) : QByteArray()
  1062. },
  1063. {
  1064. "photo",
  1065. SerializeString(path)
  1066. },
  1067. }));
  1068. }
  1069. return _output->writeBlock(block);
  1070. }
  1071. Result JsonWriter::writeUserpicsEnd() {
  1072. Expects(_output != nullptr);
  1073. return _output->writeBlock(popNesting());
  1074. }
  1075. Result JsonWriter::writeStoriesStart(const Data::StoriesInfo &data) {
  1076. Expects(_output != nullptr);
  1077. auto block = prepareObjectItemStart("stories");
  1078. return _output->writeBlock(block + pushNesting(Context::kArray));
  1079. }
  1080. Result JsonWriter::writeStoriesSlice(const Data::StoriesSlice &data) {
  1081. Expects(_output != nullptr);
  1082. if (data.list.empty()) {
  1083. return Result::Success();
  1084. }
  1085. auto block = QByteArray();
  1086. for (const auto &story : data.list) {
  1087. using SkipReason = Data::File::SkipReason;
  1088. const auto &file = story.file();
  1089. Assert(!file.relativePath.isEmpty()
  1090. || file.skipReason != SkipReason::None);
  1091. const auto path = [&]() -> Data::Utf8String {
  1092. switch (file.skipReason) {
  1093. case SkipReason::Unavailable:
  1094. return "(Photo unavailable, please try again later)";
  1095. case SkipReason::FileSize:
  1096. return "(Photo exceeds maximum size. "
  1097. "Change data exporting settings to download.)";
  1098. case SkipReason::FileType:
  1099. return "(Photo not included. "
  1100. "Change data exporting settings to download.)";
  1101. case SkipReason::None: return FormatFilePath(file);
  1102. }
  1103. Unexpected("Skip reason while writing story path.");
  1104. }();
  1105. block.append(prepareArrayItemStart());
  1106. block.append(SerializeObject(_context, {
  1107. {
  1108. "date",
  1109. story.date ? SerializeDate(story.date) : QByteArray()
  1110. },
  1111. {
  1112. "date_unixtime",
  1113. story.date ? SerializeDateRaw(story.date) : QByteArray()
  1114. },
  1115. {
  1116. "expires",
  1117. story.expires ? SerializeDate(story.expires) : QByteArray()
  1118. },
  1119. {
  1120. "expires_unixtime",
  1121. story.expires ? SerializeDateRaw(story.expires) : QByteArray()
  1122. },
  1123. {
  1124. "pinned",
  1125. story.pinned ? "true" : "false"
  1126. },
  1127. {
  1128. "media",
  1129. SerializeString(path)
  1130. },
  1131. }));
  1132. }
  1133. return _output->writeBlock(block);
  1134. }
  1135. Result JsonWriter::writeStoriesEnd() {
  1136. Expects(_output != nullptr);
  1137. return _output->writeBlock(popNesting());
  1138. }
  1139. Result JsonWriter::writeContactsList(const Data::ContactsList &data) {
  1140. Expects(_output != nullptr);
  1141. if (const auto result = writeSavedContacts(data); !result) {
  1142. return result;
  1143. } else if (const auto result = writeFrequentContacts(data); !result) {
  1144. return result;
  1145. }
  1146. return Result::Success();
  1147. }
  1148. Result JsonWriter::writeSavedContacts(const Data::ContactsList &data) {
  1149. Expects(_output != nullptr);
  1150. auto block = prepareObjectItemStart("contacts");
  1151. block.append(pushNesting(Context::kObject));
  1152. block.append(prepareObjectItemStart("about"));
  1153. block.append(SerializeString(_environment.aboutContacts));
  1154. block.append(prepareObjectItemStart("list"));
  1155. block.append(pushNesting(Context::kArray));
  1156. for (const auto index : Data::SortedContactsIndices(data)) {
  1157. const auto &contact = data.list[index];
  1158. block.append(prepareArrayItemStart());
  1159. if (contact.firstName.isEmpty()
  1160. && contact.lastName.isEmpty()
  1161. && contact.phoneNumber.isEmpty()) {
  1162. block.append(SerializeObject(_context, {
  1163. { "date", SerializeDate(contact.date) },
  1164. { "date_unixtime", SerializeDateRaw(contact.date) },
  1165. }));
  1166. } else {
  1167. block.append(SerializeObject(_context, {
  1168. {
  1169. "user_id",
  1170. (contact.userId
  1171. ? Data::NumberToString(contact.userId.bare)
  1172. : QByteArray())
  1173. },
  1174. { "first_name", SerializeString(contact.firstName) },
  1175. { "last_name", SerializeString(contact.lastName) },
  1176. {
  1177. "phone_number",
  1178. SerializeString(
  1179. Data::FormatPhoneNumber(contact.phoneNumber))
  1180. },
  1181. { "date", SerializeDate(contact.date) },
  1182. { "date_unixtime", SerializeDateRaw(contact.date) },
  1183. }));
  1184. }
  1185. }
  1186. block.append(popNesting());
  1187. return _output->writeBlock(block + popNesting());
  1188. }
  1189. Result JsonWriter::writeFrequentContacts(const Data::ContactsList &data) {
  1190. Expects(_output != nullptr);
  1191. auto block = prepareObjectItemStart("frequent_contacts");
  1192. block.append(pushNesting(Context::kObject));
  1193. block.append(prepareObjectItemStart("about"));
  1194. block.append(SerializeString(_environment.aboutFrequent));
  1195. block.append(prepareObjectItemStart("list"));
  1196. block.append(pushNesting(Context::kArray));
  1197. const auto writeList = [&](
  1198. const std::vector<Data::TopPeer> &peers,
  1199. Data::Utf8String category) {
  1200. for (const auto &top : peers) {
  1201. const auto type = [&] {
  1202. if (const auto chat = top.peer.chat()) {
  1203. return chat->username.isEmpty()
  1204. ? (chat->isBroadcast
  1205. ? "private_channel"
  1206. : (chat->isSupergroup
  1207. ? "private_supergroup"
  1208. : "private_group"))
  1209. : (chat->isBroadcast
  1210. ? "public_channel"
  1211. : "public_supergroup");
  1212. }
  1213. return "user";
  1214. }();
  1215. block.append(prepareArrayItemStart());
  1216. block.append(SerializeObject(_context, {
  1217. { "id", Data::NumberToString(Data::PeerToBareId(top.peer.id())) },
  1218. { "category", SerializeString(category) },
  1219. { "type", SerializeString(type) },
  1220. { "name", StringAllowNull(top.peer.name()) },
  1221. { "rating", Data::NumberToString(top.rating) },
  1222. }));
  1223. }
  1224. };
  1225. writeList(data.correspondents, "people");
  1226. writeList(data.inlineBots, "inline_bots");
  1227. writeList(data.phoneCalls, "calls");
  1228. block.append(popNesting());
  1229. return _output->writeBlock(block + popNesting());
  1230. }
  1231. Result JsonWriter::writeSessionsList(const Data::SessionsList &data) {
  1232. Expects(_output != nullptr);
  1233. if (const auto result = writeSessions(data); !result) {
  1234. return result;
  1235. } else if (const auto result = writeWebSessions(data); !result) {
  1236. return result;
  1237. }
  1238. return Result::Success();
  1239. }
  1240. Result JsonWriter::writeOtherData(const Data::File &data) {
  1241. Expects(_output != nullptr);
  1242. Expects(data.skipReason == Data::File::SkipReason::None);
  1243. Expects(!data.relativePath.isEmpty());
  1244. QFile f(pathWithRelativePath(data.relativePath));
  1245. if (!f.open(QIODevice::ReadOnly)) {
  1246. return Result(Result::Type::FatalError, f.fileName());
  1247. }
  1248. const auto content = f.readAll();
  1249. if (content.isEmpty()) {
  1250. return Result::Success();
  1251. }
  1252. auto error = QJsonParseError{ 0, QJsonParseError::NoError };
  1253. const auto document = QJsonDocument::fromJson(content, &error);
  1254. if (error.error != QJsonParseError::NoError) {
  1255. return Result(Result::Type::FatalError, f.fileName());
  1256. }
  1257. auto block = prepareObjectItemStart("other_data");
  1258. Fn<void(const QJsonObject &data)> pushObject;
  1259. Fn<void(const QJsonArray &data)> pushArray;
  1260. Fn<void(const QJsonValue &data)> pushValue;
  1261. pushObject = [&](const QJsonObject &data) {
  1262. block.append(pushNesting(Context::kObject));
  1263. for (auto i = data.begin(); i != data.end(); ++i) {
  1264. if ((*i).type() != QJsonValue::Undefined) {
  1265. block.append(prepareObjectItemStart(i.key().toUtf8()));
  1266. pushValue(*i);
  1267. }
  1268. }
  1269. block.append(popNesting());
  1270. };
  1271. pushArray = [&](const QJsonArray &data) {
  1272. block.append(pushNesting(Context::kArray));
  1273. for (auto i = data.begin(); i != data.end(); ++i) {
  1274. if ((*i).type() != QJsonValue::Undefined) {
  1275. block.append(prepareArrayItemStart());
  1276. pushValue(*i);
  1277. }
  1278. }
  1279. block.append(popNesting());
  1280. };
  1281. pushValue = [&](const QJsonValue &data) {
  1282. switch (data.type()) {
  1283. case QJsonValue::Null:
  1284. block.append("null");
  1285. return;
  1286. case QJsonValue::Bool:
  1287. block.append(data.toBool() ? "true" : "false");
  1288. return;
  1289. case QJsonValue::Double:
  1290. block.append(Data::NumberToString(data.toDouble()));
  1291. return;
  1292. case QJsonValue::String:
  1293. block.append(SerializeString(data.toString().toUtf8()));
  1294. return;
  1295. case QJsonValue::Array:
  1296. return pushArray(data.toArray());
  1297. case QJsonValue::Object:
  1298. return pushObject(data.toObject());
  1299. }
  1300. Unexpected("Type of json valuein JsonWriter::writeOtherData.");
  1301. };
  1302. if (document.isObject()) {
  1303. pushObject(document.object());
  1304. } else {
  1305. pushArray(document.array());
  1306. }
  1307. return _output->writeBlock(block);
  1308. }
  1309. Result JsonWriter::writeSessions(const Data::SessionsList &data) {
  1310. Expects(_output != nullptr);
  1311. auto block = prepareObjectItemStart("sessions");
  1312. block.append(pushNesting(Context::kObject));
  1313. block.append(prepareObjectItemStart("about"));
  1314. block.append(SerializeString(_environment.aboutSessions));
  1315. block.append(prepareObjectItemStart("list"));
  1316. block.append(pushNesting(Context::kArray));
  1317. for (const auto &session : data.list) {
  1318. block.append(prepareArrayItemStart());
  1319. block.append(SerializeObject(_context, {
  1320. { "last_active", SerializeDate(session.lastActive) },
  1321. { "last_active_unixtime", SerializeDateRaw(session.lastActive) },
  1322. { "last_ip", SerializeString(session.ip) },
  1323. { "last_country", SerializeString(session.country) },
  1324. { "last_region", SerializeString(session.region) },
  1325. {
  1326. "application_name",
  1327. StringAllowNull(session.applicationName)
  1328. },
  1329. {
  1330. "application_version",
  1331. StringAllowEmpty(session.applicationVersion)
  1332. },
  1333. { "device_model", SerializeString(session.deviceModel) },
  1334. { "platform", SerializeString(session.platform) },
  1335. { "system_version", SerializeString(session.systemVersion) },
  1336. { "created", SerializeDate(session.created) },
  1337. { "created_unixtime", SerializeDateRaw(session.created) },
  1338. }));
  1339. }
  1340. block.append(popNesting());
  1341. return _output->writeBlock(block + popNesting());
  1342. }
  1343. Result JsonWriter::writeWebSessions(const Data::SessionsList &data) {
  1344. Expects(_output != nullptr);
  1345. auto block = prepareObjectItemStart("web_sessions");
  1346. block.append(pushNesting(Context::kObject));
  1347. block.append(prepareObjectItemStart("about"));
  1348. block.append(SerializeString(_environment.aboutWebSessions));
  1349. block.append(prepareObjectItemStart("list"));
  1350. block.append(pushNesting(Context::kArray));
  1351. for (const auto &session : data.webList) {
  1352. block.append(prepareArrayItemStart());
  1353. block.append(SerializeObject(_context, {
  1354. { "last_active", SerializeDate(session.lastActive) },
  1355. { "last_active_unixtime", SerializeDateRaw(session.lastActive) },
  1356. { "last_ip", SerializeString(session.ip) },
  1357. { "last_region", SerializeString(session.region) },
  1358. { "bot_username", StringAllowNull(session.botUsername) },
  1359. { "domain_name", StringAllowNull(session.domain) },
  1360. { "browser", SerializeString(session.browser) },
  1361. { "platform", SerializeString(session.platform) },
  1362. { "created", SerializeDate(session.created) },
  1363. { "created_unixtime", SerializeDateRaw(session.created) },
  1364. }));
  1365. }
  1366. block.append(popNesting());
  1367. return _output->writeBlock(block + popNesting());
  1368. }
  1369. Result JsonWriter::writeDialogsStart(const Data::DialogsInfo &data) {
  1370. return Result::Success();
  1371. }
  1372. Result JsonWriter::writeDialogStart(const Data::DialogInfo &data) {
  1373. Expects(_output != nullptr);
  1374. if (!_settings.onlySinglePeer()) {
  1375. const auto result = validateDialogsMode(data.isLeftChannel);
  1376. if (!result) {
  1377. return result;
  1378. }
  1379. }
  1380. using Type = Data::DialogInfo::Type;
  1381. const auto TypeString = [](Type type) {
  1382. switch (type) {
  1383. case Type::Unknown: return "";
  1384. case Type::Self: return "saved_messages";
  1385. case Type::Replies: return "replies";
  1386. case Type::VerifyCodes: return "verification_codes";
  1387. case Type::Personal: return "personal_chat";
  1388. case Type::Bot: return "bot_chat";
  1389. case Type::PrivateGroup: return "private_group";
  1390. case Type::PrivateSupergroup: return "private_supergroup";
  1391. case Type::PublicSupergroup: return "public_supergroup";
  1392. case Type::PrivateChannel: return "private_channel";
  1393. case Type::PublicChannel: return "public_channel";
  1394. }
  1395. Unexpected("Dialog type in TypeString.");
  1396. };
  1397. auto block = _settings.onlySinglePeer()
  1398. ? QByteArray()
  1399. : prepareArrayItemStart();
  1400. block.append(pushNesting(Context::kObject));
  1401. if (data.type != Type::Self
  1402. && data.type != Type::Replies
  1403. && data.type != Type::VerifyCodes) {
  1404. block.append(prepareObjectItemStart("name")
  1405. + StringAllowNull(data.name));
  1406. }
  1407. block.append(prepareObjectItemStart("type")
  1408. + StringAllowNull(TypeString(data.type)));
  1409. block.append(prepareObjectItemStart("id")
  1410. + Data::NumberToString(Data::PeerToBareId(data.peerId)));
  1411. block.append(prepareObjectItemStart("messages"));
  1412. block.append(pushNesting(Context::kArray));
  1413. return _output->writeBlock(block);
  1414. }
  1415. Result JsonWriter::validateDialogsMode(bool isLeftChannel) {
  1416. const auto mode = isLeftChannel
  1417. ? DialogsMode::Left
  1418. : DialogsMode::Chats;
  1419. if (_dialogsMode == mode) {
  1420. return Result::Success();
  1421. } else if (_dialogsMode != DialogsMode::None) {
  1422. if (const auto result = writeChatsEnd(); !result) {
  1423. return result;
  1424. }
  1425. }
  1426. _dialogsMode = mode;
  1427. return writeChatsStart(
  1428. isLeftChannel ? "left_chats" : "chats",
  1429. (isLeftChannel
  1430. ? _environment.aboutLeftChats
  1431. : _environment.aboutChats));
  1432. }
  1433. Result JsonWriter::writeDialogSlice(const Data::MessagesSlice &data) {
  1434. Expects(_output != nullptr);
  1435. auto block = QByteArray();
  1436. for (const auto &message : data.list) {
  1437. if (Data::SkipMessageByDate(message, _settings)) {
  1438. continue;
  1439. }
  1440. block.append(prepareArrayItemStart() + SerializeMessage(
  1441. _context,
  1442. message,
  1443. data.peers,
  1444. _environment.internalLinksDomain));
  1445. }
  1446. return block.isEmpty() ? Result::Success() : _output->writeBlock(block);
  1447. }
  1448. Result JsonWriter::writeDialogEnd() {
  1449. Expects(_output != nullptr);
  1450. auto block = popNesting();
  1451. return _output->writeBlock(block + popNesting());
  1452. }
  1453. Result JsonWriter::writeDialogsEnd() {
  1454. if (_settings.onlySinglePeer()) {
  1455. return Result::Success();
  1456. }
  1457. return writeChatsEnd();
  1458. }
  1459. Result JsonWriter::writeChatsStart(
  1460. const QByteArray &listName,
  1461. const QByteArray &about) {
  1462. Expects(_output != nullptr);
  1463. auto block = prepareObjectItemStart(listName);
  1464. block.append(pushNesting(Context::kObject));
  1465. block.append(prepareObjectItemStart("about"));
  1466. block.append(SerializeString(about));
  1467. block.append(prepareObjectItemStart("list"));
  1468. return _output->writeBlock(block + pushNesting(Context::kArray));
  1469. }
  1470. Result JsonWriter::writeChatsEnd() {
  1471. Expects(_output != nullptr);
  1472. auto block = popNesting();
  1473. return _output->writeBlock(block + popNesting());
  1474. }
  1475. Result JsonWriter::finish() {
  1476. Expects(_output != nullptr);
  1477. if (_settings.onlySinglePeer()) {
  1478. Assert(_context.nesting.empty());
  1479. return Result::Success();
  1480. }
  1481. auto block = popNesting();
  1482. Assert(_context.nesting.empty());
  1483. return _output->writeBlock(block);
  1484. }
  1485. QString JsonWriter::mainFilePath() {
  1486. return pathWithRelativePath(mainFileRelativePath());
  1487. }
  1488. QString JsonWriter::mainFileRelativePath() const {
  1489. return "result.json";
  1490. }
  1491. QString JsonWriter::pathWithRelativePath(const QString &path) const {
  1492. return _settings.path + path;
  1493. }
  1494. std::unique_ptr<File> JsonWriter::fileWithRelativePath(
  1495. const QString &path) const {
  1496. return std::make_unique<File>(pathWithRelativePath(path), _stats);
  1497. }
  1498. } // namespace Output
  1499. } // namespace Export