export_output_abstract.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  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_abstract.h"
  8. #include "export/output/export_output_html_and_json.h"
  9. #include "export/output/export_output_html.h"
  10. #include "export/output/export_output_json.h"
  11. #include "export/output/export_output_stats.h"
  12. #include "export/output/export_output_result.h"
  13. #include <QtCore/QDir>
  14. #include <QtCore/QDate>
  15. namespace Export {
  16. namespace Output {
  17. QString NormalizePath(const Settings &settings) {
  18. QDir folder(settings.path);
  19. const auto path = folder.absolutePath();
  20. auto result = path.endsWith('/') ? path : (path + '/');
  21. if (!folder.exists() && !settings.forceSubPath) {
  22. return result;
  23. }
  24. const auto mode = QDir::AllEntries | QDir::NoDotAndDotDot;
  25. const auto list = folder.entryInfoList(mode);
  26. if (list.isEmpty() && !settings.forceSubPath) {
  27. return result;
  28. }
  29. const auto date = QDate::currentDate();
  30. const auto base = QString(settings.onlySinglePeer()
  31. ? "ChatExport_%1"
  32. : "DataExport_%1"
  33. ).arg(date.toString(Qt::ISODate));
  34. const auto add = [&](int i) {
  35. return base + (i ? " (" + QString::number(i) + ')' : QString());
  36. };
  37. auto index = 0;
  38. while (QDir(result + add(index)).exists()) {
  39. ++index;
  40. }
  41. result += add(index) + '/';
  42. return result;
  43. }
  44. std::unique_ptr<AbstractWriter> CreateWriter(Format format) {
  45. switch (format) {
  46. case Format::Html: return std::make_unique<HtmlWriter>();
  47. case Format::Json: return std::make_unique<JsonWriter>();
  48. case Format::HtmlAndJson: return std::make_unique<HtmlAndJsonWriter>();
  49. }
  50. Unexpected("Format in Export::Output::CreateWriter.");
  51. }
  52. Stats AbstractWriter::produceTestExample(
  53. const QString &path,
  54. const Environment &environment) {
  55. auto result = Stats();
  56. const auto folder = QDir(path).absolutePath();
  57. auto settings = Settings();
  58. settings.format = format();
  59. settings.path = (folder.endsWith('/') ? folder : (folder + '/'))
  60. + "ExportExample/";
  61. settings.types = Settings::Type::AllMask;
  62. settings.fullChats = Settings::Type::AllMask
  63. & ~(Settings::Type::PublicChannels | Settings::Type::PublicGroups);
  64. settings.media.types = MediaSettings::Type::AllMask;
  65. settings.media.sizeLimit = 1024 * 1024;
  66. const auto check = [](Result result) {
  67. Assert(result.isSuccess());
  68. };
  69. check(start(settings, environment, &result));
  70. const auto counter = [&] {
  71. static auto GlobalCounter = 0;
  72. return ++GlobalCounter;
  73. };
  74. const auto date = [&] {
  75. return time(nullptr) - 86400 + counter();
  76. };
  77. const auto prevdate = [&] {
  78. return date() - 86400;
  79. };
  80. auto personal = Data::PersonalInfo();
  81. personal.bio = "Nice text about me.";
  82. personal.user.info.firstName = "John";
  83. personal.user.info.lastName = "Preston";
  84. personal.user.info.phoneNumber = "447400000000";
  85. personal.user.info.date = date();
  86. personal.user.username = "preston";
  87. personal.user.info.userId = counter();
  88. personal.user.isBot = false;
  89. personal.user.isSelf = true;
  90. check(writePersonal(personal));
  91. const auto generatePhoto = [&] {
  92. static auto index = 0;
  93. auto result = Data::Photo();
  94. result.date = date();
  95. result.id = counter();
  96. result.image.width = 512;
  97. result.image.height = 512;
  98. result.image.file.relativePath = "files/photo_"
  99. + QString::number(++index)
  100. + ".jpg";
  101. return result;
  102. };
  103. auto userpics = Data::UserpicsInfo();
  104. userpics.count = 3;
  105. auto userpicsSlice1 = Data::UserpicsSlice();
  106. userpicsSlice1.list.push_back(generatePhoto());
  107. userpicsSlice1.list.push_back(generatePhoto());
  108. auto userpicsSlice2 = Data::UserpicsSlice();
  109. userpicsSlice2.list.push_back(generatePhoto());
  110. check(writeUserpicsStart(userpics));
  111. check(writeUserpicsSlice(userpicsSlice1));
  112. check(writeUserpicsSlice(userpicsSlice2));
  113. check(writeUserpicsEnd());
  114. auto contacts = Data::ContactsList();
  115. auto topUser = Data::TopPeer();
  116. auto user = personal.user;
  117. auto peerUser = Data::Peer{ user };
  118. topUser.peer = peerUser;
  119. topUser.rating = 0.5;
  120. auto topChat = Data::TopPeer();
  121. auto chat = Data::Chat();
  122. chat.bareId = counter();
  123. chat.title = "Group chat";
  124. auto peerChat = Data::Peer{ chat };
  125. topChat.peer = peerChat;
  126. topChat.rating = 0.25;
  127. auto topBot = Data::TopPeer();
  128. auto bot = Data::User();
  129. bot.info.date = date();
  130. bot.isBot = true;
  131. bot.info.firstName = "Bot";
  132. bot.info.lastName = "Father";
  133. bot.info.userId = counter();
  134. bot.username = "botfather";
  135. auto peerBot = Data::Peer{ bot };
  136. topBot.peer = peerBot;
  137. topBot.rating = 0.125;
  138. auto peers = std::map<PeerId, Data::Peer>();
  139. peers.emplace(peerUser.id(), peerUser);
  140. peers.emplace(peerBot.id(), peerBot);
  141. peers.emplace(peerChat.id(), peerChat);
  142. contacts.correspondents.push_back(topUser);
  143. contacts.correspondents.push_back(topChat);
  144. contacts.inlineBots.push_back(topBot);
  145. contacts.inlineBots.push_back(topBot);
  146. contacts.phoneCalls.push_back(topUser);
  147. contacts.list.push_back(user.info);
  148. contacts.list.push_back(bot.info);
  149. check(writeContactsList(contacts));
  150. auto sessions = Data::SessionsList();
  151. auto session = Data::Session();
  152. session.applicationName = "Telegram Desktop";
  153. session.applicationVersion = "1.3.8";
  154. session.country = "GB";
  155. session.created = date();
  156. session.deviceModel = "PC";
  157. session.ip = "127.0.0.1";
  158. session.lastActive = date();
  159. session.platform = "Windows";
  160. session.region = "London";
  161. session.systemVersion = "10";
  162. sessions.list.push_back(session);
  163. sessions.list.push_back(session);
  164. auto webSession = Data::WebSession();
  165. webSession.botUsername = "botfather";
  166. webSession.browser = "Google Chrome";
  167. webSession.created = date();
  168. webSession.domain = "telegram.org";
  169. webSession.ip = "127.0.0.1";
  170. webSession.lastActive = date();
  171. webSession.platform = "Windows";
  172. webSession.region = "London, GB";
  173. sessions.webList.push_back(webSession);
  174. sessions.webList.push_back(webSession);
  175. check(writeSessionsList(sessions));
  176. auto sampleMessage = [&] {
  177. auto message = Data::Message();
  178. message.id = counter();
  179. message.date = prevdate();
  180. message.edited = date();
  181. static auto count = 0;
  182. if (++count % 3 == 0) {
  183. message.forwardedFromId = peerFromUser(user.info.userId);
  184. message.forwardedDate = date();
  185. } else if (count % 3 == 2) {
  186. message.forwardedFromName = "Test hidden forward";
  187. message.forwardedDate = date();
  188. }
  189. message.fromId = user.info.userId;
  190. message.replyToMsgId = counter();
  191. message.viaBotId = bot.info.userId;
  192. message.text.push_back(Data::TextPart{
  193. Data::TextPart::Type::Text,
  194. ("Text message " + QString::number(counter())).toUtf8()
  195. });
  196. return message;
  197. };
  198. auto sliceBot1 = Data::MessagesSlice();
  199. sliceBot1.peers = peers;
  200. sliceBot1.list.push_back(sampleMessage());
  201. sliceBot1.list.push_back([&] {
  202. auto message = sampleMessage();
  203. message.media.content = generatePhoto();
  204. message.media.ttl = counter();
  205. return message;
  206. }());
  207. sliceBot1.list.push_back([&] {
  208. auto message = sampleMessage();
  209. auto document = Data::Document();
  210. document.date = prevdate();
  211. document.duration = counter();
  212. auto photo = generatePhoto();
  213. document.file = photo.image.file;
  214. document.width = photo.image.width;
  215. document.height = photo.image.height;
  216. document.id = counter();
  217. message.media.content = document;
  218. return message;
  219. }());
  220. sliceBot1.list.push_back([&] {
  221. auto message = sampleMessage();
  222. auto contact = Data::SharedContact();
  223. contact.info = user.info;
  224. message.media.content = contact;
  225. return message;
  226. }());
  227. auto sliceBot2 = Data::MessagesSlice();
  228. sliceBot2.peers = peers;
  229. sliceBot2.list.push_back([&] {
  230. auto message = sampleMessage();
  231. auto point = Data::GeoPoint();
  232. point.latitude = 1.5;
  233. point.longitude = 2.8;
  234. point.valid = true;
  235. message.media.content = point;
  236. message.media.ttl = counter();
  237. return message;
  238. }());
  239. sliceBot2.list.push_back([&] {
  240. auto message = sampleMessage();
  241. message.replyToMsgId = sliceBot1.list.back().id;
  242. auto venue = Data::Venue();
  243. venue.point.latitude = 1.5;
  244. venue.point.longitude = 2.8;
  245. venue.point.valid = true;
  246. venue.address = "Test address";
  247. venue.title = "Test venue";
  248. message.media.content = venue;
  249. return message;
  250. }());
  251. sliceBot2.list.push_back([&] {
  252. auto message = sampleMessage();
  253. auto game = Data::Game();
  254. game.botId = bot.info.userId;
  255. game.title = "Test game";
  256. game.description = "Test game description";
  257. game.id = counter();
  258. game.shortName = "testgame";
  259. message.media.content = game;
  260. return message;
  261. }());
  262. sliceBot2.list.push_back([&] {
  263. auto message = sampleMessage();
  264. auto invoice = Data::Invoice();
  265. invoice.amount = counter();
  266. invoice.currency = "GBP";
  267. invoice.title = "Huge invoice.";
  268. invoice.description = "So money.";
  269. invoice.receiptMsgId = sliceBot2.list.front().id;
  270. message.media.content = invoice;
  271. return message;
  272. }());
  273. auto serviceMessage = [&] {
  274. auto message = Data::Message();
  275. message.id = counter();
  276. message.date = prevdate();
  277. message.fromId = user.info.userId;
  278. return message;
  279. };
  280. auto sliceChat1 = Data::MessagesSlice();
  281. sliceChat1.peers = peers;
  282. sliceChat1.list.push_back([&] {
  283. auto message = serviceMessage();
  284. auto action = Data::ActionChatCreate();
  285. action.title = "Test chat";
  286. action.userIds.push_back(user.info.userId);
  287. action.userIds.push_back(bot.info.userId);
  288. message.action.content = action;
  289. return message;
  290. }());
  291. sliceChat1.list.push_back([&] {
  292. auto message = serviceMessage();
  293. auto action = Data::ActionChatEditTitle();
  294. action.title = "New title";
  295. message.action.content = action;
  296. return message;
  297. }());
  298. sliceChat1.list.push_back([&] {
  299. auto message = serviceMessage();
  300. auto action = Data::ActionChatEditPhoto();
  301. action.photo = generatePhoto();
  302. message.action.content = action;
  303. return message;
  304. }());
  305. sliceChat1.list.push_back([&] {
  306. auto message = serviceMessage();
  307. auto action = Data::ActionChatDeletePhoto();
  308. message.action.content = action;
  309. return message;
  310. }());
  311. sliceChat1.list.push_back([&] {
  312. auto message = serviceMessage();
  313. auto action = Data::ActionChatAddUser();
  314. action.userIds.push_back(user.info.userId);
  315. action.userIds.push_back(bot.info.userId);
  316. message.action.content = action;
  317. return message;
  318. }());
  319. sliceChat1.list.push_back([&] {
  320. auto message = serviceMessage();
  321. auto action = Data::ActionChatDeleteUser();
  322. action.userId = bot.info.userId;
  323. message.action.content = action;
  324. return message;
  325. }());
  326. sliceChat1.list.push_back([&] {
  327. auto message = serviceMessage();
  328. auto action = Data::ActionChatJoinedByLink();
  329. action.inviterId = bot.info.userId;
  330. message.action.content = action;
  331. return message;
  332. }());
  333. sliceChat1.list.push_back([&] {
  334. auto message = serviceMessage();
  335. auto action = Data::ActionChannelCreate();
  336. action.title = "Channel name";
  337. message.action.content = action;
  338. return message;
  339. }());
  340. sliceChat1.list.push_back([&] {
  341. auto message = serviceMessage();
  342. auto action = Data::ActionChatMigrateTo();
  343. action.channelId = ChannelId(chat.bareId);
  344. message.action.content = action;
  345. return message;
  346. }());
  347. sliceChat1.list.push_back([&] {
  348. auto message = serviceMessage();
  349. auto action = Data::ActionChannelMigrateFrom();
  350. action.chatId = ChatId(chat.bareId);
  351. action.title = "Supergroup now";
  352. message.action.content = action;
  353. return message;
  354. }());
  355. auto sliceChat2 = Data::MessagesSlice();
  356. sliceChat2.peers = peers;
  357. sliceChat2.list.push_back([&] {
  358. auto message = serviceMessage();
  359. auto action = Data::ActionPinMessage();
  360. message.replyToMsgId = sliceChat1.list.back().id;
  361. message.action.content = action;
  362. return message;
  363. }());
  364. sliceChat2.list.push_back([&] {
  365. auto message = serviceMessage();
  366. auto action = Data::ActionHistoryClear();
  367. message.action.content = action;
  368. return message;
  369. }());
  370. sliceChat2.list.push_back([&] {
  371. auto message = serviceMessage();
  372. auto action = Data::ActionGameScore();
  373. action.score = counter();
  374. action.gameId = counter();
  375. message.replyToMsgId = sliceChat2.list.back().id;
  376. message.action.content = action;
  377. return message;
  378. }());
  379. sliceChat2.list.push_back([&] {
  380. auto message = serviceMessage();
  381. auto action = Data::ActionPaymentSent();
  382. action.amount = counter();
  383. action.currency = "GBP";
  384. message.replyToMsgId = sliceChat2.list.front().id;
  385. message.action.content = action;
  386. return message;
  387. }());
  388. sliceChat2.list.push_back([&] {
  389. auto message = serviceMessage();
  390. auto action = Data::ActionPhoneCall();
  391. action.duration = counter();
  392. action.discardReason = Data::ActionPhoneCall::DiscardReason::Busy;
  393. message.action.content = action;
  394. return message;
  395. }());
  396. sliceChat2.list.push_back([&] {
  397. auto message = serviceMessage();
  398. auto action = Data::ActionScreenshotTaken();
  399. message.action.content = action;
  400. return message;
  401. }());
  402. sliceChat2.list.push_back([&] {
  403. auto message = serviceMessage();
  404. auto action = Data::ActionCustomAction();
  405. action.message = "Custom chat action.";
  406. message.action.content = action;
  407. return message;
  408. }());
  409. sliceChat2.list.push_back([&] {
  410. auto message = serviceMessage();
  411. auto action = Data::ActionBotAllowed();
  412. action.domain = "telegram.org";
  413. message.action.content = action;
  414. return message;
  415. }());
  416. sliceChat2.list.push_back([&] {
  417. auto message = serviceMessage();
  418. auto action = Data::ActionSecureValuesSent();
  419. using Type = Data::ActionSecureValuesSent::Type;
  420. action.types.push_back(Type::BankStatement);
  421. action.types.push_back(Type::Phone);
  422. message.action.content = action;
  423. return message;
  424. }());
  425. sliceChat2.list.push_back([&] {
  426. auto message = serviceMessage();
  427. auto action = Data::ActionContactSignUp();
  428. message.action.content = action;
  429. return message;
  430. }());
  431. auto dialogs = Data::DialogsInfo();
  432. auto dialogBot = Data::DialogInfo();
  433. dialogBot.messagesCountPerSplit.push_back(sliceBot1.list.size());
  434. dialogBot.messagesCountPerSplit.push_back(sliceBot2.list.size());
  435. dialogBot.type = Data::DialogInfo::Type::Bot;
  436. dialogBot.name = peerBot.name();
  437. dialogBot.onlyMyMessages = false;
  438. dialogBot.peerId = peerBot.id();
  439. dialogBot.relativePath = "chats/chat_"
  440. + QString::number(counter())
  441. + '/';
  442. dialogBot.splits.push_back(0);
  443. dialogBot.splits.push_back(1);
  444. dialogBot.topMessageDate = sliceBot2.list.back().date;
  445. dialogBot.topMessageId = sliceBot2.list.back().id;
  446. auto dialogChat = Data::DialogInfo();
  447. dialogChat.messagesCountPerSplit.push_back(sliceChat1.list.size());
  448. dialogChat.messagesCountPerSplit.push_back(sliceChat2.list.size());
  449. dialogChat.type = Data::DialogInfo::Type::PrivateGroup;
  450. dialogChat.name = peerChat.name();
  451. dialogChat.onlyMyMessages = true;
  452. dialogChat.peerId = peerChat.id();
  453. dialogChat.relativePath = "chats/chat_"
  454. + QString::number(counter())
  455. + '/';
  456. dialogChat.splits.push_back(0);
  457. dialogChat.splits.push_back(1);
  458. dialogChat.topMessageDate = sliceChat2.list.back().date;
  459. dialogChat.topMessageId = sliceChat2.list.back().id;
  460. dialogs.chats.push_back(dialogBot);
  461. dialogs.chats.push_back(dialogChat);
  462. check(writeDialogsStart(dialogs));
  463. check(writeDialogStart(dialogBot));
  464. check(writeDialogSlice(sliceBot1));
  465. check(writeDialogSlice(sliceBot2));
  466. check(writeDialogEnd());
  467. check(writeDialogStart(dialogChat));
  468. check(writeDialogSlice(sliceChat1));
  469. check(writeDialogSlice(sliceChat2));
  470. check(writeDialogEnd());
  471. check(writeDialogsEnd());
  472. check(finish());
  473. return result;
  474. }
  475. } // namespace Output
  476. } // namespace Export