1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "apiwrap.h"
9
10 #include "api/api_authorizations.h"
11 #include "api/api_attached_stickers.h"
12 #include "api/api_blocked_peers.h"
13 #include "api/api_cloud_password.h"
14 #include "api/api_hash.h"
15 #include "api/api_invite_links.h"
16 #include "api/api_media.h"
17 #include "api/api_peer_photo.h"
18 #include "api/api_polls.h"
19 #include "api/api_sending.h"
20 #include "api/api_text_entities.h"
21 #include "api/api_self_destruct.h"
22 #include "api/api_sensitive_content.h"
23 #include "api/api_global_privacy.h"
24 #include "api/api_updates.h"
25 #include "api/api_user_privacy.h"
26 #include "api/api_views.h"
27 #include "api/api_confirm_phone.h"
28 #include "data/stickers/data_stickers.h"
29 #include "data/data_drafts.h"
30 #include "data/data_changes.h"
31 #include "data/data_photo.h"
32 #include "data/data_web_page.h"
33 #include "data/data_folder.h"
34 #include "data/data_media_types.h"
35 #include "data/data_sparse_ids.h"
36 #include "data/data_search_controller.h"
37 #include "data/data_scheduled_messages.h"
38 #include "data/data_channel_admins.h"
39 #include "data/data_session.h"
40 #include "data/data_channel.h"
41 #include "data/data_chat.h"
42 #include "data/data_user.h"
43 #include "data/data_cloud_themes.h"
44 #include "data/data_chat_filters.h"
45 #include "data/data_histories.h"
46 #include "data/data_wall_paper.h"
47 #include "data/stickers/data_stickers.h"
48 #include "dialogs/dialogs_key.h"
49 #include "core/core_cloud_password.h"
50 #include "core/application.h"
51 #include "base/unixtime.h"
52 #include "base/random.h"
53 #include "base/qt_adapters.h"
54 #include "base/call_delayed.h"
55 #include "lang/lang_keys.h"
56 #include "mainwindow.h"
57 #include "mainwidget.h"
58 #include "boxes/add_contact_box.h"
59 #include "mtproto/mtproto_config.h"
60 #include "history/history.h"
61 #include "history/history_message.h"
62 #include "history/history_item_components.h"
63 #include "main/main_session.h"
64 #include "main/main_session_settings.h"
65 #include "main/main_account.h"
66 #include "ui/boxes/confirm_box.h"
67 #include "boxes/stickers_box.h"
68 #include "boxes/sticker_set_box.h"
69 #include "window/notifications_manager.h"
70 #include "window/window_lock_widgets.h"
71 #include "window/window_session_controller.h"
72 #include "inline_bots/inline_bot_result.h"
73 #include "chat_helpers/message_field.h"
74 #include "ui/item_text_options.h"
75 #include "ui/emoji_config.h"
76 #include "ui/chat/attach/attach_prepare.h"
77 #include "ui/toasts/common_toasts.h"
78 #include "support/support_helper.h"
79 #include "storage/localimageloader.h"
80 #include "storage/download_manager_mtproto.h"
81 #include "storage/file_upload.h"
82 #include "storage/storage_facade.h"
83 #include "storage/storage_shared_media.h"
84 #include "storage/storage_user_photos.h"
85 #include "storage/storage_media_prepare.h"
86 #include "storage/storage_account.h"
87 #include "facades.h"
88 #include "app.h" // App::quitting
89
90 namespace {
91
92 // 1 second wait before reload members in channel after adding.
93 constexpr auto kReloadChannelMembersTimeout = 1000;
94
95 // Save draft to the cloud with 1 sec extra delay.
96 constexpr auto kSaveCloudDraftTimeout = 1000;
97
98 // Max users in one super group invite request.
99 constexpr auto kMaxUsersPerInvite = 100;
100
101 // How many messages from chat history server should forward to user,
102 // that was added to this chat.
103 constexpr auto kForwardMessagesOnAdd = 100;
104
105 constexpr auto kTopPromotionInterval = TimeId(60 * 60);
106 constexpr auto kTopPromotionMinDelay = TimeId(10);
107 constexpr auto kSmallDelayMs = 5;
108 constexpr auto kUnreadMentionsPreloadIfLess = 5;
109 constexpr auto kUnreadMentionsFirstRequestLimit = 10;
110 constexpr auto kUnreadMentionsNextRequestLimit = 100;
111 constexpr auto kSharedMediaLimit = 100;
112 constexpr auto kReadFeaturedSetsTimeout = crl::time(1000);
113 constexpr auto kFileLoaderQueueStopTimeout = crl::time(5000);
114 constexpr auto kStickersByEmojiInvalidateTimeout = crl::time(6 * 1000);
115 constexpr auto kNotifySettingSaveTimeout = crl::time(1000);
116 constexpr auto kDialogsFirstLoad = 20;
117 constexpr auto kDialogsPerPage = 500;
118 constexpr auto kJoinErrorDuration = 5 * crl::time(1000);
119
120 using PhotoFileLocationId = Data::PhotoFileLocationId;
121 using DocumentFileLocationId = Data::DocumentFileLocationId;
122 using UpdatedFileReferences = Data::UpdatedFileReferences;
123
UnixtimeFromMsgId(mtpMsgId msgId)124 [[nodiscard]] TimeId UnixtimeFromMsgId(mtpMsgId msgId) {
125 return TimeId(msgId >> 32);
126 }
127
128 } // namespace
129
ApiWrap(not_null<Main::Session * > session)130 ApiWrap::ApiWrap(not_null<Main::Session*> session)
131 : MTP::Sender(&session->account().mtp())
132 , _session(session)
133 , _messageDataResolveDelayed([=] { resolveMessageDatas(); })
__anon7f6fb6620302null134 , _webPagesTimer([=] { resolveWebPages(); })
__anon7f6fb6620402null135 , _draftsSaveTimer([=] { saveDraftsToCloud(); })
__anon7f6fb6620502null136 , _featuredSetsReadTimer([=] { readFeaturedSets(); })
137 , _dialogsLoadState(std::make_unique<DialogsLoadState>())
138 , _fileLoader(std::make_unique<TaskQueue>(kFileLoaderQueueStopTimeout))
__anon7f6fb6620602null139 , _topPromotionTimer([=] { refreshTopPromotion(); })
__anon7f6fb6620702null140 , _updateNotifySettingsTimer([=] { sendNotifySettingsUpdates(); })
141 , _authorizations(std::make_unique<Api::Authorizations>(this))
142 , _attachedStickers(std::make_unique<Api::AttachedStickers>(this))
143 , _blockedPeers(std::make_unique<Api::BlockedPeers>(this))
144 , _cloudPassword(std::make_unique<Api::CloudPassword>(this))
145 , _selfDestruct(std::make_unique<Api::SelfDestruct>(this))
146 , _sensitiveContent(std::make_unique<Api::SensitiveContent>(this))
147 , _globalPrivacy(std::make_unique<Api::GlobalPrivacy>(this))
148 , _userPrivacy(std::make_unique<Api::UserPrivacy>(this))
149 , _inviteLinks(std::make_unique<Api::InviteLinks>(this))
150 , _views(std::make_unique<Api::ViewsManager>(this))
151 , _confirmPhone(std::make_unique<Api::ConfirmPhone>(this))
152 , _peerPhoto(std::make_unique<Api::PeerPhoto>(this))
153 , _polls(std::make_unique<Api::Polls>(this)) {
__anon7f6fb6620802null154 crl::on_main(session, [=] {
155 // You can't use _session->lifetime() in the constructor,
156 // only queued, because it is not constructed yet.
157 _session->data().chatsFilters().changed(
158 ) | rpl::filter([=] {
159 return _session->data().chatsFilters().archiveNeeded();
160 }) | rpl::start_with_next([=] {
161 requestMoreDialogsIfNeeded();
162 }, _session->lifetime());
163
164 setupSupportMode();
165
166 Core::App().settings().proxy().connectionTypeValue(
167 ) | rpl::start_with_next([=] {
168 refreshTopPromotion();
169 }, _session->lifetime());
170 });
171 }
172
173 ApiWrap::~ApiWrap() = default;
174
session() const175 Main::Session &ApiWrap::session() const {
176 return *_session;
177 }
178
local() const179 Storage::Account &ApiWrap::local() const {
180 return _session->local();
181 }
182
updates() const183 Api::Updates &ApiWrap::updates() const {
184 return _session->updates();
185 }
186
setupSupportMode()187 void ApiWrap::setupSupportMode() {
188 if (!_session->supportMode()) {
189 return;
190 }
191
192 _session->settings().supportChatsTimeSliceValue(
193 ) | rpl::start_with_next([=](int seconds) {
194 _dialogsLoadTill = seconds ? std::max(base::unixtime::now() - seconds, 0) : 0;
195 refreshDialogsLoadBlocked();
196 }, _session->lifetime());
197 }
198
requestChangelog(const QString & sinceVersion,Fn<void (const MTPUpdates & result)> callback)199 void ApiWrap::requestChangelog(
200 const QString &sinceVersion,
201 Fn<void(const MTPUpdates &result)> callback) {
202 request(MTPhelp_GetAppChangelog(
203 MTP_string(sinceVersion)
204 )).done(
205 callback
206 ).send();
207 }
208
refreshTopPromotion()209 void ApiWrap::refreshTopPromotion() {
210 const auto now = base::unixtime::now();
211 const auto next = (_topPromotionNextRequestTime != 0)
212 ? _topPromotionNextRequestTime
213 : now;
214 if (_topPromotionRequestId) {
215 getTopPromotionDelayed(now, next);
216 return;
217 }
218 const auto key = [&]() -> std::pair<QString, uint32> {
219 if (!Core::App().settings().proxy().isEnabled()) {
220 return {};
221 }
222 const auto &proxy = Core::App().settings().proxy().selected();
223 if (proxy.type != MTP::ProxyData::Type::Mtproto) {
224 return {};
225 }
226 return { proxy.host, proxy.port };
227 }();
228 if (_topPromotionKey == key && now < next) {
229 getTopPromotionDelayed(now, next);
230 return;
231 }
232 _topPromotionKey = key;
233 _topPromotionRequestId = request(MTPhelp_GetPromoData(
234 )).done([=](const MTPhelp_PromoData &result) {
235 _topPromotionRequestId = 0;
236 topPromotionDone(result);
237 }).fail([=](const MTP::Error &error) {
238 _topPromotionRequestId = 0;
239 const auto now = base::unixtime::now();
240 const auto next = _topPromotionNextRequestTime = now
241 + kTopPromotionInterval;
242 if (!_topPromotionTimer.isActive()) {
243 getTopPromotionDelayed(now, next);
244 }
245 }).send();
246 }
247
getTopPromotionDelayed(TimeId now,TimeId next)248 void ApiWrap::getTopPromotionDelayed(TimeId now, TimeId next) {
249 _topPromotionTimer.callOnce(std::min(
250 std::max(next - now, kTopPromotionMinDelay),
251 kTopPromotionInterval) * crl::time(1000));
252 };
253
topPromotionDone(const MTPhelp_PromoData & proxy)254 void ApiWrap::topPromotionDone(const MTPhelp_PromoData &proxy) {
255 _topPromotionNextRequestTime = proxy.match([&](const auto &data) {
256 return data.vexpires().v;
257 });
258 getTopPromotionDelayed(
259 base::unixtime::now(),
260 _topPromotionNextRequestTime);
261
262 proxy.match([&](const MTPDhelp_promoDataEmpty &data) {
263 _session->data().setTopPromoted(nullptr, QString(), QString());
264 }, [&](const MTPDhelp_promoData &data) {
265 _session->data().processChats(data.vchats());
266 _session->data().processUsers(data.vusers());
267 const auto peerId = peerFromMTP(data.vpeer());
268 const auto history = _session->data().history(peerId);
269 _session->data().setTopPromoted(
270 history,
271 data.vpsa_type().value_or_empty(),
272 data.vpsa_message().value_or_empty());
273 });
274 }
275
requestDeepLinkInfo(const QString & path,Fn<void (const MTPDhelp_deepLinkInfo & result)> callback)276 void ApiWrap::requestDeepLinkInfo(
277 const QString &path,
278 Fn<void(const MTPDhelp_deepLinkInfo &result)> callback) {
279 request(_deepLinkInfoRequestId).cancel();
280 _deepLinkInfoRequestId = request(MTPhelp_GetDeepLinkInfo(
281 MTP_string(path)
282 )).done([=](const MTPhelp_DeepLinkInfo &result) {
283 _deepLinkInfoRequestId = 0;
284 if (result.type() == mtpc_help_deepLinkInfo) {
285 callback(result.c_help_deepLinkInfo());
286 }
287 }).fail([=](const MTP::Error &error) {
288 _deepLinkInfoRequestId = 0;
289 }).send();
290 }
291
requestTermsUpdate()292 void ApiWrap::requestTermsUpdate() {
293 if (_termsUpdateRequestId) {
294 return;
295 }
296 const auto now = crl::now();
297 if (_termsUpdateSendAt && now < _termsUpdateSendAt) {
298 base::call_delayed(_termsUpdateSendAt - now, _session, [=] {
299 requestTermsUpdate();
300 });
301 return;
302 }
303
304 constexpr auto kTermsUpdateTimeoutMin = 10 * crl::time(1000);
305 constexpr auto kTermsUpdateTimeoutMax = 86400 * crl::time(1000);
306
307 _termsUpdateRequestId = request(MTPhelp_GetTermsOfServiceUpdate(
308 )).done([=](const MTPhelp_TermsOfServiceUpdate &result) {
309 _termsUpdateRequestId = 0;
310
311 const auto requestNext = [&](auto &&data) {
312 const auto timeout = (data.vexpires().v - base::unixtime::now());
313 _termsUpdateSendAt = crl::now() + std::clamp(
314 timeout * crl::time(1000),
315 kTermsUpdateTimeoutMin,
316 kTermsUpdateTimeoutMax);
317 requestTermsUpdate();
318 };
319 switch (result.type()) {
320 case mtpc_help_termsOfServiceUpdateEmpty: {
321 const auto &data = result.c_help_termsOfServiceUpdateEmpty();
322 requestNext(data);
323 } break;
324 case mtpc_help_termsOfServiceUpdate: {
325 const auto &data = result.c_help_termsOfServiceUpdate();
326 const auto &terms = data.vterms_of_service();
327 const auto &fields = terms.c_help_termsOfService();
328 session().lockByTerms(
329 Window::TermsLock::FromMTP(_session, fields));
330 requestNext(data);
331 } break;
332 default: Unexpected("Type in requestTermsUpdate().");
333 }
334 }).fail([=](const MTP::Error &error) {
335 _termsUpdateRequestId = 0;
336 _termsUpdateSendAt = crl::now() + kTermsUpdateTimeoutMin;
337 requestTermsUpdate();
338 }).send();
339 }
340
acceptTerms(bytes::const_span id)341 void ApiWrap::acceptTerms(bytes::const_span id) {
342 request(MTPhelp_AcceptTermsOfService(
343 MTP_dataJSON(MTP_bytes(id))
344 )).done([=](const MTPBool &result) {
345 requestTermsUpdate();
346 }).send();
347 }
348
checkChatInvite(const QString & hash,FnMut<void (const MTPChatInvite &)> done,Fn<void (const MTP::Error &)> fail)349 void ApiWrap::checkChatInvite(
350 const QString &hash,
351 FnMut<void(const MTPChatInvite &)> done,
352 Fn<void(const MTP::Error &)> fail) {
353 request(base::take(_checkInviteRequestId)).cancel();
354 _checkInviteRequestId = request(MTPmessages_CheckChatInvite(
355 MTP_string(hash)
356 )).done(std::move(done)).fail(std::move(fail)).send();
357 }
358
importChatInvite(const QString & hash,bool isGroup)359 void ApiWrap::importChatInvite(const QString &hash, bool isGroup) {
360 request(MTPmessages_ImportChatInvite(
361 MTP_string(hash)
362 )).done([=](const MTPUpdates &result) {
363 applyUpdates(result);
364
365 Ui::hideLayer();
366 const auto handleChats = [&](const MTPVector<MTPChat> &chats) {
367 if (chats.v.isEmpty()) {
368 return;
369 }
370 const auto peerId = chats.v[0].match([](const MTPDchat &data) {
371 return peerFromChat(data.vid().v);
372 }, [](const MTPDchannel &data) {
373 return peerFromChannel(data.vid().v);
374 }, [](auto&&) {
375 return PeerId(0);
376 });
377 if (const auto peer = _session->data().peerLoaded(peerId)) {
378 const auto &windows = _session->windows();
379 if (!windows.empty()) {
380 windows.front()->showPeerHistory(
381 peer,
382 Window::SectionShow::Way::Forward);
383 }
384 }
385 };
386 result.match([&](const MTPDupdates &data) {
387 handleChats(data.vchats());
388 }, [&](const MTPDupdatesCombined &data) {
389 handleChats(data.vchats());
390 }, [&](auto &&) {
391 LOG(("API Error: unexpected update cons %1 "
392 "(ApiWrap::importChatInvite)").arg(result.type()));
393 });
394 }).fail([=](const MTP::Error &error) {
395 const auto &type = error.type();
396 Ui::hideLayer();
397 Ui::ShowMultilineToast({ .text = { [&] {
398 if (type == qstr("INVITE_REQUEST_SENT")) {
399 return isGroup
400 ? tr::lng_group_request_sent(tr::now)
401 : tr::lng_group_request_sent_channel(tr::now);
402 } else if (type == qstr("CHANNELS_TOO_MUCH")) {
403 return tr::lng_join_channel_error(tr::now);
404 } else if (type == qstr("USERS_TOO_MUCH")) {
405 return tr::lng_group_invite_no_room(tr::now);
406 } else {
407 return tr::lng_group_invite_bad_link(tr::now);
408 }
409 }() }, .duration = kJoinErrorDuration });
410 }).send();
411 }
412
savePinnedOrder(Data::Folder * folder)413 void ApiWrap::savePinnedOrder(Data::Folder *folder) {
414 const auto &order = _session->data().pinnedChatsOrder(
415 folder,
416 FilterId());
417 const auto input = [](const Dialogs::Key &key) {
418 if (const auto history = key.history()) {
419 return MTP_inputDialogPeer(history->peer->input);
420 } else if (const auto folder = key.folder()) {
421 return MTP_inputDialogPeerFolder(MTP_int(folder->id()));
422 }
423 Unexpected("Key type in pinnedDialogsOrder().");
424 };
425 auto peers = QVector<MTPInputDialogPeer>();
426 peers.reserve(order.size());
427 ranges::transform(
428 order,
429 ranges::back_inserter(peers),
430 input);
431 request(MTPmessages_ReorderPinnedDialogs(
432 MTP_flags(MTPmessages_ReorderPinnedDialogs::Flag::f_force),
433 MTP_int(folder ? folder->id() : 0),
434 MTP_vector(peers)
435 )).send();
436 }
437
toggleHistoryArchived(not_null<History * > history,bool archived,Fn<void ()> callback)438 void ApiWrap::toggleHistoryArchived(
439 not_null<History*> history,
440 bool archived,
441 Fn<void()> callback) {
442 if (const auto already = _historyArchivedRequests.take(history)) {
443 request(already->first).cancel();
444 }
445 const auto isPinned = history->isPinnedDialog(0);
446 const auto archiveId = Data::Folder::kId;
447 const auto requestId = request(MTPfolders_EditPeerFolders(
448 MTP_vector<MTPInputFolderPeer>(
449 1,
450 MTP_inputFolderPeer(
451 history->peer->input,
452 MTP_int(archived ? archiveId : 0)))
453 )).done([=](const MTPUpdates &result) {
454 applyUpdates(result);
455 if (archived) {
456 history->setFolder(_session->data().folder(archiveId));
457 } else {
458 history->clearFolder();
459 }
460 if (const auto data = _historyArchivedRequests.take(history)) {
461 data->second();
462 }
463 if (isPinned) {
464 _session->data().notifyPinnedDialogsOrderUpdated();
465 }
466 }).fail([=](const MTP::Error &error) {
467 _historyArchivedRequests.remove(history);
468 }).send();
469 _historyArchivedRequests.emplace(history, requestId, callback);
470 }
471
sendMessageFail(const MTP::Error & error,not_null<PeerData * > peer,uint64 randomId,FullMsgId itemId)472 void ApiWrap::sendMessageFail(
473 const MTP::Error &error,
474 not_null<PeerData*> peer,
475 uint64 randomId,
476 FullMsgId itemId) {
477 if (error.type() == qstr("PEER_FLOOD")) {
478 Ui::show(Box<Ui::InformBox>(
479 PeerFloodErrorText(&session(), PeerFloodType::Send)));
480 } else if (error.type() == qstr("USER_BANNED_IN_CHANNEL")) {
481 const auto link = textcmdLink(
482 session().createInternalLinkFull(qsl("spambot")),
483 tr::lng_cant_more_info(tr::now));
484 Ui::show(Box<Ui::InformBox>(tr::lng_error_public_groups_denied(
485 tr::now,
486 lt_more_info,
487 link)));
488 } else if (error.type().startsWith(qstr("SLOWMODE_WAIT_"))) {
489 const auto chop = qstr("SLOWMODE_WAIT_").size();
490 const auto left = base::StringViewMid(error.type(), chop).toInt();
491 if (const auto channel = peer->asChannel()) {
492 const auto seconds = channel->slowmodeSeconds();
493 if (seconds >= left) {
494 channel->growSlowmodeLastMessage(
495 base::unixtime::now() - (left - seconds));
496 } else {
497 requestFullPeer(peer);
498 }
499 }
500 } else if (error.type() == qstr("SCHEDULE_STATUS_PRIVATE")) {
501 auto &scheduled = _session->data().scheduledMessages();
502 Assert(peer->isUser());
503 if (const auto item = scheduled.lookupItem(peer->id, itemId.msg)) {
504 scheduled.removeSending(item);
505 Ui::show(Box<Ui::InformBox>(tr::lng_cant_do_this(tr::now)));
506 }
507 }
508 if (const auto item = _session->data().message(itemId)) {
509 Assert(randomId != 0);
510 _session->data().unregisterMessageRandomId(randomId);
511 item->sendFailed();
512 }
513 }
514
requestMessageData(ChannelData * channel,MsgId msgId,RequestMessageDataCallback callback)515 void ApiWrap::requestMessageData(
516 ChannelData *channel,
517 MsgId msgId,
518 RequestMessageDataCallback callback) {
519 auto &requests = channel
520 ? _channelMessageDataRequests[channel][msgId]
521 : _messageDataRequests[msgId];
522 if (callback) {
523 requests.callbacks.push_back(callback);
524 }
525 if (!requests.requestId) {
526 _messageDataResolveDelayed.call();
527 }
528 }
529
collectMessageIds(const MessageDataRequests & requests)530 QVector<MTPInputMessage> ApiWrap::collectMessageIds(
531 const MessageDataRequests &requests) {
532 auto result = QVector<MTPInputMessage>();
533 result.reserve(requests.size());
534 for (const auto &[msgId, request] : requests) {
535 if (request.requestId > 0) {
536 continue;
537 }
538 result.push_back(MTP_inputMessageID(MTP_int(msgId)));
539 }
540 return result;
541 }
542
messageDataRequests(ChannelData * channel,bool onlyExisting)543 auto ApiWrap::messageDataRequests(ChannelData *channel, bool onlyExisting)
544 -> MessageDataRequests* {
545 if (channel) {
546 auto i = _channelMessageDataRequests.find(channel);
547 if (i == end(_channelMessageDataRequests)) {
548 if (onlyExisting) {
549 return nullptr;
550 }
551 i = _channelMessageDataRequests.emplace(
552 channel,
553 MessageDataRequests()).first;
554 }
555 return &i->second;
556 }
557 return &_messageDataRequests;
558 }
559
resolveMessageDatas()560 void ApiWrap::resolveMessageDatas() {
561 if (_messageDataRequests.empty() && _channelMessageDataRequests.empty()) {
562 return;
563 }
564
565 const auto ids = collectMessageIds(_messageDataRequests);
566 if (!ids.isEmpty()) {
567 const auto requestId = request(MTPmessages_GetMessages(
568 MTP_vector<MTPInputMessage>(ids)
569 )).done([=](
570 const MTPmessages_Messages &result,
571 mtpRequestId requestId) {
572 _session->data().processExistingMessages(nullptr, result);
573 finalizeMessageDataRequest(nullptr, requestId);
574 }).fail([=](const MTP::Error &error, mtpRequestId requestId) {
575 finalizeMessageDataRequest(nullptr, requestId);
576 }).afterDelay(kSmallDelayMs).send();
577
578 for (auto &[msgId, request] : _messageDataRequests) {
579 if (request.requestId > 0) {
580 continue;
581 }
582 request.requestId = requestId;
583 }
584 }
585 for (auto j = _channelMessageDataRequests.begin(); j != _channelMessageDataRequests.cend();) {
586 if (j->second.empty()) {
587 j = _channelMessageDataRequests.erase(j);
588 continue;
589 }
590 const auto ids = collectMessageIds(j->second);
591 if (!ids.isEmpty()) {
592 const auto channel = j->first;
593 const auto requestId = request(MTPchannels_GetMessages(
594 channel->inputChannel,
595 MTP_vector<MTPInputMessage>(ids)
596 )).done([=](
597 const MTPmessages_Messages &result,
598 mtpRequestId requestId) {
599 _session->data().processExistingMessages(channel, result);
600 finalizeMessageDataRequest(channel, requestId);
601 }).fail([=](const MTP::Error &error, mtpRequestId requestId) {
602 finalizeMessageDataRequest(channel, requestId);
603 }).afterDelay(kSmallDelayMs).send();
604
605 for (auto &[msgId, request] : j->second) {
606 if (request.requestId > 0) {
607 continue;
608 }
609 request.requestId = requestId;
610 }
611 }
612 ++j;
613 }
614 }
615
finalizeMessageDataRequest(ChannelData * channel,mtpRequestId requestId)616 void ApiWrap::finalizeMessageDataRequest(
617 ChannelData *channel,
618 mtpRequestId requestId) {
619 auto requests = messageDataRequests(channel, true);
620 if (requests) {
621 for (auto i = requests->begin(); i != requests->cend();) {
622 if (i->second.requestId == requestId) {
623 for (const auto &callback : i->second.callbacks) {
624 callback(channel, i->first);
625 }
626 i = requests->erase(i);
627 } else {
628 ++i;
629 }
630 }
631 if (channel && requests->empty()) {
632 _channelMessageDataRequests.remove(channel);
633 }
634 }
635 }
636
exportDirectMessageLink(not_null<HistoryItem * > item,bool inRepliesContext)637 QString ApiWrap::exportDirectMessageLink(
638 not_null<HistoryItem*> item,
639 bool inRepliesContext) {
640 Expects(item->history()->peer->isChannel());
641
642 const auto itemId = item->fullId();
643 const auto channel = item->history()->peer->asChannel();
644 const auto fallback = [&] {
645 auto linkChannel = channel;
646 auto linkItemId = item->id;
647 auto linkCommentId = MsgId();
648 auto linkThreadId = MsgId();
649 if (inRepliesContext) {
650 if (const auto rootId = item->replyToTop()) {
651 const auto root = item->history()->owner().message(
652 peerToChannel(channel->id),
653 rootId);
654 const auto sender = root
655 ? root->discussionPostOriginalSender()
656 : nullptr;
657 if (sender && sender->hasUsername()) {
658 // Comment to a public channel.
659 const auto forwarded = root->Get<HistoryMessageForwarded>();
660 linkItemId = forwarded->savedFromMsgId;
661 if (linkItemId) {
662 linkChannel = sender;
663 linkCommentId = item->id;
664 } else {
665 linkItemId = item->id;
666 }
667 } else {
668 // Reply in a thread, maybe comment in a private channel.
669 linkThreadId = rootId;
670 }
671 }
672 }
673 const auto base = linkChannel->hasUsername()
674 ? linkChannel->username
675 : "c/" + QString::number(peerToChannel(linkChannel->id).bare);
676 const auto query = base
677 + '/'
678 + QString::number(linkItemId.bare)
679 + (linkCommentId
680 ? "?comment=" + QString::number(linkCommentId.bare)
681 : linkThreadId
682 ? "?thread=" + QString::number(linkThreadId.bare)
683 : "");
684 if (linkChannel->hasUsername()
685 && !linkChannel->isMegagroup()
686 && !linkCommentId
687 && !linkThreadId) {
688 if (const auto media = item->media()) {
689 if (const auto document = media->document()) {
690 if (document->isVideoMessage()) {
691 return qsl("https://telesco.pe/") + query;
692 }
693 }
694 }
695 }
696 return session().createInternalLinkFull(query);
697 };
698 const auto i = _unlikelyMessageLinks.find(itemId);
699 const auto current = (i != end(_unlikelyMessageLinks))
700 ? i->second
701 : fallback();
702 request(MTPchannels_ExportMessageLink(
703 MTP_flags(inRepliesContext
704 ? MTPchannels_ExportMessageLink::Flag::f_thread
705 : MTPchannels_ExportMessageLink::Flag(0)),
706 channel->inputChannel,
707 MTP_int(item->id)
708 )).done([=](const MTPExportedMessageLink &result) {
709 const auto link = result.match([&](const auto &data) {
710 return qs(data.vlink());
711 });
712 if (current != link) {
713 _unlikelyMessageLinks.emplace_or_assign(itemId, link);
714 }
715 }).send();
716 return current;
717 }
718
requestContacts()719 void ApiWrap::requestContacts() {
720 if (_session->data().contactsLoaded().current() || _contactsRequestId) {
721 return;
722 }
723 _contactsRequestId = request(MTPcontacts_GetContacts(
724 MTP_long(0) // hash
725 )).done([=](const MTPcontacts_Contacts &result) {
726 _contactsRequestId = 0;
727 if (result.type() == mtpc_contacts_contactsNotModified) {
728 return;
729 }
730 Assert(result.type() == mtpc_contacts_contacts);
731 const auto &d = result.c_contacts_contacts();
732 _session->data().processUsers(d.vusers());
733 for (const auto &contact : d.vcontacts().v) {
734 if (contact.type() != mtpc_contact) continue;
735
736 const auto userId = UserId(contact.c_contact().vuser_id());
737 if (userId == _session->userId()) {
738 _session->user()->setIsContact(true);
739 }
740 }
741 _session->data().contactsLoaded() = true;
742 }).fail([=](const MTP::Error &error) {
743 _contactsRequestId = 0;
744 }).send();
745 }
746
requestDialogs(Data::Folder * folder)747 void ApiWrap::requestDialogs(Data::Folder *folder) {
748 if (folder && !_foldersLoadState.contains(folder)) {
749 _foldersLoadState.emplace(folder, DialogsLoadState());
750 }
751 requestMoreDialogs(folder);
752 }
753
requestMoreDialogs(Data::Folder * folder)754 void ApiWrap::requestMoreDialogs(Data::Folder *folder) {
755 const auto state = dialogsLoadState(folder);
756 if (!state) {
757 return;
758 } else if (state->requestId) {
759 return;
760 } else if (_dialogsLoadBlockedByDate.current()) {
761 return;
762 }
763
764 const auto firstLoad = !state->offsetDate;
765 const auto loadCount = firstLoad ? kDialogsFirstLoad : kDialogsPerPage;
766 const auto flags = MTPmessages_GetDialogs::Flag::f_exclude_pinned
767 | MTPmessages_GetDialogs::Flag::f_folder_id;
768 const auto hash = uint64(0);
769 state->requestId = request(MTPmessages_GetDialogs(
770 MTP_flags(flags),
771 MTP_int(folder ? folder->id() : 0),
772 MTP_int(state->offsetDate),
773 MTP_int(state->offsetId),
774 (state->offsetPeer
775 ? state->offsetPeer->input
776 : MTP_inputPeerEmpty()),
777 MTP_int(loadCount),
778 MTP_long(hash)
779 )).done([=](const MTPmessages_Dialogs &result) {
780 const auto state = dialogsLoadState(folder);
781 const auto count = result.match([](
782 const MTPDmessages_dialogsNotModified &) {
783 LOG(("API Error: not-modified received for requested dialogs."));
784 return 0;
785 }, [&](const MTPDmessages_dialogs &data) {
786 if (state) {
787 state->listReceived = true;
788 dialogsLoadFinish(folder); // may kill 'state'.
789 }
790 return int(data.vdialogs().v.size());
791 }, [&](const MTPDmessages_dialogsSlice &data) {
792 updateDialogsOffset(
793 folder,
794 data.vdialogs().v,
795 data.vmessages().v);
796 return data.vcount().v;
797 });
798 result.match([](const MTPDmessages_dialogsNotModified & data) {
799 LOG(("API Error: not-modified received for requested dialogs."));
800 }, [&](const auto &data) {
801 _session->data().processUsers(data.vusers());
802 _session->data().processChats(data.vchats());
803 _session->data().applyDialogs(
804 folder,
805 data.vmessages().v,
806 data.vdialogs().v,
807 count);
808 });
809
810 if (!folder
811 && (!_dialogsLoadState || !_dialogsLoadState->listReceived)) {
812 refreshDialogsLoadBlocked();
813 }
814 requestMoreDialogsIfNeeded();
815 _session->data().chatsListChanged(folder);
816 }).fail([=](const MTP::Error &error) {
817 dialogsLoadState(folder)->requestId = 0;
818 }).send();
819
820 if (!state->pinnedReceived) {
821 requestPinnedDialogs(folder);
822 }
823 if (!folder) {
824 refreshDialogsLoadBlocked();
825 }
826 }
827
refreshDialogsLoadBlocked()828 void ApiWrap::refreshDialogsLoadBlocked() {
829 _dialogsLoadMayBlockByDate = _dialogsLoadState
830 && !_dialogsLoadState->listReceived
831 && (_dialogsLoadTill > 0);
832 _dialogsLoadBlockedByDate = _dialogsLoadState
833 && !_dialogsLoadState->listReceived
834 && !_dialogsLoadState->requestId
835 && (_dialogsLoadTill > 0)
836 && (_dialogsLoadState->offsetDate > 0)
837 && (_dialogsLoadState->offsetDate <= _dialogsLoadTill);
838 }
839
requestMoreDialogsIfNeeded()840 void ApiWrap::requestMoreDialogsIfNeeded() {
841 const auto dialogsReady = !_dialogsLoadState
842 || _dialogsLoadState->listReceived;
843 if (_session->data().chatsFilters().loadNextExceptions(dialogsReady)) {
844 return;
845 } else if (_dialogsLoadState && !_dialogsLoadState->listReceived) {
846 if (_dialogsLoadState->requestId) {
847 return;
848 }
849 requestDialogs(nullptr);
850 } else if (const auto folder = _session->data().folderLoaded(
851 Data::Folder::kId)) {
852 if (_session->data().chatsFilters().archiveNeeded()) {
853 requestMoreDialogs(folder);
854 }
855 }
856 requestContacts();
857 }
858
updateDialogsOffset(Data::Folder * folder,const QVector<MTPDialog> & dialogs,const QVector<MTPMessage> & messages)859 void ApiWrap::updateDialogsOffset(
860 Data::Folder *folder,
861 const QVector<MTPDialog> &dialogs,
862 const QVector<MTPMessage> &messages) {
863 auto lastDate = TimeId(0);
864 auto lastPeer = PeerId(0);
865 auto lastMsgId = MsgId(0);
866 for (const auto &dialog : ranges::views::reverse(dialogs)) {
867 dialog.match([&](const auto &dialog) {
868 const auto peer = peerFromMTP(dialog.vpeer());
869 const auto messageId = dialog.vtop_message().v;
870 if (!peer || !messageId) {
871 return;
872 }
873 if (!lastPeer) {
874 lastPeer = peer;
875 }
876 if (!lastMsgId) {
877 lastMsgId = messageId;
878 }
879 for (const auto &message : ranges::views::reverse(messages)) {
880 if (IdFromMessage(message) == messageId
881 && PeerFromMessage(message) == peer) {
882 if (const auto date = DateFromMessage(message)) {
883 lastDate = date;
884 }
885 return;
886 }
887 }
888 });
889 if (lastDate) {
890 break;
891 }
892 }
893 if (const auto state = dialogsLoadState(folder)) {
894 if (lastDate) {
895 state->offsetDate = lastDate;
896 state->offsetId = lastMsgId;
897 state->offsetPeer = _session->data().peer(lastPeer);
898 state->requestId = 0;
899 } else {
900 state->listReceived = true;
901 dialogsLoadFinish(folder);
902 }
903 }
904 }
905
dialogsLoadState(Data::Folder * folder)906 auto ApiWrap::dialogsLoadState(Data::Folder *folder) -> DialogsLoadState* {
907 if (!folder) {
908 return _dialogsLoadState.get();
909 }
910 const auto i = _foldersLoadState.find(folder);
911 return (i != end(_foldersLoadState)) ? &i->second : nullptr;
912 }
913
dialogsLoadFinish(Data::Folder * folder)914 void ApiWrap::dialogsLoadFinish(Data::Folder *folder) {
915 const auto notify = [&] {
916 Core::App().postponeCall(crl::guard(_session, [=] {
917 _session->data().chatsListDone(folder);
918 }));
919 };
920 const auto state = dialogsLoadState(folder);
921 if (!state || !state->listReceived || !state->pinnedReceived) {
922 return;
923 }
924 if (folder) {
925 _foldersLoadState.remove(folder);
926 notify();
927 } else {
928 _dialogsLoadState = nullptr;
929 notify();
930 }
931 }
932
requestPinnedDialogs(Data::Folder * folder)933 void ApiWrap::requestPinnedDialogs(Data::Folder *folder) {
934 const auto state = dialogsLoadState(folder);
935 if (!state || state->pinnedReceived || state->pinnedRequestId) {
936 return;
937 }
938
939 const auto finalize = [=] {
940 if (const auto state = dialogsLoadState(folder)) {
941 state->pinnedRequestId = 0;
942 state->pinnedReceived = true;
943 dialogsLoadFinish(folder);
944 }
945 };
946 state->pinnedRequestId = request(MTPmessages_GetPinnedDialogs(
947 MTP_int(folder ? folder->id() : 0)
948 )).done([=](const MTPmessages_PeerDialogs &result) {
949 finalize();
950 result.match([&](const MTPDmessages_peerDialogs &data) {
951 _session->data().processUsers(data.vusers());
952 _session->data().processChats(data.vchats());
953 _session->data().clearPinnedChats(folder);
954 _session->data().applyDialogs(
955 folder,
956 data.vmessages().v,
957 data.vdialogs().v);
958 _session->data().chatsListChanged(folder);
959 _session->data().notifyPinnedDialogsOrderUpdated();
960 });
961 }).fail([=](const MTP::Error &error) {
962 finalize();
963 }).send();
964 }
965
requestMoreBlockedByDateDialogs()966 void ApiWrap::requestMoreBlockedByDateDialogs() {
967 if (!_dialogsLoadState) {
968 return;
969 }
970 const auto max = _session->settings().supportChatsTimeSlice();
971 _dialogsLoadTill = _dialogsLoadState->offsetDate
972 ? (_dialogsLoadState->offsetDate - max)
973 : (base::unixtime::now() - max);
974 refreshDialogsLoadBlocked();
975 requestDialogs();
976 }
977
dialogsLoadMayBlockByDate() const978 rpl::producer<bool> ApiWrap::dialogsLoadMayBlockByDate() const {
979 return _dialogsLoadMayBlockByDate.value();
980 }
981
dialogsLoadBlockedByDate() const982 rpl::producer<bool> ApiWrap::dialogsLoadBlockedByDate() const {
983 return _dialogsLoadBlockedByDate.value();
984 }
985
requestWallPaper(const QString & slug,Fn<void (const Data::WallPaper &)> done,Fn<void (const MTP::Error &)> fail)986 void ApiWrap::requestWallPaper(
987 const QString &slug,
988 Fn<void(const Data::WallPaper &)> done,
989 Fn<void(const MTP::Error &)> fail) {
990 if (_wallPaperSlug != slug) {
991 _wallPaperSlug = slug;
992 if (_wallPaperRequestId) {
993 request(base::take(_wallPaperRequestId)).cancel();
994 }
995 }
996 _wallPaperDone = std::move(done);
997 _wallPaperFail = std::move(fail);
998 if (_wallPaperRequestId) {
999 return;
1000 }
1001 _wallPaperRequestId = request(MTPaccount_GetWallPaper(
1002 MTP_inputWallPaperSlug(MTP_string(slug))
1003 )).done([=](const MTPWallPaper &result) {
1004 _wallPaperRequestId = 0;
1005 _wallPaperSlug = QString();
1006 if (const auto paper = Data::WallPaper::Create(_session, result)) {
1007 if (const auto done = base::take(_wallPaperDone)) {
1008 done(*paper);
1009 }
1010 } else if (const auto fail = base::take(_wallPaperFail)) {
1011 fail(MTP::Error::Local("BAD_DOCUMENT", "In a wallpaper."));
1012 }
1013 }).fail([=](const MTP::Error &error) {
1014 _wallPaperRequestId = 0;
1015 _wallPaperSlug = QString();
1016 if (const auto fail = base::take(_wallPaperFail)) {
1017 fail(error);
1018 }
1019 }).send();
1020 }
1021
requestFullPeer(not_null<PeerData * > peer)1022 void ApiWrap::requestFullPeer(not_null<PeerData*> peer) {
1023 if (_fullPeerRequests.contains(peer)) {
1024 return;
1025 }
1026
1027 const auto requestId = [&] {
1028 const auto failHandler = [=](const MTP::Error &error) {
1029 _fullPeerRequests.remove(peer);
1030 migrateFail(peer, error);
1031 };
1032 if (const auto user = peer->asUser()) {
1033 if (_session->supportMode()) {
1034 _session->supportHelper().refreshInfo(user);
1035 }
1036 return request(MTPusers_GetFullUser(
1037 user->inputUser
1038 )).done([=](const MTPUserFull &result, mtpRequestId requestId) {
1039 gotUserFull(user, result, requestId);
1040 }).fail(failHandler).send();
1041 } else if (const auto chat = peer->asChat()) {
1042 return request(MTPmessages_GetFullChat(
1043 chat->inputChat
1044 )).done([=](
1045 const MTPmessages_ChatFull &result,
1046 mtpRequestId requestId) {
1047 gotChatFull(peer, result, requestId);
1048 }).fail(failHandler).send();
1049 } else if (const auto channel = peer->asChannel()) {
1050 return request(MTPchannels_GetFullChannel(
1051 channel->inputChannel
1052 )).done([=](
1053 const MTPmessages_ChatFull &result,
1054 mtpRequestId requestId) {
1055 gotChatFull(peer, result, requestId);
1056 migrateDone(channel, channel);
1057 }).fail(failHandler).send();
1058 }
1059 Unexpected("Peer type in requestFullPeer.");
1060 }();
1061 _fullPeerRequests.insert(peer, requestId);
1062 }
1063
processFullPeer(not_null<PeerData * > peer,const MTPmessages_ChatFull & result)1064 void ApiWrap::processFullPeer(
1065 not_null<PeerData*> peer,
1066 const MTPmessages_ChatFull &result) {
1067 gotChatFull(peer, result, mtpRequestId(0));
1068 }
1069
processFullPeer(not_null<UserData * > user,const MTPUserFull & result)1070 void ApiWrap::processFullPeer(
1071 not_null<UserData*> user,
1072 const MTPUserFull &result) {
1073 gotUserFull(user, result, mtpRequestId(0));
1074 }
1075
gotChatFull(not_null<PeerData * > peer,const MTPmessages_ChatFull & result,mtpRequestId req)1076 void ApiWrap::gotChatFull(
1077 not_null<PeerData*> peer,
1078 const MTPmessages_ChatFull &result,
1079 mtpRequestId req) {
1080 const auto &d = result.c_messages_chatFull();
1081 _session->data().applyMaximumChatVersions(d.vchats());
1082
1083 _session->data().processUsers(d.vusers());
1084 _session->data().processChats(d.vchats());
1085
1086 d.vfull_chat().match([&](const MTPDchatFull &data) {
1087 if (const auto chat = peer->asChat()) {
1088 Data::ApplyChatUpdate(chat, data);
1089 } else {
1090 LOG(("MTP Error: bad type in gotChatFull for channel: %1"
1091 ).arg(d.vfull_chat().type()));
1092 }
1093 }, [&](const MTPDchannelFull &data) {
1094 if (const auto channel = peer->asChannel()) {
1095 Data::ApplyChannelUpdate(channel, data);
1096 } else {
1097 LOG(("MTP Error: bad type in gotChatFull for chat: %1"
1098 ).arg(d.vfull_chat().type()));
1099 }
1100 });
1101
1102 if (req) {
1103 const auto i = _fullPeerRequests.find(peer);
1104 if (i != _fullPeerRequests.cend() && i.value() == req) {
1105 _fullPeerRequests.erase(i);
1106 }
1107 }
1108 _session->changes().peerUpdated(
1109 peer,
1110 Data::PeerUpdate::Flag::FullInfo);
1111 }
1112
gotUserFull(not_null<UserData * > user,const MTPUserFull & result,mtpRequestId req)1113 void ApiWrap::gotUserFull(
1114 not_null<UserData*> user,
1115 const MTPUserFull &result,
1116 mtpRequestId req) {
1117 const auto &d = result.c_userFull();
1118 if (user == _session->user() && !_session->validateSelf(d.vuser())) {
1119 constexpr auto kRequestUserAgainTimeout = crl::time(10000);
1120 base::call_delayed(kRequestUserAgainTimeout, _session, [=] {
1121 requestFullPeer(user);
1122 });
1123 return;
1124 }
1125 Data::ApplyUserUpdate(user, d);
1126
1127 if (req) {
1128 const auto i = _fullPeerRequests.find(user);
1129 if (i != _fullPeerRequests.cend() && i.value() == req) {
1130 _fullPeerRequests.erase(i);
1131 }
1132 }
1133 _session->changes().peerUpdated(
1134 user,
1135 Data::PeerUpdate::Flag::FullInfo);
1136 }
1137
requestPeer(not_null<PeerData * > peer)1138 void ApiWrap::requestPeer(not_null<PeerData*> peer) {
1139 if (_fullPeerRequests.contains(peer) || _peerRequests.contains(peer)) {
1140 return;
1141 }
1142
1143 const auto requestId = [&] {
1144 const auto failHandler = [=](const MTP::Error &error) {
1145 _peerRequests.remove(peer);
1146 };
1147 const auto chatHandler = [=](const MTPmessages_Chats &result) {
1148 _peerRequests.remove(peer);
1149 const auto &chats = result.match([](const auto &data) {
1150 return data.vchats();
1151 });
1152 _session->data().applyMaximumChatVersions(chats);
1153 _session->data().processChats(chats);
1154 };
1155 if (const auto user = peer->asUser()) {
1156 return request(MTPusers_GetUsers(
1157 MTP_vector<MTPInputUser>(1, user->inputUser)
1158 )).done([=](const MTPVector<MTPUser> &result) {
1159 _peerRequests.remove(user);
1160 _session->data().processUsers(result);
1161 }).fail(failHandler).send();
1162 } else if (const auto chat = peer->asChat()) {
1163 return request(MTPmessages_GetChats(
1164 MTP_vector<MTPlong>(1, chat->inputChat)
1165 )).done(chatHandler).fail(failHandler).send();
1166 } else if (const auto channel = peer->asChannel()) {
1167 return request(MTPchannels_GetChannels(
1168 MTP_vector<MTPInputChannel>(1, channel->inputChannel)
1169 )).done(chatHandler).fail(failHandler).send();
1170 }
1171 Unexpected("Peer type in requestPeer.");
1172 }();
1173 _peerRequests.insert(peer, requestId);
1174 }
1175
requestPeerSettings(not_null<PeerData * > peer)1176 void ApiWrap::requestPeerSettings(not_null<PeerData*> peer) {
1177 if (!_requestedPeerSettings.emplace(peer).second) {
1178 return;
1179 }
1180 request(MTPmessages_GetPeerSettings(
1181 peer->input
1182 )).done([=](const MTPPeerSettings &result) {
1183 peer->setSettings(result);
1184 _requestedPeerSettings.erase(peer);
1185 }).fail([=](const MTP::Error &error) {
1186 _requestedPeerSettings.erase(peer);
1187 }).send();
1188 }
1189
migrateChat(not_null<ChatData * > chat,FnMut<void (not_null<ChannelData * >)> done,Fn<void (const MTP::Error &)> fail)1190 void ApiWrap::migrateChat(
1191 not_null<ChatData*> chat,
1192 FnMut<void(not_null<ChannelData*>)> done,
1193 Fn<void(const MTP::Error &)> fail) {
1194 const auto callback = [&] {
1195 return MigrateCallbacks{ std::move(done), std::move(fail) };
1196 };
1197 const auto i = _migrateCallbacks.find(chat);
1198 if (i != _migrateCallbacks.end()) {
1199 i->second.push_back(callback());
1200 return;
1201 }
1202 _migrateCallbacks.emplace(chat).first->second.push_back(callback());
1203 if (const auto channel = chat->migrateTo()) {
1204 session().changes().peerUpdated(
1205 chat,
1206 Data::PeerUpdate::Flag::Migration);
1207 crl::on_main([=] {
1208 migrateDone(chat, channel);
1209 });
1210 } else if (chat->isDeactivated()) {
1211 crl::on_main([=] {
1212 migrateFail(
1213 chat,
1214 MTP::Error::Local(
1215 "BAD_MIGRATION",
1216 "Chat is already deactivated"));
1217 });
1218 return;
1219 } else if (!chat->amCreator()) {
1220 crl::on_main([=] {
1221 migrateFail(
1222 chat,
1223 MTP::Error::Local(
1224 "BAD_MIGRATION",
1225 "Current user is not the creator of that chat"));
1226 });
1227 return;
1228 }
1229
1230 request(MTPmessages_MigrateChat(
1231 chat->inputChat
1232 )).done([=](const MTPUpdates &result) {
1233 applyUpdates(result);
1234 session().changes().sendNotifications();
1235
1236 if (const auto channel = chat->migrateTo()) {
1237 if (auto handlers = _migrateCallbacks.take(chat)) {
1238 _migrateCallbacks.emplace(channel, std::move(*handlers));
1239 }
1240 requestFullPeer(channel);
1241 } else {
1242 migrateFail(
1243 chat,
1244 MTP::Error::Local("MIGRATION_FAIL", "No channel"));
1245 }
1246 }).fail([=](const MTP::Error &error) {
1247 migrateFail(chat, error);
1248 }).send();
1249 }
1250
migrateDone(not_null<PeerData * > peer,not_null<ChannelData * > channel)1251 void ApiWrap::migrateDone(
1252 not_null<PeerData*> peer,
1253 not_null<ChannelData*> channel) {
1254 session().changes().sendNotifications();
1255 if (auto handlers = _migrateCallbacks.take(peer)) {
1256 for (auto &handler : *handlers) {
1257 if (handler.done) {
1258 handler.done(channel);
1259 }
1260 }
1261 }
1262 }
1263
migrateFail(not_null<PeerData * > peer,const MTP::Error & error)1264 void ApiWrap::migrateFail(not_null<PeerData*> peer, const MTP::Error &error) {
1265 const auto &type = error.type();
1266 if (type == qstr("CHANNELS_TOO_MUCH")) {
1267 Ui::show(Box<Ui::InformBox>(tr::lng_migrate_error(tr::now)));
1268 }
1269 if (auto handlers = _migrateCallbacks.take(peer)) {
1270 for (auto &handler : *handlers) {
1271 if (handler.fail) {
1272 handler.fail(error);
1273 }
1274 }
1275 }
1276 }
1277
markMediaRead(const base::flat_set<not_null<HistoryItem * >> & items)1278 void ApiWrap::markMediaRead(
1279 const base::flat_set<not_null<HistoryItem*>> &items) {
1280 auto markedIds = QVector<MTPint>();
1281 auto channelMarkedIds = base::flat_map<
1282 not_null<ChannelData*>,
1283 QVector<MTPint>>();
1284 markedIds.reserve(items.size());
1285 for (const auto &item : items) {
1286 if ((!item->isUnreadMedia() || item->out())
1287 && !item->isUnreadMention()) {
1288 continue;
1289 }
1290 item->markMediaRead();
1291 if (!item->isRegular()) {
1292 continue;
1293 }
1294 if (const auto channel = item->history()->peer->asChannel()) {
1295 channelMarkedIds[channel].push_back(MTP_int(item->id));
1296 } else {
1297 markedIds.push_back(MTP_int(item->id));
1298 }
1299 }
1300 if (!markedIds.isEmpty()) {
1301 request(MTPmessages_ReadMessageContents(
1302 MTP_vector<MTPint>(markedIds)
1303 )).done([=](const MTPmessages_AffectedMessages &result) {
1304 applyAffectedMessages(result);
1305 }).send();
1306 }
1307 for (const auto &channelIds : channelMarkedIds) {
1308 request(MTPchannels_ReadMessageContents(
1309 channelIds.first->inputChannel,
1310 MTP_vector<MTPint>(channelIds.second)
1311 )).send();
1312 }
1313 }
1314
markMediaRead(not_null<HistoryItem * > item)1315 void ApiWrap::markMediaRead(not_null<HistoryItem*> item) {
1316 if ((!item->isUnreadMedia() || item->out())
1317 && !item->isUnreadMention()) {
1318 return;
1319 }
1320 item->markMediaRead();
1321 if (!item->isRegular()) {
1322 return;
1323 }
1324 const auto ids = MTP_vector<MTPint>(1, MTP_int(item->id));
1325 if (const auto channel = item->history()->peer->asChannel()) {
1326 request(MTPchannels_ReadMessageContents(
1327 channel->inputChannel,
1328 ids
1329 )).send();
1330 } else {
1331 request(MTPmessages_ReadMessageContents(
1332 ids
1333 )).done([=](const MTPmessages_AffectedMessages &result) {
1334 applyAffectedMessages(result);
1335 }).send();
1336 }
1337 }
1338
requestPeers(const QList<PeerData * > & peers)1339 void ApiWrap::requestPeers(const QList<PeerData*> &peers) {
1340 QVector<MTPlong> chats;
1341 QVector<MTPInputChannel> channels;
1342 QVector<MTPInputUser> users;
1343 chats.reserve(peers.size());
1344 channels.reserve(peers.size());
1345 users.reserve(peers.size());
1346 for (const auto peer : peers) {
1347 if (!peer
1348 || _fullPeerRequests.contains(peer)
1349 || _peerRequests.contains(peer)) {
1350 continue;
1351 }
1352 if (const auto user = peer->asUser()) {
1353 users.push_back(user->inputUser);
1354 } else if (const auto chat = peer->asChat()) {
1355 chats.push_back(chat->inputChat);
1356 } else if (const auto channel = peer->asChannel()) {
1357 channels.push_back(channel->inputChannel);
1358 }
1359 }
1360 const auto handleChats = [=](const MTPmessages_Chats &result) {
1361 _session->data().processChats(result.match([](const auto &data) {
1362 return data.vchats();
1363 }));
1364 };
1365 if (!chats.isEmpty()) {
1366 request(MTPmessages_GetChats(
1367 MTP_vector<MTPlong>(chats)
1368 )).done(handleChats).send();
1369 }
1370 if (!channels.isEmpty()) {
1371 request(MTPchannels_GetChannels(
1372 MTP_vector<MTPInputChannel>(channels)
1373 )).done(handleChats).send();
1374 }
1375 if (!users.isEmpty()) {
1376 request(MTPusers_GetUsers(
1377 MTP_vector<MTPInputUser>(users)
1378 )).done([=](const MTPVector<MTPUser> &result) {
1379 _session->data().processUsers(result);
1380 }).send();
1381 }
1382 }
1383
requestLastParticipants(not_null<ChannelData * > channel)1384 void ApiWrap::requestLastParticipants(not_null<ChannelData*> channel) {
1385 if (!channel->isMegagroup()
1386 || _participantsRequests.contains(channel)) {
1387 return;
1388 }
1389
1390 const auto offset = 0;
1391 const auto participantsHash = uint64(0);
1392 const auto requestId = request(MTPchannels_GetParticipants(
1393 channel->inputChannel,
1394 MTP_channelParticipantsRecent(),
1395 MTP_int(offset),
1396 MTP_int(_session->serverConfig().chatSizeMax),
1397 MTP_long(participantsHash)
1398 )).done([=](const MTPchannels_ChannelParticipants &result) {
1399 _participantsRequests.remove(channel);
1400 parseChannelParticipants(channel, result, [&](
1401 int availableCount,
1402 const QVector<MTPChannelParticipant> &list) {
1403 applyLastParticipantsList(
1404 channel,
1405 availableCount,
1406 list);
1407 });
1408 }).fail([this, channel](const MTP::Error &error) {
1409 _participantsRequests.remove(channel);
1410 }).send();
1411
1412 _participantsRequests.insert(channel, requestId);
1413 }
1414
requestBots(not_null<ChannelData * > channel)1415 void ApiWrap::requestBots(not_null<ChannelData*> channel) {
1416 if (!channel->isMegagroup() || _botsRequests.contains(channel)) {
1417 return;
1418 }
1419
1420 const auto offset = 0;
1421 const auto participantsHash = uint64(0);
1422 const auto requestId = request(MTPchannels_GetParticipants(
1423 channel->inputChannel,
1424 MTP_channelParticipantsBots(),
1425 MTP_int(offset),
1426 MTP_int(_session->serverConfig().chatSizeMax),
1427 MTP_long(participantsHash)
1428 )).done([=](const MTPchannels_ChannelParticipants &result) {
1429 _botsRequests.remove(channel);
1430 parseChannelParticipants(channel, result, [&](
1431 int availableCount,
1432 const QVector<MTPChannelParticipant> &list) {
1433 applyBotsList(
1434 channel,
1435 availableCount,
1436 list);
1437 });
1438 }).fail([=](const MTP::Error &error) {
1439 _botsRequests.remove(channel);
1440 }).send();
1441
1442 _botsRequests.insert(channel, requestId);
1443 }
1444
requestAdmins(not_null<ChannelData * > channel)1445 void ApiWrap::requestAdmins(not_null<ChannelData*> channel) {
1446 if (!channel->isMegagroup() || _adminsRequests.contains(channel)) {
1447 return;
1448 }
1449
1450 const auto offset = 0;
1451 const auto participantsHash = uint64(0);
1452 const auto requestId = request(MTPchannels_GetParticipants(
1453 channel->inputChannel,
1454 MTP_channelParticipantsAdmins(),
1455 MTP_int(offset),
1456 MTP_int(_session->serverConfig().chatSizeMax),
1457 MTP_long(participantsHash)
1458 )).done([=](const MTPchannels_ChannelParticipants &result) {
1459 _adminsRequests.remove(channel);
1460 result.match([&](const MTPDchannels_channelParticipants &data) {
1461 Data::ApplyMegagroupAdmins(channel, data);
1462 }, [&](const MTPDchannels_channelParticipantsNotModified &) {
1463 LOG(("API Error: channels.channelParticipantsNotModified received!"));
1464 });
1465 }).fail([=](const MTP::Error &error) {
1466 _adminsRequests.remove(channel);
1467 }).send();
1468
1469 _adminsRequests.insert(channel, requestId);
1470 }
1471
applyLastParticipantsList(not_null<ChannelData * > channel,int availableCount,const QVector<MTPChannelParticipant> & list)1472 void ApiWrap::applyLastParticipantsList(
1473 not_null<ChannelData*> channel,
1474 int availableCount,
1475 const QVector<MTPChannelParticipant> &list) {
1476 channel->mgInfo->lastAdmins.clear();
1477 channel->mgInfo->lastRestricted.clear();
1478 channel->mgInfo->lastParticipants.clear();
1479 channel->mgInfo->lastParticipantsStatus = MegagroupInfo::LastParticipantsUpToDate
1480 | MegagroupInfo::LastParticipantsOnceReceived;
1481
1482 auto botStatus = channel->mgInfo->botStatus;
1483 for (const auto &p : list) {
1484 const auto participantId = p.match([](
1485 const MTPDchannelParticipantBanned &data) {
1486 return peerFromMTP(data.vpeer());
1487 }, [](const MTPDchannelParticipantLeft &data) {
1488 return peerFromMTP(data.vpeer());
1489 }, [](const auto &data) {
1490 return peerFromUser(data.vuser_id());
1491 });
1492 if (!participantId) {
1493 continue;
1494 }
1495 const auto participant = _session->data().peer(participantId);
1496 const auto user = participant->asUser();
1497 const auto adminCanEdit = (p.type() == mtpc_channelParticipantAdmin)
1498 ? p.c_channelParticipantAdmin().is_can_edit()
1499 : (p.type() == mtpc_channelParticipantCreator)
1500 ? channel->amCreator()
1501 : false;
1502 const auto adminRights = (p.type() == mtpc_channelParticipantAdmin)
1503 ? ChatAdminRightsInfo(p.c_channelParticipantAdmin().vadmin_rights())
1504 : (p.type() == mtpc_channelParticipantCreator)
1505 ? ChatAdminRightsInfo(p.c_channelParticipantCreator().vadmin_rights())
1506 : ChatAdminRightsInfo();
1507 const auto restrictedRights = (p.type() == mtpc_channelParticipantBanned)
1508 ? ChatRestrictionsInfo(
1509 p.c_channelParticipantBanned().vbanned_rights())
1510 : ChatRestrictionsInfo();
1511 if (p.type() == mtpc_channelParticipantCreator) {
1512 Assert(user != nullptr);
1513 const auto &creator = p.c_channelParticipantCreator();
1514 const auto rank = qs(creator.vrank().value_or_empty());
1515 channel->mgInfo->creator = user;
1516 channel->mgInfo->creatorRank = rank;
1517 if (!channel->mgInfo->admins.empty()) {
1518 Data::ChannelAdminChanges(channel).add(
1519 peerToUser(participantId),
1520 rank);
1521 }
1522 }
1523 if (user
1524 && !base::contains(channel->mgInfo->lastParticipants, user)) {
1525 channel->mgInfo->lastParticipants.push_back(user);
1526 if (adminRights.flags) {
1527 channel->mgInfo->lastAdmins.emplace(
1528 user,
1529 MegagroupInfo::Admin{ adminRights, adminCanEdit });
1530 } else if (restrictedRights.flags) {
1531 channel->mgInfo->lastRestricted.emplace(
1532 user,
1533 MegagroupInfo::Restricted{ restrictedRights });
1534 }
1535 if (user->isBot()) {
1536 channel->mgInfo->bots.insert(user);
1537 if (channel->mgInfo->botStatus != 0 && channel->mgInfo->botStatus < 2) {
1538 channel->mgInfo->botStatus = 2;
1539 }
1540 }
1541 }
1542 }
1543 //
1544 // getParticipants(Recent) sometimes can't return all members,
1545 // only some last subset, size of this subset is availableCount.
1546 //
1547 // So both list size and availableCount have nothing to do with
1548 // the full supergroup members count.
1549 //
1550 //if (list.isEmpty()) {
1551 // channel->setMembersCount(channel->mgInfo->lastParticipants.size());
1552 //} else {
1553 // channel->setMembersCount(availableCount);
1554 //}
1555 session().changes().peerUpdated(
1556 channel,
1557 (Data::PeerUpdate::Flag::Members | Data::PeerUpdate::Flag::Admins));
1558
1559 channel->mgInfo->botStatus = botStatus;
1560 _session->changes().peerUpdated(
1561 channel,
1562 Data::PeerUpdate::Flag::FullInfo);
1563 }
1564
applyBotsList(not_null<ChannelData * > channel,int availableCount,const QVector<MTPChannelParticipant> & list)1565 void ApiWrap::applyBotsList(
1566 not_null<ChannelData*> channel,
1567 int availableCount,
1568 const QVector<MTPChannelParticipant> &list) {
1569 const auto history = _session->data().historyLoaded(channel);
1570 channel->mgInfo->bots.clear();
1571 channel->mgInfo->botStatus = -1;
1572
1573 auto needBotsInfos = false;
1574 auto botStatus = channel->mgInfo->botStatus;
1575 auto keyboardBotFound = !history || !history->lastKeyboardFrom;
1576 for (const auto &p : list) {
1577 const auto participantId = p.match([](
1578 const MTPDchannelParticipantBanned &data) {
1579 return peerFromMTP(data.vpeer());
1580 }, [](const MTPDchannelParticipantLeft &data) {
1581 return peerFromMTP(data.vpeer());
1582 }, [](const auto &data) {
1583 return peerFromUser(data.vuser_id());
1584 });
1585 if (!participantId) {
1586 continue;
1587 }
1588
1589 const auto participant = _session->data().peer(participantId);
1590 const auto user = participant->asUser();
1591 if (user && user->isBot()) {
1592 channel->mgInfo->bots.insert(user);
1593 botStatus = 2;// (botStatus > 0/* || !i.key()->botInfo->readsAllHistory*/) ? 2 : 1;
1594 if (!user->botInfo->inited) {
1595 needBotsInfos = true;
1596 }
1597 }
1598 if (!keyboardBotFound
1599 && participant->id == history->lastKeyboardFrom) {
1600 keyboardBotFound = true;
1601 }
1602 }
1603 if (needBotsInfos) {
1604 requestFullPeer(channel);
1605 }
1606 if (!keyboardBotFound) {
1607 history->clearLastKeyboard();
1608 }
1609
1610 channel->mgInfo->botStatus = botStatus;
1611 _session->changes().peerUpdated(
1612 channel,
1613 Data::PeerUpdate::Flag::FullInfo);
1614 }
1615
requestSelfParticipant(not_null<ChannelData * > channel)1616 void ApiWrap::requestSelfParticipant(not_null<ChannelData*> channel) {
1617 if (_selfParticipantRequests.contains(channel)) {
1618 return;
1619 }
1620
1621 const auto finalize = [=](
1622 UserId inviter = -1,
1623 TimeId inviteDate = 0,
1624 bool inviteViaRequest = false) {
1625 channel->inviter = inviter;
1626 channel->inviteDate = inviteDate;
1627 channel->inviteViaRequest = inviteViaRequest;
1628 if (const auto history = _session->data().historyLoaded(channel)) {
1629 if (history->lastMessageKnown()) {
1630 history->checkLocalMessages();
1631 history->owner().sendHistoryChangeNotifications();
1632 } else {
1633 history->owner().histories().requestDialogEntry(history);
1634 }
1635 }
1636 };
1637 _selfParticipantRequests.emplace(channel);
1638 request(MTPchannels_GetParticipant(
1639 channel->inputChannel,
1640 MTP_inputPeerSelf()
1641 )).done([=](const MTPchannels_ChannelParticipant &result) {
1642 _selfParticipantRequests.erase(channel);
1643 result.match([&](const MTPDchannels_channelParticipant &data) {
1644 _session->data().processUsers(data.vusers());
1645
1646 const auto &participant = data.vparticipant();
1647 participant.match([&](const MTPDchannelParticipantSelf &data) {
1648 finalize(
1649 data.vinviter_id().v,
1650 data.vdate().v,
1651 data.is_via_invite());
1652 }, [&](const MTPDchannelParticipantCreator &) {
1653 if (channel->mgInfo) {
1654 channel->mgInfo->creator = _session->user();
1655 }
1656 finalize(_session->userId(), channel->date);
1657 }, [&](const MTPDchannelParticipantAdmin &data) {
1658 const auto inviter = data.is_self()
1659 ? data.vinviter_id().value_or(-1)
1660 : -1;
1661 finalize(inviter, data.vdate().v);
1662 }, [&](const MTPDchannelParticipantBanned &data) {
1663 LOG(("API Error: Got self banned participant."));
1664 finalize();
1665 }, [&](const MTPDchannelParticipant &data) {
1666 LOG(("API Error: Got self regular participant."));
1667 finalize();
1668 }, [&](const MTPDchannelParticipantLeft &data) {
1669 LOG(("API Error: Got self left participant."));
1670 finalize();
1671 });
1672 });
1673 }).fail([=](const MTP::Error &error) {
1674 _selfParticipantRequests.erase(channel);
1675 if (error.type() == qstr("CHANNEL_PRIVATE")) {
1676 channel->privateErrorReceived();
1677 }
1678 finalize();
1679 }).afterDelay(kSmallDelayMs).send();
1680 }
1681
kickParticipant(not_null<ChatData * > chat,not_null<PeerData * > participant)1682 void ApiWrap::kickParticipant(
1683 not_null<ChatData*> chat,
1684 not_null<PeerData*> participant) {
1685 Expects(participant->isUser());
1686
1687 request(MTPmessages_DeleteChatUser(
1688 MTP_flags(0),
1689 chat->inputChat,
1690 participant->asUser()->inputUser
1691 )).done([=](const MTPUpdates &result) {
1692 applyUpdates(result);
1693 }).send();
1694 }
1695
kickParticipant(not_null<ChannelData * > channel,not_null<PeerData * > participant,ChatRestrictionsInfo currentRights)1696 void ApiWrap::kickParticipant(
1697 not_null<ChannelData*> channel,
1698 not_null<PeerData*> participant,
1699 ChatRestrictionsInfo currentRights) {
1700 const auto kick = KickRequest(channel, participant);
1701 if (_kickRequests.contains(kick)) return;
1702
1703 const auto rights = ChannelData::KickedRestrictedRights(participant);
1704 const auto requestId = request(MTPchannels_EditBanned(
1705 channel->inputChannel,
1706 participant->input,
1707 MTP_chatBannedRights(
1708 MTP_flags(
1709 MTPDchatBannedRights::Flags::from_raw(uint32(rights.flags))),
1710 MTP_int(rights.until))
1711 )).done([=](const MTPUpdates &result) {
1712 applyUpdates(result);
1713
1714 _kickRequests.remove(KickRequest(channel, participant));
1715 channel->applyEditBanned(participant, currentRights, rights);
1716 }).fail([this, kick](const MTP::Error &error) {
1717 _kickRequests.remove(kick);
1718 }).send();
1719
1720 _kickRequests.emplace(kick, requestId);
1721 }
1722
unblockParticipant(not_null<ChannelData * > channel,not_null<PeerData * > participant)1723 void ApiWrap::unblockParticipant(
1724 not_null<ChannelData*> channel,
1725 not_null<PeerData*> participant) {
1726 const auto kick = KickRequest(channel, participant);
1727 if (_kickRequests.contains(kick)) {
1728 return;
1729 }
1730
1731 const auto requestId = request(MTPchannels_EditBanned(
1732 channel->inputChannel,
1733 participant->input,
1734 MTP_chatBannedRights(MTP_flags(0), MTP_int(0))
1735 )).done([=](const MTPUpdates &result) {
1736 applyUpdates(result);
1737
1738 _kickRequests.remove(KickRequest(channel, participant));
1739 if (channel->kickedCount() > 0) {
1740 channel->setKickedCount(channel->kickedCount() - 1);
1741 } else {
1742 channel->updateFullForced();
1743 }
1744 }).fail([=](const MTP::Error &error) {
1745 _kickRequests.remove(kick);
1746 }).send();
1747
1748 _kickRequests.emplace(kick, requestId);
1749 }
1750
deleteAllFromUser(not_null<ChannelData * > channel,not_null<UserData * > from)1751 void ApiWrap::deleteAllFromUser(
1752 not_null<ChannelData*> channel,
1753 not_null<UserData*> from) {
1754 const auto history = _session->data().historyLoaded(channel);
1755 const auto ids = history
1756 ? history->collectMessagesFromUserToDelete(from)
1757 : QVector<MsgId>();
1758 const auto channelId = peerToChannel(channel->id);
1759 for (const auto &msgId : ids) {
1760 if (const auto item = _session->data().message(channelId, msgId)) {
1761 item->destroy();
1762 }
1763 }
1764
1765 _session->data().sendHistoryChangeNotifications();
1766
1767 deleteAllFromUserSend(channel, from);
1768 }
1769
deleteAllFromUserSend(not_null<ChannelData * > channel,not_null<UserData * > from)1770 void ApiWrap::deleteAllFromUserSend(
1771 not_null<ChannelData*> channel,
1772 not_null<UserData*> from) {
1773 request(MTPchannels_DeleteUserHistory(
1774 channel->inputChannel,
1775 from->inputUser
1776 )).done([=](const MTPmessages_AffectedHistory &result) {
1777 const auto offset = applyAffectedHistory(channel, result);
1778 if (offset > 0) {
1779 deleteAllFromUserSend(channel, from);
1780 } else if (const auto history = _session->data().historyLoaded(channel)) {
1781 history->requestChatListMessage();
1782 }
1783 }).send();
1784 }
1785
requestChannelMembersForAdd(not_null<ChannelData * > channel,Fn<void (const MTPchannels_ChannelParticipants &)> callback)1786 void ApiWrap::requestChannelMembersForAdd(
1787 not_null<ChannelData*> channel,
1788 Fn<void(const MTPchannels_ChannelParticipants&)> callback) {
1789 _channelMembersForAddCallback = std::move(callback);
1790 if (_channelMembersForAdd == channel) {
1791 return;
1792 }
1793 request(base::take(_channelMembersForAddRequestId)).cancel();
1794
1795 const auto offset = 0;
1796 const auto participantsHash = uint64(0);
1797
1798 _channelMembersForAdd = channel;
1799 _channelMembersForAddRequestId = request(MTPchannels_GetParticipants(
1800 channel->inputChannel,
1801 MTP_channelParticipantsRecent(),
1802 MTP_int(offset),
1803 MTP_int(_session->serverConfig().chatSizeMax),
1804 MTP_long(participantsHash)
1805 )).done([=](const MTPchannels_ChannelParticipants &result) {
1806 base::take(_channelMembersForAddRequestId);
1807 base::take(_channelMembersForAdd);
1808 base::take(_channelMembersForAddCallback)(result);
1809 }).fail([=](const MTP::Error &error) {
1810 base::take(_channelMembersForAddRequestId);
1811 base::take(_channelMembersForAdd);
1812 base::take(_channelMembersForAddCallback);
1813 }).send();
1814 }
1815
scheduleStickerSetRequest(uint64 setId,uint64 access)1816 void ApiWrap::scheduleStickerSetRequest(uint64 setId, uint64 access) {
1817 if (!_stickerSetRequests.contains(setId)) {
1818 _stickerSetRequests.insert(setId, qMakePair(access, 0));
1819 }
1820 }
1821
requestStickerSets()1822 void ApiWrap::requestStickerSets() {
1823 for (auto i = _stickerSetRequests.begin(), j = i, e = _stickerSetRequests.end(); i != e; i = j) {
1824 ++j;
1825 if (i.value().second) continue;
1826
1827 auto waitMs = (j == e) ? 0 : kSmallDelayMs;
1828 const auto id = MTP_inputStickerSetID(
1829 MTP_long(i.key()),
1830 MTP_long(i.value().first));
1831 i.value().second = request(MTPmessages_GetStickerSet(
1832 id
1833 )).done([=, setId = i.key()](const MTPmessages_StickerSet &result) {
1834 gotStickerSet(setId, result);
1835 }).fail([=, setId = i.key()](const MTP::Error &error) {
1836 _stickerSetRequests.remove(setId);
1837 }).afterDelay(waitMs).send();
1838 }
1839 }
1840
saveStickerSets(const Data::StickersSetsOrder & localOrder,const Data::StickersSetsOrder & localRemoved,bool setsMasks)1841 void ApiWrap::saveStickerSets(
1842 const Data::StickersSetsOrder &localOrder,
1843 const Data::StickersSetsOrder &localRemoved,
1844 bool setsMasks) {
1845 auto &setDisenableRequests = setsMasks
1846 ? _maskSetDisenableRequests
1847 : _stickerSetDisenableRequests;
1848 const auto reorderRequestId = [=]() -> mtpRequestId & {
1849 return setsMasks
1850 ? _masksReorderRequestId
1851 : _stickersReorderRequestId;
1852 };
1853 for (auto requestId : base::take(setDisenableRequests)) {
1854 request(requestId).cancel();
1855 }
1856 request(base::take(reorderRequestId())).cancel();
1857 request(base::take(_stickersClearRecentRequestId)).cancel();
1858 request(base::take(_stickersClearRecentAttachedRequestId)).cancel();
1859
1860 const auto stickersSaveOrder = [=] {
1861 if (localOrder.size() < 2) {
1862 return;
1863 }
1864 QVector<MTPlong> mtpOrder;
1865 mtpOrder.reserve(localOrder.size());
1866 for (const auto setId : std::as_const(localOrder)) {
1867 mtpOrder.push_back(MTP_long(setId));
1868 }
1869
1870 const auto flags = setsMasks
1871 ? MTPmessages_ReorderStickerSets::Flag::f_masks
1872 : MTPmessages_ReorderStickerSets::Flags(0);
1873 reorderRequestId() = request(MTPmessages_ReorderStickerSets(
1874 MTP_flags(flags),
1875 MTP_vector<MTPlong>(mtpOrder)
1876 )).done([=](const MTPBool &result) {
1877 reorderRequestId() = 0;
1878 }).fail([=](const MTP::Error &error) {
1879 reorderRequestId() = 0;
1880 if (setsMasks) {
1881 _session->data().stickers().setLastMasksUpdate(0);
1882 updateMasks();
1883 } else {
1884 _session->data().stickers().setLastUpdate(0);
1885 updateStickers();
1886 }
1887 }).send();
1888 };
1889
1890 const auto stickerSetDisenabled = [=](mtpRequestId requestId) {
1891 auto &setDisenableRequests = setsMasks
1892 ? _maskSetDisenableRequests
1893 : _stickerSetDisenableRequests;
1894 setDisenableRequests.remove(requestId);
1895 if (setDisenableRequests.empty()) {
1896 stickersSaveOrder();
1897 }
1898 };
1899
1900 auto writeInstalled = true,
1901 writeRecent = false,
1902 writeCloudRecent = false,
1903 writeCloudRecentAttached = false,
1904 writeFaved = false,
1905 writeArchived = false;
1906 auto &recent = _session->data().stickers().getRecentPack();
1907 auto &sets = _session->data().stickers().setsRef();
1908
1909 auto &order = setsMasks
1910 ? _session->data().stickers().maskSetsOrder()
1911 : _session->data().stickers().setsOrder();
1912 auto &orderRef = setsMasks
1913 ? _session->data().stickers().maskSetsOrderRef()
1914 : _session->data().stickers().setsOrderRef();
1915
1916 using Flag = Data::StickersSetFlag;
1917 for (const auto removedSetId : localRemoved) {
1918 if ((removedSetId == Data::Stickers::CloudRecentSetId)
1919 || (removedSetId == Data::Stickers::CloudRecentAttachedSetId)) {
1920 if (sets.remove(Data::Stickers::CloudRecentSetId) != 0) {
1921 writeCloudRecent = true;
1922 }
1923 if (sets.remove(Data::Stickers::CloudRecentAttachedSetId) != 0) {
1924 writeCloudRecentAttached = true;
1925 }
1926 if (sets.remove(Data::Stickers::CustomSetId)) {
1927 writeInstalled = true;
1928 }
1929 if (!recent.isEmpty()) {
1930 recent.clear();
1931 writeRecent = true;
1932 }
1933
1934 const auto isAttached =
1935 (removedSetId == Data::Stickers::CloudRecentAttachedSetId);
1936 const auto flags = isAttached
1937 ? MTPmessages_ClearRecentStickers::Flag::f_attached
1938 : MTPmessages_ClearRecentStickers::Flags(0);
1939 auto &requestId = isAttached
1940 ? _stickersClearRecentAttachedRequestId
1941 : _stickersClearRecentRequestId;
1942 const auto finish = [=] {
1943 (isAttached
1944 ? _stickersClearRecentAttachedRequestId
1945 : _stickersClearRecentRequestId) = 0;
1946 };
1947 requestId = request(MTPmessages_ClearRecentStickers(
1948 MTP_flags(flags)
1949 )).done([=](const MTPBool &result) {
1950 finish();
1951 }).fail([=](const MTP::Error &error) {
1952 finish();
1953 }).send();
1954 continue;
1955 }
1956
1957 auto it = sets.find(removedSetId);
1958 if (it != sets.cend()) {
1959 const auto set = it->second.get();
1960 for (auto i = recent.begin(); i != recent.cend();) {
1961 if (set->stickers.indexOf(i->first) >= 0) {
1962 i = recent.erase(i);
1963 writeRecent = true;
1964 } else {
1965 ++i;
1966 }
1967 }
1968 const auto archived = !!(set->flags & Flag::Archived);
1969 if (!archived) {
1970 const auto featured = !!(set->flags & Flag::Featured);
1971 const auto special = !!(set->flags & Flag::Special);
1972 const auto setId = set->mtpInput();
1973
1974 auto requestId = request(MTPmessages_UninstallStickerSet(
1975 setId
1976 )).done([=](const MTPBool &result, mtpRequestId requestId) {
1977 stickerSetDisenabled(requestId);
1978 }).fail([=](const MTP::Error &error, mtpRequestId requestId) {
1979 stickerSetDisenabled(requestId);
1980 }).afterDelay(kSmallDelayMs).send();
1981
1982 setDisenableRequests.insert(requestId);
1983
1984 const auto removeIndex = order.indexOf(set->id);
1985 if (removeIndex >= 0) {
1986 orderRef.removeAt(removeIndex);
1987 }
1988 if (!featured && !special) {
1989 sets.erase(it);
1990 } else {
1991 if (archived) {
1992 writeArchived = true;
1993 }
1994 set->flags &= ~(Flag::Installed | Flag::Archived);
1995 set->installDate = TimeId(0);
1996 }
1997 }
1998 }
1999 }
2000
2001 // Clear all installed flags, set only for sets from order.
2002 for (auto &[id, set] : sets) {
2003 const auto archived = !!(set->flags & Flag::Archived);
2004 const auto masks = !!(set->flags & Flag::Masks);
2005 if (!archived && (setsMasks == masks)) {
2006 set->flags &= ~Flag::Installed;
2007 }
2008 }
2009
2010 orderRef.clear();
2011 for (const auto setId : std::as_const(localOrder)) {
2012 auto it = sets.find(setId);
2013 if (it == sets.cend()) {
2014 continue;
2015 }
2016 const auto set = it->second.get();
2017 const auto archived = !!(set->flags & Flag::Archived);
2018 if (archived && !localRemoved.contains(set->id)) {
2019 const auto mtpSetId = set->mtpInput();
2020
2021 const auto requestId = request(MTPmessages_InstallStickerSet(
2022 mtpSetId,
2023 MTP_boolFalse()
2024 )).done([=](
2025 const MTPmessages_StickerSetInstallResult &result,
2026 mtpRequestId requestId) {
2027 stickerSetDisenabled(requestId);
2028 }).fail([=](
2029 const MTP::Error &error,
2030 mtpRequestId requestId) {
2031 stickerSetDisenabled(requestId);
2032 }).afterDelay(kSmallDelayMs).send();
2033
2034 setDisenableRequests.insert(requestId);
2035
2036 set->flags &= ~Flag::Archived;
2037 writeArchived = true;
2038 }
2039 orderRef.push_back(setId);
2040 set->flags |= Flag::Installed;
2041 if (!set->installDate) {
2042 set->installDate = base::unixtime::now();
2043 }
2044 }
2045
2046 for (auto it = sets.begin(); it != sets.cend();) {
2047 const auto set = it->second.get();
2048 if ((set->flags & Flag::Featured)
2049 || (set->flags & Flag::Installed)
2050 || (set->flags & Flag::Archived)
2051 || (set->flags & Flag::Special)) {
2052 ++it;
2053 } else {
2054 it = sets.erase(it);
2055 }
2056 }
2057
2058 auto &storage = local();
2059 if (writeInstalled && !setsMasks) {
2060 storage.writeInstalledStickers();
2061 }
2062 if (writeInstalled && setsMasks) {
2063 storage.writeInstalledMasks();
2064 }
2065 if (writeRecent) {
2066 session().saveSettings();
2067 }
2068 if (writeArchived) {
2069 if (setsMasks) {
2070 storage.writeArchivedMasks();
2071 } else {
2072 storage.writeArchivedStickers();
2073 }
2074 }
2075 if (writeCloudRecent) {
2076 storage.writeRecentStickers();
2077 }
2078 if (writeCloudRecentAttached) {
2079 storage.writeRecentMasks();
2080 }
2081 if (writeFaved) {
2082 storage.writeFavedStickers();
2083 }
2084 _session->data().stickers().notifyUpdated();
2085
2086 if (setDisenableRequests.empty()) {
2087 stickersSaveOrder();
2088 } else {
2089 requestSendDelayed();
2090 }
2091 }
2092
joinChannel(not_null<ChannelData * > channel)2093 void ApiWrap::joinChannel(not_null<ChannelData*> channel) {
2094 if (channel->amIn()) {
2095 session().changes().peerUpdated(
2096 channel,
2097 Data::PeerUpdate::Flag::ChannelAmIn);
2098 } else if (!_channelAmInRequests.contains(channel)) {
2099 auto requestId = request(MTPchannels_JoinChannel(
2100 channel->inputChannel
2101 )).done([=](const MTPUpdates &result) {
2102 _channelAmInRequests.remove(channel);
2103 applyUpdates(result);
2104 }).fail([=](const MTP::Error &error) {
2105 const auto &type = error.type();
2106 if (type == qstr("CHANNEL_PRIVATE")
2107 && channel->invitePeekExpires()) {
2108 channel->privateErrorReceived();
2109 } else {
2110 const auto text = [&] {
2111 if (type == qstr("INVITE_REQUEST_SENT")) {
2112 return channel->isMegagroup()
2113 ? tr::lng_group_request_sent(tr::now)
2114 : tr::lng_group_request_sent_channel(tr::now);
2115 } else if (type == qstr("CHANNEL_PRIVATE")
2116 || type == qstr("CHANNEL_PUBLIC_GROUP_NA")
2117 || type == qstr("USER_BANNED_IN_CHANNEL")) {
2118 return channel->isMegagroup()
2119 ? tr::lng_group_not_accessible(tr::now)
2120 : tr::lng_channel_not_accessible(tr::now);
2121 } else if (type == qstr("CHANNELS_TOO_MUCH")) {
2122 return tr::lng_join_channel_error(tr::now);
2123 } else if (type == qstr("USERS_TOO_MUCH")) {
2124 return tr::lng_group_full(tr::now);
2125 }
2126 return QString();
2127 }();
2128 if (!text.isEmpty()) {
2129 Ui::ShowMultilineToast({
2130 .text = { text },
2131 .duration = kJoinErrorDuration,
2132 });
2133 }
2134 }
2135 _channelAmInRequests.remove(channel);
2136 }).send();
2137
2138 _channelAmInRequests.insert(channel, requestId);
2139 }
2140 }
2141
leaveChannel(not_null<ChannelData * > channel)2142 void ApiWrap::leaveChannel(not_null<ChannelData*> channel) {
2143 if (!channel->amIn()) {
2144 session().changes().peerUpdated(
2145 channel,
2146 Data::PeerUpdate::Flag::ChannelAmIn);
2147 } else if (!_channelAmInRequests.contains(channel)) {
2148 auto requestId = request(MTPchannels_LeaveChannel(
2149 channel->inputChannel
2150 )).done([=](const MTPUpdates &result) {
2151 _channelAmInRequests.remove(channel);
2152 applyUpdates(result);
2153 }).fail([=](const MTP::Error &error) {
2154 _channelAmInRequests.remove(channel);
2155 }).send();
2156
2157 _channelAmInRequests.insert(channel, requestId);
2158 }
2159 }
2160
requestNotifySettings(const MTPInputNotifyPeer & peer)2161 void ApiWrap::requestNotifySettings(const MTPInputNotifyPeer &peer) {
2162 const auto key = [&] {
2163 switch (peer.type()) {
2164 case mtpc_inputNotifyUsers: return peerFromUser(0);
2165 case mtpc_inputNotifyChats: return peerFromChat(0);
2166 case mtpc_inputNotifyBroadcasts: return peerFromChannel(0);
2167 case mtpc_inputNotifyPeer: {
2168 const auto &inner = peer.c_inputNotifyPeer().vpeer();
2169 switch (inner.type()) {
2170 case mtpc_inputPeerSelf:
2171 return _session->userPeerId();
2172 case mtpc_inputPeerEmpty:
2173 return PeerId(0);
2174 case mtpc_inputPeerChannel:
2175 return peerFromChannel(
2176 inner.c_inputPeerChannel().vchannel_id());
2177 case mtpc_inputPeerChat:
2178 return peerFromChat(inner.c_inputPeerChat().vchat_id());
2179 case mtpc_inputPeerUser:
2180 return peerFromUser(inner.c_inputPeerUser().vuser_id());
2181 }
2182 Unexpected("Type in ApiRequest::requestNotifySettings peer.");
2183 } break;
2184 }
2185 Unexpected("Type in ApiRequest::requestNotifySettings.");
2186 }();
2187 if (_notifySettingRequests.find(key) != end(_notifySettingRequests)) {
2188 return;
2189 }
2190 const auto requestId = request(MTPaccount_GetNotifySettings(
2191 peer
2192 )).done([=](const MTPPeerNotifySettings &result) {
2193 applyNotifySettings(peer, result);
2194 _notifySettingRequests.erase(key);
2195 }).fail([=](const MTP::Error &error) {
2196 applyNotifySettings(
2197 peer,
2198 MTP_peerNotifySettings(
2199 MTP_flags(0),
2200 MTPBool(),
2201 MTPBool(),
2202 MTPint(),
2203 MTPstring()));
2204 _notifySettingRequests.erase(key);
2205 }).send();
2206
2207 _notifySettingRequests.emplace(key, requestId);
2208 }
2209
updateNotifySettingsDelayed(not_null<const PeerData * > peer)2210 void ApiWrap::updateNotifySettingsDelayed(not_null<const PeerData*> peer) {
2211 _updateNotifySettingsPeers.emplace(peer);
2212 _updateNotifySettingsTimer.callOnce(kNotifySettingSaveTimeout);
2213 }
2214
sendNotifySettingsUpdates()2215 void ApiWrap::sendNotifySettingsUpdates() {
2216 while (!_updateNotifySettingsPeers.empty()) {
2217 const auto peer = *_updateNotifySettingsPeers.begin();
2218 _updateNotifySettingsPeers.erase(_updateNotifySettingsPeers.begin());
2219 request(MTPaccount_UpdateNotifySettings(
2220 MTP_inputNotifyPeer(peer->input),
2221 peer->notifySerialize()
2222 )).afterDelay(_updateNotifySettingsPeers.empty() ? 0 : 10).send();
2223 }
2224 }
2225
saveDraftToCloudDelayed(not_null<History * > history)2226 void ApiWrap::saveDraftToCloudDelayed(not_null<History*> history) {
2227 _draftsSaveRequestIds.emplace(history, 0);
2228 if (!_draftsSaveTimer.isActive()) {
2229 _draftsSaveTimer.callOnce(kSaveCloudDraftTimeout);
2230 }
2231 }
2232
updatePrivacyLastSeens()2233 void ApiWrap::updatePrivacyLastSeens() {
2234 const auto now = base::unixtime::now();
2235 _session->data().enumerateUsers([&](UserData *user) {
2236 if (user->isSelf() || !user->isLoaded()) {
2237 return;
2238 }
2239 if (user->onlineTill <= 0) {
2240 return;
2241 }
2242
2243 if (user->onlineTill + 3 * 86400 >= now) {
2244 user->onlineTill = -2; // recently
2245 } else if (user->onlineTill + 7 * 86400 >= now) {
2246 user->onlineTill = -3; // last week
2247 } else if (user->onlineTill + 30 * 86400 >= now) {
2248 user->onlineTill = -4; // last month
2249 } else {
2250 user->onlineTill = 0;
2251 }
2252 session().changes().peerUpdated(
2253 user,
2254 Data::PeerUpdate::Flag::OnlineStatus);
2255 });
2256
2257 if (_contactsStatusesRequestId) {
2258 request(_contactsStatusesRequestId).cancel();
2259 }
2260 _contactsStatusesRequestId = request(MTPcontacts_GetStatuses(
2261 )).done([=](const MTPVector<MTPContactStatus> &result) {
2262 _contactsStatusesRequestId = 0;
2263 for (const auto &item : result.v) {
2264 Assert(item.type() == mtpc_contactStatus);
2265 auto &data = item.c_contactStatus();
2266 if (auto user = _session->data().userLoaded(data.vuser_id())) {
2267 auto oldOnlineTill = user->onlineTill;
2268 auto newOnlineTill = OnlineTillFromStatus(data.vstatus(), oldOnlineTill);
2269 if (oldOnlineTill != newOnlineTill) {
2270 user->onlineTill = newOnlineTill;
2271 session().changes().peerUpdated(
2272 user,
2273 Data::PeerUpdate::Flag::OnlineStatus);
2274 }
2275 }
2276 }
2277 }).fail([this](const MTP::Error &error) {
2278 _contactsStatusesRequestId = 0;
2279 }).send();
2280 }
2281
OnlineTillFromStatus(const MTPUserStatus & status,int currentOnlineTill)2282 int ApiWrap::OnlineTillFromStatus(
2283 const MTPUserStatus &status,
2284 int currentOnlineTill) {
2285 switch (status.type()) {
2286 case mtpc_userStatusEmpty: return 0;
2287 case mtpc_userStatusRecently:
2288 // Don't modify pseudo-online.
2289 return (currentOnlineTill > -10) ? -2 : currentOnlineTill;
2290 case mtpc_userStatusLastWeek: return -3;
2291 case mtpc_userStatusLastMonth: return -4;
2292 case mtpc_userStatusOffline: return status.c_userStatusOffline().vwas_online().v;
2293 case mtpc_userStatusOnline: return status.c_userStatusOnline().vexpires().v;
2294 }
2295 Unexpected("Bad UserStatus type.");
2296 }
2297
clearHistory(not_null<PeerData * > peer,bool revoke)2298 void ApiWrap::clearHistory(not_null<PeerData*> peer, bool revoke) {
2299 deleteHistory(peer, true, revoke);
2300 }
2301
deleteConversation(not_null<PeerData * > peer,bool revoke)2302 void ApiWrap::deleteConversation(not_null<PeerData*> peer, bool revoke) {
2303 if (const auto chat = peer->asChat()) {
2304 request(MTPmessages_DeleteChatUser(
2305 MTP_flags(0),
2306 chat->inputChat,
2307 _session->user()->inputUser
2308 )).done([=](const MTPUpdates &result) {
2309 applyUpdates(result);
2310 deleteHistory(peer, false, revoke);
2311 }).fail([=](const MTP::Error &error) {
2312 deleteHistory(peer, false, revoke);
2313 }).send();
2314 } else {
2315 deleteHistory(peer, false, revoke);
2316 }
2317 }
2318
deleteHistory(not_null<PeerData * > peer,bool justClear,bool revoke)2319 void ApiWrap::deleteHistory(
2320 not_null<PeerData*> peer,
2321 bool justClear,
2322 bool revoke) {
2323 auto deleteTillId = MsgId(0);
2324 const auto history = _session->data().history(peer);
2325 if (justClear) {
2326 // In case of clear history we need to know the last server message.
2327 while (history->lastMessageKnown()) {
2328 const auto last = history->lastMessage();
2329 if (!last) {
2330 // History is empty.
2331 return;
2332 } else if (!last->isRegular()) {
2333 // Destroy client-side message locally.
2334 last->destroy();
2335 } else {
2336 break;
2337 }
2338 }
2339 if (!history->lastMessageKnown()) {
2340 history->owner().histories().requestDialogEntry(history, [=] {
2341 Expects(history->lastMessageKnown());
2342
2343 deleteHistory(peer, justClear, revoke);
2344 });
2345 return;
2346 }
2347 deleteTillId = history->lastMessage()->id;
2348 }
2349 if (const auto channel = peer->asChannel()) {
2350 if (!justClear && !revoke) {
2351 channel->ptsWaitingForShortPoll(-1);
2352 leaveChannel(channel);
2353 } else {
2354 if (const auto migrated = peer->migrateFrom()) {
2355 deleteHistory(migrated, justClear, revoke);
2356 }
2357 if (deleteTillId || (!justClear && revoke)) {
2358 history->owner().histories().deleteAllMessages(
2359 history,
2360 deleteTillId,
2361 justClear,
2362 revoke);
2363 }
2364 }
2365 } else {
2366 history->owner().histories().deleteAllMessages(
2367 history,
2368 deleteTillId,
2369 justClear,
2370 revoke);
2371 }
2372 if (!justClear) {
2373 _session->data().deleteConversationLocally(peer);
2374 } else if (history) {
2375 history->clear(History::ClearType::ClearHistory);
2376 }
2377 }
2378
applyUpdates(const MTPUpdates & updates,uint64 sentMessageRandomId)2379 void ApiWrap::applyUpdates(
2380 const MTPUpdates &updates,
2381 uint64 sentMessageRandomId) {
2382 this->updates().applyUpdates(updates, sentMessageRandomId);
2383 }
2384
applyAffectedHistory(PeerData * peer,const MTPmessages_AffectedHistory & result)2385 int ApiWrap::applyAffectedHistory(
2386 PeerData *peer,
2387 const MTPmessages_AffectedHistory &result) {
2388 const auto &data = result.c_messages_affectedHistory();
2389 if (const auto channel = peer ? peer->asChannel() : nullptr) {
2390 channel->ptsUpdateAndApply(data.vpts().v, data.vpts_count().v);
2391 } else {
2392 updates().updateAndApply(data.vpts().v, data.vpts_count().v);
2393 }
2394 return data.voffset().v;
2395 }
2396
applyAffectedMessages(not_null<PeerData * > peer,const MTPmessages_AffectedMessages & result)2397 void ApiWrap::applyAffectedMessages(
2398 not_null<PeerData*> peer,
2399 const MTPmessages_AffectedMessages &result) {
2400 const auto &data = result.c_messages_affectedMessages();
2401 if (const auto channel = peer->asChannel()) {
2402 channel->ptsUpdateAndApply(data.vpts().v, data.vpts_count().v);
2403 } else {
2404 applyAffectedMessages(result);
2405 }
2406 }
2407
applyAffectedMessages(const MTPmessages_AffectedMessages & result)2408 void ApiWrap::applyAffectedMessages(
2409 const MTPmessages_AffectedMessages &result) {
2410 const auto &data = result.c_messages_affectedMessages();
2411 updates().updateAndApply(data.vpts().v, data.vpts_count().v);
2412 }
2413
saveCurrentDraftToCloud()2414 void ApiWrap::saveCurrentDraftToCloud() {
2415 Core::App().saveCurrentDraftsToHistories();
2416
2417 for (const auto &controller : _session->windows()) {
2418 if (const auto history = controller->activeChatCurrent().history()) {
2419 _session->local().writeDrafts(history);
2420
2421 const auto localDraft = history->localDraft();
2422 const auto cloudDraft = history->cloudDraft();
2423 if (!Data::draftsAreEqual(localDraft, cloudDraft)
2424 && !_session->supportMode()) {
2425 saveDraftToCloudDelayed(history);
2426 }
2427 }
2428 }
2429 }
2430
saveDraftsToCloud()2431 void ApiWrap::saveDraftsToCloud() {
2432 for (auto i = _draftsSaveRequestIds.begin(), e = _draftsSaveRequestIds.end(); i != e; ++i) {
2433 if (i->second) continue; // sent already
2434
2435 auto history = i->first;
2436 auto cloudDraft = history->cloudDraft();
2437 auto localDraft = history->localDraft();
2438 if (cloudDraft && cloudDraft->saveRequestId) {
2439 request(base::take(cloudDraft->saveRequestId)).cancel();
2440 }
2441 if (!_session->supportMode()) {
2442 cloudDraft = history->createCloudDraft(localDraft);
2443 } else if (!cloudDraft) {
2444 cloudDraft = history->createCloudDraft(nullptr);
2445 }
2446
2447 auto flags = MTPmessages_SaveDraft::Flags(0);
2448 auto &textWithTags = cloudDraft->textWithTags;
2449 if (cloudDraft->previewState != Data::PreviewState::Allowed) {
2450 flags |= MTPmessages_SaveDraft::Flag::f_no_webpage;
2451 }
2452 if (cloudDraft->msgId) {
2453 flags |= MTPmessages_SaveDraft::Flag::f_reply_to_msg_id;
2454 }
2455 if (!textWithTags.tags.isEmpty()) {
2456 flags |= MTPmessages_SaveDraft::Flag::f_entities;
2457 }
2458 auto entities = Api::EntitiesToMTP(
2459 _session,
2460 TextUtilities::ConvertTextTagsToEntities(textWithTags.tags),
2461 Api::ConvertOption::SkipLocal);
2462
2463 history->startSavingCloudDraft();
2464 cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(
2465 MTP_flags(flags),
2466 MTP_int(cloudDraft->msgId),
2467 history->peer->input,
2468 MTP_string(textWithTags.text),
2469 entities
2470 )).done([=](const MTPBool &result, const MTP::Response &response) {
2471 history->finishSavingCloudDraft(
2472 UnixtimeFromMsgId(response.outerMsgId));
2473
2474 if (const auto cloudDraft = history->cloudDraft()) {
2475 if (cloudDraft->saveRequestId == response.requestId) {
2476 cloudDraft->saveRequestId = 0;
2477 history->draftSavedToCloud();
2478 }
2479 }
2480 auto i = _draftsSaveRequestIds.find(history);
2481 if (i != _draftsSaveRequestIds.cend()
2482 && i->second == response.requestId) {
2483 _draftsSaveRequestIds.erase(history);
2484 checkQuitPreventFinished();
2485 }
2486 }).fail([=](const MTP::Error &error, const MTP::Response &response) {
2487 history->finishSavingCloudDraft(
2488 UnixtimeFromMsgId(response.outerMsgId));
2489
2490 if (const auto cloudDraft = history->cloudDraft()) {
2491 if (cloudDraft->saveRequestId == response.requestId) {
2492 history->clearCloudDraft();
2493 }
2494 }
2495 auto i = _draftsSaveRequestIds.find(history);
2496 if (i != _draftsSaveRequestIds.cend()
2497 && i->second == response.requestId) {
2498 _draftsSaveRequestIds.erase(history);
2499 checkQuitPreventFinished();
2500 }
2501 }).send();
2502
2503 i->second = cloudDraft->saveRequestId;
2504 }
2505 }
2506
isQuitPrevent()2507 bool ApiWrap::isQuitPrevent() {
2508 if (_draftsSaveRequestIds.empty()) {
2509 return false;
2510 }
2511 LOG(("ApiWrap prevents quit, saving drafts..."));
2512 saveDraftsToCloud();
2513 return true;
2514 }
2515
checkQuitPreventFinished()2516 void ApiWrap::checkQuitPreventFinished() {
2517 if (_draftsSaveRequestIds.empty()) {
2518 if (App::quitting()) {
2519 LOG(("ApiWrap doesn't prevent quit any more."));
2520 }
2521 Core::App().quitPreventFinished();
2522 }
2523 }
2524
registerModifyRequest(const QString & key,mtpRequestId requestId)2525 void ApiWrap::registerModifyRequest(
2526 const QString &key,
2527 mtpRequestId requestId) {
2528 const auto i = _modifyRequests.find(key);
2529 if (i != end(_modifyRequests)) {
2530 request(i->second).cancel();
2531 i->second = requestId;
2532 } else {
2533 _modifyRequests.emplace(key, requestId);
2534 }
2535 }
2536
clearModifyRequest(const QString & key)2537 void ApiWrap::clearModifyRequest(const QString &key) {
2538 _modifyRequests.remove(key);
2539 }
2540
applyNotifySettings(MTPInputNotifyPeer notifyPeer,const MTPPeerNotifySettings & settings)2541 void ApiWrap::applyNotifySettings(
2542 MTPInputNotifyPeer notifyPeer,
2543 const MTPPeerNotifySettings &settings) {
2544 switch (notifyPeer.type()) {
2545 case mtpc_inputNotifyUsers:
2546 _session->data().applyNotifySetting(MTP_notifyUsers(), settings);
2547 break;
2548 case mtpc_inputNotifyChats:
2549 _session->data().applyNotifySetting(MTP_notifyChats(), settings);
2550 break;
2551 case mtpc_inputNotifyBroadcasts:
2552 _session->data().applyNotifySetting(
2553 MTP_notifyBroadcasts(),
2554 settings);
2555 break;
2556 case mtpc_inputNotifyPeer: {
2557 auto &peer = notifyPeer.c_inputNotifyPeer().vpeer();
2558 const auto apply = [&](PeerId peerId) {
2559 _session->data().applyNotifySetting(
2560 MTP_notifyPeer(peerToMTP(peerId)),
2561 settings);
2562 };
2563 switch (peer.type()) {
2564 case mtpc_inputPeerEmpty:
2565 apply(0);
2566 break;
2567 case mtpc_inputPeerSelf:
2568 apply(_session->userPeerId());
2569 break;
2570 case mtpc_inputPeerUser:
2571 apply(peerFromUser(peer.c_inputPeerUser().vuser_id()));
2572 break;
2573 case mtpc_inputPeerChat:
2574 apply(peerFromChat(peer.c_inputPeerChat().vchat_id()));
2575 break;
2576 case mtpc_inputPeerChannel:
2577 apply(peerFromChannel(peer.c_inputPeerChannel().vchannel_id()));
2578 break;
2579 }
2580 } break;
2581 }
2582 Core::App().notifications().checkDelayed();
2583 }
2584
gotStickerSet(uint64 setId,const MTPmessages_StickerSet & result)2585 void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) {
2586 _stickerSetRequests.remove(setId);
2587 _session->data().stickers().feedSetFull(result);
2588 }
2589
requestWebPageDelayed(WebPageData * page)2590 void ApiWrap::requestWebPageDelayed(WebPageData *page) {
2591 if (page->pendingTill <= 0) return;
2592 _webPagesPending.insert(page, 0);
2593 auto left = (page->pendingTill - base::unixtime::now()) * 1000;
2594 if (!_webPagesTimer.isActive() || left <= _webPagesTimer.remainingTime()) {
2595 _webPagesTimer.callOnce((left < 0 ? 0 : left) + 1);
2596 }
2597 }
2598
clearWebPageRequest(WebPageData * page)2599 void ApiWrap::clearWebPageRequest(WebPageData *page) {
2600 _webPagesPending.remove(page);
2601 if (_webPagesPending.isEmpty() && _webPagesTimer.isActive()) {
2602 _webPagesTimer.cancel();
2603 }
2604 }
2605
clearWebPageRequests()2606 void ApiWrap::clearWebPageRequests() {
2607 _webPagesPending.clear();
2608 _webPagesTimer.cancel();
2609 }
2610
resolveWebPages()2611 void ApiWrap::resolveWebPages() {
2612 auto ids = QVector<MTPInputMessage>(); // temp_req_id = -1
2613 using IndexAndMessageIds = QPair<int32, QVector<MTPInputMessage>>;
2614 using MessageIdsByChannel = base::flat_map<ChannelData*, IndexAndMessageIds>;
2615 MessageIdsByChannel idsByChannel; // temp_req_id = -index - 2
2616
2617 ids.reserve(_webPagesPending.size());
2618 int32 t = base::unixtime::now(), m = INT_MAX;
2619 for (auto i = _webPagesPending.begin(); i != _webPagesPending.cend(); ++i) {
2620 if (i.value() > 0) continue;
2621 if (i.key()->pendingTill <= t) {
2622 const auto item = _session->data().findWebPageItem(i.key());
2623 if (item) {
2624 if (item->channelId() == NoChannel) {
2625 ids.push_back(MTP_inputMessageID(MTP_int(item->id)));
2626 i.value() = -1;
2627 } else {
2628 auto channel = item->history()->peer->asChannel();
2629 auto channelMap = idsByChannel.find(channel);
2630 if (channelMap == idsByChannel.cend()) {
2631 channelMap = idsByChannel.emplace(
2632 channel,
2633 IndexAndMessageIds(
2634 idsByChannel.size(),
2635 QVector<MTPInputMessage>(
2636 1,
2637 MTP_inputMessageID(MTP_int(item->id))))).first;
2638 } else {
2639 channelMap->second.second.push_back(
2640 MTP_inputMessageID(MTP_int(item->id)));
2641 }
2642 i.value() = -channelMap->second.first - 2;
2643 }
2644 }
2645 } else {
2646 m = qMin(m, i.key()->pendingTill - t);
2647 }
2648 }
2649
2650 auto requestId = mtpRequestId(0);
2651 if (!ids.isEmpty()) {
2652 requestId = request(MTPmessages_GetMessages(
2653 MTP_vector<MTPInputMessage>(ids)
2654 )).done([=](
2655 const MTPmessages_Messages &result,
2656 mtpRequestId requestId) {
2657 gotWebPages(nullptr, result, requestId);
2658 }).afterDelay(kSmallDelayMs).send();
2659 }
2660 QVector<mtpRequestId> reqsByIndex(idsByChannel.size(), 0);
2661 for (auto i = idsByChannel.cbegin(), e = idsByChannel.cend(); i != e; ++i) {
2662 reqsByIndex[i->second.first] = request(MTPchannels_GetMessages(
2663 i->first->inputChannel,
2664 MTP_vector<MTPInputMessage>(i->second.second)
2665 )).done([=, channel = i->first](
2666 const MTPmessages_Messages &result,
2667 mtpRequestId requestId) {
2668 gotWebPages(channel, result, requestId);
2669 }).afterDelay(kSmallDelayMs).send();
2670 }
2671 if (requestId || !reqsByIndex.isEmpty()) {
2672 for (auto &pendingRequestId : _webPagesPending) {
2673 if (pendingRequestId > 0) continue;
2674 if (pendingRequestId < 0) {
2675 if (pendingRequestId == -1) {
2676 pendingRequestId = requestId;
2677 } else {
2678 pendingRequestId = reqsByIndex[-pendingRequestId - 2];
2679 }
2680 }
2681 }
2682 }
2683
2684 if (m < INT_MAX) {
2685 _webPagesTimer.callOnce(m * 1000);
2686 }
2687 }
2688
requestParticipantsCountDelayed(not_null<ChannelData * > channel)2689 void ApiWrap::requestParticipantsCountDelayed(
2690 not_null<ChannelData*> channel) {
2691 _participantsCountRequestTimer.call(
2692 kReloadChannelMembersTimeout,
2693 [=] { channel->updateFullForced(); });
2694 }
2695
2696 template <typename Request>
requestFileReference(Data::FileOrigin origin,FileReferencesHandler && handler,Request && data)2697 void ApiWrap::requestFileReference(
2698 Data::FileOrigin origin,
2699 FileReferencesHandler &&handler,
2700 Request &&data) {
2701 const auto i = _fileReferenceHandlers.find(origin);
2702 if (i != end(_fileReferenceHandlers)) {
2703 i->second.push_back(std::move(handler));
2704 return;
2705 }
2706 auto handlers = std::vector<FileReferencesHandler>();
2707 handlers.push_back(std::move(handler));
2708 _fileReferenceHandlers.emplace(origin, std::move(handlers));
2709
2710 request(std::move(data)).done([=](const auto &result) {
2711 const auto parsed = Data::GetFileReferences(result);
2712 for (const auto &p : parsed.data) {
2713 // Unpack here the parsed pair by hand to workaround a GCC bug.
2714 // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87122
2715 const auto &origin = p.first;
2716 const auto &reference = p.second;
2717 const auto documentId = std::get_if<DocumentFileLocationId>(
2718 &origin);
2719 if (documentId) {
2720 _session->data().document(
2721 documentId->id
2722 )->refreshFileReference(reference);
2723 }
2724 const auto photoId = std::get_if<PhotoFileLocationId>(&origin);
2725 if (photoId) {
2726 _session->data().photo(
2727 photoId->id
2728 )->refreshFileReference(reference);
2729 }
2730 }
2731 const auto i = _fileReferenceHandlers.find(origin);
2732 Assert(i != end(_fileReferenceHandlers));
2733 auto handlers = std::move(i->second);
2734 _fileReferenceHandlers.erase(i);
2735 for (auto &handler : handlers) {
2736 handler(parsed);
2737 }
2738 }).fail([=](const MTP::Error &error) {
2739 const auto i = _fileReferenceHandlers.find(origin);
2740 Assert(i != end(_fileReferenceHandlers));
2741 auto handlers = std::move(i->second);
2742 _fileReferenceHandlers.erase(i);
2743 for (auto &handler : handlers) {
2744 handler(UpdatedFileReferences());
2745 }
2746 }).send();
2747 }
2748
refreshFileReference(Data::FileOrigin origin,not_null<Storage::DownloadMtprotoTask * > task,int requestId,const QByteArray & current)2749 void ApiWrap::refreshFileReference(
2750 Data::FileOrigin origin,
2751 not_null<Storage::DownloadMtprotoTask*> task,
2752 int requestId,
2753 const QByteArray ¤t) {
2754 return refreshFileReference(origin, crl::guard(task, [=](
2755 const UpdatedFileReferences &data) {
2756 task->refreshFileReferenceFrom(data, requestId, current);
2757 }));
2758 }
2759
refreshFileReference(Data::FileOrigin origin,FileReferencesHandler && handler)2760 void ApiWrap::refreshFileReference(
2761 Data::FileOrigin origin,
2762 FileReferencesHandler &&handler) {
2763 const auto request = [&](
2764 auto &&data,
2765 Fn<void()> &&additional = nullptr) {
2766 requestFileReference(
2767 origin,
2768 std::move(handler),
2769 std::move(data));
2770 if (additional) {
2771 const auto i = _fileReferenceHandlers.find(origin);
2772 Assert(i != end(_fileReferenceHandlers));
2773 if (i->second.size() == 1) {
2774 i->second.push_back([=](auto&&) {
2775 additional();
2776 });
2777 }
2778 }
2779 };
2780 const auto fail = [&] {
2781 handler(UpdatedFileReferences());
2782 };
2783 v::match(origin.data, [&](Data::FileOriginMessage data) {
2784 if (const auto item = _session->data().message(data)) {
2785 if (item->isScheduled()) {
2786 const auto &scheduled = _session->data().scheduledMessages();
2787 const auto realId = scheduled.lookupId(item);
2788 request(MTPmessages_GetScheduledMessages(
2789 item->history()->peer->input,
2790 MTP_vector<MTPint>(1, MTP_int(realId))));
2791 } else if (const auto channel = item->history()->peer->asChannel()) {
2792 request(MTPchannels_GetMessages(
2793 channel->inputChannel,
2794 MTP_vector<MTPInputMessage>(
2795 1,
2796 MTP_inputMessageID(MTP_int(item->id)))));
2797 } else {
2798 request(MTPmessages_GetMessages(
2799 MTP_vector<MTPInputMessage>(
2800 1,
2801 MTP_inputMessageID(MTP_int(item->id)))));
2802 }
2803 } else {
2804 fail();
2805 }
2806 }, [&](Data::FileOriginUserPhoto data) {
2807 if (const auto user = _session->data().user(data.userId)) {
2808 request(MTPphotos_GetUserPhotos(
2809 user->inputUser,
2810 MTP_int(-1),
2811 MTP_long(data.photoId),
2812 MTP_int(1)));
2813 } else {
2814 fail();
2815 }
2816 }, [&](Data::FileOriginPeerPhoto data) {
2817 fail();
2818 }, [&](Data::FileOriginStickerSet data) {
2819 const auto isRecentAttached =
2820 (data.setId == Data::Stickers::CloudRecentAttachedSetId);
2821 if (data.setId == Data::Stickers::CloudRecentSetId
2822 || data.setId == Data::Stickers::RecentSetId
2823 || isRecentAttached) {
2824 auto done = [=] { crl::on_main(_session, [=] {
2825 if (isRecentAttached) {
2826 local().writeRecentMasks();
2827 } else {
2828 local().writeRecentStickers();
2829 }
2830 }); };
2831 request(MTPmessages_GetRecentStickers(
2832 MTP_flags(isRecentAttached
2833 ? MTPmessages_GetRecentStickers::Flag::f_attached
2834 : MTPmessages_GetRecentStickers::Flags(0)),
2835 MTP_long(0)),
2836 std::move(done));
2837 } else if (data.setId == Data::Stickers::FavedSetId) {
2838 request(MTPmessages_GetFavedStickers(MTP_long(0)),
2839 [=] { crl::on_main(_session, [=] { local().writeFavedStickers(); }); });
2840 } else {
2841 request(MTPmessages_GetStickerSet(
2842 MTP_inputStickerSetID(
2843 MTP_long(data.setId),
2844 MTP_long(data.accessHash))),
2845 [=] { crl::on_main(_session, [=] {
2846 local().writeInstalledStickers();
2847 local().writeRecentStickers();
2848 local().writeFavedStickers();
2849 }); });
2850 }
2851 }, [&](Data::FileOriginSavedGifs data) {
2852 request(
2853 MTPmessages_GetSavedGifs(MTP_long(0)),
2854 [=] { crl::on_main(_session, [=] { local().writeSavedGifs(); }); });
2855 }, [&](Data::FileOriginWallpaper data) {
2856 const auto useSlug = data.ownerId
2857 && (data.ownerId != session().userId())
2858 && !data.slug.isEmpty();
2859 request(MTPaccount_GetWallPaper(useSlug
2860 ? MTP_inputWallPaperSlug(MTP_string(data.slug))
2861 : MTP_inputWallPaper(
2862 MTP_long(data.paperId),
2863 MTP_long(data.accessHash))));
2864 }, [&](Data::FileOriginTheme data) {
2865 request(MTPaccount_GetTheme(
2866 MTP_string(Data::CloudThemes::Format()),
2867 MTP_inputTheme(
2868 MTP_long(data.themeId),
2869 MTP_long(data.accessHash)),
2870 MTP_long(0)));
2871 }, [&](v::null_t) {
2872 fail();
2873 });
2874 }
2875
gotWebPages(ChannelData * channel,const MTPmessages_Messages & result,mtpRequestId req)2876 void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req) {
2877 WebPageData::ApplyChanges(_session, channel, result);
2878 for (auto i = _webPagesPending.begin(); i != _webPagesPending.cend();) {
2879 if (i.value() == req) {
2880 if (i.key()->pendingTill > 0) {
2881 i.key()->pendingTill = -1;
2882 _session->data().notifyWebPageUpdateDelayed(i.key());
2883 }
2884 i = _webPagesPending.erase(i);
2885 } else {
2886 ++i;
2887 }
2888 }
2889 _session->data().sendWebPageGamePollNotifications();
2890 }
2891
updateStickers()2892 void ApiWrap::updateStickers() {
2893 const auto now = crl::now();
2894 requestStickers(now);
2895 requestRecentStickers(now);
2896 requestFavedStickers(now);
2897 requestFeaturedStickers(now);
2898 requestSavedGifs(now);
2899 }
2900
updateMasks()2901 void ApiWrap::updateMasks() {
2902 const auto now = crl::now();
2903 requestMasks(now);
2904 requestRecentStickers(now, true);
2905 }
2906
requestRecentStickersForce(bool attached)2907 void ApiWrap::requestRecentStickersForce(bool attached) {
2908 requestRecentStickersWithHash(0, attached);
2909 }
2910
setGroupStickerSet(not_null<ChannelData * > megagroup,const StickerSetIdentifier & set)2911 void ApiWrap::setGroupStickerSet(
2912 not_null<ChannelData*> megagroup,
2913 const StickerSetIdentifier &set) {
2914 Expects(megagroup->mgInfo != nullptr);
2915
2916 megagroup->mgInfo->stickerSet = set;
2917 request(MTPchannels_SetStickers(
2918 megagroup->inputChannel,
2919 Data::InputStickerSet(set)
2920 )).send();
2921 _session->data().stickers().notifyUpdated();
2922 }
2923
stickersByEmoji(not_null<EmojiPtr> emoji)2924 std::vector<not_null<DocumentData*>> *ApiWrap::stickersByEmoji(
2925 not_null<EmojiPtr> emoji) {
2926 const auto it = _stickersByEmoji.find(emoji);
2927 const auto sendRequest = [&] {
2928 if (it == _stickersByEmoji.end()) {
2929 return true;
2930 }
2931 const auto received = it->second.received;
2932 const auto now = crl::now();
2933 return (received > 0)
2934 && (received + kStickersByEmojiInvalidateTimeout) <= now;
2935 }();
2936 if (sendRequest) {
2937 const auto hash = (it != _stickersByEmoji.end())
2938 ? it->second.hash
2939 : uint64(0);
2940 request(MTPmessages_GetStickers(
2941 MTP_string(emoji->text()),
2942 MTP_long(hash)
2943 )).done([=](const MTPmessages_Stickers &result) {
2944 if (result.type() == mtpc_messages_stickersNotModified) {
2945 return;
2946 }
2947 Assert(result.type() == mtpc_messages_stickers);
2948 const auto &data = result.c_messages_stickers();
2949 auto &entry = _stickersByEmoji[emoji];
2950 entry.list.clear();
2951 entry.list.reserve(data.vstickers().v.size());
2952 for (const auto &sticker : data.vstickers().v) {
2953 const auto document = _session->data().processDocument(
2954 sticker);
2955 if (document->sticker()) {
2956 entry.list.push_back(document);
2957 }
2958 }
2959 entry.hash = data.vhash().v;
2960 entry.received = crl::now();
2961 _session->data().stickers().notifyUpdated();
2962 }).send();
2963 }
2964 if (it == _stickersByEmoji.end()) {
2965 _stickersByEmoji.emplace(emoji, StickersByEmoji());
2966 } else if (it->second.received > 0) {
2967 return &it->second.list;
2968 }
2969 return nullptr;
2970 }
2971
requestStickers(TimeId now)2972 void ApiWrap::requestStickers(TimeId now) {
2973 if (!_session->data().stickers().updateNeeded(now)
2974 || _stickersUpdateRequest) {
2975 return;
2976 }
2977 const auto done = [=](const MTPmessages_AllStickers &result) {
2978 _session->data().stickers().setLastUpdate(crl::now());
2979 _stickersUpdateRequest = 0;
2980
2981 result.match([&](const MTPDmessages_allStickersNotModified&) {
2982 }, [&](const MTPDmessages_allStickers &data) {
2983 _session->data().stickers().setsReceived(
2984 data.vsets().v,
2985 data.vhash().v);
2986 });
2987 };
2988 _stickersUpdateRequest = request(MTPmessages_GetAllStickers(
2989 MTP_long(Api::CountStickersHash(_session, true))
2990 )).done(done).fail([=](const MTP::Error &error) {
2991 LOG(("App Fail: Failed to get stickers!"));
2992 done(MTP_messages_allStickersNotModified());
2993 }).send();
2994 }
2995
requestMasks(TimeId now)2996 void ApiWrap::requestMasks(TimeId now) {
2997 if (!_session->data().stickers().masksUpdateNeeded(now)
2998 || _masksUpdateRequest) {
2999 return;
3000 }
3001 const auto done = [=](const MTPmessages_AllStickers &result) {
3002 _session->data().stickers().setLastMasksUpdate(crl::now());
3003 _masksUpdateRequest = 0;
3004
3005 result.match([&](const MTPDmessages_allStickersNotModified&) {
3006 }, [&](const MTPDmessages_allStickers &data) {
3007 _session->data().stickers().masksReceived(
3008 data.vsets().v,
3009 data.vhash().v);
3010 });
3011 };
3012 _masksUpdateRequest = request(MTPmessages_GetMaskStickers(
3013 MTP_long(Api::CountMasksHash(_session, true))
3014 )).done(done).fail([=](const MTP::Error &error) {
3015 LOG(("App Fail: Failed to get masks!"));
3016 done(MTP_messages_allStickersNotModified());
3017 }).send();
3018 }
3019
requestRecentStickers(TimeId now,bool attached)3020 void ApiWrap::requestRecentStickers(TimeId now, bool attached) {
3021 const auto needed = attached
3022 ? _session->data().stickers().recentAttachedUpdateNeeded(now)
3023 : _session->data().stickers().recentUpdateNeeded(now);
3024 if (!needed) {
3025 return;
3026 }
3027 requestRecentStickersWithHash(
3028 Api::CountRecentStickersHash(_session, attached), attached);
3029 }
3030
requestRecentStickersWithHash(uint64 hash,bool attached)3031 void ApiWrap::requestRecentStickersWithHash(uint64 hash, bool attached) {
3032 const auto requestId = [=]() -> mtpRequestId & {
3033 return attached
3034 ? _recentAttachedStickersUpdateRequest
3035 : _recentStickersUpdateRequest;
3036 };
3037 if (requestId()) {
3038 return;
3039 }
3040 const auto finish = [=] {
3041 auto &stickers = _session->data().stickers();
3042 if (attached) {
3043 stickers.setLastRecentAttachedUpdate(crl::now());
3044 } else {
3045 stickers.setLastRecentUpdate(crl::now());
3046 }
3047 requestId() = 0;
3048 };
3049 const auto flags = attached
3050 ? MTPmessages_getRecentStickers::Flag::f_attached
3051 : MTPmessages_getRecentStickers::Flags(0);
3052 requestId() = request(MTPmessages_GetRecentStickers(
3053 MTP_flags(flags),
3054 MTP_long(hash)
3055 )).done([=](const MTPmessages_RecentStickers &result) {
3056 finish();
3057
3058 switch (result.type()) {
3059 case mtpc_messages_recentStickersNotModified: return;
3060 case mtpc_messages_recentStickers: {
3061 auto &d = result.c_messages_recentStickers();
3062 _session->data().stickers().specialSetReceived(
3063 attached
3064 ? Data::Stickers::CloudRecentAttachedSetId
3065 : Data::Stickers::CloudRecentSetId,
3066 tr::lng_recent_stickers(tr::now),
3067 d.vstickers().v,
3068 d.vhash().v,
3069 d.vpacks().v,
3070 d.vdates().v);
3071 } return;
3072 default: Unexpected("Type in ApiWrap::recentStickersDone()");
3073 }
3074 }).fail([=](const MTP::Error &error) {
3075 finish();
3076
3077 LOG(("App Fail: Failed to get recent stickers!"));
3078 }).send();
3079 }
3080
requestFavedStickers(TimeId now)3081 void ApiWrap::requestFavedStickers(TimeId now) {
3082 if (!_session->data().stickers().favedUpdateNeeded(now)
3083 || _favedStickersUpdateRequest) {
3084 return;
3085 }
3086 _favedStickersUpdateRequest = request(MTPmessages_GetFavedStickers(
3087 MTP_long(Api::CountFavedStickersHash(_session))
3088 )).done([=](const MTPmessages_FavedStickers &result) {
3089 _session->data().stickers().setLastFavedUpdate(crl::now());
3090 _favedStickersUpdateRequest = 0;
3091
3092 switch (result.type()) {
3093 case mtpc_messages_favedStickersNotModified: return;
3094 case mtpc_messages_favedStickers: {
3095 auto &d = result.c_messages_favedStickers();
3096 _session->data().stickers().specialSetReceived(
3097 Data::Stickers::FavedSetId,
3098 Lang::Hard::FavedSetTitle(),
3099 d.vstickers().v,
3100 d.vhash().v,
3101 d.vpacks().v);
3102 } return;
3103 default: Unexpected("Type in ApiWrap::favedStickersDone()");
3104 }
3105 }).fail([=](const MTP::Error &error) {
3106 _session->data().stickers().setLastFavedUpdate(crl::now());
3107 _favedStickersUpdateRequest = 0;
3108
3109 LOG(("App Fail: Failed to get faved stickers!"));
3110 }).send();
3111 }
3112
requestFeaturedStickers(TimeId now)3113 void ApiWrap::requestFeaturedStickers(TimeId now) {
3114 if (!_session->data().stickers().featuredUpdateNeeded(now)
3115 || _featuredStickersUpdateRequest) {
3116 return;
3117 }
3118 _featuredStickersUpdateRequest = request(MTPmessages_GetFeaturedStickers(
3119 MTP_long(Api::CountFeaturedStickersHash(_session))
3120 )).done([=](const MTPmessages_FeaturedStickers &result) {
3121 _session->data().stickers().setLastFeaturedUpdate(crl::now());
3122 _featuredStickersUpdateRequest = 0;
3123
3124 switch (result.type()) {
3125 case mtpc_messages_featuredStickersNotModified: return;
3126 case mtpc_messages_featuredStickers: {
3127 auto &d = result.c_messages_featuredStickers();
3128 _session->data().stickers().featuredSetsReceived(
3129 d.vsets().v,
3130 d.vunread().v,
3131 d.vhash().v);
3132 } return;
3133 default: Unexpected("Type in ApiWrap::featuredStickersDone()");
3134 }
3135 }).fail([=](const MTP::Error &error) {
3136 _session->data().stickers().setLastFeaturedUpdate(crl::now());
3137 _featuredStickersUpdateRequest = 0;
3138
3139 LOG(("App Fail: Failed to get featured stickers!"));
3140 }).send();
3141 }
3142
requestSavedGifs(TimeId now)3143 void ApiWrap::requestSavedGifs(TimeId now) {
3144 if (!_session->data().stickers().savedGifsUpdateNeeded(now)
3145 || _savedGifsUpdateRequest) {
3146 return;
3147 }
3148 _savedGifsUpdateRequest = request(MTPmessages_GetSavedGifs(
3149 MTP_long(Api::CountSavedGifsHash(_session))
3150 )).done([=](const MTPmessages_SavedGifs &result) {
3151 _session->data().stickers().setLastSavedGifsUpdate(crl::now());
3152 _savedGifsUpdateRequest = 0;
3153
3154 switch (result.type()) {
3155 case mtpc_messages_savedGifsNotModified: return;
3156 case mtpc_messages_savedGifs: {
3157 auto &d = result.c_messages_savedGifs();
3158 _session->data().stickers().gifsReceived(
3159 d.vgifs().v,
3160 d.vhash().v);
3161 } return;
3162 default: Unexpected("Type in ApiWrap::savedGifsDone()");
3163 }
3164 }).fail([=](const MTP::Error &error) {
3165 _session->data().stickers().setLastSavedGifsUpdate(crl::now());
3166 _savedGifsUpdateRequest = 0;
3167
3168 LOG(("App Fail: Failed to get saved gifs!"));
3169 }).send();
3170 }
3171
readFeaturedSetDelayed(uint64 setId)3172 void ApiWrap::readFeaturedSetDelayed(uint64 setId) {
3173 if (!_featuredSetsRead.contains(setId)) {
3174 _featuredSetsRead.insert(setId);
3175 _featuredSetsReadTimer.callOnce(kReadFeaturedSetsTimeout);
3176 }
3177 }
3178
readFeaturedSets()3179 void ApiWrap::readFeaturedSets() {
3180 const auto &sets = _session->data().stickers().sets();
3181 auto count = _session->data().stickers().featuredSetsUnreadCount();
3182 QVector<MTPlong> wrappedIds;
3183 wrappedIds.reserve(_featuredSetsRead.size());
3184 for (const auto setId : _featuredSetsRead) {
3185 const auto it = sets.find(setId);
3186 if (it != sets.cend()) {
3187 it->second->flags &= ~Data::StickersSetFlag::Unread;
3188 wrappedIds.append(MTP_long(setId));
3189 if (count) {
3190 --count;
3191 }
3192 }
3193 }
3194 _featuredSetsRead.clear();
3195
3196 if (!wrappedIds.empty()) {
3197 auto requestData = MTPmessages_ReadFeaturedStickers(
3198 MTP_vector<MTPlong>(wrappedIds));
3199 request(std::move(requestData)).done([=](const MTPBool &result) {
3200 local().writeFeaturedStickers();
3201 _session->data().stickers().notifyUpdated();
3202 }).send();
3203
3204 _session->data().stickers().setFeaturedSetsUnreadCount(count);
3205 }
3206 }
3207
parseChannelParticipants(not_null<ChannelData * > channel,const MTPchannels_ChannelParticipants & result,Fn<void (int availableCount,const QVector<MTPChannelParticipant> & list)> callbackList,Fn<void ()> callbackNotModified)3208 void ApiWrap::parseChannelParticipants(
3209 not_null<ChannelData*> channel,
3210 const MTPchannels_ChannelParticipants &result,
3211 Fn<void(
3212 int availableCount,
3213 const QVector<MTPChannelParticipant> &list)> callbackList,
3214 Fn<void()> callbackNotModified) {
3215 result.match([&](const MTPDchannels_channelParticipants &data) {
3216 _session->data().processUsers(data.vusers());
3217 if (channel->mgInfo) {
3218 refreshChannelAdmins(channel, data.vparticipants().v);
3219 }
3220 if (callbackList) {
3221 callbackList(data.vcount().v, data.vparticipants().v);
3222 }
3223 }, [&](const MTPDchannels_channelParticipantsNotModified &) {
3224 if (callbackNotModified) {
3225 callbackNotModified();
3226 } else {
3227 LOG(("API Error: "
3228 "channels.channelParticipantsNotModified received!"));
3229 }
3230 });
3231 }
3232
refreshChannelAdmins(not_null<ChannelData * > channel,const QVector<MTPChannelParticipant> & participants)3233 void ApiWrap::refreshChannelAdmins(
3234 not_null<ChannelData*> channel,
3235 const QVector<MTPChannelParticipant> &participants) {
3236 Data::ChannelAdminChanges changes(channel);
3237 for (const auto &p : participants) {
3238 const auto participantId = p.match([](
3239 const MTPDchannelParticipantBanned &data) {
3240 return peerFromMTP(data.vpeer());
3241 }, [](const MTPDchannelParticipantLeft &data) {
3242 return peerFromMTP(data.vpeer());
3243 }, [](const auto &data) {
3244 return peerFromUser(data.vuser_id());
3245 });
3246 const auto userId = peerToUser(participantId);
3247 p.match([&](const MTPDchannelParticipantAdmin &data) {
3248 Assert(peerIsUser(participantId));
3249 changes.add(userId, qs(data.vrank().value_or_empty()));
3250 }, [&](const MTPDchannelParticipantCreator &data) {
3251 Assert(peerIsUser(participantId));
3252 const auto rank = qs(data.vrank().value_or_empty());
3253 if (const auto info = channel->mgInfo.get()) {
3254 info->creator = channel->owner().userLoaded(userId);
3255 info->creatorRank = rank;
3256 }
3257 changes.add(userId, rank);
3258 }, [&](const auto &data) {
3259 if (userId) {
3260 changes.remove(userId);
3261 }
3262 });
3263 }
3264 }
3265
parseRecentChannelParticipants(not_null<ChannelData * > channel,const MTPchannels_ChannelParticipants & result,Fn<void (int availableCount,const QVector<MTPChannelParticipant> & list)> callbackList,Fn<void ()> callbackNotModified)3266 void ApiWrap::parseRecentChannelParticipants(
3267 not_null<ChannelData*> channel,
3268 const MTPchannels_ChannelParticipants &result,
3269 Fn<void(
3270 int availableCount,
3271 const QVector<MTPChannelParticipant> &list)> callbackList,
3272 Fn<void()> callbackNotModified) {
3273 parseChannelParticipants(channel, result, [&](
3274 int availableCount,
3275 const QVector<MTPChannelParticipant> &list) {
3276 auto applyLast = channel->isMegagroup()
3277 && (channel->mgInfo->lastParticipants.size() <= list.size());
3278 if (applyLast) {
3279 applyLastParticipantsList(
3280 channel,
3281 availableCount,
3282 list);
3283 }
3284 if (callbackList) {
3285 callbackList(availableCount, list);
3286 }
3287 }, std::move(callbackNotModified));
3288 }
3289
jumpToDate(Dialogs::Key chat,const QDate & date)3290 void ApiWrap::jumpToDate(Dialogs::Key chat, const QDate &date) {
3291 if (const auto peer = chat.peer()) {
3292 jumpToHistoryDate(peer, date);
3293 }
3294 }
3295
3296 template <typename Callback>
requestMessageAfterDate(not_null<PeerData * > peer,const QDate & date,Callback && callback)3297 void ApiWrap::requestMessageAfterDate(
3298 not_null<PeerData*> peer,
3299 const QDate &date,
3300 Callback &&callback) {
3301 // API returns a message with date <= offset_date.
3302 // So we request a message with offset_date = desired_date - 1 and add_offset = -1.
3303 // This should give us the first message with date >= desired_date.
3304 const auto offsetId = 0;
3305 const auto offsetDate = static_cast<int>(date.startOfDay().toSecsSinceEpoch()) - 1;
3306 const auto addOffset = -1;
3307 const auto limit = 1;
3308 const auto maxId = 0;
3309 const auto minId = 0;
3310 const auto historyHash = uint64(0);
3311 request(MTPmessages_GetHistory(
3312 peer->input,
3313 MTP_int(offsetId),
3314 MTP_int(offsetDate),
3315 MTP_int(addOffset),
3316 MTP_int(limit),
3317 MTP_int(maxId),
3318 MTP_int(minId),
3319 MTP_long(historyHash)
3320 )).done([
3321 =,
3322 callback = std::forward<Callback>(callback)
3323 ](const MTPmessages_Messages &result) {
3324 auto getMessagesList = [&]() -> const QVector<MTPMessage>* {
3325 auto handleMessages = [&](auto &messages) {
3326 _session->data().processUsers(messages.vusers());
3327 _session->data().processChats(messages.vchats());
3328 return &messages.vmessages().v;
3329 };
3330 switch (result.type()) {
3331 case mtpc_messages_messages:
3332 return handleMessages(result.c_messages_messages());
3333 case mtpc_messages_messagesSlice:
3334 return handleMessages(result.c_messages_messagesSlice());
3335 case mtpc_messages_channelMessages: {
3336 auto &messages = result.c_messages_channelMessages();
3337 if (peer && peer->isChannel()) {
3338 peer->asChannel()->ptsReceived(messages.vpts().v);
3339 } else {
3340 LOG(("API Error: received messages.channelMessages when no channel was passed! (ApiWrap::jumpToDate)"));
3341 }
3342 return handleMessages(messages);
3343 } break;
3344 case mtpc_messages_messagesNotModified: {
3345 LOG(("API Error: received messages.messagesNotModified! (ApiWrap::jumpToDate)"));
3346 } break;
3347 }
3348 return nullptr;
3349 };
3350
3351 if (const auto list = getMessagesList()) {
3352 _session->data().processMessages(*list, NewMessageType::Existing);
3353 for (const auto &message : *list) {
3354 if (DateFromMessage(message) >= offsetDate) {
3355 callback(IdFromMessage(message));
3356 return;
3357 }
3358 }
3359 }
3360 callback(ShowAtUnreadMsgId);
3361 }).send();
3362 }
3363
jumpToHistoryDate(not_null<PeerData * > peer,const QDate & date)3364 void ApiWrap::jumpToHistoryDate(not_null<PeerData*> peer, const QDate &date) {
3365 if (const auto channel = peer->migrateTo()) {
3366 jumpToHistoryDate(channel, date);
3367 return;
3368 }
3369 const auto jumpToDateInPeer = [=] {
3370 requestMessageAfterDate(peer, date, [=](MsgId resultId) {
3371 Ui::showPeerHistory(peer, resultId);
3372 });
3373 };
3374 if (const auto chat = peer->migrateFrom()) {
3375 requestMessageAfterDate(chat, date, [=](MsgId resultId) {
3376 if (resultId) {
3377 Ui::showPeerHistory(chat, resultId);
3378 } else {
3379 jumpToDateInPeer();
3380 }
3381 });
3382 } else {
3383 jumpToDateInPeer();
3384 }
3385 }
3386
preloadEnoughUnreadMentions(not_null<History * > history)3387 void ApiWrap::preloadEnoughUnreadMentions(not_null<History*> history) {
3388 auto fullCount = history->getUnreadMentionsCount();
3389 auto loadedCount = history->getUnreadMentionsLoadedCount();
3390 auto allLoaded = (fullCount >= 0) ? (loadedCount >= fullCount) : false;
3391 if (fullCount < 0 || loadedCount >= kUnreadMentionsPreloadIfLess || allLoaded) {
3392 return;
3393 }
3394 if (_unreadMentionsRequests.contains(history)) {
3395 return;
3396 }
3397 auto offsetId = loadedCount ? history->getMaxLoadedUnreadMention() : 1;
3398 auto limit = loadedCount ? kUnreadMentionsNextRequestLimit : kUnreadMentionsFirstRequestLimit;
3399 auto addOffset = loadedCount ? -(limit + 1) : -limit;
3400 auto maxId = 0;
3401 auto minId = 0;
3402 auto requestId = request(MTPmessages_GetUnreadMentions(history->peer->input, MTP_int(offsetId), MTP_int(addOffset), MTP_int(limit), MTP_int(maxId), MTP_int(minId))).done([this, history](const MTPmessages_Messages &result) {
3403 _unreadMentionsRequests.remove(history);
3404 history->addUnreadMentionsSlice(result);
3405 }).fail([this, history](const MTP::Error &error) {
3406 _unreadMentionsRequests.remove(history);
3407 }).send();
3408 _unreadMentionsRequests.emplace(history, requestId);
3409 }
3410
checkForUnreadMentions(const base::flat_set<MsgId> & possiblyReadMentions,ChannelData * channel)3411 void ApiWrap::checkForUnreadMentions(
3412 const base::flat_set<MsgId> &possiblyReadMentions,
3413 ChannelData *channel) {
3414 for (auto msgId : possiblyReadMentions) {
3415 requestMessageData(channel, msgId, [=](
3416 ChannelData *channel,
3417 MsgId msgId) {
3418 if (const auto item = _session->data().message(channel, msgId)) {
3419 if (item->mentionsMe()) {
3420 item->markMediaRead();
3421 }
3422 }
3423 });
3424 }
3425 }
3426
addChatParticipants(not_null<PeerData * > peer,const std::vector<not_null<UserData * >> & users,Fn<void (bool)> done)3427 void ApiWrap::addChatParticipants(
3428 not_null<PeerData*> peer,
3429 const std::vector<not_null<UserData*>> &users,
3430 Fn<void(bool)> done) {
3431 if (const auto chat = peer->asChat()) {
3432 for (const auto &user : users) {
3433 request(MTPmessages_AddChatUser(
3434 chat->inputChat,
3435 user->inputUser,
3436 MTP_int(kForwardMessagesOnAdd)
3437 )).done([=](const MTPUpdates &result) {
3438 applyUpdates(result);
3439 if (done) done(true);
3440 }).fail([=](const MTP::Error &error) {
3441 ShowAddParticipantsError(error.type(), peer, { 1, user });
3442 if (done) done(false);
3443 }).afterDelay(crl::time(5)).send();
3444 }
3445 } else if (const auto channel = peer->asChannel()) {
3446 const auto hasBot = ranges::any_of(users, &UserData::isBot);
3447 if (!peer->isMegagroup() && hasBot) {
3448 ShowAddParticipantsError("USER_BOT", peer, users);
3449 return;
3450 }
3451 auto list = QVector<MTPInputUser>();
3452 list.reserve(qMin(int(users.size()), int(kMaxUsersPerInvite)));
3453 const auto send = [&] {
3454 const auto callback = base::take(done);
3455 request(MTPchannels_InviteToChannel(
3456 channel->inputChannel,
3457 MTP_vector<MTPInputUser>(list)
3458 )).done([=](const MTPUpdates &result) {
3459 applyUpdates(result);
3460 requestParticipantsCountDelayed(channel);
3461 if (callback) callback(true);
3462 }).fail([=](const MTP::Error &error) {
3463 ShowAddParticipantsError(error.type(), peer, users);
3464 if (callback) callback(false);
3465 }).afterDelay(crl::time(5)).send();
3466 };
3467 for (const auto &user : users) {
3468 list.push_back(user->inputUser);
3469 if (list.size() == kMaxUsersPerInvite) {
3470 send();
3471 list.clear();
3472 }
3473 }
3474 if (!list.empty()) {
3475 send();
3476 }
3477 } else {
3478 Unexpected("User in ApiWrap::addChatParticipants.");
3479 }
3480 }
3481
requestSharedMediaCount(not_null<PeerData * > peer,Storage::SharedMediaType type)3482 void ApiWrap::requestSharedMediaCount(
3483 not_null<PeerData*> peer,
3484 Storage::SharedMediaType type) {
3485 requestSharedMedia(peer, type, 0, SliceType::Before);
3486 }
3487
requestSharedMedia(not_null<PeerData * > peer,SharedMediaType type,MsgId messageId,SliceType slice)3488 void ApiWrap::requestSharedMedia(
3489 not_null<PeerData*> peer,
3490 SharedMediaType type,
3491 MsgId messageId,
3492 SliceType slice) {
3493 const auto key = std::make_tuple(peer, type, messageId, slice);
3494 if (_sharedMediaRequests.contains(key)) {
3495 return;
3496 }
3497
3498 const auto prepared = Api::PrepareSearchRequest(
3499 peer,
3500 type,
3501 QString(),
3502 messageId,
3503 slice);
3504 if (!prepared) {
3505 return;
3506 }
3507
3508 const auto history = _session->data().history(peer);
3509 auto &histories = history->owner().histories();
3510 const auto requestType = Data::Histories::RequestType::History;
3511 histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
3512 return request(
3513 std::move(*prepared)
3514 ).done([=](const MTPmessages_Messages &result) {
3515 const auto key = std::make_tuple(peer, type, messageId, slice);
3516 _sharedMediaRequests.remove(key);
3517 sharedMediaDone(peer, type, messageId, slice, result);
3518 finish();
3519 }).fail([=](const MTP::Error &error) {
3520 _sharedMediaRequests.remove(key);
3521 finish();
3522 }).send();
3523 });
3524 _sharedMediaRequests.emplace(key);
3525 }
3526
sharedMediaDone(not_null<PeerData * > peer,SharedMediaType type,MsgId messageId,SliceType slice,const MTPmessages_Messages & result)3527 void ApiWrap::sharedMediaDone(
3528 not_null<PeerData*> peer,
3529 SharedMediaType type,
3530 MsgId messageId,
3531 SliceType slice,
3532 const MTPmessages_Messages &result) {
3533 auto parsed = Api::ParseSearchResult(
3534 peer,
3535 type,
3536 messageId,
3537 slice,
3538 result);
3539 _session->storage().add(Storage::SharedMediaAddSlice(
3540 peer->id,
3541 type,
3542 std::move(parsed.messageIds),
3543 parsed.noSkipRange,
3544 parsed.fullCount
3545 ));
3546 if (type == SharedMediaType::Pinned && !parsed.messageIds.empty()) {
3547 peer->owner().history(peer)->setHasPinnedMessages(true);
3548 }
3549 }
3550
requestUserPhotos(not_null<UserData * > user,PhotoId afterId)3551 void ApiWrap::requestUserPhotos(
3552 not_null<UserData*> user,
3553 PhotoId afterId) {
3554 if (_userPhotosRequests.contains(user)) {
3555 return;
3556 }
3557
3558 auto limit = kSharedMediaLimit;
3559
3560 auto requestId = request(MTPphotos_GetUserPhotos(
3561 user->inputUser,
3562 MTP_int(0),
3563 MTP_long(afterId),
3564 MTP_int(limit)
3565 )).done([this, user, afterId](const MTPphotos_Photos &result) {
3566 _userPhotosRequests.remove(user);
3567 userPhotosDone(user, afterId, result);
3568 }).fail([this, user](const MTP::Error &error) {
3569 _userPhotosRequests.remove(user);
3570 }).send();
3571 _userPhotosRequests.emplace(user, requestId);
3572 }
3573
userPhotosDone(not_null<UserData * > user,PhotoId photoId,const MTPphotos_Photos & result)3574 void ApiWrap::userPhotosDone(
3575 not_null<UserData*> user,
3576 PhotoId photoId,
3577 const MTPphotos_Photos &result) {
3578 auto fullCount = 0;
3579 auto &photos = *[&] {
3580 switch (result.type()) {
3581 case mtpc_photos_photos: {
3582 auto &d = result.c_photos_photos();
3583 _session->data().processUsers(d.vusers());
3584 fullCount = d.vphotos().v.size();
3585 return &d.vphotos().v;
3586 } break;
3587
3588 case mtpc_photos_photosSlice: {
3589 auto &d = result.c_photos_photosSlice();
3590 _session->data().processUsers(d.vusers());
3591 fullCount = d.vcount().v;
3592 return &d.vphotos().v;
3593 } break;
3594 }
3595 Unexpected("photos.Photos type in userPhotosDone()");
3596 }();
3597
3598 auto photoIds = std::vector<PhotoId>();
3599 photoIds.reserve(photos.size());
3600 for (auto &photo : photos) {
3601 if (auto photoData = _session->data().processPhoto(photo)) {
3602 photoIds.push_back(photoData->id);
3603 }
3604 }
3605 _session->storage().add(Storage::UserPhotosAddSlice(
3606 peerToUser(user->id),
3607 std::move(photoIds),
3608 fullCount
3609 ));
3610 }
3611
sendAction(const SendAction & action)3612 void ApiWrap::sendAction(const SendAction &action) {
3613 if (!action.options.scheduled) {
3614 _session->data().histories().readInbox(action.history);
3615 action.history->getReadyFor(ShowAtTheEndMsgId);
3616 }
3617 _sendActions.fire_copy(action);
3618 }
3619
finishForwarding(const SendAction & action)3620 void ApiWrap::finishForwarding(const SendAction &action) {
3621 const auto history = action.history;
3622 auto toForward = history->resolveForwardDraft();
3623 if (!toForward.items.empty()) {
3624 const auto error = GetErrorTextForSending(
3625 history->peer,
3626 toForward.items);
3627 if (!error.isEmpty()) {
3628 return;
3629 }
3630
3631 forwardMessages(std::move(toForward), action);
3632 _session->data().cancelForwarding(history);
3633 }
3634
3635 _session->data().sendHistoryChangeNotifications();
3636 _session->changes().historyUpdated(
3637 history,
3638 (action.options.scheduled
3639 ? Data::HistoryUpdate::Flag::ScheduledSent
3640 : Data::HistoryUpdate::Flag::MessageSent));
3641 }
3642
forwardMessages(Data::ResolvedForwardDraft && draft,const SendAction & action,FnMut<void ()> && successCallback)3643 void ApiWrap::forwardMessages(
3644 Data::ResolvedForwardDraft &&draft,
3645 const SendAction &action,
3646 FnMut<void()> &&successCallback) {
3647 Expects(!draft.items.empty());
3648
3649 auto &histories = _session->data().histories();
3650
3651 struct SharedCallback {
3652 int requestsLeft = 0;
3653 FnMut<void()> callback;
3654 };
3655 const auto shared = successCallback
3656 ? std::make_shared<SharedCallback>()
3657 : std::shared_ptr<SharedCallback>();
3658 if (successCallback) {
3659 shared->callback = std::move(successCallback);
3660 }
3661
3662 const auto count = int(draft.items.size());
3663 const auto genClientSideMessage = action.generateLocal
3664 && (count < 2)
3665 && (draft.options == Data::ForwardOptions::PreserveInfo);
3666 const auto history = action.history;
3667 const auto peer = history->peer;
3668
3669 if (!action.options.scheduled) {
3670 histories.readInbox(history);
3671 }
3672 const auto anonymousPost = peer->amAnonymous();
3673 const auto silentPost = ShouldSendSilent(peer, action.options);
3674
3675 auto flags = MessageFlags();
3676 auto sendFlags = MTPmessages_ForwardMessages::Flags(0);
3677 FillMessagePostFlags(action, peer, flags);
3678 if (silentPost) {
3679 sendFlags |= MTPmessages_ForwardMessages::Flag::f_silent;
3680 }
3681 if (action.options.scheduled) {
3682 flags |= MessageFlag::IsOrWasScheduled;
3683 sendFlags |= MTPmessages_ForwardMessages::Flag::f_schedule_date;
3684 }
3685 if (draft.options != Data::ForwardOptions::PreserveInfo) {
3686 sendFlags |= MTPmessages_ForwardMessages::Flag::f_drop_author;
3687 }
3688 if (draft.options == Data::ForwardOptions::NoNamesAndCaptions) {
3689 sendFlags |= MTPmessages_ForwardMessages::Flag::f_drop_media_captions;
3690 }
3691
3692 auto forwardFrom = draft.items.front()->history()->peer;
3693 auto ids = QVector<MTPint>();
3694 auto randomIds = QVector<MTPlong>();
3695 auto localIds = std::shared_ptr<base::flat_map<uint64, FullMsgId>>();
3696
3697 const auto sendAccumulated = [&] {
3698 if (shared) {
3699 ++shared->requestsLeft;
3700 }
3701 const auto requestType = Data::Histories::RequestType::Send;
3702 const auto idsCopy = localIds;
3703 histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
3704 history->sendRequestId = request(MTPmessages_ForwardMessages(
3705 MTP_flags(sendFlags),
3706 forwardFrom->input,
3707 MTP_vector<MTPint>(ids),
3708 MTP_vector<MTPlong>(randomIds),
3709 peer->input,
3710 MTP_int(action.options.scheduled)
3711 )).done([=](const MTPUpdates &result) {
3712 applyUpdates(result);
3713 if (shared && !--shared->requestsLeft) {
3714 shared->callback();
3715 }
3716 finish();
3717 }).fail([=](const MTP::Error &error) {
3718 if (idsCopy) {
3719 for (const auto &[randomId, itemId] : *idsCopy) {
3720 sendMessageFail(error, peer, randomId, itemId);
3721 }
3722 } else {
3723 sendMessageFail(error, peer);
3724 }
3725 finish();
3726 }).afterRequest(
3727 history->sendRequestId
3728 ).send();
3729 return history->sendRequestId;
3730 });
3731
3732 ids.resize(0);
3733 randomIds.resize(0);
3734 localIds = nullptr;
3735 };
3736
3737 ids.reserve(count);
3738 randomIds.reserve(count);
3739 for (const auto item : draft.items) {
3740 const auto randomId = base::RandomValue<uint64>();
3741 if (genClientSideMessage) {
3742 const auto newId = FullMsgId(
3743 peerToChannel(peer->id),
3744 _session->data().nextLocalMessageId());
3745 const auto self = _session->user();
3746 const auto messageFromId = anonymousPost
3747 ? PeerId(0)
3748 : self->id;
3749 const auto messagePostAuthor = peer->isBroadcast()
3750 ? self->name
3751 : QString();
3752 history->addNewLocalMessage(
3753 newId.msg,
3754 flags,
3755 HistoryItem::NewMessageDate(action.options.scheduled),
3756 messageFromId,
3757 messagePostAuthor,
3758 item);
3759 _session->data().registerMessageRandomId(randomId, newId);
3760 if (!localIds) {
3761 localIds = std::make_shared<base::flat_map<uint64, FullMsgId>>();
3762 }
3763 localIds->emplace(randomId, newId);
3764 }
3765 const auto newFrom = item->history()->peer;
3766 if (forwardFrom != newFrom) {
3767 sendAccumulated();
3768 forwardFrom = newFrom;
3769 }
3770 ids.push_back(MTP_int(item->id));
3771 randomIds.push_back(MTP_long(randomId));
3772 }
3773 sendAccumulated();
3774 _session->data().sendHistoryChangeNotifications();
3775 }
3776
shareContact(const QString & phone,const QString & firstName,const QString & lastName,const SendAction & action)3777 void ApiWrap::shareContact(
3778 const QString &phone,
3779 const QString &firstName,
3780 const QString &lastName,
3781 const SendAction &action) {
3782 const auto userId = UserId(0);
3783 sendSharedContact(phone, firstName, lastName, userId, action);
3784 }
3785
shareContact(not_null<UserData * > user,const SendAction & action)3786 void ApiWrap::shareContact(
3787 not_null<UserData*> user,
3788 const SendAction &action) {
3789 const auto userId = peerToUser(user->id);
3790 const auto phone = _session->data().findContactPhone(user);
3791 if (phone.isEmpty()) {
3792 return;
3793 }
3794 sendSharedContact(
3795 phone,
3796 user->firstName,
3797 user->lastName,
3798 userId,
3799 action);
3800 }
3801
sendSharedContact(const QString & phone,const QString & firstName,const QString & lastName,UserId userId,const SendAction & action)3802 void ApiWrap::sendSharedContact(
3803 const QString &phone,
3804 const QString &firstName,
3805 const QString &lastName,
3806 UserId userId,
3807 const SendAction &action) {
3808 sendAction(action);
3809
3810 const auto history = action.history;
3811 const auto peer = history->peer;
3812
3813 const auto newId = FullMsgId(
3814 history->channelId(),
3815 _session->data().nextLocalMessageId());
3816 const auto anonymousPost = peer->amAnonymous();
3817
3818 auto flags = NewMessageFlags(peer);
3819 if (action.replyTo) {
3820 flags |= MessageFlag::HasReplyInfo;
3821 }
3822 const auto replyHeader = NewMessageReplyHeader(action);
3823 FillMessagePostFlags(action, peer, flags);
3824 if (action.options.scheduled) {
3825 flags |= MessageFlag::IsOrWasScheduled;
3826 }
3827 const auto messageFromId = anonymousPost ? 0 : _session->userPeerId();
3828 const auto messagePostAuthor = peer->isBroadcast()
3829 ? _session->user()->name
3830 : QString();
3831 const auto viaBotId = UserId();
3832 const auto item = history->addNewLocalMessage(
3833 newId.msg,
3834 flags,
3835 viaBotId,
3836 action.replyTo,
3837 HistoryItem::NewMessageDate(action.options.scheduled),
3838 messageFromId,
3839 messagePostAuthor,
3840 TextWithEntities(),
3841 MTP_messageMediaContact(
3842 MTP_string(phone),
3843 MTP_string(firstName),
3844 MTP_string(lastName),
3845 MTP_string(), // vcard
3846 MTP_long(userId.bare)),
3847 HistoryMessageMarkupData());
3848
3849 const auto media = MTP_inputMediaContact(
3850 MTP_string(phone),
3851 MTP_string(firstName),
3852 MTP_string(lastName),
3853 MTP_string()); // vcard
3854 sendMedia(item, media, action.options);
3855
3856 _session->data().sendHistoryChangeNotifications();
3857 _session->changes().historyUpdated(
3858 history,
3859 (action.options.scheduled
3860 ? Data::HistoryUpdate::Flag::ScheduledSent
3861 : Data::HistoryUpdate::Flag::MessageSent));
3862 }
3863
sendVoiceMessage(QByteArray result,VoiceWaveform waveform,int duration,const SendAction & action)3864 void ApiWrap::sendVoiceMessage(
3865 QByteArray result,
3866 VoiceWaveform waveform,
3867 int duration,
3868 const SendAction &action) {
3869 const auto caption = TextWithTags();
3870 const auto to = fileLoadTaskOptions(action);
3871 _fileLoader->addTask(std::make_unique<FileLoadTask>(
3872 &session(),
3873 result,
3874 duration,
3875 waveform,
3876 to,
3877 caption));
3878 }
3879
editMedia(Ui::PreparedList && list,SendMediaType type,TextWithTags && caption,const SendAction & action)3880 void ApiWrap::editMedia(
3881 Ui::PreparedList &&list,
3882 SendMediaType type,
3883 TextWithTags &&caption,
3884 const SendAction &action) {
3885 if (list.files.empty()) return;
3886
3887 auto &file = list.files.front();
3888 const auto to = fileLoadTaskOptions(action);
3889 _fileLoader->addTask(std::make_unique<FileLoadTask>(
3890 &session(),
3891 file.path,
3892 file.content,
3893 std::move(file.information),
3894 type,
3895 to,
3896 caption));
3897 }
3898
sendFiles(Ui::PreparedList && list,SendMediaType type,TextWithTags && caption,std::shared_ptr<SendingAlbum> album,const SendAction & action)3899 void ApiWrap::sendFiles(
3900 Ui::PreparedList &&list,
3901 SendMediaType type,
3902 TextWithTags &&caption,
3903 std::shared_ptr<SendingAlbum> album,
3904 const SendAction &action) {
3905 const auto haveCaption = !caption.text.isEmpty();
3906 if (haveCaption && !list.canAddCaption(album != nullptr)) {
3907 auto message = MessageToSend(action.history);
3908 message.textWithTags = base::take(caption);
3909 message.action = action;
3910 message.action.clearDraft = false;
3911 sendMessage(std::move(message));
3912 }
3913
3914 const auto to = fileLoadTaskOptions(action);
3915 if (album) {
3916 album->options = to.options;
3917 }
3918 auto tasks = std::vector<std::unique_ptr<Task>>();
3919 tasks.reserve(list.files.size());
3920 for (auto &file : list.files) {
3921 const auto uploadWithType = !album
3922 ? type
3923 : (file.type == Ui::PreparedFile::Type::Photo
3924 && type != SendMediaType::File)
3925 ? SendMediaType::Photo
3926 : SendMediaType::File;
3927 tasks.push_back(std::make_unique<FileLoadTask>(
3928 &session(),
3929 file.path,
3930 file.content,
3931 std::move(file.information),
3932 uploadWithType,
3933 to,
3934 caption,
3935 album));
3936 caption = TextWithTags();
3937 }
3938 if (album) {
3939 _sendingAlbums.emplace(album->groupId, album);
3940 album->items.reserve(tasks.size());
3941 for (const auto &task : tasks) {
3942 album->items.emplace_back(task->id());
3943 }
3944 }
3945 _fileLoader->addTasks(std::move(tasks));
3946 }
3947
sendFile(const QByteArray & fileContent,SendMediaType type,const SendAction & action)3948 void ApiWrap::sendFile(
3949 const QByteArray &fileContent,
3950 SendMediaType type,
3951 const SendAction &action) {
3952 const auto to = fileLoadTaskOptions(action);
3953 auto caption = TextWithTags();
3954 _fileLoader->addTask(std::make_unique<FileLoadTask>(
3955 &session(),
3956 QString(),
3957 fileContent,
3958 nullptr,
3959 type,
3960 to,
3961 caption));
3962 }
3963
sendUploadedPhoto(FullMsgId localId,const MTPInputFile & file,Api::SendOptions options,std::vector<MTPInputDocument> attachedStickers)3964 void ApiWrap::sendUploadedPhoto(
3965 FullMsgId localId,
3966 const MTPInputFile &file,
3967 Api::SendOptions options,
3968 std::vector<MTPInputDocument> attachedStickers) {
3969 if (const auto item = _session->data().message(localId)) {
3970 const auto media = Api::PrepareUploadedPhoto(
3971 file,
3972 std::move(attachedStickers));
3973 if (const auto groupId = item->groupId()) {
3974 uploadAlbumMedia(item, groupId, media);
3975 } else {
3976 sendMedia(item, media, options);
3977 }
3978 }
3979 }
3980
sendUploadedDocument(FullMsgId localId,const MTPInputFile & file,const std::optional<MTPInputFile> & thumb,Api::SendOptions options,std::vector<MTPInputDocument> attachedStickers)3981 void ApiWrap::sendUploadedDocument(
3982 FullMsgId localId,
3983 const MTPInputFile &file,
3984 const std::optional<MTPInputFile> &thumb,
3985 Api::SendOptions options,
3986 std::vector<MTPInputDocument> attachedStickers) {
3987 if (const auto item = _session->data().message(localId)) {
3988 if (!item->media() || !item->media()->document()) {
3989 return;
3990 }
3991 const auto media = Api::PrepareUploadedDocument(
3992 item,
3993 file,
3994 thumb,
3995 std::move(attachedStickers));
3996 const auto groupId = item->groupId();
3997 if (groupId) {
3998 uploadAlbumMedia(item, groupId, media);
3999 } else {
4000 sendMedia(item, media, options);
4001 }
4002 }
4003 }
4004
cancelLocalItem(not_null<HistoryItem * > item)4005 void ApiWrap::cancelLocalItem(not_null<HistoryItem*> item) {
4006 Expects(item->isSending());
4007
4008 if (const auto groupId = item->groupId()) {
4009 sendAlbumWithCancelled(item, groupId);
4010 }
4011 }
4012
sendMessage(MessageToSend && message)4013 void ApiWrap::sendMessage(MessageToSend &&message) {
4014 const auto history = message.action.history;
4015 const auto peer = history->peer;
4016 auto &textWithTags = message.textWithTags;
4017
4018 auto action = message.action;
4019 action.generateLocal = true;
4020 sendAction(action);
4021
4022 if (!peer->canWrite() || Api::SendDice(message)) {
4023 return;
4024 }
4025 local().saveRecentSentHashtags(textWithTags.text);
4026
4027 auto sending = TextWithEntities();
4028 auto left = TextWithEntities {
4029 textWithTags.text,
4030 TextUtilities::ConvertTextTagsToEntities(textWithTags.tags)
4031 };
4032 auto prepareFlags = Ui::ItemTextOptions(
4033 history,
4034 _session->user()).flags;
4035 TextUtilities::PrepareForSending(left, prepareFlags);
4036
4037 HistoryItem *lastMessage = nullptr;
4038
4039 auto &histories = history->owner().histories();
4040 const auto requestType = Data::Histories::RequestType::Send;
4041
4042 while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
4043 auto newId = FullMsgId(
4044 peerToChannel(peer->id),
4045 _session->data().nextLocalMessageId());
4046 auto randomId = base::RandomValue<uint64>();
4047
4048 TextUtilities::Trim(sending);
4049
4050 _session->data().registerMessageRandomId(randomId, newId);
4051 _session->data().registerMessageSentData(randomId, peer->id, sending.text);
4052
4053 MTPstring msgText(MTP_string(sending.text));
4054 auto flags = NewMessageFlags(peer);
4055 auto sendFlags = MTPmessages_SendMessage::Flags(0);
4056 if (action.replyTo) {
4057 flags |= MessageFlag::HasReplyInfo;
4058 sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to_msg_id;
4059 }
4060 const auto replyHeader = NewMessageReplyHeader(action);
4061 MTPMessageMedia media = MTP_messageMediaEmpty();
4062 if (message.webPageId == CancelledWebPageId) {
4063 sendFlags |= MTPmessages_SendMessage::Flag::f_no_webpage;
4064 } else if (message.webPageId) {
4065 auto page = _session->data().webpage(message.webPageId);
4066 media = MTP_messageMediaWebPage(
4067 MTP_webPagePending(
4068 MTP_long(page->id),
4069 MTP_int(page->pendingTill)));
4070 }
4071 const auto anonymousPost = peer->amAnonymous();
4072 const auto silentPost = ShouldSendSilent(peer, action.options);
4073 FillMessagePostFlags(action, peer, flags);
4074 if (silentPost) {
4075 sendFlags |= MTPmessages_SendMessage::Flag::f_silent;
4076 }
4077 const auto sentEntities = Api::EntitiesToMTP(
4078 _session,
4079 sending.entities,
4080 Api::ConvertOption::SkipLocal);
4081 if (!sentEntities.v.isEmpty()) {
4082 sendFlags |= MTPmessages_SendMessage::Flag::f_entities;
4083 }
4084 const auto clearCloudDraft = action.clearDraft;
4085 if (clearCloudDraft) {
4086 sendFlags |= MTPmessages_SendMessage::Flag::f_clear_draft;
4087 history->clearCloudDraft();
4088 history->startSavingCloudDraft();
4089 }
4090 auto messageFromId = anonymousPost ? 0 : _session->userPeerId();
4091 auto messagePostAuthor = peer->isBroadcast()
4092 ? _session->user()->name
4093 : QString();
4094 if (action.options.scheduled) {
4095 flags |= MessageFlag::IsOrWasScheduled;
4096 sendFlags |= MTPmessages_SendMessage::Flag::f_schedule_date;
4097 }
4098 const auto viaBotId = UserId();
4099 lastMessage = history->addNewLocalMessage(
4100 newId.msg,
4101 flags,
4102 viaBotId,
4103 action.replyTo,
4104 HistoryItem::NewMessageDate(action.options.scheduled),
4105 messageFromId,
4106 messagePostAuthor,
4107 sending,
4108 media,
4109 HistoryMessageMarkupData());
4110 histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
4111 history->sendRequestId = request(MTPmessages_SendMessage(
4112 MTP_flags(sendFlags),
4113 peer->input,
4114 MTP_int(action.replyTo),
4115 msgText,
4116 MTP_long(randomId),
4117 MTPReplyMarkup(),
4118 sentEntities,
4119 MTP_int(action.options.scheduled)
4120 )).done([=](
4121 const MTPUpdates &result,
4122 const MTP::Response &response) {
4123 applyUpdates(result, randomId);
4124 if (clearCloudDraft) {
4125 history->finishSavingCloudDraft(
4126 UnixtimeFromMsgId(response.outerMsgId));
4127 }
4128 finish();
4129 }).fail([=](
4130 const MTP::Error &error,
4131 const MTP::Response &response) {
4132 if (error.type() == qstr("MESSAGE_EMPTY")) {
4133 lastMessage->destroy();
4134 } else {
4135 sendMessageFail(error, peer, randomId, newId);
4136 }
4137 if (clearCloudDraft) {
4138 history->finishSavingCloudDraft(
4139 UnixtimeFromMsgId(response.outerMsgId));
4140 }
4141 finish();
4142 }).afterRequest(history->sendRequestId
4143 ).send();
4144 return history->sendRequestId;
4145 });
4146 }
4147
4148 finishForwarding(action);
4149 }
4150
sendBotStart(not_null<UserData * > bot,PeerData * chat)4151 void ApiWrap::sendBotStart(not_null<UserData*> bot, PeerData *chat) {
4152 Expects(bot->isBot());
4153 Expects(chat == nullptr || !bot->botInfo->startGroupToken.isEmpty());
4154
4155 if (chat && chat->isChannel() && !chat->isMegagroup()) {
4156 ShowAddParticipantsError("USER_BOT", chat, { 1, bot });
4157 return;
4158 }
4159
4160 auto &info = bot->botInfo;
4161 auto &token = chat ? info->startGroupToken : info->startToken;
4162 if (token.isEmpty()) {
4163 auto message = ApiWrap::MessageToSend(_session->data().history(bot));
4164 message.textWithTags = { qsl("/start"), TextWithTags::Tags() };
4165 sendMessage(std::move(message));
4166 return;
4167 }
4168 const auto randomId = base::RandomValue<uint64>();
4169 request(MTPmessages_StartBot(
4170 bot->inputUser,
4171 chat ? chat->input : MTP_inputPeerEmpty(),
4172 MTP_long(randomId),
4173 MTP_string(base::take(token))
4174 )).done([=](const MTPUpdates &result) {
4175 applyUpdates(result);
4176 }).fail([=](const MTP::Error &error) {
4177 if (chat) {
4178 ShowAddParticipantsError(error.type(), chat, { 1, bot });
4179 }
4180 }).send();
4181 }
4182
sendInlineResult(not_null<UserData * > bot,not_null<InlineBots::Result * > data,const SendAction & action)4183 void ApiWrap::sendInlineResult(
4184 not_null<UserData*> bot,
4185 not_null<InlineBots::Result*> data,
4186 const SendAction &action) {
4187 sendAction(action);
4188
4189 const auto history = action.history;
4190 const auto peer = history->peer;
4191 const auto newId = FullMsgId(
4192 peerToChannel(peer->id),
4193 _session->data().nextLocalMessageId());
4194 const auto randomId = base::RandomValue<uint64>();
4195
4196 auto flags = NewMessageFlags(peer);
4197 auto sendFlags = MTPmessages_SendInlineBotResult::Flag::f_clear_draft | 0;
4198 if (action.replyTo) {
4199 flags |= MessageFlag::HasReplyInfo;
4200 sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_reply_to_msg_id;
4201 }
4202 const auto anonymousPost = peer->amAnonymous();
4203 const auto silentPost = ShouldSendSilent(peer, action.options);
4204 FillMessagePostFlags(action, peer, flags);
4205 if (silentPost) {
4206 sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_silent;
4207 }
4208 if (bot) {
4209 flags |= MessageFlag::HasViaBot;
4210 }
4211 if (action.options.scheduled) {
4212 flags |= MessageFlag::IsOrWasScheduled;
4213 sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_schedule_date;
4214 }
4215
4216 const auto messageFromId = anonymousPost ? 0 : _session->userPeerId();
4217 const auto messagePostAuthor = peer->isBroadcast()
4218 ? _session->user()->name
4219 : QString();
4220
4221 _session->data().registerMessageRandomId(randomId, newId);
4222
4223 data->addToHistory(
4224 history,
4225 flags,
4226 newId.msg,
4227 messageFromId,
4228 HistoryItem::NewMessageDate(action.options.scheduled),
4229 bot ? peerToUser(bot->id) : 0,
4230 action.replyTo,
4231 messagePostAuthor);
4232
4233 history->clearCloudDraft();
4234 history->startSavingCloudDraft();
4235
4236 auto &histories = history->owner().histories();
4237 const auto requestType = Data::Histories::RequestType::Send;
4238 histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
4239 history->sendRequestId = request(MTPmessages_SendInlineBotResult(
4240 MTP_flags(sendFlags),
4241 peer->input,
4242 MTP_int(action.replyTo),
4243 MTP_long(randomId),
4244 MTP_long(data->getQueryId()),
4245 MTP_string(data->getId()),
4246 MTP_int(action.options.scheduled)
4247 )).done([=](
4248 const MTPUpdates &result,
4249 const MTP::Response &response) {
4250 applyUpdates(result, randomId);
4251 history->finishSavingCloudDraft(
4252 UnixtimeFromMsgId(response.outerMsgId));
4253 finish();
4254 }).fail([=](
4255 const MTP::Error &error,
4256 const MTP::Response &response) {
4257 sendMessageFail(error, peer, randomId, newId);
4258 history->finishSavingCloudDraft(
4259 UnixtimeFromMsgId(response.outerMsgId));
4260 finish();
4261 }).afterRequest(history->sendRequestId
4262 ).send();
4263 return history->sendRequestId;
4264 });
4265 finishForwarding(action);
4266 }
4267
uploadAlbumMedia(not_null<HistoryItem * > item,const MessageGroupId & groupId,const MTPInputMedia & media)4268 void ApiWrap::uploadAlbumMedia(
4269 not_null<HistoryItem*> item,
4270 const MessageGroupId &groupId,
4271 const MTPInputMedia &media) {
4272 const auto localId = item->fullId();
4273 const auto failed = [=] {
4274
4275 };
4276 request(MTPmessages_UploadMedia(
4277 item->history()->peer->input,
4278 media
4279 )).done([=](const MTPMessageMedia &result) {
4280 const auto item = _session->data().message(localId);
4281 if (!item) {
4282 failed();
4283 return;
4284 }
4285 if (const auto media = item->media()) {
4286 if (const auto photo = media->photo()) {
4287 photo->setWaitingForAlbum();
4288 } else if (const auto document = media->document()) {
4289 document->setWaitingForAlbum();
4290 }
4291 }
4292
4293 switch (result.type()) {
4294 case mtpc_messageMediaPhoto: {
4295 const auto &data = result.c_messageMediaPhoto();
4296 const auto photo = data.vphoto();
4297 if (!photo || photo->type() != mtpc_photo) {
4298 failed();
4299 return;
4300 }
4301 const auto &fields = photo->c_photo();
4302 const auto flags = MTPDinputMediaPhoto::Flags(0)
4303 | (data.vttl_seconds()
4304 ? MTPDinputMediaPhoto::Flag::f_ttl_seconds
4305 : MTPDinputMediaPhoto::Flag(0));
4306 const auto media = MTP_inputMediaPhoto(
4307 MTP_flags(flags),
4308 MTP_inputPhoto(
4309 fields.vid(),
4310 fields.vaccess_hash(),
4311 fields.vfile_reference()),
4312 MTP_int(data.vttl_seconds().value_or_empty()));
4313 sendAlbumWithUploaded(item, groupId, media);
4314 } break;
4315
4316 case mtpc_messageMediaDocument: {
4317 const auto &data = result.c_messageMediaDocument();
4318 const auto document = data.vdocument();
4319 if (!document || document->type() != mtpc_document) {
4320 failed();
4321 return;
4322 }
4323 const auto &fields = document->c_document();
4324 const auto flags = MTPDinputMediaDocument::Flags(0)
4325 | (data.vttl_seconds()
4326 ? MTPDinputMediaDocument::Flag::f_ttl_seconds
4327 : MTPDinputMediaDocument::Flag(0));
4328 const auto media = MTP_inputMediaDocument(
4329 MTP_flags(flags),
4330 MTP_inputDocument(
4331 fields.vid(),
4332 fields.vaccess_hash(),
4333 fields.vfile_reference()),
4334 MTP_int(data.vttl_seconds().value_or_empty()),
4335 MTPstring()); // query
4336 sendAlbumWithUploaded(item, groupId, media);
4337 } break;
4338 }
4339 }).fail([=](const MTP::Error &error) {
4340 failed();
4341 }).send();
4342 }
4343
sendMedia(not_null<HistoryItem * > item,const MTPInputMedia & media,Api::SendOptions options)4344 void ApiWrap::sendMedia(
4345 not_null<HistoryItem*> item,
4346 const MTPInputMedia &media,
4347 Api::SendOptions options) {
4348 const auto randomId = base::RandomValue<uint64>();
4349 _session->data().registerMessageRandomId(randomId, item->fullId());
4350
4351 sendMediaWithRandomId(item, media, options, randomId);
4352 }
4353
sendMediaWithRandomId(not_null<HistoryItem * > item,const MTPInputMedia & media,Api::SendOptions options,uint64 randomId)4354 void ApiWrap::sendMediaWithRandomId(
4355 not_null<HistoryItem*> item,
4356 const MTPInputMedia &media,
4357 Api::SendOptions options,
4358 uint64 randomId) {
4359 const auto history = item->history();
4360 const auto replyTo = item->replyToId();
4361
4362 auto caption = item->originalText();
4363 TextUtilities::Trim(caption);
4364 auto sentEntities = Api::EntitiesToMTP(
4365 _session,
4366 caption.entities,
4367 Api::ConvertOption::SkipLocal);
4368
4369 const auto updateRecentStickers = Api::HasAttachedStickers(media);
4370
4371 const auto flags = MTPmessages_SendMedia::Flags(0)
4372 | (replyTo
4373 ? MTPmessages_SendMedia::Flag::f_reply_to_msg_id
4374 : MTPmessages_SendMedia::Flag(0))
4375 | (ShouldSendSilent(history->peer, options)
4376 ? MTPmessages_SendMedia::Flag::f_silent
4377 : MTPmessages_SendMedia::Flag(0))
4378 | (!sentEntities.v.isEmpty()
4379 ? MTPmessages_SendMedia::Flag::f_entities
4380 : MTPmessages_SendMedia::Flag(0))
4381 | (options.scheduled
4382 ? MTPmessages_SendMedia::Flag::f_schedule_date
4383 : MTPmessages_SendMedia::Flag(0));
4384
4385 auto &histories = history->owner().histories();
4386 const auto requestType = Data::Histories::RequestType::Send;
4387 histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
4388 const auto peer = history->peer;
4389 const auto itemId = item->fullId();
4390 history->sendRequestId = request(MTPmessages_SendMedia(
4391 MTP_flags(flags),
4392 peer->input,
4393 MTP_int(replyTo),
4394 media,
4395 MTP_string(caption.text),
4396 MTP_long(randomId),
4397 MTPReplyMarkup(),
4398 sentEntities,
4399 MTP_int(options.scheduled)
4400 )).done([=](const MTPUpdates &result) {
4401 applyUpdates(result);
4402 finish();
4403
4404 if (updateRecentStickers) {
4405 requestRecentStickersForce(true);
4406 }
4407 }).fail([=](const MTP::Error &error) {
4408 sendMessageFail(error, peer, randomId, itemId);
4409 finish();
4410 }).afterRequest(
4411 history->sendRequestId
4412 ).send();
4413 return history->sendRequestId;
4414 });
4415 }
4416
sendAlbumWithUploaded(not_null<HistoryItem * > item,const MessageGroupId & groupId,const MTPInputMedia & media)4417 void ApiWrap::sendAlbumWithUploaded(
4418 not_null<HistoryItem*> item,
4419 const MessageGroupId &groupId,
4420 const MTPInputMedia &media) {
4421 const auto localId = item->fullId();
4422 const auto randomId = base::RandomValue<uint64>();
4423 _session->data().registerMessageRandomId(randomId, localId);
4424
4425 const auto albumIt = _sendingAlbums.find(groupId.raw());
4426 Assert(albumIt != _sendingAlbums.end());
4427 const auto &album = albumIt->second;
4428 album->fillMedia(item, media, randomId);
4429 sendAlbumIfReady(album.get());
4430 }
4431
sendAlbumWithCancelled(not_null<HistoryItem * > item,const MessageGroupId & groupId)4432 void ApiWrap::sendAlbumWithCancelled(
4433 not_null<HistoryItem*> item,
4434 const MessageGroupId &groupId) {
4435 const auto albumIt = _sendingAlbums.find(groupId.raw());
4436 if (albumIt == _sendingAlbums.end()) {
4437 // Sometimes we destroy item being sent already after the album
4438 // was sent successfully. For example the message could be loaded
4439 // from server (by messages.getHistory or updateNewMessage) and
4440 // added to history and after that updateMessageID was received with
4441 // the same message id, in this case we destroy a detached local
4442 // item and sendAlbumWithCancelled is called for already sent album.
4443 return;
4444 }
4445 const auto &album = albumIt->second;
4446 album->removeItem(item);
4447 sendAlbumIfReady(album.get());
4448 }
4449
sendAlbumIfReady(not_null<SendingAlbum * > album)4450 void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
4451 const auto groupId = album->groupId;
4452 if (album->items.empty()) {
4453 _sendingAlbums.remove(groupId);
4454 return;
4455 }
4456 auto sample = (HistoryItem*)nullptr;
4457 auto medias = QVector<MTPInputSingleMedia>();
4458 medias.reserve(album->items.size());
4459 for (const auto &item : album->items) {
4460 if (!item.media) {
4461 return;
4462 } else if (!sample) {
4463 sample = _session->data().message(item.msgId);
4464 }
4465 medias.push_back(*item.media);
4466 }
4467 if (!sample) {
4468 _sendingAlbums.remove(groupId);
4469 return;
4470 } else if (medias.size() < 2) {
4471 const auto &single = medias.front().c_inputSingleMedia();
4472 sendMediaWithRandomId(
4473 sample,
4474 single.vmedia(),
4475 album->options,
4476 single.vrandom_id().v);
4477 _sendingAlbums.remove(groupId);
4478 return;
4479 }
4480 const auto history = sample->history();
4481 const auto replyTo = sample->replyToId();
4482 const auto flags = MTPmessages_SendMultiMedia::Flags(0)
4483 | (replyTo
4484 ? MTPmessages_SendMultiMedia::Flag::f_reply_to_msg_id
4485 : MTPmessages_SendMultiMedia::Flag(0))
4486 | (ShouldSendSilent(history->peer, album->options)
4487 ? MTPmessages_SendMultiMedia::Flag::f_silent
4488 : MTPmessages_SendMultiMedia::Flag(0))
4489 | (album->options.scheduled
4490 ? MTPmessages_SendMultiMedia::Flag::f_schedule_date
4491 : MTPmessages_SendMultiMedia::Flag(0));
4492 auto &histories = history->owner().histories();
4493 const auto requestType = Data::Histories::RequestType::Send;
4494 histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
4495 const auto peer = history->peer;
4496 history->sendRequestId = request(MTPmessages_SendMultiMedia(
4497 MTP_flags(flags),
4498 peer->input,
4499 MTP_int(replyTo),
4500 MTP_vector<MTPInputSingleMedia>(medias),
4501 MTP_int(album->options.scheduled)
4502 )).done([=](const MTPUpdates &result) {
4503 _sendingAlbums.remove(groupId);
4504 applyUpdates(result);
4505 finish();
4506 }).fail([=](const MTP::Error &error) {
4507 if (const auto album = _sendingAlbums.take(groupId)) {
4508 for (const auto &item : (*album)->items) {
4509 sendMessageFail(error, peer, item.randomId, item.msgId);
4510 }
4511 } else {
4512 sendMessageFail(error, peer);
4513 }
4514 finish();
4515 }).afterRequest(
4516 history->sendRequestId
4517 ).send();
4518 return history->sendRequestId;
4519 });
4520 }
4521
fileLoadTaskOptions(const SendAction & action) const4522 FileLoadTo ApiWrap::fileLoadTaskOptions(const SendAction &action) const {
4523 const auto peer = action.history->peer;
4524 return FileLoadTo(
4525 peer->id,
4526 action.options,
4527 action.replyTo,
4528 action.replaceMediaOf);
4529 }
4530
reloadContactSignupSilent()4531 void ApiWrap::reloadContactSignupSilent() {
4532 if (_contactSignupSilentRequestId) {
4533 return;
4534 }
4535 const auto requestId = request(MTPaccount_GetContactSignUpNotification(
4536 )).done([=](const MTPBool &result) {
4537 _contactSignupSilentRequestId = 0;
4538 const auto silent = mtpIsTrue(result);
4539 _contactSignupSilent = silent;
4540 _contactSignupSilentChanges.fire_copy(silent);
4541 }).fail([=](const MTP::Error &error) {
4542 _contactSignupSilentRequestId = 0;
4543 }).send();
4544 _contactSignupSilentRequestId = requestId;
4545 }
4546
contactSignupSilent() const4547 rpl::producer<bool> ApiWrap::contactSignupSilent() const {
4548 return _contactSignupSilent
4549 ? _contactSignupSilentChanges.events_starting_with_copy(
4550 *_contactSignupSilent)
4551 : (_contactSignupSilentChanges.events() | rpl::type_erased());
4552 }
4553
contactSignupSilentCurrent() const4554 std::optional<bool> ApiWrap::contactSignupSilentCurrent() const {
4555 return _contactSignupSilent;
4556 }
4557
saveContactSignupSilent(bool silent)4558 void ApiWrap::saveContactSignupSilent(bool silent) {
4559 request(base::take(_contactSignupSilentRequestId)).cancel();
4560
4561 const auto requestId = request(MTPaccount_SetContactSignUpNotification(
4562 MTP_bool(silent)
4563 )).done([=](const MTPBool &) {
4564 _contactSignupSilentRequestId = 0;
4565 _contactSignupSilent = silent;
4566 _contactSignupSilentChanges.fire_copy(silent);
4567 }).fail([=](const MTP::Error &error) {
4568 _contactSignupSilentRequestId = 0;
4569 }).send();
4570 _contactSignupSilentRequestId = requestId;
4571 }
4572
saveSelfBio(const QString & text)4573 void ApiWrap::saveSelfBio(const QString &text) {
4574 if (_bio.requestId) {
4575 if (text != _bio.requestedText) {
4576 request(_bio.requestId).cancel();
4577 } else {
4578 return;
4579 }
4580 }
4581 _bio.requestedText = text;
4582 _bio.requestId = request(MTPaccount_UpdateProfile(
4583 MTP_flags(MTPaccount_UpdateProfile::Flag::f_about),
4584 MTPstring(),
4585 MTPstring(),
4586 MTP_string(text)
4587 )).done([=](const MTPUser &result) {
4588 _bio.requestId = 0;
4589
4590 _session->data().processUser(result);
4591 _session->user()->setAbout(_bio.requestedText);
4592 }).fail([=](const MTP::Error &error) {
4593 _bio.requestId = 0;
4594 }).send();
4595 }
4596
authorizations()4597 Api::Authorizations &ApiWrap::authorizations() {
4598 return *_authorizations;
4599 }
4600
attachedStickers()4601 Api::AttachedStickers &ApiWrap::attachedStickers() {
4602 return *_attachedStickers;
4603 }
4604
blockedPeers()4605 Api::BlockedPeers &ApiWrap::blockedPeers() {
4606 return *_blockedPeers;
4607 }
4608
cloudPassword()4609 Api::CloudPassword &ApiWrap::cloudPassword() {
4610 return *_cloudPassword;
4611 }
4612
selfDestruct()4613 Api::SelfDestruct &ApiWrap::selfDestruct() {
4614 return *_selfDestruct;
4615 }
4616
sensitiveContent()4617 Api::SensitiveContent &ApiWrap::sensitiveContent() {
4618 return *_sensitiveContent;
4619 }
4620
globalPrivacy()4621 Api::GlobalPrivacy &ApiWrap::globalPrivacy() {
4622 return *_globalPrivacy;
4623 }
4624
userPrivacy()4625 Api::UserPrivacy &ApiWrap::userPrivacy() {
4626 return *_userPrivacy;
4627 }
4628
inviteLinks()4629 Api::InviteLinks &ApiWrap::inviteLinks() {
4630 return *_inviteLinks;
4631 }
4632
views()4633 Api::ViewsManager &ApiWrap::views() {
4634 return *_views;
4635 }
4636
confirmPhone()4637 Api::ConfirmPhone &ApiWrap::confirmPhone() {
4638 return *_confirmPhone;
4639 }
4640
peerPhoto()4641 Api::PeerPhoto &ApiWrap::peerPhoto() {
4642 return *_peerPhoto;
4643 }
4644
polls()4645 Api::Polls &ApiWrap::polls() {
4646 return *_polls;
4647 }
4648