api_peer_photo.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  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_peer_photo.h"
  8. #include "api/api_updates.h"
  9. #include "apiwrap.h"
  10. #include "base/random.h"
  11. #include "base/unixtime.h"
  12. #include "data/stickers/data_stickers.h"
  13. #include "data/data_channel.h"
  14. #include "data/data_chat.h"
  15. #include "data/data_document.h"
  16. #include "data/data_file_origin.h"
  17. #include "data/data_peer.h"
  18. #include "data/data_photo.h"
  19. #include "data/data_session.h"
  20. #include "data/data_user.h"
  21. #include "data/data_user_photos.h"
  22. #include "history/history.h"
  23. #include "main/main_session.h"
  24. #include "storage/file_upload.h"
  25. #include "storage/localimageloader.h"
  26. #include "storage/storage_user_photos.h"
  27. #include <QtCore/QBuffer>
  28. namespace Api {
  29. namespace {
  30. constexpr auto kSharedMediaLimit = 100;
  31. [[nodiscard]] std::shared_ptr<FilePrepareResult> PreparePeerPhoto(
  32. MTP::DcId dcId,
  33. PeerId peerId,
  34. QImage &&image) {
  35. PreparedPhotoThumbs photoThumbs;
  36. QVector<MTPPhotoSize> photoSizes;
  37. QByteArray jpeg;
  38. QBuffer jpegBuffer(&jpeg);
  39. image.save(&jpegBuffer, "JPG", 87);
  40. const auto scaled = [&](int size) {
  41. return image.scaled(
  42. size,
  43. size,
  44. Qt::KeepAspectRatio,
  45. Qt::SmoothTransformation);
  46. };
  47. const auto push = [&](
  48. const char *type,
  49. QImage &&image,
  50. QByteArray bytes = QByteArray()) {
  51. photoSizes.push_back(MTP_photoSize(
  52. MTP_string(type),
  53. MTP_int(image.width()),
  54. MTP_int(image.height()), MTP_int(0)));
  55. photoThumbs.emplace(type[0], PreparedPhotoThumb{
  56. .image = std::move(image),
  57. .bytes = std::move(bytes)
  58. });
  59. };
  60. push("a", scaled(160));
  61. push("b", scaled(320));
  62. push("c", std::move(image), jpeg);
  63. const auto id = base::RandomValue<PhotoId>();
  64. const auto photo = MTP_photo(
  65. MTP_flags(0),
  66. MTP_long(id),
  67. MTP_long(0),
  68. MTP_bytes(),
  69. MTP_int(base::unixtime::now()),
  70. MTP_vector<MTPPhotoSize>(photoSizes),
  71. MTPVector<MTPVideoSize>(),
  72. MTP_int(dcId));
  73. auto result = MakePreparedFile({
  74. .id = id,
  75. .type = SendMediaType::Photo,
  76. });
  77. result->type = SendMediaType::Photo;
  78. result->setFileData(jpeg);
  79. result->thumbId = id;
  80. result->thumbname = "thumb.jpg";
  81. result->photo = photo;
  82. result->photoThumbs = photoThumbs;
  83. return result;
  84. }
  85. [[nodiscard]] std::optional<MTPVideoSize> PrepareMtpMarkup(
  86. not_null<Main::Session*> session,
  87. const PeerPhoto::UserPhoto &d) {
  88. const auto &documentId = d.markupDocumentId;
  89. const auto &colors = d.markupColors;
  90. if (!documentId || colors.empty()) {
  91. return std::nullopt;
  92. }
  93. const auto document = session->data().document(documentId);
  94. if (const auto sticker = document->sticker()) {
  95. if (sticker->isStatic()) {
  96. return std::nullopt;
  97. }
  98. const auto serializeColor = [](const QColor &color) {
  99. return (quint32(std::clamp(color.red(), 0, 255)) << 16)
  100. | (quint32(std::clamp(color.green(), 0, 255)) << 8)
  101. | quint32(std::clamp(color.blue(), 0, 255));
  102. };
  103. auto mtpColors = QVector<MTPint>();
  104. mtpColors.reserve(colors.size());
  105. ranges::transform(
  106. colors,
  107. ranges::back_inserter(mtpColors),
  108. [&](const QColor &c) { return MTP_int(serializeColor(c)); });
  109. if (sticker->setType == Data::StickersType::Emoji) {
  110. return MTP_videoSizeEmojiMarkup(
  111. MTP_long(document->id),
  112. MTP_vector(mtpColors));
  113. } else if (sticker->set.id && sticker->set.accessHash) {
  114. return MTP_videoSizeStickerMarkup(
  115. MTP_inputStickerSetID(
  116. MTP_long(sticker->set.id),
  117. MTP_long(sticker->set.accessHash)),
  118. MTP_long(document->id),
  119. MTP_vector(mtpColors));
  120. } else if (!sticker->set.shortName.isEmpty()) {
  121. return MTP_videoSizeStickerMarkup(
  122. MTP_inputStickerSetShortName(
  123. MTP_string(sticker->set.shortName)),
  124. MTP_long(document->id),
  125. MTP_vector(mtpColors));
  126. } else {
  127. return MTP_videoSizeEmojiMarkup(
  128. MTP_long(document->id),
  129. MTP_vector(mtpColors));
  130. }
  131. }
  132. return std::nullopt;
  133. }
  134. } // namespace
  135. PeerPhoto::PeerPhoto(not_null<ApiWrap*> api)
  136. : _session(&api->session())
  137. , _api(&api->instance()) {
  138. crl::on_main(_session, [=] {
  139. // You can't use _session->lifetime() in the constructor,
  140. // only queued, because it is not constructed yet.
  141. _session->uploader().photoReady(
  142. ) | rpl::start_with_next([=](const Storage::UploadedMedia &data) {
  143. ready(data.fullId, data.info.file, std::nullopt);
  144. }, _session->lifetime());
  145. });
  146. }
  147. void PeerPhoto::upload(
  148. not_null<PeerData*> peer,
  149. UserPhoto &&photo,
  150. Fn<void()> done) {
  151. upload(peer, std::move(photo), UploadType::Default, std::move(done));
  152. }
  153. void PeerPhoto::uploadFallback(not_null<PeerData*> peer, UserPhoto &&photo) {
  154. upload(peer, std::move(photo), UploadType::Fallback, nullptr);
  155. }
  156. void PeerPhoto::updateSelf(
  157. not_null<PhotoData*> photo,
  158. Data::FileOrigin origin,
  159. Fn<void()> done) {
  160. const auto send = [=](auto resend) -> void {
  161. const auto usedFileReference = photo->fileReference();
  162. _api.request(MTPphotos_UpdateProfilePhoto(
  163. MTP_flags(0),
  164. MTPInputUser(), // bot
  165. photo->mtpInput()
  166. )).done([=](const MTPphotos_Photo &result) {
  167. result.match([&](const MTPDphotos_photo &data) {
  168. _session->data().processPhoto(data.vphoto());
  169. _session->data().processUsers(data.vusers());
  170. });
  171. if (done) {
  172. done();
  173. }
  174. }).fail([=](const MTP::Error &error) {
  175. if (error.code() == 400
  176. && error.type().startsWith(u"FILE_REFERENCE_"_q)) {
  177. photo->session().api().refreshFileReference(origin, [=](
  178. const auto &) {
  179. if (photo->fileReference() != usedFileReference) {
  180. resend(resend);
  181. }
  182. });
  183. }
  184. }).send();
  185. };
  186. send(send);
  187. }
  188. void PeerPhoto::upload(
  189. not_null<PeerData*> peer,
  190. UserPhoto &&photo,
  191. UploadType type,
  192. Fn<void()> done) {
  193. peer = peer->migrateToOrMe();
  194. const auto mtpMarkup = PrepareMtpMarkup(_session, photo);
  195. const auto fakeId = FullMsgId(
  196. peer->id,
  197. _session->data().nextLocalMessageId());
  198. const auto already = ranges::find(
  199. _uploads,
  200. peer,
  201. [](const auto &pair) { return pair.second.peer; });
  202. if (already != end(_uploads)) {
  203. _session->uploader().cancel(already->first);
  204. _uploads.erase(already);
  205. }
  206. _uploads.emplace(
  207. fakeId,
  208. UploadValue{ peer, type, std::move(done) });
  209. if (mtpMarkup) {
  210. ready(fakeId, std::nullopt, mtpMarkup);
  211. } else {
  212. const auto ready = PreparePeerPhoto(
  213. _api.instance().mainDcId(),
  214. peer->id,
  215. base::take(photo.image));
  216. _session->uploader().upload(fakeId, ready);
  217. }
  218. }
  219. void PeerPhoto::suggest(not_null<PeerData*> peer, UserPhoto &&photo) {
  220. upload(peer, std::move(photo), UploadType::Suggestion, nullptr);
  221. }
  222. void PeerPhoto::clear(not_null<PhotoData*> photo) {
  223. const auto self = _session->user();
  224. if (self->userpicPhotoId() == photo->id) {
  225. _api.request(MTPphotos_UpdateProfilePhoto(
  226. MTP_flags(0),
  227. MTPInputUser(), // bot
  228. MTP_inputPhotoEmpty()
  229. )).done([=](const MTPphotos_Photo &result) {
  230. self->setPhoto(MTP_userProfilePhotoEmpty());
  231. }).send();
  232. } else if (photo->peer && photo->peer->userpicPhotoId() == photo->id) {
  233. const auto applier = [=](const MTPUpdates &result) {
  234. _session->updates().applyUpdates(result);
  235. };
  236. if (const auto chat = photo->peer->asChat()) {
  237. _api.request(MTPmessages_EditChatPhoto(
  238. chat->inputChat,
  239. MTP_inputChatPhotoEmpty()
  240. )).done(applier).send();
  241. } else if (const auto channel = photo->peer->asChannel()) {
  242. _api.request(MTPchannels_EditPhoto(
  243. channel->inputChannel,
  244. MTP_inputChatPhotoEmpty()
  245. )).done(applier).send();
  246. }
  247. } else {
  248. const auto fallbackPhotoId = SyncUserFallbackPhotoViewer(self);
  249. if (fallbackPhotoId && (*fallbackPhotoId) == photo->id) {
  250. _api.request(MTPphotos_UpdateProfilePhoto(
  251. MTP_flags(MTPphotos_UpdateProfilePhoto::Flag::f_fallback),
  252. MTPInputUser(), // bot
  253. MTP_inputPhotoEmpty()
  254. )).send();
  255. _session->storage().add(Storage::UserPhotosSetBack(
  256. peerToUser(self->id),
  257. PhotoId()));
  258. } else {
  259. _api.request(MTPphotos_DeletePhotos(
  260. MTP_vector<MTPInputPhoto>(1, photo->mtpInput())
  261. )).send();
  262. _session->storage().remove(Storage::UserPhotosRemoveOne(
  263. peerToUser(self->id),
  264. photo->id));
  265. }
  266. }
  267. }
  268. void PeerPhoto::clearPersonal(not_null<UserData*> user) {
  269. _api.request(MTPphotos_UploadContactProfilePhoto(
  270. MTP_flags(MTPphotos_UploadContactProfilePhoto::Flag::f_save),
  271. user->inputUser,
  272. MTPInputFile(),
  273. MTPInputFile(), // video
  274. MTPdouble(), // video_start_ts
  275. MTPVideoSize() // video_emoji_markup
  276. )).done([=](const MTPphotos_Photo &result) {
  277. result.match([&](const MTPDphotos_photo &data) {
  278. _session->data().processPhoto(data.vphoto());
  279. _session->data().processUsers(data.vusers());
  280. });
  281. }).send();
  282. if (!user->userpicPhotoUnknown() && user->hasPersonalPhoto()) {
  283. _session->storage().remove(Storage::UserPhotosRemoveOne(
  284. peerToUser(user->id),
  285. user->userpicPhotoId()));
  286. }
  287. }
  288. void PeerPhoto::set(not_null<PeerData*> peer, not_null<PhotoData*> photo) {
  289. if (peer->userpicPhotoId() == photo->id) {
  290. return;
  291. }
  292. if (peer == _session->user()) {
  293. _api.request(MTPphotos_UpdateProfilePhoto(
  294. MTP_flags(0),
  295. MTPInputUser(), // bot
  296. photo->mtpInput()
  297. )).done([=](const MTPphotos_Photo &result) {
  298. result.match([&](const MTPDphotos_photo &data) {
  299. _session->data().processPhoto(data.vphoto());
  300. _session->data().processUsers(data.vusers());
  301. });
  302. }).send();
  303. } else {
  304. const auto applier = [=](const MTPUpdates &result) {
  305. _session->updates().applyUpdates(result);
  306. };
  307. if (const auto chat = peer->asChat()) {
  308. _api.request(MTPmessages_EditChatPhoto(
  309. chat->inputChat,
  310. MTP_inputChatPhoto(photo->mtpInput())
  311. )).done(applier).send();
  312. } else if (const auto channel = peer->asChannel()) {
  313. _api.request(MTPchannels_EditPhoto(
  314. channel->inputChannel,
  315. MTP_inputChatPhoto(photo->mtpInput())
  316. )).done(applier).send();
  317. }
  318. }
  319. }
  320. void PeerPhoto::ready(
  321. const FullMsgId &msgId,
  322. std::optional<MTPInputFile> file,
  323. std::optional<MTPVideoSize> videoSize) {
  324. const auto maybeUploadValue = _uploads.take(msgId);
  325. if (!maybeUploadValue) {
  326. return;
  327. }
  328. const auto peer = maybeUploadValue->peer;
  329. const auto type = maybeUploadValue->type;
  330. const auto done = maybeUploadValue->done;
  331. const auto applier = [=](const MTPUpdates &result) {
  332. _session->updates().applyUpdates(result);
  333. if (done) {
  334. done();
  335. }
  336. };
  337. const auto botUserInput = [&] {
  338. const auto user = peer->asUser();
  339. return (user && user->botInfo && user->botInfo->canEditInformation)
  340. ? std::make_optional<MTPInputUser>(user->inputUser)
  341. : std::nullopt;
  342. }();
  343. if (peer->isSelf() || botUserInput) {
  344. using Flag = MTPphotos_UploadProfilePhoto::Flag;
  345. const auto none = MTPphotos_UploadProfilePhoto::Flags(0);
  346. _api.request(MTPphotos_UploadProfilePhoto(
  347. MTP_flags((file ? Flag::f_file : none)
  348. | (botUserInput ? Flag::f_bot : none)
  349. | (videoSize ? Flag::f_video_emoji_markup : none)
  350. | ((type == UploadType::Fallback) ? Flag::f_fallback : none)),
  351. botUserInput ? (*botUserInput) : MTPInputUser(), // bot
  352. file ? (*file) : MTPInputFile(),
  353. MTPInputFile(), // video
  354. MTPdouble(), // video_start_ts
  355. videoSize ? (*videoSize) : MTPVideoSize() // video_emoji_markup
  356. )).done([=](const MTPphotos_Photo &result) {
  357. const auto photoId = _session->data().processPhoto(
  358. result.data().vphoto())->id;
  359. _session->data().processUsers(result.data().vusers());
  360. if (type == UploadType::Fallback) {
  361. _session->storage().add(Storage::UserPhotosSetBack(
  362. peerToUser(peer->id),
  363. photoId));
  364. }
  365. if (done) {
  366. done();
  367. }
  368. }).send();
  369. } else if (const auto chat = peer->asChat()) {
  370. const auto history = _session->data().history(chat);
  371. using Flag = MTPDinputChatUploadedPhoto::Flag;
  372. const auto none = MTPDinputChatUploadedPhoto::Flags(0);
  373. history->sendRequestId = _api.request(MTPmessages_EditChatPhoto(
  374. chat->inputChat,
  375. MTP_inputChatUploadedPhoto(
  376. MTP_flags((file ? Flag::f_file : none)
  377. | (videoSize ? Flag::f_video_emoji_markup : none)),
  378. file ? (*file) : MTPInputFile(),
  379. MTPInputFile(), // video
  380. MTPdouble(), // video_start_ts
  381. videoSize ? (*videoSize) : MTPVideoSize()) // video_emoji_markup
  382. )).done(applier).afterRequest(history->sendRequestId).send();
  383. } else if (const auto channel = peer->asChannel()) {
  384. using Flag = MTPDinputChatUploadedPhoto::Flag;
  385. const auto none = MTPDinputChatUploadedPhoto::Flags(0);
  386. const auto history = _session->data().history(channel);
  387. history->sendRequestId = _api.request(MTPchannels_EditPhoto(
  388. channel->inputChannel,
  389. MTP_inputChatUploadedPhoto(
  390. MTP_flags((file ? Flag::f_file : none)
  391. | (videoSize ? Flag::f_video_emoji_markup : none)),
  392. file ? (*file) : MTPInputFile(),
  393. MTPInputFile(), // video
  394. MTPdouble(), // video_start_ts
  395. videoSize ? (*videoSize) : MTPVideoSize()) // video_emoji_markup
  396. )).done(applier).afterRequest(history->sendRequestId).send();
  397. } else if (const auto user = peer->asUser()) {
  398. using Flag = MTPphotos_UploadContactProfilePhoto::Flag;
  399. const auto none = MTPphotos_UploadContactProfilePhoto::Flags(0);
  400. _api.request(MTPphotos_UploadContactProfilePhoto(
  401. MTP_flags((file ? Flag::f_file : none)
  402. | (videoSize ? Flag::f_video_emoji_markup : none)
  403. | ((type == UploadType::Suggestion)
  404. ? Flag::f_suggest
  405. : Flag::f_save)),
  406. user->inputUser,
  407. file ? (*file) : MTPInputFile(),
  408. MTPInputFile(), // video
  409. MTPdouble(), // video_start_ts
  410. videoSize ? (*videoSize) : MTPVideoSize() // video_emoji_markup
  411. )).done([=](const MTPphotos_Photo &result) {
  412. result.match([&](const MTPDphotos_photo &data) {
  413. _session->data().processPhoto(data.vphoto());
  414. _session->data().processUsers(data.vusers());
  415. });
  416. if (type != UploadType::Suggestion) {
  417. user->updateFullForced();
  418. }
  419. if (done) {
  420. done();
  421. }
  422. }).send();
  423. }
  424. }
  425. void PeerPhoto::requestUserPhotos(
  426. not_null<UserData*> user,
  427. UserPhotoId afterId) {
  428. if (_userPhotosRequests.contains(user)) {
  429. return;
  430. }
  431. const auto requestId = _api.request(MTPphotos_GetUserPhotos(
  432. user->inputUser,
  433. MTP_int(0),
  434. MTP_long(afterId),
  435. MTP_int(kSharedMediaLimit)
  436. )).done([this, user](const MTPphotos_Photos &result) {
  437. _userPhotosRequests.remove(user);
  438. auto fullCount = result.match([](const MTPDphotos_photos &d) {
  439. return int(d.vphotos().v.size());
  440. }, [](const MTPDphotos_photosSlice &d) {
  441. return d.vcount().v;
  442. });
  443. auto &owner = _session->data();
  444. auto photoIds = result.match([&](const auto &data) {
  445. owner.processUsers(data.vusers());
  446. auto photoIds = std::vector<PhotoId>();
  447. photoIds.reserve(data.vphotos().v.size());
  448. for (const auto &single : data.vphotos().v) {
  449. const auto photo = owner.processPhoto(single);
  450. if (!photo->isNull()) {
  451. photoIds.push_back(photo->id);
  452. }
  453. }
  454. return photoIds;
  455. });
  456. if (!user->userpicPhotoUnknown() && user->hasPersonalPhoto()) {
  457. const auto photo = owner.photo(user->userpicPhotoId());
  458. if (!photo->isNull()) {
  459. ++fullCount;
  460. photoIds.insert(begin(photoIds), photo->id);
  461. }
  462. }
  463. _session->storage().add(Storage::UserPhotosAddSlice(
  464. peerToUser(user->id),
  465. std::move(photoIds),
  466. fullCount
  467. ));
  468. }).fail([this, user] {
  469. _userPhotosRequests.remove(user);
  470. }).send();
  471. _userPhotosRequests.emplace(user, requestId);
  472. }
  473. auto PeerPhoto::emojiList(EmojiListType type) -> EmojiListData & {
  474. switch (type) {
  475. case EmojiListType::Profile: return _profileEmojiList;
  476. case EmojiListType::Group: return _groupEmojiList;
  477. case EmojiListType::Background: return _backgroundEmojiList;
  478. case EmojiListType::NoChannelStatus: return _noChannelStatusEmojiList;
  479. }
  480. Unexpected("Type in PeerPhoto::emojiList.");
  481. }
  482. auto PeerPhoto::emojiList(EmojiListType type) const
  483. -> const EmojiListData & {
  484. return const_cast<PeerPhoto*>(this)->emojiList(type);
  485. }
  486. void PeerPhoto::requestEmojiList(EmojiListType type) {
  487. auto &list = emojiList(type);
  488. if (list.requestId) {
  489. return;
  490. }
  491. const auto send = [&](auto &&request) {
  492. return _api.request(
  493. std::move(request)
  494. ).done([=](const MTPEmojiList &result) {
  495. auto &list = emojiList(type);
  496. list.requestId = 0;
  497. result.match([](const MTPDemojiListNotModified &data) {
  498. }, [&](const MTPDemojiList &data) {
  499. list.list = ranges::views::all(
  500. data.vdocument_id().v
  501. ) | ranges::views::transform(
  502. &MTPlong::v
  503. ) | ranges::to_vector;
  504. });
  505. }).fail([=] {
  506. emojiList(type).requestId = 0;
  507. }).send();
  508. };
  509. list.requestId = (type == EmojiListType::Profile)
  510. ? send(MTPaccount_GetDefaultProfilePhotoEmojis())
  511. : (type == EmojiListType::Group)
  512. ? send(MTPaccount_GetDefaultGroupPhotoEmojis())
  513. : (type == EmojiListType::NoChannelStatus)
  514. ? send(MTPaccount_GetChannelRestrictedStatusEmojis())
  515. : send(MTPaccount_GetDefaultBackgroundEmojis());
  516. }
  517. rpl::producer<PeerPhoto::EmojiList> PeerPhoto::emojiListValue(
  518. EmojiListType type) {
  519. auto &list = emojiList(type);
  520. if (list.list.current().empty() && !list.requestId) {
  521. requestEmojiList(type);
  522. }
  523. return list.list.value();
  524. }
  525. // Non-personal photo in case a personal photo is set.
  526. void PeerPhoto::registerNonPersonalPhoto(
  527. not_null<UserData*> user,
  528. not_null<PhotoData*> photo) {
  529. _nonPersonalPhotos.emplace_or_assign(user, photo);
  530. }
  531. void PeerPhoto::unregisterNonPersonalPhoto(not_null<UserData*> user) {
  532. _nonPersonalPhotos.erase(user);
  533. }
  534. PhotoData *PeerPhoto::nonPersonalPhoto(
  535. not_null<UserData*> user) const {
  536. const auto i = _nonPersonalPhotos.find(user);
  537. return (i != end(_nonPersonalPhotos)) ? i->second.get() : nullptr;
  538. }
  539. } // namespace Api