data_shortcut_messages.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  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 "data/business/data_shortcut_messages.h"
  8. #include "api/api_hash.h"
  9. #include "apiwrap.h"
  10. #include "base/unixtime.h"
  11. #include "data/data_peer.h"
  12. #include "data/data_session.h"
  13. #include "api/api_text_entities.h"
  14. #include "main/main_session.h"
  15. #include "history/history.h"
  16. #include "history/history_item_components.h"
  17. #include "history/history_item_helpers.h"
  18. #include "apiwrap.h"
  19. namespace Data {
  20. namespace {
  21. constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
  22. [[nodiscard]] MsgId RemoteToLocalMsgId(MsgId id) {
  23. Expects(IsServerMsgId(id));
  24. return ScheduledMaxMsgId + id + 1;
  25. }
  26. [[nodiscard]] MsgId LocalToRemoteMsgId(MsgId id) {
  27. Expects(IsShortcutMsgId(id));
  28. return (id - ScheduledMaxMsgId - 1);
  29. }
  30. [[nodiscard]] bool TooEarlyForRequest(crl::time received) {
  31. return (received > 0) && (received + kRequestTimeLimit > crl::now());
  32. }
  33. [[nodiscard]] MTPMessage PrepareMessage(
  34. BusinessShortcutId shortcutId,
  35. const MTPMessage &message) {
  36. return message.match([&](const MTPDmessageEmpty &data) {
  37. return MTP_messageEmpty(
  38. data.vflags(),
  39. data.vid(),
  40. data.vpeer_id() ? *data.vpeer_id() : MTPPeer());
  41. }, [&](const MTPDmessageService &data) {
  42. return MTP_messageService(
  43. data.vflags(),
  44. data.vid(),
  45. data.vfrom_id() ? *data.vfrom_id() : MTPPeer(),
  46. data.vpeer_id(),
  47. data.vreply_to() ? *data.vreply_to() : MTPMessageReplyHeader(),
  48. data.vdate(),
  49. data.vaction(),
  50. data.vreactions() ? *data.vreactions() : MTPMessageReactions(),
  51. MTP_int(data.vttl_period().value_or_empty()));
  52. }, [&](const MTPDmessage &data) {
  53. return MTP_message(
  54. MTP_flags(data.vflags().v
  55. | MTPDmessage::Flag::f_quick_reply_shortcut_id),
  56. data.vid(),
  57. data.vfrom_id() ? *data.vfrom_id() : MTPPeer(),
  58. MTPint(), // from_boosts_applied
  59. data.vpeer_id(),
  60. data.vsaved_peer_id() ? *data.vsaved_peer_id() : MTPPeer(),
  61. data.vfwd_from() ? *data.vfwd_from() : MTPMessageFwdHeader(),
  62. MTP_long(data.vvia_bot_id().value_or_empty()),
  63. MTP_long(data.vvia_business_bot_id().value_or_empty()),
  64. data.vreply_to() ? *data.vreply_to() : MTPMessageReplyHeader(),
  65. data.vdate(),
  66. data.vmessage(),
  67. data.vmedia() ? *data.vmedia() : MTPMessageMedia(),
  68. data.vreply_markup() ? *data.vreply_markup() : MTPReplyMarkup(),
  69. (data.ventities()
  70. ? *data.ventities()
  71. : MTPVector<MTPMessageEntity>()),
  72. MTP_int(data.vviews().value_or_empty()),
  73. MTP_int(data.vforwards().value_or_empty()),
  74. data.vreplies() ? *data.vreplies() : MTPMessageReplies(),
  75. MTP_int(data.vedit_date().value_or_empty()),
  76. MTP_bytes(data.vpost_author().value_or_empty()),
  77. MTP_long(data.vgrouped_id().value_or_empty()),
  78. MTPMessageReactions(),
  79. MTPVector<MTPRestrictionReason>(),
  80. MTP_int(data.vttl_period().value_or_empty()),
  81. MTP_int(shortcutId),
  82. MTP_long(data.veffect().value_or_empty()),
  83. (data.vfactcheck() ? *data.vfactcheck() : MTPFactCheck()),
  84. MTP_int(data.vreport_delivery_until_date().value_or_empty()),
  85. MTP_long(data.vpaid_message_stars().value_or_empty()));
  86. });
  87. }
  88. } // namespace
  89. bool IsShortcutMsgId(MsgId id) {
  90. return (id > ScheduledMaxMsgId) && (id < ShortcutMaxMsgId);
  91. }
  92. ShortcutMessages::ShortcutMessages(not_null<Session*> owner)
  93. : _session(&owner->session())
  94. , _history(owner->history(_session->userPeerId()))
  95. , _clearTimer([=] { clearOldRequests(); }) {
  96. owner->itemRemoved(
  97. ) | rpl::filter([](not_null<const HistoryItem*> item) {
  98. return item->isBusinessShortcut();
  99. }) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
  100. remove(item);
  101. }, _lifetime);
  102. }
  103. ShortcutMessages::~ShortcutMessages() {
  104. for (const auto &request : _requests) {
  105. _session->api().request(request.second.requestId).cancel();
  106. }
  107. }
  108. void ShortcutMessages::clearOldRequests() {
  109. const auto now = crl::now();
  110. while (true) {
  111. const auto i = ranges::find_if(_requests, [&](const auto &value) {
  112. const auto &request = value.second;
  113. return !request.requestId
  114. && (request.lastReceived + kRequestTimeLimit <= now);
  115. });
  116. if (i == end(_requests)) {
  117. break;
  118. }
  119. _requests.erase(i);
  120. }
  121. }
  122. void ShortcutMessages::updateShortcuts(const QVector<MTPQuickReply> &list) {
  123. auto shortcuts = parseShortcuts(list);
  124. auto changes = std::vector<ShortcutIdChange>();
  125. for (auto &[id, shortcut] : _shortcuts.list) {
  126. if (shortcuts.list.contains(id)) {
  127. continue;
  128. }
  129. auto foundId = BusinessShortcutId();
  130. for (auto &[realId, real] : shortcuts.list) {
  131. if (real.name == shortcut.name) {
  132. foundId = realId;
  133. break;
  134. }
  135. }
  136. if (foundId) {
  137. mergeMessagesFromTo(id, foundId);
  138. changes.push_back({ .oldId = id, .newId = foundId });
  139. } else {
  140. shortcuts.list.emplace(id, shortcut);
  141. }
  142. }
  143. const auto changed = !_shortcutsLoaded
  144. || (shortcuts != _shortcuts);
  145. if (changed) {
  146. _shortcuts = std::move(shortcuts);
  147. _shortcutsLoaded = true;
  148. for (const auto &change : changes) {
  149. _shortcutIdChanges.fire_copy(change);
  150. }
  151. _shortcutsChanged.fire({});
  152. } else {
  153. Assert(changes.empty());
  154. }
  155. }
  156. void ShortcutMessages::mergeMessagesFromTo(
  157. BusinessShortcutId fromId,
  158. BusinessShortcutId toId) {
  159. auto &to = _data[toId];
  160. const auto i = _data.find(fromId);
  161. if (i == end(_data)) {
  162. return;
  163. }
  164. auto &from = i->second;
  165. auto destroy = base::flat_set<not_null<HistoryItem*>>();
  166. for (auto &item : from.items) {
  167. if (item->isSending() || item->hasFailed()) {
  168. item->setRealShortcutId(toId);
  169. to.items.push_back(std::move(item));
  170. } else {
  171. destroy.emplace(item.get());
  172. }
  173. }
  174. for (const auto &item : destroy) {
  175. item->destroy();
  176. }
  177. _data.remove(fromId);
  178. cancelRequest(fromId);
  179. _updates.fire_copy(toId);
  180. if (!destroy.empty()) {
  181. cancelRequest(toId);
  182. request(toId);
  183. }
  184. }
  185. Shortcuts ShortcutMessages::parseShortcuts(
  186. const QVector<MTPQuickReply> &list) const {
  187. auto result = Shortcuts();
  188. for (const auto &reply : list) {
  189. const auto shortcut = parseShortcut(reply);
  190. result.list.emplace(shortcut.id, shortcut);
  191. }
  192. return result;
  193. }
  194. Shortcut ShortcutMessages::parseShortcut(const MTPQuickReply &reply) const {
  195. const auto &data = reply.data();
  196. return Shortcut{
  197. .id = BusinessShortcutId(data.vshortcut_id().v),
  198. .count = data.vcount().v,
  199. .name = qs(data.vshortcut()),
  200. .topMessageId = localMessageId(data.vtop_message().v),
  201. };
  202. }
  203. MsgId ShortcutMessages::localMessageId(MsgId remoteId) const {
  204. return RemoteToLocalMsgId(remoteId);
  205. }
  206. MsgId ShortcutMessages::lookupId(not_null<const HistoryItem*> item) const {
  207. Expects(item->isBusinessShortcut());
  208. Expects(!item->isSending());
  209. Expects(!item->hasFailed());
  210. return LocalToRemoteMsgId(item->id);
  211. }
  212. int ShortcutMessages::count(BusinessShortcutId shortcutId) const {
  213. const auto i = _data.find(shortcutId);
  214. return (i != end(_data)) ? i->second.items.size() : 0;
  215. }
  216. void ShortcutMessages::apply(const MTPDupdateQuickReplies &update) {
  217. updateShortcuts(update.vquick_replies().v);
  218. scheduleShortcutsReload();
  219. }
  220. void ShortcutMessages::scheduleShortcutsReload() {
  221. const auto hasUnknownMessages = [&] {
  222. const auto selfId = _session->userPeerId();
  223. for (const auto &[id, shortcut] : _shortcuts.list) {
  224. if (!_session->data().message({ selfId, shortcut.topMessageId })) {
  225. return true;
  226. }
  227. }
  228. return false;
  229. };
  230. if (hasUnknownMessages()) {
  231. _shortcutsLoaded = false;
  232. const auto cancelledId = base::take(_shortcutsRequestId);
  233. _session->api().request(cancelledId).cancel();
  234. crl::on_main(_session, [=] {
  235. if (cancelledId || hasUnknownMessages()) {
  236. preloadShortcuts();
  237. }
  238. });
  239. }
  240. }
  241. void ShortcutMessages::apply(const MTPDupdateNewQuickReply &update) {
  242. const auto &reply = update.vquick_reply();
  243. auto foundId = BusinessShortcutId();
  244. const auto shortcut = parseShortcut(reply);
  245. for (auto &[id, existing] : _shortcuts.list) {
  246. if (id == shortcut.id) {
  247. foundId = id;
  248. break;
  249. } else if (existing.name == shortcut.name) {
  250. foundId = id;
  251. break;
  252. }
  253. }
  254. if (foundId == shortcut.id) {
  255. auto &already = _shortcuts.list[shortcut.id];
  256. if (already != shortcut) {
  257. already = shortcut;
  258. _shortcutsChanged.fire({});
  259. }
  260. return;
  261. } else if (foundId) {
  262. _shortcuts.list.emplace(shortcut.id, shortcut);
  263. mergeMessagesFromTo(foundId, shortcut.id);
  264. _shortcuts.list.remove(foundId);
  265. _shortcutIdChanges.fire({ foundId, shortcut.id });
  266. _shortcutsChanged.fire({});
  267. }
  268. }
  269. void ShortcutMessages::apply(const MTPDupdateQuickReplyMessage &update) {
  270. const auto &message = update.vmessage();
  271. const auto shortcutId = BusinessShortcutIdFromMessage(message);
  272. if (!shortcutId) {
  273. return;
  274. }
  275. const auto loaded = _data.contains(shortcutId);
  276. auto &list = _data[shortcutId];
  277. append(shortcutId, list, message);
  278. sort(list);
  279. _updates.fire_copy(shortcutId);
  280. updateCount(shortcutId);
  281. if (!loaded) {
  282. request(shortcutId);
  283. }
  284. }
  285. void ShortcutMessages::updateCount(BusinessShortcutId shortcutId) {
  286. const auto i = _data.find(shortcutId);
  287. const auto j = _shortcuts.list.find(shortcutId);
  288. if (j == end(_shortcuts.list)) {
  289. return;
  290. }
  291. const auto count = (i != end(_data))
  292. ? int(i->second.itemById.size())
  293. : 0;
  294. if (j->second.count != count) {
  295. _shortcuts.list[shortcutId].count = count;
  296. _shortcutsChanged.fire({});
  297. }
  298. }
  299. void ShortcutMessages::apply(
  300. const MTPDupdateDeleteQuickReplyMessages &update) {
  301. const auto shortcutId = update.vshortcut_id().v;
  302. if (!shortcutId) {
  303. return;
  304. }
  305. auto i = _data.find(shortcutId);
  306. if (i == end(_data)) {
  307. return;
  308. }
  309. for (const auto &id : update.vmessages().v) {
  310. const auto &list = i->second;
  311. const auto j = list.itemById.find(id.v);
  312. if (j != end(list.itemById)) {
  313. j->second->destroy();
  314. i = _data.find(shortcutId);
  315. if (i == end(_data)) {
  316. break;
  317. }
  318. }
  319. }
  320. _updates.fire_copy(shortcutId);
  321. updateCount(shortcutId);
  322. cancelRequest(shortcutId);
  323. request(shortcutId);
  324. }
  325. void ShortcutMessages::apply(const MTPDupdateDeleteQuickReply &update) {
  326. const auto shortcutId = update.vshortcut_id().v;
  327. if (!shortcutId) {
  328. return;
  329. }
  330. auto i = _data.find(shortcutId);
  331. while (i != end(_data) && !i->second.itemById.empty()) {
  332. i->second.itemById.back().second->destroy();
  333. i = _data.find(shortcutId);
  334. }
  335. _updates.fire_copy(shortcutId);
  336. if (_data.contains(shortcutId)) {
  337. updateCount(shortcutId);
  338. } else {
  339. _shortcuts.list.remove(shortcutId);
  340. _shortcutIdChanges.fire({ shortcutId, 0 });
  341. }
  342. }
  343. void ShortcutMessages::apply(
  344. const MTPDupdateMessageID &update,
  345. not_null<HistoryItem*> local) {
  346. const auto id = update.vid().v;
  347. const auto i = _data.find(local->shortcutId());
  348. Assert(i != end(_data));
  349. auto &list = i->second;
  350. const auto j = list.itemById.find(id);
  351. if (j != end(list.itemById) || !IsServerMsgId(id)) {
  352. local->destroy();
  353. } else {
  354. Assert(!list.itemById.contains(local->id));
  355. local->setRealId(localMessageId(id));
  356. list.itemById.emplace(id, local);
  357. }
  358. }
  359. void ShortcutMessages::appendSending(not_null<HistoryItem*> item) {
  360. Expects(item->isSending());
  361. Expects(item->isBusinessShortcut());
  362. const auto shortcutId = item->shortcutId();
  363. auto &list = _data[shortcutId];
  364. list.items.emplace_back(item);
  365. sort(list);
  366. _updates.fire_copy(shortcutId);
  367. }
  368. void ShortcutMessages::removeSending(not_null<HistoryItem*> item) {
  369. Expects(item->isSending() || item->hasFailed());
  370. Expects(item->isBusinessShortcut());
  371. item->destroy();
  372. }
  373. rpl::producer<> ShortcutMessages::updates(BusinessShortcutId shortcutId) {
  374. request(shortcutId);
  375. return _updates.events(
  376. ) | rpl::filter([=](BusinessShortcutId value) {
  377. return (value == shortcutId);
  378. }) | rpl::to_empty;
  379. }
  380. Data::MessagesSlice ShortcutMessages::list(BusinessShortcutId shortcutId) {
  381. auto result = Data::MessagesSlice();
  382. const auto i = _data.find(shortcutId);
  383. if (i == end(_data)) {
  384. const auto i = _requests.find(shortcutId);
  385. if (i == end(_requests)) {
  386. return result;
  387. }
  388. result.fullCount = result.skippedAfter = result.skippedBefore = 0;
  389. return result;
  390. }
  391. const auto &list = i->second.items;
  392. result.skippedAfter = result.skippedBefore = 0;
  393. result.fullCount = int(list.size());
  394. result.ids = ranges::views::all(
  395. list
  396. ) | ranges::views::transform(
  397. &HistoryItem::fullId
  398. ) | ranges::to_vector;
  399. return result;
  400. }
  401. void ShortcutMessages::preloadShortcuts() {
  402. if (_shortcutsLoaded || _shortcutsRequestId) {
  403. return;
  404. }
  405. const auto owner = &_session->data();
  406. _shortcutsRequestId = owner->session().api().request(
  407. MTPmessages_GetQuickReplies(MTP_long(_shortcutsHash))
  408. ).done([=](const MTPmessages_QuickReplies &result) {
  409. result.match([&](const MTPDmessages_quickReplies &data) {
  410. owner->processUsers(data.vusers());
  411. owner->processChats(data.vchats());
  412. updateShortcuts(data.vquick_replies().v);
  413. }, [&](const MTPDmessages_quickRepliesNotModified &) {
  414. if (!_shortcutsLoaded) {
  415. _shortcutsLoaded = true;
  416. _shortcutsChanged.fire({});
  417. }
  418. });
  419. }).send();
  420. }
  421. const Shortcuts &ShortcutMessages::shortcuts() const {
  422. return _shortcuts;
  423. }
  424. bool ShortcutMessages::shortcutsLoaded() const {
  425. return _shortcutsLoaded;
  426. }
  427. rpl::producer<> ShortcutMessages::shortcutsChanged() const {
  428. return _shortcutsChanged.events();
  429. }
  430. auto ShortcutMessages::shortcutIdChanged() const
  431. -> rpl::producer<ShortcutIdChange> {
  432. return _shortcutIdChanges.events();
  433. }
  434. BusinessShortcutId ShortcutMessages::emplaceShortcut(QString name) {
  435. Expects(_shortcutsLoaded);
  436. for (auto &[id, shortcut] : _shortcuts.list) {
  437. if (shortcut.name == name) {
  438. return id;
  439. }
  440. }
  441. const auto result = --_localShortcutId;
  442. _shortcuts.list.emplace(result, Shortcut{ .id = result, .name = name });
  443. return result;
  444. }
  445. Shortcut ShortcutMessages::lookupShortcut(BusinessShortcutId id) const {
  446. const auto i = _shortcuts.list.find(id);
  447. Ensures(i != end(_shortcuts.list));
  448. return i->second;
  449. }
  450. BusinessShortcutId ShortcutMessages::lookupShortcutId(
  451. const QString &name) const {
  452. for (const auto &[id, shortcut] : _shortcuts.list) {
  453. if (!shortcut.name.compare(name, Qt::CaseInsensitive)) {
  454. return id;
  455. }
  456. }
  457. return {};
  458. }
  459. void ShortcutMessages::editShortcut(
  460. BusinessShortcutId id,
  461. QString name,
  462. Fn<void()> done,
  463. Fn<void(QString)> fail) {
  464. name = name.trimmed();
  465. if (name.isEmpty()) {
  466. fail(QString());
  467. return;
  468. }
  469. const auto finish = [=] {
  470. const auto i = _shortcuts.list.find(id);
  471. if (i != end(_shortcuts.list)) {
  472. i->second.name = name;
  473. _shortcutsChanged.fire({});
  474. }
  475. done();
  476. };
  477. for (const auto &[existingId, shortcut] : _shortcuts.list) {
  478. if (shortcut.name == name) {
  479. if (existingId == id) {
  480. //done();
  481. //return;
  482. break;
  483. } else if (_data[existingId].items.empty() && !shortcut.count) {
  484. removeShortcut(existingId);
  485. break;
  486. } else {
  487. fail(u"SHORTCUT_OCCUPIED"_q);
  488. return;
  489. }
  490. }
  491. }
  492. _session->api().request(MTPmessages_EditQuickReplyShortcut(
  493. MTP_int(id),
  494. MTP_string(name)
  495. )).done(finish).fail([=](const MTP::Error &error) {
  496. const auto type = error.type();
  497. if (type == u"SHORTCUT_ID_INVALID"_q) {
  498. // Not on the server (yet).
  499. finish();
  500. } else {
  501. fail(type);
  502. }
  503. }).send();
  504. }
  505. void ShortcutMessages::removeShortcut(BusinessShortcutId shortcutId) {
  506. auto i = _data.find(shortcutId);
  507. while (i != end(_data)) {
  508. if (i->second.items.empty()) {
  509. _data.erase(i);
  510. } else {
  511. i->second.items.front()->destroy();
  512. }
  513. i = _data.find(shortcutId);
  514. }
  515. _shortcuts.list.remove(shortcutId);
  516. _shortcutIdChanges.fire({ shortcutId, 0 });
  517. _session->api().request(MTPmessages_DeleteQuickReplyShortcut(
  518. MTP_int(shortcutId)
  519. )).send();
  520. }
  521. void ShortcutMessages::cancelRequest(BusinessShortcutId shortcutId) {
  522. const auto j = _requests.find(shortcutId);
  523. if (j != end(_requests)) {
  524. _session->api().request(j->second.requestId).cancel();
  525. _requests.erase(j);
  526. }
  527. }
  528. void ShortcutMessages::request(BusinessShortcutId shortcutId) {
  529. auto &request = _requests[shortcutId];
  530. if (request.requestId || TooEarlyForRequest(request.lastReceived)) {
  531. return;
  532. }
  533. const auto i = _data.find(shortcutId);
  534. const auto hash = (i != end(_data))
  535. ? countListHash(i->second)
  536. : uint64(0);
  537. request.requestId = _session->api().request(
  538. MTPmessages_GetQuickReplyMessages(
  539. MTP_flags(0),
  540. MTP_int(shortcutId),
  541. MTPVector<MTPint>(),
  542. MTP_long(hash))
  543. ).done([=](const MTPmessages_Messages &result) {
  544. parse(shortcutId, result);
  545. }).fail([=] {
  546. _requests.remove(shortcutId);
  547. }).send();
  548. }
  549. void ShortcutMessages::parse(
  550. BusinessShortcutId shortcutId,
  551. const MTPmessages_Messages &list) {
  552. auto &request = _requests[shortcutId];
  553. request.lastReceived = crl::now();
  554. request.requestId = 0;
  555. if (!_clearTimer.isActive()) {
  556. _clearTimer.callOnce(kRequestTimeLimit * 2);
  557. }
  558. list.match([&](const MTPDmessages_messagesNotModified &data) {
  559. }, [&](const auto &data) {
  560. _session->data().processUsers(data.vusers());
  561. _session->data().processChats(data.vchats());
  562. const auto &messages = data.vmessages().v;
  563. if (messages.isEmpty()) {
  564. clearNotSending(shortcutId);
  565. return;
  566. }
  567. auto received = base::flat_set<not_null<HistoryItem*>>();
  568. auto clear = base::flat_set<not_null<HistoryItem*>>();
  569. auto &list = _data.emplace(shortcutId, List()).first->second;
  570. for (const auto &message : messages) {
  571. if (const auto item = append(shortcutId, list, message)) {
  572. received.emplace(item);
  573. }
  574. }
  575. for (const auto &owned : list.items) {
  576. const auto item = owned.get();
  577. if (!item->isSending() && !received.contains(item)) {
  578. clear.emplace(item);
  579. }
  580. }
  581. updated(shortcutId, received, clear);
  582. });
  583. }
  584. HistoryItem *ShortcutMessages::append(
  585. BusinessShortcutId shortcutId,
  586. List &list,
  587. const MTPMessage &message) {
  588. const auto id = message.match([&](const auto &data) {
  589. return data.vid().v;
  590. });
  591. const auto i = list.itemById.find(id);
  592. if (i != end(list.itemById)) {
  593. const auto existing = i->second;
  594. message.match([&](const MTPDmessage &data) {
  595. if (data.is_edit_hide()) {
  596. existing->applyEdition(HistoryMessageEdition(_session, data));
  597. } else {
  598. existing->updateSentContent({
  599. qs(data.vmessage()),
  600. Api::EntitiesFromMTP(
  601. _session,
  602. data.ventities().value_or_empty())
  603. }, data.vmedia());
  604. existing->updateReplyMarkup(
  605. HistoryMessageMarkupData(data.vreply_markup()));
  606. existing->updateForwardedInfo(data.vfwd_from());
  607. }
  608. existing->updateDate(data.vdate().v);
  609. _history->owner().requestItemTextRefresh(existing);
  610. }, [&](const auto &data) {});
  611. return existing;
  612. }
  613. if (!IsServerMsgId(id)) {
  614. LOG(("API Error: Bad id in quick reply messages: %1.").arg(id));
  615. return nullptr;
  616. }
  617. const auto item = _session->data().addNewMessage(
  618. localMessageId(id),
  619. PrepareMessage(shortcutId, message),
  620. MessageFlags(), // localFlags
  621. NewMessageType::Existing);
  622. if (!item
  623. || item->history() != _history
  624. || item->shortcutId() != shortcutId) {
  625. LOG(("API Error: Bad data received in quick reply messages."));
  626. return nullptr;
  627. }
  628. list.items.emplace_back(item);
  629. list.itemById.emplace(id, item);
  630. return item;
  631. }
  632. void ShortcutMessages::clearNotSending(BusinessShortcutId shortcutId) {
  633. const auto i = _data.find(shortcutId);
  634. if (i == end(_data)) {
  635. return;
  636. }
  637. auto clear = base::flat_set<not_null<HistoryItem*>>();
  638. for (const auto &owned : i->second.items) {
  639. if (!owned->isSending() && !owned->hasFailed()) {
  640. clear.emplace(owned.get());
  641. }
  642. }
  643. updated(shortcutId, {}, clear);
  644. }
  645. void ShortcutMessages::updated(
  646. BusinessShortcutId shortcutId,
  647. const base::flat_set<not_null<HistoryItem*>> &added,
  648. const base::flat_set<not_null<HistoryItem*>> &clear) {
  649. if (!clear.empty()) {
  650. for (const auto &item : clear) {
  651. item->destroy();
  652. }
  653. }
  654. const auto i = _data.find(shortcutId);
  655. if (i != end(_data)) {
  656. sort(i->second);
  657. }
  658. if (!added.empty() || !clear.empty()) {
  659. _updates.fire_copy(shortcutId);
  660. }
  661. }
  662. void ShortcutMessages::sort(List &list) {
  663. ranges::sort(list.items, ranges::less(), &HistoryItem::position);
  664. }
  665. void ShortcutMessages::remove(not_null<const HistoryItem*> item) {
  666. const auto shortcutId = item->shortcutId();
  667. const auto i = _data.find(shortcutId);
  668. Assert(i != end(_data));
  669. auto &list = i->second;
  670. if (!item->isSending() && !item->hasFailed()) {
  671. list.itemById.remove(lookupId(item));
  672. }
  673. const auto k = ranges::find(list.items, item, &OwnedItem::get);
  674. Assert(k != list.items.end());
  675. k->release();
  676. list.items.erase(k);
  677. if (list.items.empty()) {
  678. _data.erase(i);
  679. }
  680. _updates.fire_copy(shortcutId);
  681. updateCount(shortcutId);
  682. }
  683. uint64 ShortcutMessages::countListHash(const List &list) const {
  684. using namespace Api;
  685. auto hash = HashInit();
  686. auto &&serverside = ranges::views::all(
  687. list.items
  688. ) | ranges::views::filter([](const OwnedItem &item) {
  689. return !item->isSending() && !item->hasFailed();
  690. }) | ranges::views::reverse;
  691. for (const auto &item : serverside) {
  692. HashUpdate(hash, lookupId(item.get()).bare);
  693. if (const auto edited = item->Get<HistoryMessageEdited>()) {
  694. HashUpdate(hash, edited->date);
  695. } else {
  696. HashUpdate(hash, TimeId(0));
  697. }
  698. }
  699. return HashFinalize(hash);
  700. }
  701. MTPInputQuickReplyShortcut ShortcutIdToMTP(
  702. not_null<Main::Session*> session,
  703. BusinessShortcutId id) {
  704. return id
  705. ? MTP_inputQuickReplyShortcut(MTP_string(
  706. session->data().shortcutMessages().lookupShortcut(id).name))
  707. : MTPInputQuickReplyShortcut();
  708. }
  709. } // namespace Data