2019-01-04 11:09:48 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
|
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
|
|
*/
|
|
|
|
#include "data/data_chat.h"
|
|
|
|
|
2019-01-13 08:03:34 +00:00
|
|
|
#include "data/data_user.h"
|
2019-01-14 06:34:51 +00:00
|
|
|
#include "data/data_channel.h"
|
2019-01-13 08:03:34 +00:00
|
|
|
#include "data/data_session.h"
|
|
|
|
#include "history/history.h"
|
|
|
|
#include "auth_session.h"
|
|
|
|
#include "apiwrap.h"
|
2019-01-04 11:09:48 +00:00
|
|
|
#include "observer_peer.h"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
using UpdateFlag = Notify::PeerUpdate::Flag;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
ChatData::ChatData(not_null<Data::Session*> owner, PeerId id)
|
|
|
|
: PeerData(owner, id)
|
|
|
|
, inputChat(MTP_int(bareId())) {
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChatData::setPhoto(const MTPChatPhoto &photo) {
|
|
|
|
setPhoto(userpicPhotoId(), photo);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChatData::setPhoto(PhotoId photoId, const MTPChatPhoto &photo) {
|
2019-03-22 14:19:43 +00:00
|
|
|
photo.match([&](const MTPDchatPhoto &data) {
|
2019-07-05 13:38:38 +00:00
|
|
|
updateUserpic(photoId, data.vdc_id().v, data.vphoto_small());
|
2019-03-22 14:19:43 +00:00
|
|
|
}, [&](const MTPDchatPhotoEmpty &) {
|
2019-01-04 11:09:48 +00:00
|
|
|
clearUserpic();
|
2019-03-22 14:19:43 +00:00
|
|
|
});
|
2019-01-04 11:09:48 +00:00
|
|
|
}
|
|
|
|
|
2019-01-10 06:26:08 +00:00
|
|
|
auto ChatData::DefaultAdminRights() -> AdminRights {
|
|
|
|
using Flag = AdminRight;
|
|
|
|
return Flag::f_change_info
|
|
|
|
| Flag::f_delete_messages
|
|
|
|
| Flag::f_ban_users
|
|
|
|
| Flag::f_invite_users
|
|
|
|
| Flag::f_pin_messages;
|
|
|
|
}
|
|
|
|
|
2019-01-05 10:50:04 +00:00
|
|
|
bool ChatData::canWrite() const {
|
|
|
|
// Duplicated in Data::CanWriteValue().
|
2019-01-22 13:05:06 +00:00
|
|
|
return amIn() && !amRestricted(Restriction::f_send_messages);
|
2019-01-05 10:50:04 +00:00
|
|
|
}
|
|
|
|
|
2019-01-04 11:09:48 +00:00
|
|
|
bool ChatData::canEditInformation() const {
|
2019-01-22 13:05:06 +00:00
|
|
|
return amIn() && !amRestricted(Restriction::f_change_info);
|
2019-01-05 10:50:04 +00:00
|
|
|
}
|
|
|
|
|
2019-01-07 12:55:49 +00:00
|
|
|
bool ChatData::canEditPermissions() const {
|
2019-01-22 13:05:06 +00:00
|
|
|
return amIn()
|
2019-01-14 06:34:51 +00:00
|
|
|
&& (amCreator() || (adminRights() & AdminRight::f_ban_users));
|
2019-01-07 12:55:49 +00:00
|
|
|
}
|
|
|
|
|
2019-01-08 13:57:22 +00:00
|
|
|
bool ChatData::canEditUsername() const {
|
|
|
|
return amCreator()
|
|
|
|
&& (fullFlags() & MTPDchatFull::Flag::f_can_set_username);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ChatData::canEditPreHistoryHidden() const {
|
|
|
|
return amCreator();
|
|
|
|
}
|
|
|
|
|
2019-01-05 10:50:04 +00:00
|
|
|
bool ChatData::canAddMembers() const {
|
2019-01-22 13:05:06 +00:00
|
|
|
return amIn() && !amRestricted(Restriction::f_invite_users);
|
2019-01-10 06:26:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ChatData::canSendPolls() const {
|
2019-01-22 13:05:06 +00:00
|
|
|
return amIn() && !amRestricted(Restriction::f_send_polls);
|
2019-01-10 06:26:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ChatData::canAddAdmins() const {
|
2019-01-22 13:05:06 +00:00
|
|
|
return amIn() && amCreator();
|
2019-01-10 06:26:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ChatData::canBanMembers() const {
|
|
|
|
return amCreator()
|
|
|
|
|| (adminRights() & AdminRight::f_ban_users);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ChatData::anyoneCanAddMembers() const {
|
|
|
|
return !(defaultRestrictions() & Restriction::f_invite_users);
|
2019-01-04 11:09:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ChatData::setName(const QString &newName) {
|
|
|
|
updateNameDelayed(newName.isEmpty() ? name : newName, QString(), QString());
|
|
|
|
}
|
|
|
|
|
2019-01-10 06:26:08 +00:00
|
|
|
void ChatData::applyEditAdmin(not_null<UserData*> user, bool isAdmin) {
|
|
|
|
auto flags = Notify::PeerUpdate::Flag::AdminsChanged
|
|
|
|
| Notify::PeerUpdate::Flag::None;
|
|
|
|
if (isAdmin) {
|
|
|
|
admins.emplace(user);
|
|
|
|
} else {
|
|
|
|
admins.remove(user);
|
|
|
|
}
|
|
|
|
Notify::peerUpdatedDelayed(
|
|
|
|
this,
|
|
|
|
Notify::PeerUpdate::Flag::AdminsChanged);
|
|
|
|
}
|
|
|
|
|
2019-01-04 11:09:48 +00:00
|
|
|
void ChatData::invalidateParticipants() {
|
|
|
|
participants.clear();
|
|
|
|
admins.clear();
|
2019-01-13 08:03:34 +00:00
|
|
|
setAdminRights(MTP_chatAdminRights(MTP_flags(0)));
|
|
|
|
//setDefaultRestrictions(MTP_chatBannedRights(MTP_flags(0), MTP_int(0)));
|
2019-01-04 11:09:48 +00:00
|
|
|
invitedByMe.clear();
|
|
|
|
botStatus = 0;
|
2019-01-05 10:50:04 +00:00
|
|
|
Notify::peerUpdatedDelayed(
|
|
|
|
this,
|
|
|
|
UpdateFlag::MembersChanged | UpdateFlag::AdminsChanged);
|
2019-01-04 11:09:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ChatData::setInviteLink(const QString &newInviteLink) {
|
|
|
|
if (newInviteLink != _inviteLink) {
|
|
|
|
_inviteLink = newInviteLink;
|
|
|
|
Notify::peerUpdatedDelayed(this, UpdateFlag::InviteLinkChanged);
|
|
|
|
}
|
|
|
|
}
|
2019-01-05 10:50:04 +00:00
|
|
|
|
|
|
|
void ChatData::setAdminRights(const MTPChatAdminRights &rights) {
|
2019-07-05 13:38:38 +00:00
|
|
|
if (rights.c_chatAdminRights().vflags().v == adminRights()) {
|
2019-01-05 10:50:04 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-07-05 13:38:38 +00:00
|
|
|
_adminRights.set(rights.c_chatAdminRights().vflags().v);
|
2019-01-05 10:50:04 +00:00
|
|
|
Notify::peerUpdatedDelayed(
|
|
|
|
this,
|
|
|
|
(UpdateFlag::RightsChanged
|
|
|
|
| UpdateFlag::AdminsChanged
|
|
|
|
| UpdateFlag::BannedUsersChanged));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChatData::setDefaultRestrictions(const MTPChatBannedRights &rights) {
|
2019-07-05 13:38:38 +00:00
|
|
|
if (rights.c_chatBannedRights().vflags().v == defaultRestrictions()) {
|
2019-01-05 10:50:04 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-07-05 13:38:38 +00:00
|
|
|
_defaultRestrictions.set(rights.c_chatBannedRights().vflags().v);
|
2019-01-05 10:50:04 +00:00
|
|
|
Notify::peerUpdatedDelayed(this, UpdateFlag::RightsChanged);
|
|
|
|
}
|
2019-01-13 08:03:34 +00:00
|
|
|
|
|
|
|
void ChatData::refreshBotStatus() {
|
|
|
|
if (participants.empty()) {
|
|
|
|
botStatus = 0;
|
|
|
|
} else {
|
|
|
|
const auto bot = ranges::find_if(participants, &UserData::isBot);
|
|
|
|
botStatus = (bot == end(participants)) ? -1 : 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto ChatData::applyUpdateVersion(int version) -> UpdateStatus {
|
|
|
|
if (_version > version) {
|
|
|
|
return UpdateStatus::TooOld;
|
|
|
|
} else if (_version + 1 < version) {
|
|
|
|
invalidateParticipants();
|
2019-03-15 13:34:47 +00:00
|
|
|
session().api().requestFullPeer(this);
|
2019-01-13 08:03:34 +00:00
|
|
|
return UpdateStatus::Skipped;
|
|
|
|
}
|
|
|
|
setVersion(version);
|
|
|
|
return UpdateStatus::Good;
|
|
|
|
}
|
|
|
|
|
2019-01-14 06:34:51 +00:00
|
|
|
ChannelData *ChatData::getMigrateToChannel() const {
|
|
|
|
return _migratedTo;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChatData::setMigrateToChannel(ChannelData *channel) {
|
|
|
|
if (_migratedTo != channel) {
|
|
|
|
_migratedTo = channel;
|
|
|
|
if (channel->amIn()) {
|
|
|
|
Notify::peerUpdatedDelayed(this, UpdateFlag::MigrationChanged);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-13 08:03:34 +00:00
|
|
|
namespace Data {
|
|
|
|
|
|
|
|
void ApplyChatUpdate(
|
|
|
|
not_null<ChatData*> chat,
|
|
|
|
const MTPDupdateChatParticipants &update) {
|
2019-07-05 13:38:38 +00:00
|
|
|
ApplyChatUpdate(chat, update.vparticipants());
|
2019-01-13 08:03:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ApplyChatUpdate(
|
|
|
|
not_null<ChatData*> chat,
|
|
|
|
const MTPDupdateChatParticipantAdd &update) {
|
2019-07-05 13:38:38 +00:00
|
|
|
if (chat->applyUpdateVersion(update.vversion().v)
|
2019-01-13 08:03:34 +00:00
|
|
|
!= ChatData::UpdateStatus::Good) {
|
|
|
|
return;
|
|
|
|
} else if (chat->count < 0) {
|
|
|
|
return;
|
|
|
|
}
|
2019-07-05 13:38:38 +00:00
|
|
|
const auto user = chat->owner().userLoaded(update.vuser_id().v);
|
2019-01-13 08:03:34 +00:00
|
|
|
if (!user
|
|
|
|
|| (!chat->participants.empty()
|
|
|
|
&& chat->participants.contains(user))) {
|
|
|
|
chat->invalidateParticipants();
|
|
|
|
++chat->count;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (chat->participants.empty()) {
|
|
|
|
if (chat->count > 0) { // If the count is known.
|
|
|
|
++chat->count;
|
|
|
|
}
|
|
|
|
chat->botStatus = 0;
|
|
|
|
} else {
|
|
|
|
chat->participants.emplace(user);
|
2019-07-05 13:38:38 +00:00
|
|
|
if (update.vinviter_id().v == chat->session().userId()) {
|
2019-01-13 08:03:34 +00:00
|
|
|
chat->invitedByMe.insert(user);
|
|
|
|
} else {
|
|
|
|
chat->invitedByMe.remove(user);
|
|
|
|
}
|
|
|
|
++chat->count;
|
|
|
|
if (user->isBot()) {
|
|
|
|
chat->botStatus = 2;
|
|
|
|
if (!user->botInfo->inited) {
|
|
|
|
chat->session().api().requestFullPeer(user);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Notify::peerUpdatedDelayed(
|
|
|
|
chat,
|
|
|
|
Notify::PeerUpdate::Flag::MembersChanged);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ApplyChatUpdate(
|
|
|
|
not_null<ChatData*> chat,
|
|
|
|
const MTPDupdateChatParticipantDelete &update) {
|
2019-07-05 13:38:38 +00:00
|
|
|
if (chat->applyUpdateVersion(update.vversion().v)
|
2019-01-13 08:03:34 +00:00
|
|
|
!= ChatData::UpdateStatus::Good) {
|
|
|
|
return;
|
|
|
|
} else if (chat->count <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
2019-07-05 13:38:38 +00:00
|
|
|
const auto user = chat->owner().userLoaded(update.vuser_id().v);
|
2019-01-13 08:03:34 +00:00
|
|
|
if (!user
|
|
|
|
|| (!chat->participants.empty()
|
|
|
|
&& !chat->participants.contains(user))) {
|
|
|
|
chat->invalidateParticipants();
|
|
|
|
--chat->count;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (chat->participants.empty()) {
|
|
|
|
if (chat->count > 0) {
|
|
|
|
chat->count--;
|
|
|
|
}
|
|
|
|
chat->botStatus = 0;
|
|
|
|
} else {
|
|
|
|
chat->participants.erase(user);
|
|
|
|
chat->count--;
|
|
|
|
chat->invitedByMe.remove(user);
|
|
|
|
chat->admins.remove(user);
|
|
|
|
if (user->isSelf()) {
|
|
|
|
chat->setAdminRights(MTP_chatAdminRights(MTP_flags(0)));
|
|
|
|
}
|
|
|
|
if (const auto history = chat->owner().historyLoaded(chat)) {
|
|
|
|
if (history->lastKeyboardFrom == user->id) {
|
|
|
|
history->clearLastKeyboard();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (chat->botStatus > 0 && user->botInfo) {
|
|
|
|
chat->refreshBotStatus();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Notify::peerUpdatedDelayed(
|
|
|
|
chat,
|
|
|
|
Notify::PeerUpdate::Flag::MembersChanged);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ApplyChatUpdate(
|
|
|
|
not_null<ChatData*> chat,
|
|
|
|
const MTPDupdateChatParticipantAdmin &update) {
|
2019-07-05 13:38:38 +00:00
|
|
|
if (chat->applyUpdateVersion(update.vversion().v)
|
2019-01-13 08:03:34 +00:00
|
|
|
!= ChatData::UpdateStatus::Good) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-05 13:38:38 +00:00
|
|
|
const auto user = chat->owner().userLoaded(update.vuser_id().v);
|
2019-01-13 08:03:34 +00:00
|
|
|
if (!user) {
|
|
|
|
chat->invalidateParticipants();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (user->isSelf()) {
|
2019-07-05 13:38:38 +00:00
|
|
|
chat->setAdminRights(MTP_chatAdminRights(mtpIsTrue(update.vis_admin())
|
2019-01-13 08:03:34 +00:00
|
|
|
? MTP_flags(ChatData::DefaultAdminRights())
|
|
|
|
: MTP_flags(0)));
|
|
|
|
}
|
2019-07-05 13:38:38 +00:00
|
|
|
if (mtpIsTrue(update.vis_admin())) {
|
2019-01-13 08:03:34 +00:00
|
|
|
if (chat->noParticipantInfo()) {
|
|
|
|
chat->session().api().requestFullPeer(chat);
|
|
|
|
} else {
|
|
|
|
chat->admins.emplace(user);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
chat->admins.erase(user);
|
|
|
|
}
|
|
|
|
Notify::peerUpdatedDelayed(
|
|
|
|
chat,
|
|
|
|
Notify::PeerUpdate::Flag::AdminsChanged);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ApplyChatUpdate(
|
|
|
|
not_null<ChatData*> chat,
|
|
|
|
const MTPDupdateChatDefaultBannedRights &update) {
|
2019-07-05 13:38:38 +00:00
|
|
|
if (chat->applyUpdateVersion(update.vversion().v)
|
2019-01-13 08:03:34 +00:00
|
|
|
!= ChatData::UpdateStatus::Good) {
|
|
|
|
return;
|
|
|
|
}
|
2019-07-05 13:38:38 +00:00
|
|
|
chat->setDefaultRestrictions(update.vdefault_banned_rights());
|
2019-01-13 08:03:34 +00:00
|
|
|
}
|
|
|
|
|
2019-04-23 09:40:14 +00:00
|
|
|
void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
|
2019-07-05 13:38:38 +00:00
|
|
|
ApplyChatUpdate(chat, update.vparticipants());
|
2019-04-23 09:40:14 +00:00
|
|
|
|
2019-07-05 13:38:38 +00:00
|
|
|
if (const auto info = update.vbot_info()) {
|
|
|
|
for (const auto &item : info->v) {
|
2019-04-23 09:40:14 +00:00
|
|
|
item.match([&](const MTPDbotInfo &data) {
|
2019-07-05 13:38:38 +00:00
|
|
|
const auto userId = data.vuser_id().v;
|
2019-04-23 09:40:14 +00:00
|
|
|
if (const auto bot = chat->owner().userLoaded(userId)) {
|
|
|
|
bot->setBotInfo(item);
|
|
|
|
chat->session().api().fullPeerUpdated().notify(bot);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2019-07-05 13:38:38 +00:00
|
|
|
chat->setFullFlags(update.vflags().v);
|
|
|
|
if (const auto photo = update.vchat_photo()) {
|
|
|
|
chat->setUserpicPhoto(*photo);
|
|
|
|
} else {
|
|
|
|
chat->setUserpicPhoto(MTP_photoEmpty(MTP_long(0)));
|
|
|
|
}
|
|
|
|
chat->setInviteLink(update.vexported_invite().match([&](
|
2019-04-23 09:40:14 +00:00
|
|
|
const MTPDchatInviteExported &data) {
|
2019-07-05 13:38:38 +00:00
|
|
|
return qs(data.vlink());
|
2019-04-23 09:40:14 +00:00
|
|
|
}, [&](const MTPDchatInviteEmpty &) {
|
|
|
|
return QString();
|
|
|
|
}));
|
2019-07-05 13:38:38 +00:00
|
|
|
if (const auto pinned = update.vpinned_msg_id()) {
|
|
|
|
chat->setPinnedMessageId(pinned->v);
|
2019-04-23 09:40:14 +00:00
|
|
|
} else {
|
|
|
|
chat->clearPinnedMessage();
|
|
|
|
}
|
2019-07-05 13:38:38 +00:00
|
|
|
chat->checkFolder(update.vfolder_id().value_or_empty());
|
2019-04-23 09:40:14 +00:00
|
|
|
chat->fullUpdated();
|
|
|
|
|
|
|
|
chat->session().api().applyNotifySettings(
|
|
|
|
MTP_inputNotifyPeer(chat->input),
|
2019-07-05 13:38:38 +00:00
|
|
|
update.vnotify_settings());
|
2019-04-23 09:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ApplyChatUpdate(
|
2019-01-13 08:03:34 +00:00
|
|
|
not_null<ChatData*> chat,
|
|
|
|
const MTPChatParticipants &participants) {
|
|
|
|
participants.match([&](const MTPDchatParticipantsForbidden &data) {
|
2019-07-05 13:38:38 +00:00
|
|
|
if (const auto self = data.vself_participant()) {
|
|
|
|
// self->
|
2019-01-13 08:03:34 +00:00
|
|
|
}
|
|
|
|
chat->count = -1;
|
|
|
|
chat->invalidateParticipants();
|
|
|
|
}, [&](const MTPDchatParticipants &data) {
|
2019-07-05 13:38:38 +00:00
|
|
|
const auto status = chat->applyUpdateVersion(data.vversion().v);
|
2019-01-13 08:03:34 +00:00
|
|
|
if (status == ChatData::UpdateStatus::TooOld) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Even if we skipped some updates, we got current participants
|
|
|
|
// and we've requested peer from API to have current rights.
|
2019-07-05 13:38:38 +00:00
|
|
|
chat->setVersion(data.vversion().v);
|
2019-01-13 08:03:34 +00:00
|
|
|
|
2019-07-05 13:38:38 +00:00
|
|
|
const auto &list = data.vparticipants().v;
|
2019-01-13 08:03:34 +00:00
|
|
|
chat->count = list.size();
|
|
|
|
chat->participants.clear();
|
|
|
|
chat->invitedByMe.clear();
|
|
|
|
chat->admins.clear();
|
|
|
|
chat->setAdminRights(MTP_chatAdminRights(MTP_flags(0)));
|
|
|
|
const auto selfUserId = chat->session().userId();
|
|
|
|
for (const auto &participant : list) {
|
|
|
|
const auto userId = participant.match([&](const auto &data) {
|
2019-07-05 13:38:38 +00:00
|
|
|
return data.vuser_id().v;
|
2019-01-13 08:03:34 +00:00
|
|
|
});
|
|
|
|
const auto user = chat->owner().userLoaded(userId);
|
|
|
|
if (!user) {
|
|
|
|
chat->invalidateParticipants();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
chat->participants.emplace(user);
|
|
|
|
|
|
|
|
const auto inviterId = participant.match([&](
|
|
|
|
const MTPDchatParticipantCreator &data) {
|
|
|
|
return 0;
|
|
|
|
}, [&](const auto &data) {
|
2019-07-05 13:38:38 +00:00
|
|
|
return data.vinviter_id().v;
|
2019-01-13 08:03:34 +00:00
|
|
|
});
|
|
|
|
if (inviterId == selfUserId) {
|
|
|
|
chat->invitedByMe.insert(user);
|
|
|
|
}
|
|
|
|
|
|
|
|
participant.match([&](const MTPDchatParticipantCreator &data) {
|
|
|
|
chat->creator = userId;
|
|
|
|
}, [&](const MTPDchatParticipantAdmin &data) {
|
|
|
|
chat->admins.emplace(user);
|
|
|
|
if (user->isSelf()) {
|
|
|
|
chat->setAdminRights(MTP_chatAdminRights(
|
|
|
|
MTP_flags(ChatData::DefaultAdminRights())));
|
|
|
|
}
|
|
|
|
}, [](const MTPDchatParticipant &) {
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (chat->participants.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (const auto history = chat->owner().historyLoaded(chat)) {
|
|
|
|
if (history->lastKeyboardFrom) {
|
|
|
|
const auto i = ranges::find(
|
|
|
|
chat->participants,
|
|
|
|
history->lastKeyboardFrom,
|
|
|
|
&UserData::id);
|
|
|
|
if (i == end(chat->participants)) {
|
|
|
|
history->clearLastKeyboard();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
chat->refreshBotStatus();
|
|
|
|
Notify::peerUpdatedDelayed(
|
|
|
|
chat,
|
|
|
|
Notify::PeerUpdate::Flag::MembersChanged
|
|
|
|
| Notify::PeerUpdate::Flag::AdminsChanged);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Data
|