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 "api/api_updates.h"
9
10 #include "api/api_authorizations.h"
11 #include "api/api_text_entities.h"
12 #include "api/api_user_privacy.h"
13 #include "main/main_session.h"
14 #include "main/main_account.h"
15 #include "mtproto/mtp_instance.h"
16 #include "mtproto/mtproto_config.h"
17 #include "mtproto/mtproto_dc_options.h"
18 #include "data/stickers/data_stickers.h"
19 #include "data/data_session.h"
20 #include "data/data_user.h"
21 #include "data/data_chat.h"
22 #include "data/data_changes.h"
23 #include "data/data_channel.h"
24 #include "data/data_chat_filters.h"
25 #include "data/data_cloud_themes.h"
26 #include "data/data_group_call.h"
27 #include "data/data_drafts.h"
28 #include "data/data_histories.h"
29 #include "data/data_folder.h"
30 #include "data/data_scheduled_messages.h"
31 #include "data/data_send_action.h"
32 #include "chat_helpers/emoji_interactions.h"
33 #include "lang/lang_cloud_manager.h"
34 #include "history/history.h"
35 #include "history/history_item.h"
36 #include "core/application.h"
37 #include "storage/storage_account.h"
38 #include "storage/storage_facade.h"
39 #include "storage/storage_user_photos.h"
40 #include "storage/storage_shared_media.h"
41 #include "calls/calls_instance.h"
42 #include "base/unixtime.h"
43 #include "window/window_session_controller.h"
44 #include "window/window_controller.h"
45 #include "ui/boxes/confirm_box.h"
46 #include "apiwrap.h"
47 #include "ui/text/format_values.h" // Ui::FormatPhone
48 #include "app.h" // App::quitting
49
50 namespace Api {
51 namespace {
52
53 constexpr auto kChannelGetDifferenceLimit = 100;
54
55 // 1s wait after show channel history before sending getChannelDifference.
56 constexpr auto kWaitForChannelGetDifference = crl::time(1000);
57
58 // If nothing is received in 1 min we ping.
59 constexpr auto kNoUpdatesTimeout = 60 * 1000;
60
61 // If nothing is received in 1 min when was a sleepmode we ping.
62 constexpr auto kNoUpdatesAfterSleepTimeout = 60 * crl::time(1000);
63
64 enum class DataIsLoadedResult {
65 NotLoaded = 0,
66 FromNotLoaded = 1,
67 MentionNotLoaded = 2,
68 Ok = 3,
69 };
70
ProcessScheduledMessageWithElapsedTime(not_null<Main::Session * > session,bool needToAdd,const MTPDmessage & data)71 void ProcessScheduledMessageWithElapsedTime(
72 not_null<Main::Session*> session,
73 bool needToAdd,
74 const MTPDmessage &data) {
75 if (needToAdd && !data.is_from_scheduled()) {
76 // If we still need to add a new message,
77 // we should first check if this message is in
78 // the list of scheduled messages.
79 // This is necessary to correctly update the file reference.
80 // Note that when a message is scheduled until online
81 // while the recipient is already online, the server sends
82 // an ordinary new message with skipped "from_scheduled" flag.
83 session->data().scheduledMessages().checkEntitiesAndUpdate(data);
84 }
85 }
86
IsForceLogoutNotification(const MTPDupdateServiceNotification & data)87 bool IsForceLogoutNotification(const MTPDupdateServiceNotification &data) {
88 return qs(data.vtype()).startsWith(qstr("AUTH_KEY_DROP_"));
89 }
90
HasForceLogoutNotification(const MTPUpdates & updates)91 bool HasForceLogoutNotification(const MTPUpdates &updates) {
92 const auto checkUpdate = [](const MTPUpdate &update) {
93 if (update.type() != mtpc_updateServiceNotification) {
94 return false;
95 }
96 return IsForceLogoutNotification(
97 update.c_updateServiceNotification());
98 };
99 const auto checkVector = [&](const MTPVector<MTPUpdate> &list) {
100 for (const auto &update : list.v) {
101 if (checkUpdate(update)) {
102 return true;
103 }
104 }
105 return false;
106 };
107 switch (updates.type()) {
108 case mtpc_updates:
109 return checkVector(updates.c_updates().vupdates());
110 case mtpc_updatesCombined:
111 return checkVector(updates.c_updatesCombined().vupdates());
112 case mtpc_updateShort:
113 return checkUpdate(updates.c_updateShort().vupdate());
114 }
115 return false;
116 }
117
ForwardedInfoDataLoaded(not_null<Main::Session * > session,const MTPMessageFwdHeader & header)118 bool ForwardedInfoDataLoaded(
119 not_null<Main::Session*> session,
120 const MTPMessageFwdHeader &header) {
121 return header.match([&](const MTPDmessageFwdHeader &data) {
122 if (const auto fromId = data.vfrom_id()) {
123 // Fully loaded is required in this case.
124 if (!session->data().peerLoaded(peerFromMTP(*fromId))) {
125 return false;
126 }
127 }
128 return true;
129 });
130 }
131
MentionUsersLoaded(not_null<Main::Session * > session,const MTPVector<MTPMessageEntity> & entities)132 bool MentionUsersLoaded(
133 not_null<Main::Session*> session,
134 const MTPVector<MTPMessageEntity> &entities) {
135 for (const auto &entity : entities.v) {
136 auto type = entity.type();
137 if (type == mtpc_messageEntityMentionName) {
138 if (!session->data().userLoaded(entity.c_messageEntityMentionName().vuser_id())) {
139 return false;
140 }
141 } else if (type == mtpc_inputMessageEntityMentionName) {
142 auto &inputUser = entity.c_inputMessageEntityMentionName().vuser_id();
143 if (inputUser.type() == mtpc_inputUser) {
144 if (!session->data().userLoaded(inputUser.c_inputUser().vuser_id())) {
145 return false;
146 }
147 }
148 }
149 }
150 return true;
151 }
152
AllDataLoadedForMessage(not_null<Main::Session * > session,const MTPMessage & message)153 DataIsLoadedResult AllDataLoadedForMessage(
154 not_null<Main::Session*> session,
155 const MTPMessage &message) {
156 return message.match([&](const MTPDmessage &message) {
157 if (const auto fromId = message.vfrom_id()) {
158 if (!message.is_post()
159 && !session->data().peerLoaded(peerFromMTP(*fromId))) {
160 return DataIsLoadedResult::FromNotLoaded;
161 }
162 }
163 if (const auto viaBotId = message.vvia_bot_id()) {
164 if (!session->data().userLoaded(*viaBotId)) {
165 return DataIsLoadedResult::NotLoaded;
166 }
167 }
168 if (const auto fwd = message.vfwd_from()) {
169 if (!ForwardedInfoDataLoaded(session, *fwd)) {
170 return DataIsLoadedResult::NotLoaded;
171 }
172 }
173 if (const auto entities = message.ventities()) {
174 if (!MentionUsersLoaded(session, *entities)) {
175 return DataIsLoadedResult::MentionNotLoaded;
176 }
177 }
178 return DataIsLoadedResult::Ok;
179 }, [&](const MTPDmessageService &message) {
180 if (const auto fromId = message.vfrom_id()) {
181 if (!message.is_post()
182 && !session->data().peerLoaded(peerFromMTP(*fromId))) {
183 return DataIsLoadedResult::FromNotLoaded;
184 }
185 }
186 return message.vaction().match(
187 [&](const MTPDmessageActionChatAddUser &action) {
188 for (const auto &userId : action.vusers().v) {
189 if (!session->data().userLoaded(userId)) {
190 return DataIsLoadedResult::NotLoaded;
191 }
192 }
193 return DataIsLoadedResult::Ok;
194 }, [&](const MTPDmessageActionChatJoinedByLink &action) {
195 if (!session->data().userLoaded(action.vinviter_id())) {
196 return DataIsLoadedResult::NotLoaded;
197 }
198 return DataIsLoadedResult::Ok;
199 }, [&](const MTPDmessageActionChatDeleteUser &action) {
200 if (!session->data().userLoaded(action.vuser_id())) {
201 return DataIsLoadedResult::NotLoaded;
202 }
203 return DataIsLoadedResult::Ok;
204 }, [](const auto &) {
205 return DataIsLoadedResult::Ok;
206 });
207 }, [](const MTPDmessageEmpty &message) {
208 return DataIsLoadedResult::Ok;
209 });
210 }
211
212 } // namespace
213
Updates(not_null<Main::Session * > session)214 Updates::Updates(not_null<Main::Session*> session)
215 : _session(session)
216 , _noUpdatesTimer([=] { sendPing(); })
__anon38509ee60d02null217 , _onlineTimer([=] { updateOnline(); })
218 , _ptsWaiter(this)
__anon38509ee60e02null219 , _byPtsTimer([=] { getDifferenceByPts(); })
__anon38509ee60f02null220 , _bySeqTimer([=] { getDifference(); })
__anon38509ee61002null221 , _byMinChannelTimer([=] { getDifference(); })
__anon38509ee61102null222 , _failDifferenceTimer([=] { getDifferenceAfterFail(); })
__anon38509ee61202null223 , _idleFinishTimer([=] { checkIdleFinish(); }) {
224 _ptsWaiter.setRequesting(true);
225
226 session->account().mtpUpdates(
__anon38509ee61302(const MTPUpdates &updates) 227 ) | rpl::start_with_next([=](const MTPUpdates &updates) {
228 mtpUpdateReceived(updates);
229 }, _lifetime);
230
231 session->account().mtpNewSessionCreated(
__anon38509ee61402null232 ) | rpl::start_with_next([=] {
233 mtpNewSessionCreated();
234 }, _lifetime);
235
236 api().request(MTPupdates_GetState(
__anon38509ee61502(const MTPupdates_State &result) 237 )).done([=](const MTPupdates_State &result) {
238 stateDone(result);
239 }).send();
240
241 using namespace rpl::mappers;
242 session->changes().peerUpdates(
243 Data::PeerUpdate::Flag::FullInfo
__anon38509ee61602(const Data::PeerUpdate &update) 244 ) | rpl::filter([](const Data::PeerUpdate &update) {
245 return update.peer->isChat() || update.peer->isMegagroup();
246 }) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
247 const auto peer = update.peer;
248 if (const auto list = _pendingSpeakingCallParticipants.take(peer)) {
249 if (const auto call = peer->groupCall()) {
250 for (const auto &[participantPeerId, when] : *list) {
251 call->applyActiveUpdate(
252 participantPeerId,
253 Data::LastSpokeTimes{
254 .anything = when,
255 .voice = when
256 },
257 peer->owner().peerLoaded(participantPeerId));
258 }
259 }
260 }
261 }, _lifetime);
262 }
263
session() const264 Main::Session &Updates::session() const {
265 return *_session;
266 }
267
api() const268 ApiWrap &Updates::api() const {
269 return _session->api();
270 }
271
checkLastUpdate(bool afterSleep)272 void Updates::checkLastUpdate(bool afterSleep) {
273 const auto now = crl::now();
274 const auto skip = afterSleep
275 ? kNoUpdatesAfterSleepTimeout
276 : kNoUpdatesTimeout;
277 if (_lastUpdateTime && now > _lastUpdateTime + skip) {
278 _lastUpdateTime = now;
279 sendPing();
280 }
281 }
282
feedUpdateVector(const MTPVector<MTPUpdate> & updates,SkipUpdatePolicy policy)283 void Updates::feedUpdateVector(
284 const MTPVector<MTPUpdate> &updates,
285 SkipUpdatePolicy policy) {
286 auto list = updates.v;
287 const auto hasGroupCallParticipantUpdates = ranges::contains(
288 list,
289 mtpc_updateGroupCallParticipants,
290 &MTPUpdate::type);
291 if (hasGroupCallParticipantUpdates) {
292 ranges::stable_sort(list, std::less<>(), [](const MTPUpdate &entry) {
293 if (entry.type() == mtpc_updateGroupCallParticipants) {
294 return 0;
295 } else {
296 return 1;
297 }
298 });
299 } else if (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants) {
300 return;
301 }
302 for (const auto &entry : std::as_const(list)) {
303 const auto type = entry.type();
304 if ((policy == SkipUpdatePolicy::SkipMessageIds
305 && type == mtpc_updateMessageID)
306 || (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants
307 && type != mtpc_updateGroupCallParticipants)) {
308 continue;
309 }
310 feedUpdate(entry);
311 }
312 session().data().sendHistoryChangeNotifications();
313 }
314
feedMessageIds(const MTPVector<MTPUpdate> & updates)315 void Updates::feedMessageIds(const MTPVector<MTPUpdate> &updates) {
316 for (const auto &update : updates.v) {
317 if (update.type() == mtpc_updateMessageID) {
318 feedUpdate(update);
319 }
320 }
321 }
322
setState(int32 pts,int32 date,int32 qts,int32 seq)323 void Updates::setState(int32 pts, int32 date, int32 qts, int32 seq) {
324 if (pts) {
325 _ptsWaiter.init(pts);
326 }
327 if (_updatesDate < date && !_byMinChannelTimer.isActive()) {
328 _updatesDate = date;
329 }
330 if (qts && _updatesQts < qts) {
331 _updatesQts = qts;
332 }
333 if (seq && seq != _updatesSeq) {
334 _updatesSeq = seq;
335 if (_bySeqTimer.isActive()) {
336 _bySeqTimer.cancel();
337 }
338 while (!_bySeqUpdates.empty()) {
339 const auto s = _bySeqUpdates.front().first;
340 if (s <= seq + 1) {
341 const auto v = _bySeqUpdates.front().second;
342 _bySeqUpdates.erase(_bySeqUpdates.begin());
343 if (s == seq + 1) {
344 return applyUpdates(v);
345 }
346 } else {
347 if (!_bySeqTimer.isActive()) {
348 _bySeqTimer.callOnce(PtsWaiter::kWaitForSkippedTimeout);
349 }
350 break;
351 }
352 }
353 }
354 }
355
channelDifferenceDone(not_null<ChannelData * > channel,const MTPupdates_ChannelDifference & difference)356 void Updates::channelDifferenceDone(
357 not_null<ChannelData*> channel,
358 const MTPupdates_ChannelDifference &difference) {
359 _channelFailDifferenceTimeout.remove(channel);
360
361 const auto timeout = difference.match([&](const auto &data) {
362 return data.vtimeout().value_or_empty();
363 });
364 const auto isFinal = difference.match([&](const auto &data) {
365 return data.is_final();
366 });
367 difference.match([&](const MTPDupdates_channelDifferenceEmpty &data) {
368 channel->ptsInit(data.vpts().v);
369 }, [&](const MTPDupdates_channelDifferenceTooLong &data) {
370 session().data().processUsers(data.vusers());
371 session().data().processChats(data.vchats());
372 const auto history = session().data().historyLoaded(channel->id);
373 if (history) {
374 history->setNotLoadedAtBottom();
375 requestChannelRangeDifference(history);
376 }
377 data.vdialog().match([&](const MTPDdialog &data) {
378 if (const auto pts = data.vpts()) {
379 channel->ptsInit(pts->v);
380 }
381 }, [&](const MTPDdialogFolder &) {
382 });
383 session().data().applyDialogs(
384 nullptr,
385 data.vmessages().v,
386 QVector<MTPDialog>(1, data.vdialog()));
387 session().data().channelDifferenceTooLong(channel);
388 }, [&](const MTPDupdates_channelDifference &data) {
389 feedChannelDifference(data);
390 channel->ptsInit(data.vpts().v);
391 });
392
393 channel->ptsSetRequesting(false);
394
395 if (!isFinal) {
396 MTP_LOG(0, ("getChannelDifference "
397 "{ good - after not final channelDifference was received }%1"
398 ).arg(_session->mtp().isTestMode() ? " TESTMODE" : ""));
399 getChannelDifference(channel);
400 } else if (ranges::contains(
401 _activeChats,
402 channel,
403 [](const auto &pair) { return pair.second.peer; })) {
404 channel->ptsWaitingForShortPoll(timeout
405 ? (timeout * crl::time(1000))
406 : kWaitForChannelGetDifference);
407 }
408 }
409
feedChannelDifference(const MTPDupdates_channelDifference & data)410 void Updates::feedChannelDifference(
411 const MTPDupdates_channelDifference &data) {
412 session().data().processUsers(data.vusers());
413 session().data().processChats(data.vchats());
414
415 _handlingChannelDifference = true;
416 feedMessageIds(data.vother_updates());
417 session().data().processMessages(
418 data.vnew_messages(),
419 NewMessageType::Unread);
420 feedUpdateVector(
421 data.vother_updates(),
422 SkipUpdatePolicy::SkipMessageIds);
423 _handlingChannelDifference = false;
424 }
425
channelDifferenceFail(not_null<ChannelData * > channel,const MTP::Error & error)426 void Updates::channelDifferenceFail(
427 not_null<ChannelData*> channel,
428 const MTP::Error &error) {
429 LOG(("RPC Error in getChannelDifference: %1 %2: %3").arg(
430 QString::number(error.code()),
431 error.type(),
432 error.description()));
433 failDifferenceStartTimerFor(channel);
434 }
435
stateDone(const MTPupdates_State & state)436 void Updates::stateDone(const MTPupdates_State &state) {
437 const auto &d = state.c_updates_state();
438 setState(d.vpts().v, d.vdate().v, d.vqts().v, d.vseq().v);
439
440 _lastUpdateTime = crl::now();
441 _noUpdatesTimer.callOnce(kNoUpdatesTimeout);
442 _ptsWaiter.setRequesting(false);
443
444 session().api().requestDialogs();
445 updateOnline();
446 }
447
differenceDone(const MTPupdates_Difference & result)448 void Updates::differenceDone(const MTPupdates_Difference &result) {
449 _failDifferenceTimeout = 1;
450
451 switch (result.type()) {
452 case mtpc_updates_differenceEmpty: {
453 auto &d = result.c_updates_differenceEmpty();
454 setState(_ptsWaiter.current(), d.vdate().v, _updatesQts, d.vseq().v);
455
456 _lastUpdateTime = crl::now();
457 _noUpdatesTimer.callOnce(kNoUpdatesTimeout);
458
459 _ptsWaiter.setRequesting(false);
460 } break;
461 case mtpc_updates_differenceSlice: {
462 auto &d = result.c_updates_differenceSlice();
463 feedDifference(d.vusers(), d.vchats(), d.vnew_messages(), d.vother_updates());
464
465 auto &s = d.vintermediate_state().c_updates_state();
466 setState(s.vpts().v, s.vdate().v, s.vqts().v, s.vseq().v);
467
468 _ptsWaiter.setRequesting(false);
469
470 MTP_LOG(0, ("getDifference "
471 "{ good - after a slice of difference was received }%1"
472 ).arg(_session->mtp().isTestMode() ? " TESTMODE" : ""));
473 getDifference();
474 } break;
475 case mtpc_updates_difference: {
476 auto &d = result.c_updates_difference();
477 feedDifference(d.vusers(), d.vchats(), d.vnew_messages(), d.vother_updates());
478
479 stateDone(d.vstate());
480 } break;
481 case mtpc_updates_differenceTooLong: {
482 LOG(("API Error: updates.differenceTooLong is not supported by Telegram Desktop!"));
483 } break;
484 };
485 }
486
whenGetDiffChanged(ChannelData * channel,int32 ms,base::flat_map<not_null<ChannelData * >,crl::time> & whenMap,crl::time & curTime)487 bool Updates::whenGetDiffChanged(
488 ChannelData *channel,
489 int32 ms,
490 base::flat_map<not_null<ChannelData*>, crl::time> &whenMap,
491 crl::time &curTime) {
492 if (channel) {
493 if (ms <= 0) {
494 const auto i = whenMap.find(channel);
495 if (i != whenMap.cend()) {
496 whenMap.erase(i);
497 } else {
498 return false;
499 }
500 } else {
501 auto when = crl::now() + ms;
502 const auto i = whenMap.find(channel);
503 if (i != whenMap.cend()) {
504 if (i->second > when) {
505 i->second = when;
506 } else {
507 return false;
508 }
509 } else {
510 whenMap.emplace(channel, when);
511 }
512 }
513 } else {
514 if (ms <= 0) {
515 if (curTime) {
516 curTime = 0;
517 } else {
518 return false;
519 }
520 } else {
521 auto when = crl::now() + ms;
522 if (!curTime || curTime > when) {
523 curTime = when;
524 } else {
525 return false;
526 }
527 }
528 }
529 return true;
530 }
531
ptsWaiterStartTimerFor(ChannelData * channel,crl::time ms)532 void Updates::ptsWaiterStartTimerFor(ChannelData *channel, crl::time ms) {
533 if (whenGetDiffChanged(channel, ms, _whenGetDiffByPts, _getDifferenceTimeByPts)) {
534 getDifferenceByPts();
535 }
536 }
537
failDifferenceStartTimerFor(ChannelData * channel)538 void Updates::failDifferenceStartTimerFor(ChannelData *channel) {
539 auto &timeout = [&]() -> crl::time & {
540 if (!channel) {
541 return _failDifferenceTimeout;
542 }
543 const auto i = _channelFailDifferenceTimeout.find(channel);
544 return (i == _channelFailDifferenceTimeout.end())
545 ? _channelFailDifferenceTimeout.emplace(channel, 1).first->second
546 : i->second;
547 }();
548 if (whenGetDiffChanged(channel, timeout * 1000, _whenGetDiffAfterFail, _getDifferenceTimeAfterFail)) {
549 getDifferenceAfterFail();
550 }
551 if (timeout < 64) timeout *= 2;
552 }
553
updateAndApply(int32 pts,int32 ptsCount,const MTPUpdates & updates)554 bool Updates::updateAndApply(
555 int32 pts,
556 int32 ptsCount,
557 const MTPUpdates &updates) {
558 return _ptsWaiter.updateAndApply(nullptr, pts, ptsCount, updates);
559 }
560
updateAndApply(int32 pts,int32 ptsCount,const MTPUpdate & update)561 bool Updates::updateAndApply(
562 int32 pts,
563 int32 ptsCount,
564 const MTPUpdate &update) {
565 return _ptsWaiter.updateAndApply(nullptr, pts, ptsCount, update);
566 }
567
updateAndApply(int32 pts,int32 ptsCount)568 bool Updates::updateAndApply(int32 pts, int32 ptsCount) {
569 return _ptsWaiter.updateAndApply(nullptr, pts, ptsCount);
570 }
571
feedDifference(const MTPVector<MTPUser> & users,const MTPVector<MTPChat> & chats,const MTPVector<MTPMessage> & msgs,const MTPVector<MTPUpdate> & other)572 void Updates::feedDifference(
573 const MTPVector<MTPUser> &users,
574 const MTPVector<MTPChat> &chats,
575 const MTPVector<MTPMessage> &msgs,
576 const MTPVector<MTPUpdate> &other) {
577 Core::App().checkAutoLock();
578 session().data().processUsers(users);
579 session().data().processChats(chats);
580 feedMessageIds(other);
581 session().data().processMessages(msgs, NewMessageType::Unread);
582 feedUpdateVector(other, SkipUpdatePolicy::SkipMessageIds);
583 }
584
differenceFail(const MTP::Error & error)585 void Updates::differenceFail(const MTP::Error &error) {
586 LOG(("RPC Error in getDifference: %1 %2: %3").arg(
587 QString::number(error.code()),
588 error.type(),
589 error.description()));
590 failDifferenceStartTimerFor(nullptr);
591 }
592
getDifferenceByPts()593 void Updates::getDifferenceByPts() {
594 auto now = crl::now(), wait = crl::time(0);
595 if (_getDifferenceTimeByPts) {
596 if (_getDifferenceTimeByPts > now) {
597 wait = _getDifferenceTimeByPts - now;
598 } else {
599 getDifference();
600 }
601 }
602 for (auto i = _whenGetDiffByPts.begin(); i != _whenGetDiffByPts.cend();) {
603 if (i->second > now) {
604 wait = wait ? std::min(wait, i->second - now) : (i->second - now);
605 ++i;
606 } else {
607 getChannelDifference(
608 i->first,
609 ChannelDifferenceRequest::PtsGapOrShortPoll);
610 i = _whenGetDiffByPts.erase(i);
611 }
612 }
613 if (wait) {
614 _byPtsTimer.callOnce(wait);
615 } else {
616 _byPtsTimer.cancel();
617 }
618 }
619
getDifferenceAfterFail()620 void Updates::getDifferenceAfterFail() {
621 auto now = crl::now(), wait = crl::time(0);
622 if (_getDifferenceTimeAfterFail) {
623 if (_getDifferenceTimeAfterFail > now) {
624 wait = _getDifferenceTimeAfterFail - now;
625 } else {
626 _ptsWaiter.setRequesting(false);
627 MTP_LOG(0, ("getDifference "
628 "{ force - after get difference failed }%1"
629 ).arg(_session->mtp().isTestMode() ? " TESTMODE" : ""));
630 getDifference();
631 }
632 }
633 for (auto i = _whenGetDiffAfterFail.begin(); i != _whenGetDiffAfterFail.cend();) {
634 if (i->second > now) {
635 wait = wait ? std::min(wait, i->second - now) : (i->second - now);
636 ++i;
637 } else {
638 getChannelDifference(i->first, ChannelDifferenceRequest::AfterFail);
639 i = _whenGetDiffAfterFail.erase(i);
640 }
641 }
642 if (wait) {
643 _failDifferenceTimer.callOnce(wait);
644 } else {
645 _failDifferenceTimer.cancel();
646 }
647 }
648
getDifference()649 void Updates::getDifference() {
650 _getDifferenceTimeByPts = 0;
651
652 if (requestingDifference()) {
653 return;
654 }
655
656 _bySeqUpdates.clear();
657 _bySeqTimer.cancel();
658
659 _noUpdatesTimer.cancel();
660 _getDifferenceTimeAfterFail = 0;
661
662 _ptsWaiter.setRequesting(true);
663
664 api().request(MTPupdates_GetDifference(
665 MTP_flags(0),
666 MTP_int(_ptsWaiter.current()),
667 MTPint(),
668 MTP_int(_updatesDate),
669 MTP_int(_updatesQts)
670 )).done([=](const MTPupdates_Difference &result) {
671 differenceDone(result);
672 }).fail([=](const MTP::Error &error) {
673 differenceFail(error);
674 }).send();
675 }
676
getChannelDifference(not_null<ChannelData * > channel,ChannelDifferenceRequest from)677 void Updates::getChannelDifference(
678 not_null<ChannelData*> channel,
679 ChannelDifferenceRequest from) {
680 if (from != ChannelDifferenceRequest::PtsGapOrShortPoll) {
681 _whenGetDiffByPts.remove(channel);
682 }
683
684 if (!channel->ptsInited() || channel->ptsRequesting()) return;
685
686 if (from != ChannelDifferenceRequest::AfterFail) {
687 _whenGetDiffAfterFail.remove(channel);
688 }
689
690 channel->ptsSetRequesting(true);
691
692 auto filter = MTP_channelMessagesFilterEmpty();
693 auto flags = MTPupdates_GetChannelDifference::Flag::f_force | 0;
694 if (from != ChannelDifferenceRequest::PtsGapOrShortPoll) {
695 if (!channel->ptsWaitingForSkipped()) {
696 flags = 0; // No force flag when requesting for short poll.
697 }
698 }
699 api().request(MTPupdates_GetChannelDifference(
700 MTP_flags(flags),
701 channel->inputChannel,
702 filter,
703 MTP_int(channel->pts()),
704 MTP_int(kChannelGetDifferenceLimit)
705 )).done([=](const MTPupdates_ChannelDifference &result) {
706 channelDifferenceDone(channel, result);
707 }).fail([=](const MTP::Error &error) {
708 channelDifferenceFail(channel, error);
709 }).send();
710 }
711
sendPing()712 void Updates::sendPing() {
713 _session->mtp().ping();
714 }
715
addActiveChat(rpl::producer<PeerData * > chat)716 void Updates::addActiveChat(rpl::producer<PeerData*> chat) {
717 const auto key = _activeChats.empty() ? 0 : _activeChats.back().first + 1;
718 std::move(
719 chat
720 ) | rpl::start_with_next_done([=](PeerData *peer) {
721 _activeChats[key].peer = peer;
722 if (const auto channel = peer ? peer->asChannel() : nullptr) {
723 channel->ptsWaitingForShortPoll(
724 kWaitForChannelGetDifference);
725 }
726 }, [=] {
727 _activeChats.erase(key);
728 }, _activeChats[key].lifetime);
729 }
730
requestChannelRangeDifference(not_null<History * > history)731 void Updates::requestChannelRangeDifference(not_null<History*> history) {
732 Expects(history->isChannel());
733
734 const auto channel = history->peer->asChannel();
735 if (const auto requestId = _rangeDifferenceRequests.take(channel)) {
736 api().request(*requestId).cancel();
737 }
738 const auto range = history->rangeForDifferenceRequest();
739 if (!(range.from < range.till) || !channel->pts()) {
740 return;
741 }
742
743 MTP_LOG(0, ("getChannelDifference "
744 "{ good - after channelDifferenceTooLong was received, "
745 "validating history part }%1"
746 ).arg(_session->mtp().isTestMode() ? " TESTMODE" : ""));
747 channelRangeDifferenceSend(channel, range, channel->pts());
748 }
749
channelRangeDifferenceSend(not_null<ChannelData * > channel,MsgRange range,int32 pts)750 void Updates::channelRangeDifferenceSend(
751 not_null<ChannelData*> channel,
752 MsgRange range,
753 int32 pts) {
754 Expects(range.from < range.till);
755
756 const auto limit = range.till - range.from;
757 const auto filter = MTP_channelMessagesFilter(
758 MTP_flags(0),
759 MTP_vector<MTPMessageRange>(1, MTP_messageRange(
760 MTP_int(range.from),
761 MTP_int(range.till - 1))));
762 const auto requestId = api().request(MTPupdates_GetChannelDifference(
763 MTP_flags(MTPupdates_GetChannelDifference::Flag::f_force),
764 channel->inputChannel,
765 filter,
766 MTP_int(pts),
767 MTP_int(limit)
768 )).done([=](const MTPupdates_ChannelDifference &result) {
769 _rangeDifferenceRequests.remove(channel);
770 channelRangeDifferenceDone(channel, range, result);
771 }).fail([=](const MTP::Error &error) {
772 _rangeDifferenceRequests.remove(channel);
773 }).send();
774 _rangeDifferenceRequests.emplace(channel, requestId);
775 }
776
channelRangeDifferenceDone(not_null<ChannelData * > channel,MsgRange range,const MTPupdates_ChannelDifference & result)777 void Updates::channelRangeDifferenceDone(
778 not_null<ChannelData*> channel,
779 MsgRange range,
780 const MTPupdates_ChannelDifference &result) {
781 auto nextRequestPts = int32(0);
782 auto isFinal = true;
783
784 switch (result.type()) {
785 case mtpc_updates_channelDifferenceEmpty: {
786 const auto &d = result.c_updates_channelDifferenceEmpty();
787 nextRequestPts = d.vpts().v;
788 isFinal = d.is_final();
789 } break;
790
791 case mtpc_updates_channelDifferenceTooLong: {
792 const auto &d = result.c_updates_channelDifferenceTooLong();
793
794 _session->data().processUsers(d.vusers());
795 _session->data().processChats(d.vchats());
796
797 nextRequestPts = d.vdialog().match([&](const MTPDdialog &data) {
798 return data.vpts().value_or_empty();
799 }, [&](const MTPDdialogFolder &data) {
800 return 0;
801 });
802 isFinal = d.is_final();
803 } break;
804
805 case mtpc_updates_channelDifference: {
806 const auto &d = result.c_updates_channelDifference();
807
808 feedChannelDifference(d);
809
810 nextRequestPts = d.vpts().v;
811 isFinal = d.is_final();
812 } break;
813 }
814
815 if (!isFinal && nextRequestPts) {
816 MTP_LOG(0, ("getChannelDifference "
817 "{ good - after not final channelDifference was received, "
818 "validating history part }%1"
819 ).arg(_session->mtp().isTestMode() ? " TESTMODE" : ""));
820 channelRangeDifferenceSend(channel, range, nextRequestPts);
821 }
822 }
823
mtpNewSessionCreated()824 void Updates::mtpNewSessionCreated() {
825 Core::App().checkAutoLock();
826 _updatesSeq = 0;
827 MTP_LOG(0, ("getDifference { after new_session_created }%1"
828 ).arg(_session->mtp().isTestMode() ? " TESTMODE" : ""));
829 getDifference();
830 }
831
mtpUpdateReceived(const MTPUpdates & updates)832 void Updates::mtpUpdateReceived(const MTPUpdates &updates) {
833 Core::App().checkAutoLock();
834 _lastUpdateTime = crl::now();
835 _noUpdatesTimer.callOnce(kNoUpdatesTimeout);
836 if (!requestingDifference()
837 || HasForceLogoutNotification(updates)) {
838 applyUpdates(updates);
839 } else {
840 applyGroupCallParticipantUpdates(updates);
841 }
842 }
843
applyGroupCallParticipantUpdates(const MTPUpdates & updates)844 void Updates::applyGroupCallParticipantUpdates(const MTPUpdates &updates) {
845 updates.match([&](const MTPDupdates &data) {
846 session().data().processUsers(data.vusers());
847 session().data().processChats(data.vchats());
848 feedUpdateVector(
849 data.vupdates(),
850 SkipUpdatePolicy::SkipExceptGroupCallParticipants);
851 }, [&](const MTPDupdatesCombined &data) {
852 session().data().processUsers(data.vusers());
853 session().data().processChats(data.vchats());
854 feedUpdateVector(
855 data.vupdates(),
856 SkipUpdatePolicy::SkipExceptGroupCallParticipants);
857 }, [&](const MTPDupdateShort &data) {
858 if (data.vupdate().type() == mtpc_updateGroupCallParticipants) {
859 feedUpdate(data.vupdate());
860 }
861 }, [](const auto &) {
862 });
863 }
864
pts() const865 int32 Updates::pts() const {
866 return _ptsWaiter.current();
867 }
868
updateOnline(crl::time lastNonIdleTime)869 void Updates::updateOnline(crl::time lastNonIdleTime) {
870 updateOnline(lastNonIdleTime, false);
871 }
872
isIdle() const873 bool Updates::isIdle() const {
874 return _isIdle.current();
875 }
876
isIdleValue() const877 rpl::producer<bool> Updates::isIdleValue() const {
878 return _isIdle.value();
879 }
880
updateOnline(crl::time lastNonIdleTime,bool gotOtherOffline)881 void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
882 if (!lastNonIdleTime) {
883 lastNonIdleTime = Core::App().lastNonIdleTime();
884 }
885 crl::on_main(&session(), [=] {
886 Core::App().checkAutoLock(lastNonIdleTime);
887 });
888
889 const auto &config = _session->serverConfig();
890 bool isOnline = Core::App().hasActiveWindow(&session());
891 int updateIn = config.onlineUpdatePeriod;
892 Assert(updateIn >= 0);
893 if (isOnline) {
894 const auto idle = crl::now() - lastNonIdleTime;
895 if (idle >= config.offlineIdleTimeout) {
896 isOnline = false;
897 if (!isIdle()) {
898 _isIdle = true;
899 _idleFinishTimer.callOnce(900);
900 }
901 } else {
902 updateIn = qMin(updateIn, int(config.offlineIdleTimeout - idle));
903 Assert(updateIn >= 0);
904 }
905 }
906 auto ms = crl::now();
907 if (isOnline != _lastWasOnline
908 || (isOnline && _lastSetOnline + config.onlineUpdatePeriod <= ms)
909 || (isOnline && gotOtherOffline)) {
910 api().request(base::take(_onlineRequest)).cancel();
911
912 _lastWasOnline = isOnline;
913 _lastSetOnline = ms;
914 if (!App::quitting()) {
915 _onlineRequest = api().request(MTPaccount_UpdateStatus(
916 MTP_bool(!isOnline)
917 )).send();
918 } else {
919 _onlineRequest = api().request(MTPaccount_UpdateStatus(
920 MTP_bool(!isOnline)
921 )).done([=](const MTPBool &result) {
922 Core::App().quitPreventFinished();
923 }).fail([=](const MTP::Error &error) {
924 Core::App().quitPreventFinished();
925 }).send();
926 }
927
928 const auto self = session().user();
929 self->onlineTill = base::unixtime::now() + (isOnline ? (config.onlineUpdatePeriod / 1000) : -1);
930 session().changes().peerUpdated(
931 self,
932 Data::PeerUpdate::Flag::OnlineStatus);
933 if (!isOnline) { // Went offline, so we need to save message draft to the cloud.
934 api().saveCurrentDraftToCloud();
935 }
936
937 _lastSetOnline = ms;
938 } else if (isOnline) {
939 updateIn = qMin(updateIn, int(_lastSetOnline + config.onlineUpdatePeriod - ms));
940 Assert(updateIn >= 0);
941 }
942 _onlineTimer.callOnce(updateIn);
943 }
944
checkIdleFinish(crl::time lastNonIdleTime)945 void Updates::checkIdleFinish(crl::time lastNonIdleTime) {
946 if (!lastNonIdleTime) {
947 lastNonIdleTime = Core::App().lastNonIdleTime();
948 }
949 if (crl::now() - lastNonIdleTime
950 < _session->serverConfig().offlineIdleTimeout) {
951 updateOnline(lastNonIdleTime);
952 _idleFinishTimer.cancel();
953 _isIdle = false;
954 } else {
955 _idleFinishTimer.callOnce(900);
956 }
957 }
958
lastWasOnline() const959 bool Updates::lastWasOnline() const {
960 return _lastWasOnline;
961 }
962
lastSetOnline() const963 crl::time Updates::lastSetOnline() const {
964 return _lastSetOnline;
965 }
966
isQuitPrevent()967 bool Updates::isQuitPrevent() {
968 if (!_lastWasOnline) {
969 return false;
970 }
971 LOG(("Api::Updates prevents quit, sending offline status..."));
972 updateOnline(crl::now());
973 return true;
974 }
975
handleSendActionUpdate(PeerId peerId,MsgId rootId,PeerId fromId,const MTPSendMessageAction & action)976 void Updates::handleSendActionUpdate(
977 PeerId peerId,
978 MsgId rootId,
979 PeerId fromId,
980 const MTPSendMessageAction &action) {
981 const auto history = session().data().historyLoaded(peerId);
982 if (!history) {
983 return;
984 }
985 const auto peer = history->peer;
986 const auto from = (fromId == session().userPeerId())
987 ? session().user().get()
988 : session().data().peerLoaded(fromId);
989 if (action.type() == mtpc_speakingInGroupCallAction) {
990 handleSpeakingInCall(peer, fromId, from);
991 }
992 if (!from || !from->isUser() || from->isSelf()) {
993 return;
994 } else if (action.type() == mtpc_sendMessageEmojiInteraction) {
995 handleEmojiInteraction(peer, action.c_sendMessageEmojiInteraction());
996 return;
997 } else if (action.type() == mtpc_sendMessageEmojiInteractionSeen) {
998 const auto &data = action.c_sendMessageEmojiInteractionSeen();
999 handleEmojiInteraction(peer, qs(data.vemoticon()));
1000 return;
1001 }
1002 const auto when = requestingDifference()
1003 ? 0
1004 : base::unixtime::now();
1005 session().data().sendActionManager().registerFor(
1006 history,
1007 rootId,
1008 from->asUser(),
1009 action,
1010 when);
1011 }
1012
handleEmojiInteraction(not_null<PeerData * > peer,const MTPDsendMessageEmojiInteraction & data)1013 void Updates::handleEmojiInteraction(
1014 not_null<PeerData*> peer,
1015 const MTPDsendMessageEmojiInteraction &data) {
1016 const auto json = data.vinteraction().match([&](
1017 const MTPDdataJSON &data) {
1018 return data.vdata().v;
1019 });
1020 handleEmojiInteraction(
1021 peer,
1022 data.vmsg_id().v,
1023 qs(data.vemoticon()),
1024 ChatHelpers::EmojiInteractions::Parse(json));
1025 }
1026
handleSpeakingInCall(not_null<PeerData * > peer,PeerId participantPeerId,PeerData * participantPeerLoaded)1027 void Updates::handleSpeakingInCall(
1028 not_null<PeerData*> peer,
1029 PeerId participantPeerId,
1030 PeerData *participantPeerLoaded) {
1031 if (!peer->isChat() && !peer->isChannel()) {
1032 return;
1033 }
1034 const auto call = peer->groupCall();
1035 const auto now = crl::now();
1036 if (call) {
1037 call->applyActiveUpdate(
1038 participantPeerId,
1039 Data::LastSpokeTimes{ .anything = now, .voice = now },
1040 participantPeerLoaded);
1041 } else {
1042 const auto chat = peer->asChat();
1043 const auto channel = peer->asChannel();
1044 const auto active = chat
1045 ? (chat->flags() & ChatDataFlag::CallActive)
1046 : (channel->flags() & ChannelDataFlag::CallActive);
1047 if (active) {
1048 _pendingSpeakingCallParticipants.emplace(
1049 peer).first->second[participantPeerId] = now;
1050 if (peerIsUser(participantPeerId)) {
1051 session().api().requestFullPeer(peer);
1052 }
1053 }
1054 }
1055 }
1056
handleEmojiInteraction(not_null<PeerData * > peer,MsgId messageId,const QString & emoticon,ChatHelpers::EmojiInteractionsBunch bunch)1057 void Updates::handleEmojiInteraction(
1058 not_null<PeerData*> peer,
1059 MsgId messageId,
1060 const QString &emoticon,
1061 ChatHelpers::EmojiInteractionsBunch bunch) {
1062 if (session().windows().empty()) {
1063 return;
1064 }
1065 const auto window = session().windows().front();
1066 window->emojiInteractions().startIncoming(
1067 peer,
1068 messageId,
1069 emoticon,
1070 std::move(bunch));
1071 }
1072
handleEmojiInteraction(not_null<PeerData * > peer,const QString & emoticon)1073 void Updates::handleEmojiInteraction(
1074 not_null<PeerData*> peer,
1075 const QString &emoticon) {
1076 if (session().windows().empty()) {
1077 return;
1078 }
1079 const auto window = session().windows().front();
1080 window->emojiInteractions().seenOutgoing(peer, emoticon);
1081 }
1082
applyUpdatesNoPtsCheck(const MTPUpdates & updates)1083 void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
1084 switch (updates.type()) {
1085 case mtpc_updateShortMessage: {
1086 const auto &d = updates.c_updateShortMessage();
1087 const auto flags = mtpCastFlags(d.vflags().v)
1088 | MTPDmessage::Flag::f_from_id;
1089 _session->data().addNewMessage(
1090 MTP_message(
1091 MTP_flags(flags),
1092 d.vid(),
1093 (d.is_out()
1094 ? peerToMTP(_session->userPeerId())
1095 : MTP_peerUser(d.vuser_id())),
1096 MTP_peerUser(d.vuser_id()),
1097 d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(),
1098 MTP_long(d.vvia_bot_id().value_or_empty()),
1099 d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(),
1100 d.vdate(),
1101 d.vmessage(),
1102 MTP_messageMediaEmpty(),
1103 MTPReplyMarkup(),
1104 MTP_vector<MTPMessageEntity>(d.ventities().value_or_empty()),
1105 MTPint(), // views
1106 MTPint(), // forwards
1107 MTPMessageReplies(),
1108 MTPint(), // edit_date
1109 MTPstring(),
1110 MTPlong(),
1111 //MTPMessageReactions(),
1112 MTPVector<MTPRestrictionReason>(),
1113 MTP_int(d.vttl_period().value_or_empty())),
1114 MessageFlags(),
1115 NewMessageType::Unread);
1116 } break;
1117
1118 case mtpc_updateShortChatMessage: {
1119 const auto &d = updates.c_updateShortChatMessage();
1120 const auto flags = mtpCastFlags(d.vflags().v)
1121 | MTPDmessage::Flag::f_from_id;
1122 _session->data().addNewMessage(
1123 MTP_message(
1124 MTP_flags(flags),
1125 d.vid(),
1126 MTP_peerUser(d.vfrom_id()),
1127 MTP_peerChat(d.vchat_id()),
1128 d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(),
1129 MTP_long(d.vvia_bot_id().value_or_empty()),
1130 d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(),
1131 d.vdate(),
1132 d.vmessage(),
1133 MTP_messageMediaEmpty(),
1134 MTPReplyMarkup(),
1135 MTP_vector<MTPMessageEntity>(d.ventities().value_or_empty()),
1136 MTPint(), // views
1137 MTPint(), // forwards
1138 MTPMessageReplies(),
1139 MTPint(), // edit_date
1140 MTPstring(),
1141 MTPlong(),
1142 //MTPMessageReactions(),
1143 MTPVector<MTPRestrictionReason>(),
1144 MTP_int(d.vttl_period().value_or_empty())),
1145 MessageFlags(),
1146 NewMessageType::Unread);
1147 } break;
1148
1149 case mtpc_updateShortSentMessage: {
1150 auto &d = updates.c_updateShortSentMessage();
1151 Q_UNUSED(d); // Sent message data was applied anyway.
1152 } break;
1153
1154 default: Unexpected("Type in applyUpdatesNoPtsCheck()");
1155 }
1156 }
1157
applyUpdateNoPtsCheck(const MTPUpdate & update)1158 void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
1159 switch (update.type()) {
1160 case mtpc_updateNewMessage: {
1161 auto &d = update.c_updateNewMessage();
1162 auto needToAdd = true;
1163 if (d.vmessage().type() == mtpc_message) { // index forwarded messages to links _overview
1164 const auto &data = d.vmessage().c_message();
1165 if (_session->data().checkEntitiesAndViewsUpdate(data)) { // already in blocks
1166 LOG(("Skipping message, because it is already in blocks!"));
1167 needToAdd = false;
1168 }
1169 ProcessScheduledMessageWithElapsedTime(_session, needToAdd, data);
1170 }
1171 if (needToAdd) {
1172 _session->data().addNewMessage(
1173 d.vmessage(),
1174 MessageFlags(),
1175 NewMessageType::Unread);
1176 }
1177 } break;
1178
1179 case mtpc_updateReadMessagesContents: {
1180 const auto &d = update.c_updateReadMessagesContents();
1181 auto possiblyReadMentions = base::flat_set<MsgId>();
1182 for (const auto &msgId : d.vmessages().v) {
1183 if (const auto item = _session->data().message(NoChannel, msgId.v)) {
1184 if (item->isUnreadMedia() || item->isUnreadMention()) {
1185 item->markMediaRead();
1186 _session->data().requestItemRepaint(item);
1187
1188 if (item->out()
1189 && item->history()->peer->isUser()
1190 && !requestingDifference()) {
1191 item->history()->peer->asUser()->madeAction(base::unixtime::now());
1192 }
1193 }
1194 } else {
1195 // Perhaps it was an unread mention!
1196 possiblyReadMentions.insert(msgId.v);
1197 }
1198 }
1199 session().api().checkForUnreadMentions(possiblyReadMentions);
1200 } break;
1201
1202 case mtpc_updateReadHistoryInbox: {
1203 const auto &d = update.c_updateReadHistoryInbox();
1204 const auto peer = peerFromMTP(d.vpeer());
1205 if (const auto history = _session->data().historyLoaded(peer)) {
1206 const auto folderId = d.vfolder_id().value_or_empty();
1207 history->applyInboxReadUpdate(
1208 folderId,
1209 d.vmax_id().v,
1210 d.vstill_unread_count().v);
1211 }
1212 } break;
1213
1214 case mtpc_updateReadHistoryOutbox: {
1215 const auto &d = update.c_updateReadHistoryOutbox();
1216 const auto peer = peerFromMTP(d.vpeer());
1217 if (const auto history = _session->data().historyLoaded(peer)) {
1218 history->outboxRead(d.vmax_id().v);
1219 if (!requestingDifference()) {
1220 if (const auto user = history->peer->asUser()) {
1221 user->madeAction(base::unixtime::now());
1222 }
1223 }
1224 }
1225 } break;
1226
1227 case mtpc_updateWebPage: {
1228 auto &d = update.c_updateWebPage();
1229 Q_UNUSED(d); // Web page was updated anyway.
1230 } break;
1231
1232 case mtpc_updateFolderPeers: {
1233 const auto &data = update.c_updateFolderPeers();
1234 auto &owner = _session->data();
1235 for (const auto &peer : data.vfolder_peers().v) {
1236 peer.match([&](const MTPDfolderPeer &data) {
1237 const auto peerId = peerFromMTP(data.vpeer());
1238 if (const auto history = owner.historyLoaded(peerId)) {
1239 if (const auto folderId = data.vfolder_id().v) {
1240 history->setFolder(owner.folder(folderId));
1241 } else {
1242 history->clearFolder();
1243 }
1244 }
1245 });
1246 }
1247 } break;
1248
1249 case mtpc_updateDeleteMessages: {
1250 auto &d = update.c_updateDeleteMessages();
1251 _session->data().processMessagesDeleted(NoChannel, d.vmessages().v);
1252 } break;
1253
1254 case mtpc_updateNewChannelMessage: {
1255 auto &d = update.c_updateNewChannelMessage();
1256 auto needToAdd = true;
1257 if (d.vmessage().type() == mtpc_message) { // index forwarded messages to links _overview
1258 const auto &data = d.vmessage().c_message();
1259 if (_session->data().checkEntitiesAndViewsUpdate(data)) { // already in blocks
1260 LOG(("Skipping message, because it is already in blocks!"));
1261 needToAdd = false;
1262 }
1263 ProcessScheduledMessageWithElapsedTime(_session, needToAdd, data);
1264 }
1265 if (needToAdd) {
1266 _session->data().addNewMessage(
1267 d.vmessage(),
1268 MessageFlags(),
1269 NewMessageType::Unread);
1270 }
1271 } break;
1272
1273 case mtpc_updateEditChannelMessage: {
1274 auto &d = update.c_updateEditChannelMessage();
1275 _session->data().updateEditedMessage(d.vmessage());
1276 } break;
1277
1278 case mtpc_updatePinnedChannelMessages: {
1279 const auto &d = update.c_updatePinnedChannelMessages();
1280 const auto channelId = d.vchannel_id().v;
1281 for (const auto &msgId : d.vmessages().v) {
1282 const auto item = session().data().message(channelId, msgId.v);
1283 if (item) {
1284 item->setIsPinned(d.is_pinned());
1285 }
1286 }
1287 } break;
1288
1289 case mtpc_updateEditMessage: {
1290 auto &d = update.c_updateEditMessage();
1291 _session->data().updateEditedMessage(d.vmessage());
1292 } break;
1293
1294 case mtpc_updateChannelWebPage: {
1295 auto &d = update.c_updateChannelWebPage();
1296 Q_UNUSED(d); // Web page was updated anyway.
1297 } break;
1298
1299 case mtpc_updateDeleteChannelMessages: {
1300 auto &d = update.c_updateDeleteChannelMessages();
1301 _session->data().processMessagesDeleted(d.vchannel_id().v, d.vmessages().v);
1302 } break;
1303
1304 case mtpc_updatePinnedMessages: {
1305 const auto &d = update.c_updatePinnedMessages();
1306 for (const auto &msgId : d.vmessages().v) {
1307 const auto item = session().data().message(0, msgId.v);
1308 if (item) {
1309 item->setIsPinned(d.is_pinned());
1310 }
1311 }
1312 } break;
1313
1314 default: Unexpected("Type in applyUpdateNoPtsCheck()");
1315 }
1316 }
1317
applyUpdates(const MTPUpdates & updates,uint64 sentMessageRandomId)1318 void Updates::applyUpdates(
1319 const MTPUpdates &updates,
1320 uint64 sentMessageRandomId) {
1321 const auto randomId = sentMessageRandomId;
1322
1323 switch (updates.type()) {
1324 case mtpc_updates: {
1325 auto &d = updates.c_updates();
1326 if (d.vseq().v) {
1327 if (d.vseq().v <= _updatesSeq) {
1328 return;
1329 }
1330 if (d.vseq().v > _updatesSeq + 1) {
1331 _bySeqUpdates.emplace(d.vseq().v, updates);
1332 _bySeqTimer.callOnce(PtsWaiter::kWaitForSkippedTimeout);
1333 return;
1334 }
1335 }
1336
1337 session().data().processUsers(d.vusers());
1338 session().data().processChats(d.vchats());
1339 feedUpdateVector(d.vupdates());
1340
1341 setState(0, d.vdate().v, _updatesQts, d.vseq().v);
1342 } break;
1343
1344 case mtpc_updatesCombined: {
1345 auto &d = updates.c_updatesCombined();
1346 if (d.vseq_start().v) {
1347 if (d.vseq_start().v <= _updatesSeq) {
1348 return;
1349 }
1350 if (d.vseq_start().v > _updatesSeq + 1) {
1351 _bySeqUpdates.emplace(d.vseq_start().v, updates);
1352 _bySeqTimer.callOnce(PtsWaiter::kWaitForSkippedTimeout);
1353 return;
1354 }
1355 }
1356
1357 session().data().processUsers(d.vusers());
1358 session().data().processChats(d.vchats());
1359 feedUpdateVector(d.vupdates());
1360
1361 setState(0, d.vdate().v, _updatesQts, d.vseq().v);
1362 } break;
1363
1364 case mtpc_updateShort: {
1365 auto &d = updates.c_updateShort();
1366 feedUpdate(d.vupdate());
1367
1368 setState(0, d.vdate().v, _updatesQts, _updatesSeq);
1369 } break;
1370
1371 case mtpc_updateShortMessage: {
1372 auto &d = updates.c_updateShortMessage();
1373 const auto viaBotId = d.vvia_bot_id();
1374 const auto entities = d.ventities();
1375 const auto fwd = d.vfwd_from();
1376 if (!session().data().userLoaded(d.vuser_id())
1377 || (viaBotId && !session().data().userLoaded(*viaBotId))
1378 || (entities && !MentionUsersLoaded(&session(), *entities))
1379 || (fwd && !ForwardedInfoDataLoaded(&session(), *fwd))) {
1380 MTP_LOG(0, ("getDifference "
1381 "{ good - getting user for updateShortMessage }%1"
1382 ).arg(_session->mtp().isTestMode() ? " TESTMODE" : ""));
1383 return getDifference();
1384 }
1385 if (updateAndApply(d.vpts().v, d.vpts_count().v, updates)) {
1386 // Update date as well.
1387 setState(0, d.vdate().v, _updatesQts, _updatesSeq);
1388 }
1389 } break;
1390
1391 case mtpc_updateShortChatMessage: {
1392 auto &d = updates.c_updateShortChatMessage();
1393 const auto noFrom = !session().data().userLoaded(d.vfrom_id());
1394 const auto chat = session().data().chatLoaded(d.vchat_id());
1395 const auto viaBotId = d.vvia_bot_id();
1396 const auto entities = d.ventities();
1397 const auto fwd = d.vfwd_from();
1398 if (!chat
1399 || noFrom
1400 || (viaBotId && !session().data().userLoaded(*viaBotId))
1401 || (entities && !MentionUsersLoaded(&session(), *entities))
1402 || (fwd && !ForwardedInfoDataLoaded(&session(), *fwd))) {
1403 MTP_LOG(0, ("getDifference "
1404 "{ good - getting user for updateShortChatMessage }%1"
1405 ).arg(_session->mtp().isTestMode() ? " TESTMODE" : ""));
1406 if (chat && noFrom) {
1407 session().api().requestFullPeer(chat);
1408 }
1409 return getDifference();
1410 }
1411 if (updateAndApply(d.vpts().v, d.vpts_count().v, updates)) {
1412 // Update date as well.
1413 setState(0, d.vdate().v, _updatesQts, _updatesSeq);
1414 }
1415 } break;
1416
1417 case mtpc_updateShortSentMessage: {
1418 auto &d = updates.c_updateShortSentMessage();
1419 if (!IsServerMsgId(d.vid().v)) {
1420 LOG(("API Error: Bad msgId got from server: %1").arg(d.vid().v));
1421 } else if (randomId) {
1422 auto &owner = session().data();
1423 const auto sent = owner.messageSentData(randomId);
1424 const auto lookupMessage = [&] {
1425 return sent.peerId
1426 ? owner.message(peerToChannel(sent.peerId), d.vid().v)
1427 : nullptr;
1428 };
1429 if (const auto id = owner.messageIdByRandomId(randomId)) {
1430 const auto local = owner.message(id);
1431 if (local && local->isScheduled()) {
1432 owner.scheduledMessages().sendNowSimpleMessage(d, local);
1433 }
1434 }
1435 const auto wasAlready = (lookupMessage() != nullptr);
1436 feedUpdate(MTP_updateMessageID(d.vid(), MTP_long(randomId))); // ignore real date
1437 if (const auto item = lookupMessage()) {
1438 const auto list = d.ventities();
1439 if (list && !MentionUsersLoaded(&session(), *list)) {
1440 session().api().requestMessageData(
1441 item->history()->peer->asChannel(),
1442 item->id,
1443 ApiWrap::RequestMessageDataCallback());
1444 }
1445 item->applySentMessage(sent.text, d, wasAlready);
1446 }
1447 }
1448
1449 if (updateAndApply(d.vpts().v, d.vpts_count().v, updates)) {
1450 // Update date as well.
1451 setState(0, d.vdate().v, _updatesQts, _updatesSeq);
1452 }
1453 } break;
1454
1455 case mtpc_updatesTooLong: {
1456 MTP_LOG(0, ("getDifference "
1457 "{ good - updatesTooLong received }%1"
1458 ).arg(_session->mtp().isTestMode() ? " TESTMODE" : ""));
1459 return getDifference();
1460 } break;
1461 }
1462 session().data().sendHistoryChangeNotifications();
1463 }
1464
feedUpdate(const MTPUpdate & update)1465 void Updates::feedUpdate(const MTPUpdate &update) {
1466 switch (update.type()) {
1467
1468 // New messages.
1469 case mtpc_updateNewMessage: {
1470 auto &d = update.c_updateNewMessage();
1471
1472 const auto isDataLoaded = AllDataLoadedForMessage(&session(), d.vmessage());
1473 if (!requestingDifference() && isDataLoaded != DataIsLoadedResult::Ok) {
1474 MTP_LOG(0, ("getDifference "
1475 "{ good - after not all data loaded in updateNewMessage }%1"
1476 ).arg(_session->mtp().isTestMode() ? " TESTMODE" : ""));
1477
1478 // This can be if this update was created by grouping
1479 // some short message update into an updates vector.
1480 return getDifference();
1481 }
1482
1483 updateAndApply(d.vpts().v, d.vpts_count().v, update);
1484 } break;
1485
1486 case mtpc_updateNewChannelMessage: {
1487 auto &d = update.c_updateNewChannelMessage();
1488 auto channel = session().data().channelLoaded(peerToChannel(PeerFromMessage(d.vmessage())));
1489 const auto isDataLoaded = AllDataLoadedForMessage(&session(), d.vmessage());
1490 if (!requestingDifference() && (!channel || isDataLoaded != DataIsLoadedResult::Ok)) {
1491 MTP_LOG(0, ("getDifference "
1492 "{ good - after not all data loaded in updateNewChannelMessage }%1"
1493 ).arg(_session->mtp().isTestMode() ? " TESTMODE" : ""));
1494
1495 // Request last active supergroup participants if the 'from' user was not loaded yet.
1496 // This will optimize similar getDifference() calls for almost all next messages.
1497 if (isDataLoaded == DataIsLoadedResult::FromNotLoaded && channel && channel->isMegagroup()) {
1498 if (channel->mgInfo->lastParticipants.size() < _session->serverConfig().chatSizeMax
1499 && (channel->mgInfo->lastParticipants.empty()
1500 || channel->mgInfo->lastParticipants.size() < channel->membersCount())) {
1501 session().api().requestLastParticipants(channel);
1502 }
1503 }
1504
1505 if (!_byMinChannelTimer.isActive()) { // getDifference after timeout
1506 _byMinChannelTimer.callOnce(PtsWaiter::kWaitForSkippedTimeout);
1507 }
1508 return;
1509 }
1510 if (channel && !_handlingChannelDifference) {
1511 if (channel->ptsRequesting()) { // skip global updates while getting channel difference
1512 return;
1513 }
1514 channel->ptsUpdateAndApply(d.vpts().v, d.vpts_count().v, update);
1515 } else {
1516 applyUpdateNoPtsCheck(update);
1517 }
1518 } break;
1519
1520 case mtpc_updateMessageID: {
1521 const auto &d = update.c_updateMessageID();
1522 const auto randomId = d.vrandom_id().v;
1523 if (const auto id = session().data().messageIdByRandomId(randomId)) {
1524 const auto newId = d.vid().v;
1525 if (const auto local = session().data().message(id)) {
1526 if (local->isScheduled()) {
1527 session().data().scheduledMessages().apply(d, local);
1528 } else {
1529 const auto channel = id.channel;
1530 const auto existing = session().data().message(
1531 channel,
1532 newId);
1533 if (existing && !local->mainView()) {
1534 const auto history = local->history();
1535 local->destroy();
1536 history->requestChatListMessage();
1537 } else {
1538 if (existing) {
1539 existing->destroy();
1540 }
1541 local->setRealId(d.vid().v);
1542 }
1543 }
1544 }
1545 session().data().unregisterMessageRandomId(randomId);
1546 }
1547 session().data().unregisterMessageSentData(randomId);
1548 } break;
1549
1550 // Message contents being read.
1551 case mtpc_updateReadMessagesContents: {
1552 auto &d = update.c_updateReadMessagesContents();
1553 updateAndApply(d.vpts().v, d.vpts_count().v, update);
1554 } break;
1555
1556 case mtpc_updateChannelReadMessagesContents: {
1557 auto &d = update.c_updateChannelReadMessagesContents();
1558 auto channel = session().data().channelLoaded(d.vchannel_id());
1559 if (!channel) {
1560 if (!_byMinChannelTimer.isActive()) {
1561 // getDifference after timeout.
1562 _byMinChannelTimer.callOnce(PtsWaiter::kWaitForSkippedTimeout);
1563 }
1564 return;
1565 }
1566 auto possiblyReadMentions = base::flat_set<MsgId>();
1567 for (const auto &msgId : d.vmessages().v) {
1568 if (auto item = session().data().message(channel, msgId.v)) {
1569 if (item->isUnreadMedia() || item->isUnreadMention()) {
1570 item->markMediaRead();
1571 session().data().requestItemRepaint(item);
1572 }
1573 } else {
1574 // Perhaps it was an unread mention!
1575 possiblyReadMentions.insert(msgId.v);
1576 }
1577 }
1578 session().api().checkForUnreadMentions(possiblyReadMentions, channel);
1579 } break;
1580
1581 // Edited messages.
1582 case mtpc_updateEditMessage: {
1583 auto &d = update.c_updateEditMessage();
1584 updateAndApply(d.vpts().v, d.vpts_count().v, update);
1585 } break;
1586
1587 case mtpc_updateEditChannelMessage: {
1588 auto &d = update.c_updateEditChannelMessage();
1589 auto channel = session().data().channelLoaded(peerToChannel(PeerFromMessage(d.vmessage())));
1590
1591 if (channel && !_handlingChannelDifference) {
1592 if (channel->ptsRequesting()) { // skip global updates while getting channel difference
1593 return;
1594 } else {
1595 channel->ptsUpdateAndApply(d.vpts().v, d.vpts_count().v, update);
1596 }
1597 } else {
1598 applyUpdateNoPtsCheck(update);
1599 }
1600 } break;
1601
1602 case mtpc_updatePinnedChannelMessages: {
1603 auto &d = update.c_updatePinnedChannelMessages();
1604 auto channel = session().data().channelLoaded(d.vchannel_id());
1605
1606 if (channel && !_handlingChannelDifference) {
1607 if (channel->ptsRequesting()) { // skip global updates while getting channel difference
1608 return;
1609 } else {
1610 channel->ptsUpdateAndApply(d.vpts().v, d.vpts_count().v, update);
1611 }
1612 } else {
1613 applyUpdateNoPtsCheck(update);
1614 }
1615 } break;
1616
1617 // Messages being read.
1618 case mtpc_updateReadHistoryInbox: {
1619 auto &d = update.c_updateReadHistoryInbox();
1620 updateAndApply(d.vpts().v, d.vpts_count().v, update);
1621 } break;
1622
1623 case mtpc_updateReadHistoryOutbox: {
1624 auto &d = update.c_updateReadHistoryOutbox();
1625 updateAndApply(d.vpts().v, d.vpts_count().v, update);
1626 } break;
1627
1628 case mtpc_updateReadChannelInbox: {
1629 const auto &d = update.c_updateReadChannelInbox();
1630 const auto peer = peerFromChannel(d.vchannel_id().v);
1631 if (const auto history = session().data().historyLoaded(peer)) {
1632 history->applyInboxReadUpdate(
1633 d.vfolder_id().value_or_empty(),
1634 d.vmax_id().v,
1635 d.vstill_unread_count().v,
1636 d.vpts().v);
1637 }
1638 } break;
1639
1640 case mtpc_updateReadChannelOutbox: {
1641 const auto &d = update.c_updateReadChannelOutbox();
1642 const auto peer = peerFromChannel(d.vchannel_id().v);
1643 if (const auto history = session().data().historyLoaded(peer)) {
1644 history->outboxRead(d.vmax_id().v);
1645 if (!requestingDifference()) {
1646 if (const auto user = history->peer->asUser()) {
1647 user->madeAction(base::unixtime::now());
1648 }
1649 }
1650 }
1651 } break;
1652
1653 case mtpc_updateDialogUnreadMark: {
1654 const auto &data = update.c_updateDialogUnreadMark();
1655 data.vpeer().match(
1656 [&](const MTPDdialogPeer &dialog) {
1657 const auto id = peerFromMTP(dialog.vpeer());
1658 if (const auto history = session().data().historyLoaded(id)) {
1659 history->setUnreadMark(data.is_unread());
1660 }
1661 }, [&](const MTPDdialogPeerFolder &dialog) {
1662 //const auto id = dialog.vfolder_id().v; // #TODO archive
1663 //if (const auto folder = session().data().folderLoaded(id)) {
1664 // folder->setUnreadMark(data.is_unread());
1665 //}
1666 });
1667 } break;
1668
1669 case mtpc_updateFolderPeers: {
1670 const auto &data = update.c_updateFolderPeers();
1671
1672 updateAndApply(data.vpts().v, data.vpts_count().v, update);
1673 } break;
1674
1675 case mtpc_updateDialogFilter:
1676 case mtpc_updateDialogFilterOrder:
1677 case mtpc_updateDialogFilters: {
1678 session().data().chatsFilters().apply(update);
1679 } break;
1680
1681 // Deleted messages.
1682 case mtpc_updateDeleteMessages: {
1683 auto &d = update.c_updateDeleteMessages();
1684
1685 updateAndApply(d.vpts().v, d.vpts_count().v, update);
1686 } break;
1687
1688 case mtpc_updateDeleteChannelMessages: {
1689 auto &d = update.c_updateDeleteChannelMessages();
1690 auto channel = session().data().channelLoaded(d.vchannel_id());
1691
1692 if (channel && !_handlingChannelDifference) {
1693 if (channel->ptsRequesting()) { // skip global updates while getting channel difference
1694 return;
1695 }
1696 channel->ptsUpdateAndApply(d.vpts().v, d.vpts_count().v, update);
1697 } else {
1698 applyUpdateNoPtsCheck(update);
1699 }
1700 } break;
1701
1702 case mtpc_updateNewScheduledMessage: {
1703 const auto &d = update.c_updateNewScheduledMessage();
1704 session().data().scheduledMessages().apply(d);
1705 } break;
1706
1707 case mtpc_updateDeleteScheduledMessages: {
1708 const auto &d = update.c_updateDeleteScheduledMessages();
1709 session().data().scheduledMessages().apply(d);
1710 } break;
1711
1712 case mtpc_updateWebPage: {
1713 auto &d = update.c_updateWebPage();
1714
1715 // Update web page anyway.
1716 session().data().processWebpage(d.vwebpage());
1717 session().data().sendWebPageGamePollNotifications();
1718
1719 updateAndApply(d.vpts().v, d.vpts_count().v, update);
1720 } break;
1721
1722 case mtpc_updateChannelWebPage: {
1723 auto &d = update.c_updateChannelWebPage();
1724
1725 // Update web page anyway.
1726 session().data().processWebpage(d.vwebpage());
1727 session().data().sendWebPageGamePollNotifications();
1728
1729 auto channel = session().data().channelLoaded(d.vchannel_id());
1730 if (channel && !_handlingChannelDifference) {
1731 if (channel->ptsRequesting()) { // skip global updates while getting channel difference
1732 return;
1733 } else {
1734 channel->ptsUpdateAndApply(d.vpts().v, d.vpts_count().v, update);
1735 }
1736 } else {
1737 applyUpdateNoPtsCheck(update);
1738 }
1739 } break;
1740
1741 case mtpc_updateMessagePoll: {
1742 session().data().applyUpdate(update.c_updateMessagePoll());
1743 } break;
1744
1745 case mtpc_updateUserTyping: {
1746 auto &d = update.c_updateUserTyping();
1747 handleSendActionUpdate(
1748 peerFromUser(d.vuser_id()),
1749 0,
1750 peerFromUser(d.vuser_id()),
1751 d.vaction());
1752 } break;
1753
1754 case mtpc_updateChatUserTyping: {
1755 auto &d = update.c_updateChatUserTyping();
1756 handleSendActionUpdate(
1757 peerFromChat(d.vchat_id()),
1758 0,
1759 peerFromMTP(d.vfrom_id()),
1760 d.vaction());
1761 } break;
1762
1763 case mtpc_updateChannelUserTyping: {
1764 const auto &d = update.c_updateChannelUserTyping();
1765 handleSendActionUpdate(
1766 peerFromChannel(d.vchannel_id()),
1767 d.vtop_msg_id().value_or_empty(),
1768 peerFromMTP(d.vfrom_id()),
1769 d.vaction());
1770 } break;
1771
1772 case mtpc_updateChatParticipants: {
1773 session().data().applyUpdate(update.c_updateChatParticipants());
1774 } break;
1775
1776 case mtpc_updateChatParticipantAdd: {
1777 session().data().applyUpdate(update.c_updateChatParticipantAdd());
1778 } break;
1779
1780 case mtpc_updateChatParticipantDelete: {
1781 session().data().applyUpdate(update.c_updateChatParticipantDelete());
1782 } break;
1783
1784 case mtpc_updateChatParticipantAdmin: {
1785 session().data().applyUpdate(update.c_updateChatParticipantAdmin());
1786 } break;
1787
1788 case mtpc_updateChatDefaultBannedRights: {
1789 session().data().applyUpdate(update.c_updateChatDefaultBannedRights());
1790 } break;
1791
1792 case mtpc_updateUserStatus: {
1793 auto &d = update.c_updateUserStatus();
1794 if (auto user = session().data().userLoaded(d.vuser_id())) {
1795 switch (d.vstatus().type()) {
1796 case mtpc_userStatusEmpty: user->onlineTill = 0; break;
1797 case mtpc_userStatusRecently:
1798 if (user->onlineTill > -10) { // don't modify pseudo-online
1799 user->onlineTill = -2;
1800 }
1801 break;
1802 case mtpc_userStatusLastWeek: user->onlineTill = -3; break;
1803 case mtpc_userStatusLastMonth: user->onlineTill = -4; break;
1804 case mtpc_userStatusOffline: user->onlineTill = d.vstatus().c_userStatusOffline().vwas_online().v; break;
1805 case mtpc_userStatusOnline: user->onlineTill = d.vstatus().c_userStatusOnline().vexpires().v; break;
1806 }
1807 session().changes().peerUpdated(
1808 user,
1809 Data::PeerUpdate::Flag::OnlineStatus);
1810 }
1811 if (UserId(d.vuser_id()) == session().userId()) {
1812 if (d.vstatus().type() == mtpc_userStatusOffline
1813 || d.vstatus().type() == mtpc_userStatusEmpty) {
1814 updateOnline(Core::App().lastNonIdleTime(), true);
1815 if (d.vstatus().type() == mtpc_userStatusOffline) {
1816 cSetOtherOnline(
1817 d.vstatus().c_userStatusOffline().vwas_online().v);
1818 }
1819 } else if (d.vstatus().type() == mtpc_userStatusOnline) {
1820 cSetOtherOnline(
1821 d.vstatus().c_userStatusOnline().vexpires().v);
1822 }
1823 }
1824 } break;
1825
1826 case mtpc_updateUserName: {
1827 auto &d = update.c_updateUserName();
1828 if (auto user = session().data().userLoaded(d.vuser_id())) {
1829 if (!user->isContact()) {
1830 user->setName(
1831 TextUtilities::SingleLine(qs(d.vfirst_name())),
1832 TextUtilities::SingleLine(qs(d.vlast_name())),
1833 user->nameOrPhone,
1834 TextUtilities::SingleLine(qs(d.vusername())));
1835 } else {
1836 user->setName(
1837 TextUtilities::SingleLine(user->firstName),
1838 TextUtilities::SingleLine(user->lastName),
1839 user->nameOrPhone,
1840 TextUtilities::SingleLine(qs(d.vusername())));
1841 }
1842 }
1843 } break;
1844
1845 case mtpc_updateUserPhoto: {
1846 auto &d = update.c_updateUserPhoto();
1847 if (auto user = session().data().userLoaded(d.vuser_id())) {
1848 user->setPhoto(d.vphoto());
1849 user->loadUserpic();
1850 // After that update we don't have enough information to
1851 // create a 'photo' with all necessary fields. So if
1852 // we receive second such update we end up with a 'photo_id'
1853 // in user_photos list without a loaded 'photo'.
1854 // It fails to show in media overview if you try to open it.
1855 //
1856 //if (mtpIsTrue(d.vprevious()) || !user->userpicPhotoId()) {
1857 session().storage().remove(Storage::UserPhotosRemoveAfter(
1858 peerToUser(user->id),
1859 user->userpicPhotoId()));
1860 //} else {
1861 // session().storage().add(Storage::UserPhotosAddNew(
1862 // peerToUser(user->id),
1863 // user->userpicPhotoId()));
1864 //}
1865 }
1866 } break;
1867
1868 case mtpc_updatePeerSettings: {
1869 const auto &d = update.c_updatePeerSettings();
1870 const auto peerId = peerFromMTP(d.vpeer());
1871 if (const auto peer = session().data().peerLoaded(peerId)) {
1872 peer->setSettings(d.vsettings());
1873 }
1874 } break;
1875
1876 case mtpc_updateNotifySettings: {
1877 auto &d = update.c_updateNotifySettings();
1878 session().data().applyNotifySetting(d.vpeer(), d.vnotify_settings());
1879 } break;
1880
1881 case mtpc_updateDcOptions: {
1882 auto &d = update.c_updateDcOptions();
1883 session().mtp().dcOptions().addFromList(d.vdc_options());
1884 } break;
1885
1886 case mtpc_updateConfig: {
1887 session().mtp().requestConfig();
1888 } break;
1889
1890 case mtpc_updateUserPhone: {
1891 const auto &d = update.c_updateUserPhone();
1892 if (const auto user = session().data().userLoaded(d.vuser_id())) {
1893 const auto newPhone = qs(d.vphone());
1894 if (newPhone != user->phone()) {
1895 user->setPhone(newPhone);
1896 user->setName(
1897 user->firstName,
1898 user->lastName,
1899 ((user->isContact()
1900 || user->isServiceUser()
1901 || user->isSelf()
1902 || user->phone().isEmpty())
1903 ? QString()
1904 : Ui::FormatPhone(user->phone())),
1905 user->username);
1906
1907 session().changes().peerUpdated(
1908 user,
1909 Data::PeerUpdate::Flag::PhoneNumber);
1910 }
1911 }
1912 } break;
1913
1914 case mtpc_updatePeerHistoryTTL: {
1915 const auto &d = update.c_updatePeerHistoryTTL();
1916 const auto peerId = peerFromMTP(d.vpeer());
1917 if (const auto peer = session().data().peerLoaded(peerId)) {
1918 peer->setMessagesTTL(d.vttl_period().value_or_empty());
1919 }
1920 } break;
1921
1922 case mtpc_updateNewEncryptedMessage: {
1923 } break;
1924
1925 case mtpc_updateEncryptedChatTyping: {
1926 } break;
1927
1928 case mtpc_updateEncryption: {
1929 } break;
1930
1931 case mtpc_updateEncryptedMessagesRead: {
1932 } break;
1933
1934 case mtpc_updatePhoneCall:
1935 case mtpc_updatePhoneCallSignalingData:
1936 case mtpc_updateGroupCallParticipants:
1937 case mtpc_updateGroupCallConnection:
1938 case mtpc_updateGroupCall: {
1939 Core::App().calls().handleUpdate(&session(), update);
1940 } break;
1941
1942 case mtpc_updatePeerBlocked: {
1943 const auto &d = update.c_updatePeerBlocked();
1944 if (const auto peer = session().data().peerLoaded(peerFromMTP(d.vpeer_id()))) {
1945 peer->setIsBlocked(mtpIsTrue(d.vblocked()));
1946 }
1947 } break;
1948
1949 case mtpc_updateBotCommands: {
1950 const auto &d = update.c_updateBotCommands();
1951 if (const auto peer = session().data().peerLoaded(peerFromMTP(d.vpeer()))) {
1952 const auto botId = UserId(d.vbot_id().v);
1953 if (const auto user = peer->asUser()) {
1954 if (user->isBot() && user->id == peerFromUser(botId)) {
1955 if (Data::UpdateBotCommands(user->botInfo->commands, d.vcommands())) {
1956 session().data().botCommandsChanged(user);
1957 }
1958 }
1959 } else if (const auto chat = peer->asChat()) {
1960 chat->setBotCommands(botId, d.vcommands());
1961 } else if (const auto megagroup = peer->asMegagroup()) {
1962 if (megagroup->mgInfo->updateBotCommands(botId, d.vcommands())) {
1963 session().data().botCommandsChanged(megagroup);
1964 }
1965 }
1966 }
1967 } break;
1968
1969 case mtpc_updatePendingJoinRequests: {
1970 const auto &d = update.c_updatePendingJoinRequests();
1971 if (const auto peer = session().data().peerLoaded(peerFromMTP(d.vpeer()))) {
1972 const auto count = d.vrequests_pending().v;
1973 const auto &requesters = d.vrecent_requesters().v;
1974 if (const auto chat = peer->asChat()) {
1975 chat->setPendingRequestsCount(count, requesters);
1976 } else if (const auto channel = peer->asChannel()) {
1977 channel->setPendingRequestsCount(count, requesters);
1978 }
1979 }
1980 } break;
1981
1982 case mtpc_updateServiceNotification: {
1983 const auto &d = update.c_updateServiceNotification();
1984 const auto text = TextWithEntities {
1985 qs(d.vmessage()),
1986 Api::EntitiesFromMTP(&session(), d.ventities().v)
1987 };
1988 if (IsForceLogoutNotification(d)) {
1989 Core::App().forceLogOut(&session().account(), text);
1990 } else if (d.is_popup()) {
1991 const auto &windows = session().windows();
1992 if (!windows.empty()) {
1993 windows.front()->window().show(Box<Ui::InformBox>(text));
1994 }
1995 } else {
1996 session().data().serviceNotification(text, d.vmedia());
1997 session().api().authorizations().reload();
1998 }
1999 } break;
2000
2001 case mtpc_updatePrivacy: {
2002 auto &d = update.c_updatePrivacy();
2003 const auto allChatsLoaded = [&](const MTPVector<MTPlong> &ids) {
2004 for (const auto &chatId : ids.v) {
2005 if (!session().data().chatLoaded(chatId)
2006 && !session().data().channelLoaded(chatId)) {
2007 return false;
2008 }
2009 }
2010 return true;
2011 };
2012 const auto allLoaded = [&] {
2013 for (const auto &rule : d.vrules().v) {
2014 const auto loaded = rule.match([&](
2015 const MTPDprivacyValueAllowChatParticipants & data) {
2016 return allChatsLoaded(data.vchats());
2017 }, [&](const MTPDprivacyValueDisallowChatParticipants & data) {
2018 return allChatsLoaded(data.vchats());
2019 }, [](auto &&) { return true; });
2020 if (!loaded) {
2021 return false;
2022 }
2023 }
2024 return true;
2025 };
2026 session().api().userPrivacy().apply(
2027 d.vkey().type(),
2028 d.vrules(),
2029 allLoaded());
2030 } break;
2031
2032 case mtpc_updatePinnedDialogs: {
2033 const auto &d = update.c_updatePinnedDialogs();
2034 const auto folderId = d.vfolder_id().value_or_empty();
2035 const auto loaded = !folderId
2036 || (session().data().folderLoaded(folderId) != nullptr);
2037 const auto folder = folderId
2038 ? session().data().folder(folderId).get()
2039 : nullptr;
2040 const auto done = [&] {
2041 const auto list = d.vorder();
2042 if (!list) {
2043 return false;
2044 }
2045 const auto &order = list->v;
2046 const auto notLoaded = [&](const MTPDialogPeer &peer) {
2047 return peer.match([&](const MTPDdialogPeer &data) {
2048 return !session().data().historyLoaded(
2049 peerFromMTP(data.vpeer()));
2050 }, [&](const MTPDdialogPeerFolder &data) {
2051 if (folderId) {
2052 LOG(("API Error: "
2053 "updatePinnedDialogs has nested folders."));
2054 return true;
2055 }
2056 return !session().data().folderLoaded(data.vfolder_id().v);
2057 });
2058 };
2059 if (!ranges::none_of(order, notLoaded)) {
2060 return false;
2061 }
2062 session().data().applyPinnedChats(folder, order);
2063 return true;
2064 }();
2065 if (!done) {
2066 session().api().requestPinnedDialogs(folder);
2067 }
2068 if (!loaded) {
2069 session().data().histories().requestDialogEntry(folder);
2070 }
2071 } break;
2072
2073 case mtpc_updateDialogPinned: {
2074 const auto &d = update.c_updateDialogPinned();
2075 const auto folderId = d.vfolder_id().value_or_empty();
2076 const auto folder = folderId
2077 ? session().data().folder(folderId).get()
2078 : nullptr;
2079 const auto done = d.vpeer().match([&](const MTPDdialogPeer &data) {
2080 const auto id = peerFromMTP(data.vpeer());
2081 if (const auto history = session().data().historyLoaded(id)) {
2082 history->applyPinnedUpdate(d);
2083 return true;
2084 }
2085 DEBUG_LOG(("API Error: "
2086 "pinned chat not loaded for peer %1, folder: %2"
2087 ).arg(id.value
2088 ).arg(folderId
2089 ));
2090 return false;
2091 }, [&](const MTPDdialogPeerFolder &data) {
2092 if (folderId != 0) {
2093 DEBUG_LOG(("API Error: Nested folders updateDialogPinned."));
2094 return false;
2095 }
2096 const auto id = data.vfolder_id().v;
2097 if (const auto folder = session().data().folderLoaded(id)) {
2098 folder->applyPinnedUpdate(d);
2099 return true;
2100 }
2101 DEBUG_LOG(("API Error: "
2102 "pinned folder not loaded for folderId %1, folder: %2"
2103 ).arg(id
2104 ).arg(folderId
2105 ));
2106 return false;
2107 });
2108 if (!done) {
2109 session().api().requestPinnedDialogs(folder);
2110 }
2111 } break;
2112
2113 case mtpc_updateChannel: {
2114 auto &d = update.c_updateChannel();
2115 if (const auto channel = session().data().channelLoaded(d.vchannel_id())) {
2116 channel->inviter = UserId(0);
2117 channel->inviteViaRequest = false;
2118 if (channel->amIn()) {
2119 if (channel->isMegagroup()
2120 && !channel->amCreator()
2121 && !channel->hasAdminRights()) {
2122 channel->updateFullForced();
2123 }
2124 const auto history = channel->owner().history(channel);
2125 history->requestChatListMessage();
2126 if (!history->unreadCountKnown()) {
2127 history->owner().histories().requestDialogEntry(history);
2128 }
2129 if (!channel->amCreator()) {
2130 session().api().requestSelfParticipant(channel);
2131 }
2132 }
2133 }
2134 } break;
2135
2136 case mtpc_updateChannelTooLong: {
2137 const auto &d = update.c_updateChannelTooLong();
2138 if (const auto channel = session().data().channelLoaded(d.vchannel_id())) {
2139 const auto pts = d.vpts();
2140 if (!pts || channel->pts() < pts->v) {
2141 getChannelDifference(channel);
2142 }
2143 }
2144 } break;
2145
2146 case mtpc_updateChannelMessageViews: {
2147 const auto &d = update.c_updateChannelMessageViews();
2148 if (const auto item = session().data().message(d.vchannel_id().v, d.vid().v)) {
2149 item->setViewsCount(d.vviews().v);
2150 }
2151 } break;
2152
2153 case mtpc_updateChannelMessageForwards: {
2154 const auto &d = update.c_updateChannelMessageForwards();
2155 if (const auto item = session().data().message(d.vchannel_id().v, d.vid().v)) {
2156 item->setForwardsCount(d.vforwards().v);
2157 }
2158 } break;
2159
2160 case mtpc_updateReadChannelDiscussionInbox: {
2161 const auto &d = update.c_updateReadChannelDiscussionInbox();
2162 const auto channelId = d.vchannel_id().v;
2163 const auto msgId = d.vtop_msg_id().v;
2164 const auto readTillId = d.vread_max_id().v;
2165 const auto item = session().data().message(channelId, msgId);
2166 const auto unreadCount = item
2167 ? session().data().countUnreadRepliesLocally(item, readTillId)
2168 : std::nullopt;
2169 if (item) {
2170 item->setRepliesInboxReadTill(readTillId, unreadCount);
2171 if (const auto post = item->lookupDiscussionPostOriginal()) {
2172 post->setRepliesInboxReadTill(readTillId, unreadCount);
2173 }
2174 }
2175 if (const auto broadcastId = d.vbroadcast_id()) {
2176 if (const auto post = session().data().message(
2177 broadcastId->v,
2178 d.vbroadcast_post()->v)) {
2179 post->setRepliesInboxReadTill(readTillId, unreadCount);
2180 }
2181 }
2182 } break;
2183
2184 case mtpc_updateReadChannelDiscussionOutbox: {
2185 const auto &d = update.c_updateReadChannelDiscussionOutbox();
2186 const auto channelId = d.vchannel_id().v;
2187 const auto msgId = d.vtop_msg_id().v;
2188 const auto readTillId = d.vread_max_id().v;
2189 const auto item = session().data().message(channelId, msgId);
2190 if (item) {
2191 item->setRepliesOutboxReadTill(readTillId);
2192 if (const auto post = item->lookupDiscussionPostOriginal()) {
2193 post->setRepliesOutboxReadTill(readTillId);
2194 }
2195 }
2196 } break;
2197
2198 case mtpc_updateChannelAvailableMessages: {
2199 auto &d = update.c_updateChannelAvailableMessages();
2200 if (const auto channel = session().data().channelLoaded(d.vchannel_id())) {
2201 channel->setAvailableMinId(d.vavailable_min_id().v);
2202 if (const auto history = session().data().historyLoaded(channel)) {
2203 history->clearUpTill(d.vavailable_min_id().v);
2204 }
2205 }
2206 } break;
2207
2208 // Pinned message.
2209 case mtpc_updatePinnedMessages: {
2210 const auto &d = update.c_updatePinnedMessages();
2211 updateAndApply(d.vpts().v, d.vpts_count().v, update);
2212 } break;
2213
2214 ////// Cloud sticker sets
2215 case mtpc_updateNewStickerSet: {
2216 const auto &d = update.c_updateNewStickerSet();
2217 session().data().stickers().newSetReceived(d.vstickerset());
2218 } break;
2219
2220 case mtpc_updateStickerSetsOrder: {
2221 auto &d = update.c_updateStickerSetsOrder();
2222 auto &stickers = session().data().stickers();
2223 const auto isMasks = d.is_masks();
2224 const auto &order = d.vorder().v;
2225 const auto &sets = stickers.sets();
2226 Data::StickersSetsOrder result;
2227 for (const auto &item : order) {
2228 if (sets.find(item.v) == sets.cend()) {
2229 break;
2230 }
2231 result.push_back(item.v);
2232 }
2233 const auto localSize = isMasks
2234 ? stickers.maskSetsOrder().size()
2235 : stickers.setsOrder().size();
2236 if ((result.size() != localSize) || (result.size() != order.size())) {
2237 if (isMasks) {
2238 stickers.setLastMasksUpdate(0);
2239 session().api().updateMasks();
2240 } else {
2241 stickers.setLastUpdate(0);
2242 session().api().updateStickers();
2243 }
2244 } else {
2245 if (isMasks) {
2246 stickers.maskSetsOrderRef() = std::move(result);
2247 session().local().writeInstalledMasks();
2248 } else {
2249 stickers.setsOrderRef() = std::move(result);
2250 session().local().writeInstalledStickers();
2251 }
2252 stickers.notifyUpdated();
2253 }
2254 } break;
2255
2256 case mtpc_updateStickerSets: {
2257 // Can't determine is it masks or stickers, so update both.
2258 session().data().stickers().setLastUpdate(0);
2259 session().api().updateStickers();
2260 session().data().stickers().setLastMasksUpdate(0);
2261 session().api().updateMasks();
2262 } break;
2263
2264 case mtpc_updateRecentStickers: {
2265 session().data().stickers().setLastRecentUpdate(0);
2266 session().api().updateStickers();
2267 } break;
2268
2269 case mtpc_updateFavedStickers: {
2270 session().data().stickers().setLastFavedUpdate(0);
2271 session().api().updateStickers();
2272 } break;
2273
2274 case mtpc_updateReadFeaturedStickers: {
2275 // We read some of the featured stickers, perhaps not all of them.
2276 // Here we don't know what featured sticker sets were read, so we
2277 // request all of them once again.
2278 session().data().stickers().setLastFeaturedUpdate(0);
2279 session().api().updateStickers();
2280 } break;
2281
2282 ////// Cloud saved GIFs
2283 case mtpc_updateSavedGifs: {
2284 session().data().stickers().setLastSavedGifsUpdate(0);
2285 session().api().updateStickers();
2286 } break;
2287
2288 ////// Cloud drafts
2289 case mtpc_updateDraftMessage: {
2290 const auto &data = update.c_updateDraftMessage();
2291 const auto peerId = peerFromMTP(data.vpeer());
2292 data.vdraft().match([&](const MTPDdraftMessage &data) {
2293 Data::ApplyPeerCloudDraft(&session(), peerId, data);
2294 }, [&](const MTPDdraftMessageEmpty &data) {
2295 Data::ClearPeerCloudDraft(
2296 &session(),
2297 peerId,
2298 data.vdate().value_or_empty());
2299 });
2300 } break;
2301
2302 ////// Cloud langpacks
2303 case mtpc_updateLangPack: {
2304 const auto &data = update.c_updateLangPack();
2305 Lang::CurrentCloudManager().applyLangPackDifference(data.vdifference());
2306 } break;
2307
2308 case mtpc_updateLangPackTooLong: {
2309 const auto &data = update.c_updateLangPackTooLong();
2310 const auto code = qs(data.vlang_code());
2311 if (!code.isEmpty()) {
2312 Lang::CurrentCloudManager().requestLangPackDifference(code);
2313 }
2314 } break;
2315
2316 ////// Cloud themes
2317 case mtpc_updateTheme: {
2318 const auto &data = update.c_updateTheme();
2319 session().data().cloudThemes().applyUpdate(data.vtheme());
2320 } break;
2321
2322 }
2323 }
2324
2325 } // namespace Api
2326