api_sending.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  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 "api/api_sending.h"
  8. #include "api/api_text_entities.h"
  9. #include "base/random.h"
  10. #include "base/unixtime.h"
  11. #include "data/business/data_shortcut_messages.h"
  12. #include "data/data_document.h"
  13. #include "data/data_photo.h"
  14. #include "data/data_channel.h" // ChannelData::addsSignature.
  15. #include "data/data_user.h" // UserData::name
  16. #include "data/data_session.h"
  17. #include "data/data_file_origin.h"
  18. #include "data/data_histories.h"
  19. #include "data/data_changes.h"
  20. #include "data/stickers/data_stickers.h"
  21. #include "history/history.h"
  22. #include "history/history_item.h"
  23. #include "history/history_item_helpers.h" // NewMessageFlags.
  24. #include "chat_helpers/message_field.h" // ConvertTextTagsToEntities.
  25. #include "chat_helpers/stickers_dice_pack.h" // DicePacks::kDiceString.
  26. #include "ui/text/text_entity.h" // TextWithEntities.
  27. #include "ui/item_text_options.h" // Ui::ItemTextOptions.
  28. #include "main/main_session.h"
  29. #include "main/main_app_config.h"
  30. #include "storage/localimageloader.h"
  31. #include "storage/file_upload.h"
  32. #include "mainwidget.h"
  33. #include "apiwrap.h"
  34. namespace Api {
  35. namespace {
  36. void InnerFillMessagePostFlags(
  37. const SendOptions &options,
  38. not_null<PeerData*> peer,
  39. MessageFlags &flags) {
  40. if (ShouldSendSilent(peer, options)) {
  41. flags |= MessageFlag::Silent;
  42. }
  43. if (!peer->amAnonymous()
  44. || (!peer->isBroadcast()
  45. && options.sendAs
  46. && options.sendAs != peer)) {
  47. flags |= MessageFlag::HasFromId;
  48. }
  49. const auto channel = peer->asBroadcast();
  50. if (!channel) {
  51. return;
  52. }
  53. flags |= MessageFlag::Post;
  54. // Don't display views and author of a new post when it's scheduled.
  55. if (options.scheduled) {
  56. return;
  57. }
  58. flags |= MessageFlag::HasViews;
  59. if (channel->addsSignature()) {
  60. flags |= MessageFlag::HasPostAuthor;
  61. }
  62. }
  63. void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
  64. const auto history = action.history;
  65. const auto peer = history->peer;
  66. const auto session = &history->session();
  67. const auto api = &session->api();
  68. action.clearDraft = false;
  69. action.generateLocal = false;
  70. api->sendAction(action);
  71. const auto randomId = base::RandomValue<uint64>();
  72. auto flags = NewMessageFlags(peer);
  73. auto sendFlags = MTPmessages_SendMedia::Flags(0);
  74. if (action.replyTo) {
  75. flags |= MessageFlag::HasReplyInfo;
  76. sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
  77. }
  78. const auto silentPost = ShouldSendSilent(peer, action.options);
  79. InnerFillMessagePostFlags(action.options, peer, flags);
  80. if (silentPost) {
  81. sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
  82. }
  83. const auto sendAs = action.options.sendAs;
  84. if (sendAs) {
  85. sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
  86. }
  87. const auto messagePostAuthor = peer->isBroadcast()
  88. ? session->user()->name()
  89. : QString();
  90. const auto starsPaid = std::min(
  91. peer->starsPerMessageChecked(),
  92. action.options.starsApproved);
  93. if (action.options.scheduled) {
  94. flags |= MessageFlag::IsOrWasScheduled;
  95. sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
  96. }
  97. if (action.options.shortcutId) {
  98. flags |= MessageFlag::ShortcutMessage;
  99. sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
  100. }
  101. if (action.options.effectId) {
  102. sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
  103. }
  104. if (action.options.invertCaption) {
  105. flags |= MessageFlag::InvertMedia;
  106. sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
  107. }
  108. if (starsPaid) {
  109. action.options.starsApproved -= starsPaid;
  110. sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
  111. }
  112. auto &histories = history->owner().histories();
  113. histories.sendPreparedMessage(
  114. history,
  115. action.replyTo,
  116. randomId,
  117. Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
  118. MTP_flags(sendFlags),
  119. peer->input,
  120. Data::Histories::ReplyToPlaceholder(),
  121. std::move(inputMedia),
  122. MTPstring(),
  123. MTP_long(randomId),
  124. MTPReplyMarkup(),
  125. MTPvector<MTPMessageEntity>(),
  126. MTP_int(action.options.scheduled),
  127. (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
  128. Data::ShortcutIdToMTP(session, action.options.shortcutId),
  129. MTP_long(action.options.effectId),
  130. MTP_long(starsPaid)
  131. ), [=](const MTPUpdates &result, const MTP::Response &response) {
  132. }, [=](const MTP::Error &error, const MTP::Response &response) {
  133. api->sendMessageFail(error, peer, randomId);
  134. });
  135. api->finishForwarding(action);
  136. }
  137. template <typename MediaData>
  138. void SendExistingMedia(
  139. MessageToSend &&message,
  140. not_null<MediaData*> media,
  141. Fn<MTPInputMedia()> inputMedia,
  142. Data::FileOrigin origin,
  143. std::optional<MsgId> localMessageId) {
  144. const auto history = message.action.history;
  145. const auto peer = history->peer;
  146. const auto session = &history->session();
  147. const auto api = &session->api();
  148. message.action.clearDraft = false;
  149. message.action.generateLocal = true;
  150. api->sendAction(message.action);
  151. const auto newId = FullMsgId(
  152. peer->id,
  153. localMessageId
  154. ? (*localMessageId)
  155. : session->data().nextLocalMessageId());
  156. const auto randomId = base::RandomValue<uint64>();
  157. auto &action = message.action;
  158. auto flags = NewMessageFlags(peer);
  159. auto sendFlags = MTPmessages_SendMedia::Flags(0);
  160. if (action.replyTo) {
  161. flags |= MessageFlag::HasReplyInfo;
  162. sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
  163. }
  164. const auto silentPost = ShouldSendSilent(peer, action.options);
  165. InnerFillMessagePostFlags(action.options, peer, flags);
  166. if (silentPost) {
  167. sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
  168. }
  169. const auto sendAs = action.options.sendAs;
  170. if (sendAs) {
  171. sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
  172. }
  173. auto caption = TextWithEntities{
  174. message.textWithTags.text,
  175. TextUtilities::ConvertTextTagsToEntities(message.textWithTags.tags)
  176. };
  177. TextUtilities::Trim(caption);
  178. auto sentEntities = EntitiesToMTP(
  179. session,
  180. caption.entities,
  181. ConvertOption::SkipLocal);
  182. if (!sentEntities.v.isEmpty()) {
  183. sendFlags |= MTPmessages_SendMedia::Flag::f_entities;
  184. }
  185. const auto captionText = caption.text;
  186. const auto starsPaid = std::min(
  187. peer->starsPerMessageChecked(),
  188. action.options.starsApproved);
  189. if (action.options.scheduled) {
  190. flags |= MessageFlag::IsOrWasScheduled;
  191. sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
  192. }
  193. if (action.options.shortcutId) {
  194. flags |= MessageFlag::ShortcutMessage;
  195. sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
  196. }
  197. if (action.options.effectId) {
  198. sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
  199. }
  200. if (action.options.invertCaption) {
  201. flags |= MessageFlag::InvertMedia;
  202. sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
  203. }
  204. if (starsPaid) {
  205. action.options.starsApproved -= starsPaid;
  206. sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
  207. }
  208. session->data().registerMessageRandomId(randomId, newId);
  209. history->addNewLocalMessage({
  210. .id = newId.msg,
  211. .flags = flags,
  212. .from = NewMessageFromId(action),
  213. .replyTo = action.replyTo,
  214. .date = NewMessageDate(action.options),
  215. .shortcutId = action.options.shortcutId,
  216. .starsPaid = starsPaid,
  217. .postAuthor = NewMessagePostAuthor(action),
  218. .effectId = action.options.effectId,
  219. }, media, caption);
  220. const auto performRequest = [=](const auto &repeatRequest) -> void {
  221. auto &histories = history->owner().histories();
  222. const auto session = &history->session();
  223. const auto usedFileReference = media->fileReference();
  224. histories.sendPreparedMessage(
  225. history,
  226. action.replyTo,
  227. randomId,
  228. Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
  229. MTP_flags(sendFlags),
  230. peer->input,
  231. Data::Histories::ReplyToPlaceholder(),
  232. inputMedia(),
  233. MTP_string(captionText),
  234. MTP_long(randomId),
  235. MTPReplyMarkup(),
  236. sentEntities,
  237. MTP_int(action.options.scheduled),
  238. (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
  239. Data::ShortcutIdToMTP(session, action.options.shortcutId),
  240. MTP_long(action.options.effectId),
  241. MTP_long(starsPaid)
  242. ), [=](const MTPUpdates &result, const MTP::Response &response) {
  243. }, [=](const MTP::Error &error, const MTP::Response &response) {
  244. if (error.code() == 400
  245. && error.type().startsWith(u"FILE_REFERENCE_"_q)) {
  246. api->refreshFileReference(origin, [=](const auto &result) {
  247. if (media->fileReference() != usedFileReference) {
  248. repeatRequest(repeatRequest);
  249. } else {
  250. api->sendMessageFail(error, peer, randomId, newId);
  251. }
  252. });
  253. } else {
  254. api->sendMessageFail(error, peer, randomId, newId);
  255. }
  256. });
  257. };
  258. performRequest(performRequest);
  259. api->finishForwarding(action);
  260. }
  261. } // namespace
  262. void SendExistingDocument(
  263. MessageToSend &&message,
  264. not_null<DocumentData*> document,
  265. std::optional<MsgId> localMessageId) {
  266. const auto inputMedia = [=] {
  267. return MTP_inputMediaDocument(
  268. MTP_flags(0),
  269. document->mtpInput(),
  270. MTPInputPhoto(), // video_cover
  271. MTPint(), // ttl_seconds
  272. MTPint(), // video_timestamp
  273. MTPstring()); // query
  274. };
  275. SendExistingMedia(
  276. std::move(message),
  277. document,
  278. inputMedia,
  279. document->stickerOrGifOrigin(),
  280. std::move(localMessageId));
  281. if (document->sticker()) {
  282. document->owner().stickers().incrementSticker(document);
  283. }
  284. }
  285. void SendExistingPhoto(
  286. MessageToSend &&message,
  287. not_null<PhotoData*> photo,
  288. std::optional<MsgId> localMessageId) {
  289. const auto inputMedia = [=] {
  290. return MTP_inputMediaPhoto(
  291. MTP_flags(0),
  292. photo->mtpInput(),
  293. MTPint());
  294. };
  295. SendExistingMedia(
  296. std::move(message),
  297. photo,
  298. inputMedia,
  299. Data::FileOrigin(),
  300. std::move(localMessageId));
  301. }
  302. bool SendDice(MessageToSend &message) {
  303. const auto full = QStringView(message.textWithTags.text).trimmed();
  304. auto length = 0;
  305. if (!Ui::Emoji::Find(full.data(), full.data() + full.size(), &length)
  306. || length != full.size()
  307. || !message.textWithTags.tags.isEmpty()) {
  308. return false;
  309. }
  310. auto &config = message.action.history->session().appConfig();
  311. static const auto hardcoded = std::vector<QString>{
  312. Stickers::DicePacks::kDiceString,
  313. Stickers::DicePacks::kDartString,
  314. Stickers::DicePacks::kSlotString,
  315. Stickers::DicePacks::kFballString,
  316. Stickers::DicePacks::kFballString + QChar(0xFE0F),
  317. Stickers::DicePacks::kBballString,
  318. };
  319. const auto list = config.get<std::vector<QString>>(
  320. "emojies_send_dice",
  321. hardcoded);
  322. const auto emoji = full.toString();
  323. if (!ranges::contains(list, emoji)) {
  324. return false;
  325. }
  326. const auto history = message.action.history;
  327. const auto peer = history->peer;
  328. const auto session = &history->session();
  329. const auto api = &session->api();
  330. message.textWithTags = TextWithTags();
  331. message.action.clearDraft = false;
  332. message.action.generateLocal = true;
  333. auto &action = message.action;
  334. api->sendAction(action);
  335. const auto newId = FullMsgId(
  336. peer->id,
  337. session->data().nextLocalMessageId());
  338. const auto randomId = base::RandomValue<uint64>();
  339. auto &histories = history->owner().histories();
  340. auto flags = NewMessageFlags(peer);
  341. auto sendFlags = MTPmessages_SendMedia::Flags(0);
  342. if (action.replyTo) {
  343. flags |= MessageFlag::HasReplyInfo;
  344. sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
  345. }
  346. const auto silentPost = ShouldSendSilent(peer, action.options);
  347. InnerFillMessagePostFlags(action.options, peer, flags);
  348. if (silentPost) {
  349. sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
  350. }
  351. const auto sendAs = action.options.sendAs;
  352. if (sendAs) {
  353. sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
  354. }
  355. if (action.options.scheduled) {
  356. flags |= MessageFlag::IsOrWasScheduled;
  357. sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
  358. }
  359. if (action.options.shortcutId) {
  360. flags |= MessageFlag::ShortcutMessage;
  361. sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
  362. }
  363. if (action.options.effectId) {
  364. sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
  365. }
  366. if (action.options.invertCaption) {
  367. flags |= MessageFlag::InvertMedia;
  368. sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
  369. }
  370. const auto starsPaid = std::min(
  371. peer->starsPerMessageChecked(),
  372. action.options.starsApproved);
  373. if (starsPaid) {
  374. action.options.starsApproved -= starsPaid;
  375. sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
  376. }
  377. session->data().registerMessageRandomId(randomId, newId);
  378. history->addNewLocalMessage({
  379. .id = newId.msg,
  380. .flags = flags,
  381. .from = NewMessageFromId(action),
  382. .replyTo = action.replyTo,
  383. .date = NewMessageDate(action.options),
  384. .shortcutId = action.options.shortcutId,
  385. .starsPaid = starsPaid,
  386. .postAuthor = NewMessagePostAuthor(action),
  387. .effectId = action.options.effectId,
  388. }, TextWithEntities(), MTP_messageMediaDice(
  389. MTP_int(0),
  390. MTP_string(emoji)));
  391. histories.sendPreparedMessage(
  392. history,
  393. action.replyTo,
  394. randomId,
  395. Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
  396. MTP_flags(sendFlags),
  397. peer->input,
  398. Data::Histories::ReplyToPlaceholder(),
  399. MTP_inputMediaDice(MTP_string(emoji)),
  400. MTP_string(),
  401. MTP_long(randomId),
  402. MTPReplyMarkup(),
  403. MTP_vector<MTPMessageEntity>(),
  404. MTP_int(action.options.scheduled),
  405. (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
  406. Data::ShortcutIdToMTP(session, action.options.shortcutId),
  407. MTP_long(action.options.effectId),
  408. MTP_long(starsPaid)
  409. ), [=](const MTPUpdates &result, const MTP::Response &response) {
  410. }, [=](const MTP::Error &error, const MTP::Response &response) {
  411. api->sendMessageFail(error, peer, randomId, newId);
  412. });
  413. api->finishForwarding(action);
  414. return true;
  415. }
  416. void SendLocation(SendAction action, float64 lat, float64 lon) {
  417. SendSimpleMedia(
  418. action,
  419. MTP_inputMediaGeoPoint(
  420. MTP_inputGeoPoint(
  421. MTP_flags(0),
  422. MTP_double(lat),
  423. MTP_double(lon),
  424. MTPint()))); // accuracy_radius
  425. }
  426. void SendVenue(SendAction action, Data::InputVenue venue) {
  427. SendSimpleMedia(
  428. action,
  429. MTP_inputMediaVenue(
  430. MTP_inputGeoPoint(
  431. MTP_flags(0),
  432. MTP_double(venue.lat),
  433. MTP_double(venue.lon),
  434. MTPint()), // accuracy_radius
  435. MTP_string(venue.title),
  436. MTP_string(venue.address),
  437. MTP_string(venue.provider),
  438. MTP_string(venue.id),
  439. MTP_string(venue.venueType)));
  440. }
  441. void FillMessagePostFlags(
  442. const SendAction &action,
  443. not_null<PeerData*> peer,
  444. MessageFlags &flags) {
  445. InnerFillMessagePostFlags(action.options, peer, flags);
  446. }
  447. void SendConfirmedFile(
  448. not_null<Main::Session*> session,
  449. const std::shared_ptr<FilePrepareResult> &file) {
  450. const auto isEditing = (file->type != SendMediaType::Audio)
  451. && (file->type != SendMediaType::Round)
  452. && (file->to.replaceMediaOf != 0);
  453. const auto newId = FullMsgId(
  454. file->to.peer,
  455. (isEditing
  456. ? file->to.replaceMediaOf
  457. : session->data().nextLocalMessageId()));
  458. const auto groupId = file->album ? file->album->groupId : uint64(0);
  459. if (file->album) {
  460. const auto proj = [](const SendingAlbum::Item &item) {
  461. return item.taskId;
  462. };
  463. const auto it = ranges::find(file->album->items, file->taskId, proj);
  464. Assert(it != file->album->items.end());
  465. it->msgId = newId;
  466. }
  467. const auto itemToEdit = isEditing
  468. ? session->data().message(newId)
  469. : nullptr;
  470. const auto history = session->data().history(file->to.peer);
  471. const auto peer = history->peer;
  472. if (!isEditing) {
  473. const auto histories = &session->data().histories();
  474. file->to.replyTo.messageId = histories->convertTopicReplyToId(
  475. history,
  476. file->to.replyTo.messageId);
  477. file->to.replyTo.topicRootId = histories->convertTopicReplyToId(
  478. history,
  479. file->to.replyTo.topicRootId);
  480. }
  481. session->uploader().upload(newId, file);
  482. auto action = SendAction(history, file->to.options);
  483. action.clearDraft = false;
  484. action.replyTo = file->to.replyTo;
  485. action.generateLocal = true;
  486. action.replaceMediaOf = file->to.replaceMediaOf;
  487. session->api().sendAction(action);
  488. auto caption = TextWithEntities{
  489. file->caption.text,
  490. TextUtilities::ConvertTextTagsToEntities(file->caption.tags)
  491. };
  492. const auto prepareFlags = Ui::ItemTextOptions(
  493. history,
  494. session->user()).flags;
  495. TextUtilities::PrepareForSending(caption, prepareFlags);
  496. TextUtilities::Trim(caption);
  497. auto flags = isEditing ? MessageFlags() : NewMessageFlags(peer);
  498. if (file->to.replyTo) {
  499. flags |= MessageFlag::HasReplyInfo;
  500. }
  501. FillMessagePostFlags(action, peer, flags);
  502. if (file->to.options.scheduled) {
  503. flags |= MessageFlag::IsOrWasScheduled;
  504. // Scheduled messages have no 'edited' badge.
  505. flags |= MessageFlag::HideEdited;
  506. }
  507. if (file->to.options.shortcutId) {
  508. flags |= MessageFlag::ShortcutMessage;
  509. // Shortcut messages have no 'edited' badge.
  510. flags |= MessageFlag::HideEdited;
  511. }
  512. if (file->type == SendMediaType::Audio
  513. || file->type == SendMediaType::Round) {
  514. if (!peer->isChannel() || peer->isMegagroup()) {
  515. flags |= MessageFlag::MediaIsUnread;
  516. }
  517. }
  518. if (file->to.options.invertCaption) {
  519. flags |= MessageFlag::InvertMedia;
  520. }
  521. const auto media = MTPMessageMedia([&] {
  522. if (file->type == SendMediaType::Photo) {
  523. using Flag = MTPDmessageMediaPhoto::Flag;
  524. return MTP_messageMediaPhoto(
  525. MTP_flags(Flag::f_photo
  526. | (file->spoiler ? Flag::f_spoiler : Flag())),
  527. file->photo,
  528. MTPint());
  529. } else if (file->type == SendMediaType::File) {
  530. using Flag = MTPDmessageMediaDocument::Flag;
  531. return MTP_messageMediaDocument(
  532. MTP_flags(Flag::f_document
  533. | (file->spoiler ? Flag::f_spoiler : Flag())
  534. | (file->videoCover ? Flag::f_video_cover : Flag())),
  535. file->document,
  536. MTPVector<MTPDocument>(), // alt_documents
  537. file->videoCover ? file->videoCover->photo : MTPPhoto(),
  538. MTPint(), // video_timestamp
  539. MTPint());
  540. } else if (file->type == SendMediaType::Audio) {
  541. const auto ttlSeconds = file->to.options.ttlSeconds;
  542. using Flag = MTPDmessageMediaDocument::Flag;
  543. return MTP_messageMediaDocument(
  544. MTP_flags(Flag::f_document
  545. | Flag::f_voice
  546. | (ttlSeconds ? Flag::f_ttl_seconds : Flag())
  547. | (file->videoCover ? Flag::f_video_cover : Flag())),
  548. file->document,
  549. MTPVector<MTPDocument>(), // alt_documents
  550. file->videoCover ? file->videoCover->photo : MTPPhoto(),
  551. MTPint(), // video_timestamp
  552. MTP_int(ttlSeconds));
  553. } else if (file->type == SendMediaType::Round) {
  554. using Flag = MTPDmessageMediaDocument::Flag;
  555. const auto ttlSeconds = file->to.options.ttlSeconds;
  556. return MTP_messageMediaDocument(
  557. MTP_flags(Flag::f_document
  558. | Flag::f_round
  559. | (ttlSeconds ? Flag::f_ttl_seconds : Flag())
  560. | (file->spoiler ? Flag::f_spoiler : Flag())),
  561. file->document,
  562. MTPVector<MTPDocument>(), // alt_documents
  563. MTPPhoto(), // video_cover
  564. MTPint(), // video_timestamp
  565. MTP_int(ttlSeconds));
  566. } else {
  567. Unexpected("Type in sendFilesConfirmed.");
  568. }
  569. }());
  570. if (itemToEdit) {
  571. auto edition = HistoryMessageEdition();
  572. edition.isEditHide = (flags & MessageFlag::HideEdited);
  573. edition.editDate = 0;
  574. edition.ttl = 0;
  575. edition.mtpMedia = &media;
  576. edition.textWithEntities = caption;
  577. edition.invertMedia = file->to.options.invertCaption;
  578. edition.useSameViews = true;
  579. edition.useSameForwards = true;
  580. edition.useSameMarkup = true;
  581. edition.useSameReplies = true;
  582. edition.useSameReactions = true;
  583. edition.savePreviousMedia = true;
  584. itemToEdit->applyEdition(std::move(edition));
  585. } else {
  586. history->addNewLocalMessage({
  587. .id = newId.msg,
  588. .flags = flags,
  589. .from = NewMessageFromId(action),
  590. .replyTo = file->to.replyTo,
  591. .date = NewMessageDate(file->to.options),
  592. .shortcutId = file->to.options.shortcutId,
  593. .starsPaid = std::min(
  594. history->peer->starsPerMessageChecked(),
  595. file->to.options.starsApproved),
  596. .postAuthor = NewMessagePostAuthor(action),
  597. .groupedId = groupId,
  598. .effectId = file->to.options.effectId,
  599. }, caption, media);
  600. }
  601. if (isEditing) {
  602. return;
  603. }
  604. session->data().sendHistoryChangeNotifications();
  605. if (!itemToEdit) {
  606. session->changes().historyUpdated(
  607. history,
  608. (action.options.scheduled
  609. ? Data::HistoryUpdate::Flag::ScheduledSent
  610. : Data::HistoryUpdate::Flag::MessageSent));
  611. }
  612. }
  613. } // namespace Api