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 "data/data_chat.h"
9
10 #include "data/data_user.h"
11 #include "data/data_channel.h"
12 #include "data/data_session.h"
13 #include "data/data_changes.h"
14 #include "data/data_group_call.h"
15 #include "history/history.h"
16 #include "main/main_session.h"
17 #include "apiwrap.h"
18 #include "api/api_invite_links.h"
19
20 namespace {
21
22 using UpdateFlag = Data::PeerUpdate::Flag;
23
24 } // namespace
25
ChatData(not_null<Data::Session * > owner,PeerId id)26 ChatData::ChatData(not_null<Data::Session*> owner, PeerId id)
27 : PeerData(owner, id)
28 , inputChat(MTP_long(peerToChat(id).bare)) {
29 _flags.changes(
30 ) | rpl::start_with_next([=](const Flags::Change &change) {
31 if (change.diff & ChatDataFlag::CallNotEmpty) {
32 if (const auto history = this->owner().historyLoaded(this)) {
33 history->updateChatListEntry();
34 }
35 }
36 }, _lifetime);
37 }
38
setPhoto(const MTPChatPhoto & photo)39 void ChatData::setPhoto(const MTPChatPhoto &photo) {
40 photo.match([&](const MTPDchatPhoto &data) {
41 updateUserpic(data.vphoto_id().v, data.vdc_id().v);
42 }, [&](const MTPDchatPhotoEmpty &) {
43 clearUserpic();
44 });
45 }
46
defaultAdminRights(not_null<UserData * > user)47 ChatAdminRightsInfo ChatData::defaultAdminRights(not_null<UserData*> user) {
48 const auto isCreator = (creator == peerToUser(user->id))
49 || (user->isSelf() && amCreator());
50 using Flag = AdminRight;
51 return ChatAdminRightsInfo(Flag::Other
52 | Flag::ChangeInfo
53 | Flag::DeleteMessages
54 | Flag::BanUsers
55 | Flag::InviteUsers
56 | Flag::PinMessages
57 | Flag::ManageCall
58 | (isCreator ? Flag::AddAdmins : Flag(0)));
59 }
60
canWrite() const61 bool ChatData::canWrite() const {
62 // Duplicated in Data::CanWriteValue().
63 return amIn() && !amRestricted(Restriction::SendMessages);
64 }
65
canEditInformation() const66 bool ChatData::canEditInformation() const {
67 return amIn() && !amRestricted(Restriction::ChangeInfo);
68 }
69
canEditPermissions() const70 bool ChatData::canEditPermissions() const {
71 return amIn()
72 && (amCreator() || (adminRights() & AdminRight::BanUsers));
73 }
74
canEditUsername() const75 bool ChatData::canEditUsername() const {
76 return amCreator()
77 && (flags() & ChatDataFlag::CanSetUsername);
78 }
79
canEditPreHistoryHidden() const80 bool ChatData::canEditPreHistoryHidden() const {
81 return amCreator();
82 }
83
canDeleteMessages() const84 bool ChatData::canDeleteMessages() const {
85 return amCreator()
86 || (adminRights() & AdminRight::DeleteMessages);
87 }
88
canAddMembers() const89 bool ChatData::canAddMembers() const {
90 return amIn() && !amRestricted(Restriction::InviteUsers);
91 }
92
canSendPolls() const93 bool ChatData::canSendPolls() const {
94 return amIn() && !amRestricted(Restriction::SendPolls);
95 }
96
canAddAdmins() const97 bool ChatData::canAddAdmins() const {
98 return amIn() && amCreator();
99 }
100
canBanMembers() const101 bool ChatData::canBanMembers() const {
102 return amCreator()
103 || (adminRights() & AdminRight::BanUsers);
104 }
105
anyoneCanAddMembers() const106 bool ChatData::anyoneCanAddMembers() const {
107 return !(defaultRestrictions() & Restriction::InviteUsers);
108 }
109
setName(const QString & newName)110 void ChatData::setName(const QString &newName) {
111 updateNameDelayed(newName.isEmpty() ? name : newName, QString(), QString());
112 }
113
applyEditAdmin(not_null<UserData * > user,bool isAdmin)114 void ChatData::applyEditAdmin(not_null<UserData*> user, bool isAdmin) {
115 if (isAdmin) {
116 admins.emplace(user);
117 } else {
118 admins.remove(user);
119 }
120 session().changes().peerUpdated(this, UpdateFlag::Admins);
121 }
122
invalidateParticipants()123 void ChatData::invalidateParticipants() {
124 participants.clear();
125 admins.clear();
126 setAdminRights(ChatAdminRights());
127 //setDefaultRestrictions(ChatRestrictions());
128 invitedByMe.clear();
129 botStatus = 0;
130 session().changes().peerUpdated(
131 this,
132 UpdateFlag::Members | UpdateFlag::Admins);
133 }
134
setInviteLink(const QString & newInviteLink)135 void ChatData::setInviteLink(const QString &newInviteLink) {
136 _inviteLink = newInviteLink;
137 }
138
canHaveInviteLink() const139 bool ChatData::canHaveInviteLink() const {
140 return amCreator()
141 || (adminRights() & AdminRight::InviteUsers);
142 }
143
setAdminRights(ChatAdminRights rights)144 void ChatData::setAdminRights(ChatAdminRights rights) {
145 if (rights == adminRights()) {
146 return;
147 }
148 _adminRights.set(rights);
149 if (!canHaveInviteLink()) {
150 setPendingRequestsCount(0, std::vector<UserId>{});
151 }
152 session().changes().peerUpdated(
153 this,
154 UpdateFlag::Rights | UpdateFlag::Admins | UpdateFlag::BannedUsers);
155 }
156
setDefaultRestrictions(ChatRestrictions rights)157 void ChatData::setDefaultRestrictions(ChatRestrictions rights) {
158 if (rights == defaultRestrictions()) {
159 return;
160 }
161 _defaultRestrictions.set(rights);
162 session().changes().peerUpdated(this, UpdateFlag::Rights);
163 }
164
refreshBotStatus()165 void ChatData::refreshBotStatus() {
166 if (participants.empty()) {
167 botStatus = 0;
168 } else {
169 const auto bot = ranges::none_of(participants, &UserData::isBot);
170 botStatus = bot ? -1 : 2;
171 }
172 }
173
applyUpdateVersion(int version)174 auto ChatData::applyUpdateVersion(int version) -> UpdateStatus {
175 if (_version > version) {
176 return UpdateStatus::TooOld;
177 } else if (_version + 1 < version) {
178 invalidateParticipants();
179 session().api().requestFullPeer(this);
180 return UpdateStatus::Skipped;
181 }
182 setVersion(version);
183 return UpdateStatus::Good;
184 }
185
getMigrateToChannel() const186 ChannelData *ChatData::getMigrateToChannel() const {
187 return _migratedTo;
188 }
189
setMigrateToChannel(ChannelData * channel)190 void ChatData::setMigrateToChannel(ChannelData *channel) {
191 if (_migratedTo != channel) {
192 _migratedTo = channel;
193 if (channel->amIn()) {
194 session().changes().peerUpdated(this, UpdateFlag::Migration);
195 }
196 }
197 }
198
setGroupCall(const MTPInputGroupCall & call,TimeId scheduleDate)199 void ChatData::setGroupCall(
200 const MTPInputGroupCall &call,
201 TimeId scheduleDate) {
202 if (migrateTo()) {
203 return;
204 }
205 call.match([&](const MTPDinputGroupCall &data) {
206 if (_call && _call->id() == data.vid().v) {
207 return;
208 } else if (!_call && !data.vid().v) {
209 return;
210 } else if (!data.vid().v) {
211 clearGroupCall();
212 return;
213 }
214 const auto hasCall = (_call != nullptr);
215 if (hasCall) {
216 owner().unregisterGroupCall(_call.get());
217 }
218 _call = std::make_unique<Data::GroupCall>(
219 this,
220 data.vid().v,
221 data.vaccess_hash().v,
222 scheduleDate);
223 owner().registerGroupCall(_call.get());
224 session().changes().peerUpdated(this, UpdateFlag::GroupCall);
225 addFlags(ChatDataFlag::CallActive);
226 });
227 }
228
clearGroupCall()229 void ChatData::clearGroupCall() {
230 if (!_call) {
231 return;
232 } else if (const auto group = migrateTo(); group && !group->groupCall()) {
233 group->migrateCall(base::take(_call));
234 } else {
235 owner().unregisterGroupCall(_call.get());
236 _call = nullptr;
237 }
238 session().changes().peerUpdated(this, UpdateFlag::GroupCall);
239 removeFlags(ChatDataFlag::CallActive | ChatDataFlag::CallNotEmpty);
240 }
241
setGroupCallDefaultJoinAs(PeerId peerId)242 void ChatData::setGroupCallDefaultJoinAs(PeerId peerId) {
243 _callDefaultJoinAs = peerId;
244 }
245
groupCallDefaultJoinAs() const246 PeerId ChatData::groupCallDefaultJoinAs() const {
247 return _callDefaultJoinAs;
248 }
249
setBotCommands(const MTPVector<MTPBotInfo> & data)250 void ChatData::setBotCommands(const MTPVector<MTPBotInfo> &data) {
251 if (Data::UpdateBotCommands(_botCommands, data)) {
252 owner().botCommandsChanged(this);
253 }
254 }
255
setBotCommands(UserId botId,const MTPVector<MTPBotCommand> & data)256 void ChatData::setBotCommands(
257 UserId botId,
258 const MTPVector<MTPBotCommand> &data) {
259 if (Data::UpdateBotCommands(_botCommands, botId, data)) {
260 owner().botCommandsChanged(this);
261 }
262 }
263
setPendingRequestsCount(int count,const QVector<MTPlong> & recentRequesters)264 void ChatData::setPendingRequestsCount(
265 int count,
266 const QVector<MTPlong> &recentRequesters) {
267 setPendingRequestsCount(count, ranges::views::all(
268 recentRequesters
269 ) | ranges::views::transform([&](const MTPlong &value) {
270 return UserId(value);
271 }) | ranges::to_vector);
272 }
273
setPendingRequestsCount(int count,std::vector<UserId> recentRequesters)274 void ChatData::setPendingRequestsCount(
275 int count,
276 std::vector<UserId> recentRequesters) {
277 if (_pendingRequestsCount != count
278 || _recentRequesters != recentRequesters) {
279 _pendingRequestsCount = count;
280 _recentRequesters = std::move(recentRequesters);
281 session().changes().peerUpdated(this, UpdateFlag::PendingRequests);
282 }
283 }
284
285 namespace Data {
286
ApplyChatUpdate(not_null<ChatData * > chat,const MTPDupdateChatParticipants & update)287 void ApplyChatUpdate(
288 not_null<ChatData*> chat,
289 const MTPDupdateChatParticipants &update) {
290 ApplyChatUpdate(chat, update.vparticipants());
291 }
292
ApplyChatUpdate(not_null<ChatData * > chat,const MTPDupdateChatParticipantAdd & update)293 void ApplyChatUpdate(
294 not_null<ChatData*> chat,
295 const MTPDupdateChatParticipantAdd &update) {
296 if (chat->applyUpdateVersion(update.vversion().v)
297 != ChatData::UpdateStatus::Good) {
298 return;
299 } else if (chat->count < 0) {
300 return;
301 }
302 const auto user = chat->owner().userLoaded(update.vuser_id().v);
303 const auto session = &chat->session();
304 if (!user
305 || (!chat->participants.empty()
306 && chat->participants.contains(user))) {
307 chat->invalidateParticipants();
308 ++chat->count;
309 return;
310 }
311 if (chat->participants.empty()) {
312 if (chat->count > 0) { // If the count is known.
313 ++chat->count;
314 }
315 chat->botStatus = 0;
316 } else {
317 chat->participants.emplace(user);
318 if (UserId(update.vinviter_id()) == session->userId()) {
319 chat->invitedByMe.insert(user);
320 } else {
321 chat->invitedByMe.remove(user);
322 }
323 ++chat->count;
324 if (user->isBot()) {
325 chat->botStatus = 2;
326 if (!user->botInfo->inited) {
327 session->api().requestFullPeer(user);
328 }
329 }
330 }
331 session->changes().peerUpdated(chat, UpdateFlag::Members);
332 }
333
ApplyChatUpdate(not_null<ChatData * > chat,const MTPDupdateChatParticipantDelete & update)334 void ApplyChatUpdate(
335 not_null<ChatData*> chat,
336 const MTPDupdateChatParticipantDelete &update) {
337 if (chat->applyUpdateVersion(update.vversion().v)
338 != ChatData::UpdateStatus::Good) {
339 return;
340 } else if (chat->count <= 0) {
341 return;
342 }
343 const auto user = chat->owner().userLoaded(update.vuser_id().v);
344 if (!user
345 || (!chat->participants.empty()
346 && !chat->participants.contains(user))) {
347 chat->invalidateParticipants();
348 --chat->count;
349 return;
350 }
351 if (chat->participants.empty()) {
352 if (chat->count > 0) {
353 chat->count--;
354 }
355 chat->botStatus = 0;
356 } else {
357 chat->participants.erase(user);
358 chat->count--;
359 chat->invitedByMe.remove(user);
360 chat->admins.remove(user);
361 if (user->isSelf()) {
362 chat->setAdminRights(ChatAdminRights());
363 }
364 if (const auto history = chat->owner().historyLoaded(chat)) {
365 if (history->lastKeyboardFrom == user->id) {
366 history->clearLastKeyboard();
367 }
368 }
369 if (chat->botStatus > 0 && user->isBot()) {
370 chat->refreshBotStatus();
371 }
372 }
373 chat->session().changes().peerUpdated(chat, UpdateFlag::Members);
374 }
375
ApplyChatUpdate(not_null<ChatData * > chat,const MTPDupdateChatParticipantAdmin & update)376 void ApplyChatUpdate(
377 not_null<ChatData*> chat,
378 const MTPDupdateChatParticipantAdmin &update) {
379 if (chat->applyUpdateVersion(update.vversion().v)
380 != ChatData::UpdateStatus::Good) {
381 return;
382 }
383 const auto session = &chat->session();
384 const auto user = chat->owner().userLoaded(update.vuser_id().v);
385 if (!user) {
386 chat->invalidateParticipants();
387 return;
388 }
389 if (user->isSelf()) {
390 chat->setAdminRights(mtpIsTrue(update.vis_admin())
391 ? chat->defaultAdminRights(user).flags
392 : ChatAdminRights());
393 }
394 if (mtpIsTrue(update.vis_admin())) {
395 if (chat->noParticipantInfo()) {
396 session->api().requestFullPeer(chat);
397 } else {
398 chat->admins.emplace(user);
399 }
400 } else {
401 chat->admins.erase(user);
402 }
403 session->changes().peerUpdated(chat, UpdateFlag::Admins);
404 }
405
ApplyChatUpdate(not_null<ChatData * > chat,const MTPDupdateChatDefaultBannedRights & update)406 void ApplyChatUpdate(
407 not_null<ChatData*> chat,
408 const MTPDupdateChatDefaultBannedRights &update) {
409 if (chat->applyUpdateVersion(update.vversion().v)
410 != ChatData::UpdateStatus::Good) {
411 return;
412 }
413 chat->setDefaultRestrictions(Data::ChatBannedRightsFlags(
414 update.vdefault_banned_rights()));
415 }
416
ApplyChatUpdate(not_null<ChatData * > chat,const MTPDchatFull & update)417 void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
418 ApplyChatUpdate(chat, update.vparticipants());
419
420 if (const auto call = update.vcall()) {
421 chat->setGroupCall(*call);
422 } else {
423 chat->clearGroupCall();
424 }
425 if (const auto as = update.vgroupcall_default_join_as()) {
426 chat->setGroupCallDefaultJoinAs(peerFromMTP(*as));
427 } else {
428 chat->setGroupCallDefaultJoinAs(0);
429 }
430
431 chat->setMessagesTTL(update.vttl_period().value_or_empty());
432 if (const auto info = update.vbot_info()) {
433 chat->setBotCommands(*info);
434 } else {
435 chat->setBotCommands(MTP_vector<MTPBotInfo>());
436 }
437 using Flag = ChatDataFlag;
438 const auto mask = Flag::CanSetUsername;
439 chat->setFlags((chat->flags() & ~mask)
440 | (update.is_can_set_username() ? Flag::CanSetUsername : Flag()));
441 if (const auto photo = update.vchat_photo()) {
442 chat->setUserpicPhoto(*photo);
443 } else {
444 chat->setUserpicPhoto(MTP_photoEmpty(MTP_long(0)));
445 }
446 if (const auto invite = update.vexported_invite()) {
447 chat->session().api().inviteLinks().setMyPermanent(chat, *invite);
448 } else {
449 chat->session().api().inviteLinks().clearMyPermanent(chat);
450 }
451 if (const auto pinned = update.vpinned_msg_id()) {
452 SetTopPinnedMessageId(chat, pinned->v);
453 }
454 chat->checkFolder(update.vfolder_id().value_or_empty());
455 chat->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
456 chat->fullUpdated();
457 chat->setAbout(qs(update.vabout()));
458 chat->setPendingRequestsCount(
459 update.vrequests_pending().value_or_empty(),
460 update.vrecent_requesters().value_or_empty());
461
462 chat->session().api().applyNotifySettings(
463 MTP_inputNotifyPeer(chat->input),
464 update.vnotify_settings());
465 }
466
ApplyChatUpdate(not_null<ChatData * > chat,const MTPChatParticipants & participants)467 void ApplyChatUpdate(
468 not_null<ChatData*> chat,
469 const MTPChatParticipants &participants) {
470 const auto session = &chat->session();
471 participants.match([&](const MTPDchatParticipantsForbidden &data) {
472 if (const auto self = data.vself_participant()) {
473 // self->
474 }
475 chat->count = -1;
476 chat->invalidateParticipants();
477 }, [&](const MTPDchatParticipants &data) {
478 const auto status = chat->applyUpdateVersion(data.vversion().v);
479 if (status == ChatData::UpdateStatus::TooOld) {
480 return;
481 }
482 // Even if we skipped some updates, we got current participants
483 // and we've requested peer from API to have current rights.
484 chat->setVersion(data.vversion().v);
485
486 const auto &list = data.vparticipants().v;
487 chat->count = list.size();
488 chat->participants.clear();
489 chat->invitedByMe.clear();
490 chat->admins.clear();
491 chat->setAdminRights(ChatAdminRights());
492 const auto selfUserId = session->userId();
493 for (const auto &participant : list) {
494 const auto userId = participant.match([&](const auto &data) {
495 return data.vuser_id().v;
496 });
497 const auto user = chat->owner().userLoaded(userId);
498 if (!user) {
499 chat->invalidateParticipants();
500 break;
501 }
502
503 chat->participants.emplace(user);
504
505 const auto inviterId = participant.match([&](
506 const MTPDchatParticipantCreator &data) {
507 return UserId(0);
508 }, [&](const auto &data) {
509 return UserId(data.vinviter_id());
510 });
511 if (inviterId == selfUserId) {
512 chat->invitedByMe.insert(user);
513 }
514
515 participant.match([&](const MTPDchatParticipantCreator &data) {
516 chat->creator = userId;
517 }, [&](const MTPDchatParticipantAdmin &data) {
518 chat->admins.emplace(user);
519 if (user->isSelf()) {
520 chat->setAdminRights(
521 chat->defaultAdminRights(user).flags);
522 }
523 }, [](const MTPDchatParticipant &) {
524 });
525 }
526 if (chat->participants.empty()) {
527 return;
528 }
529 if (const auto history = chat->owner().historyLoaded(chat)) {
530 if (history->lastKeyboardFrom) {
531 const auto i = ranges::find(
532 chat->participants,
533 history->lastKeyboardFrom,
534 &UserData::id);
535 if (i == end(chat->participants)) {
536 history->clearLastKeyboard();
537 }
538 }
539 }
540 chat->refreshBotStatus();
541 session->changes().peerUpdated(
542 chat,
543 UpdateFlag::Members | UpdateFlag::Admins);
544 });
545 }
546
547 } // namespace Data
548