| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585 |
- /*
- 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"
- #include "core/application.h"
- #include "data/data_user.h"
- #include "data/data_channel.h"
- #include "data/data_session.h"
- #include "data/data_changes.h"
- #include "data/data_group_call.h"
- #include "data/data_message_reactions.h"
- #include "data/notify/data_notify_settings.h"
- #include "history/history.h"
- #include "main/main_session.h"
- #include "apiwrap.h"
- #include "api/api_invite_links.h"
- namespace {
- using UpdateFlag = Data::PeerUpdate::Flag;
- } // namespace
- ChatData::ChatData(not_null<Data::Session*> owner, PeerId id)
- : PeerData(owner, id)
- , inputChat(MTP_long(peerToChat(id).bare)) {
- _flags.changes(
- ) | rpl::start_with_next([=](const Flags::Change &change) {
- if (change.diff & Flag::CallNotEmpty) {
- if (const auto history = this->owner().historyLoaded(this)) {
- history->updateChatListEntry();
- }
- }
- }, _lifetime);
- }
- void ChatData::setPhoto(const MTPChatPhoto &photo) {
- photo.match([&](const MTPDchatPhoto &data) {
- updateUserpic(
- data.vphoto_id().v,
- data.vdc_id().v,
- data.is_has_video());
- }, [&](const MTPDchatPhotoEmpty &) {
- clearUserpic();
- });
- }
- ChatAdminRightsInfo ChatData::defaultAdminRights(not_null<UserData*> user) {
- const auto isCreator = (creator == peerToUser(user->id))
- || (user->isSelf() && amCreator());
- using Flag = ChatAdminRight;
- return ChatAdminRightsInfo(Flag::Other
- | Flag::ChangeInfo
- | Flag::DeleteMessages
- | Flag::BanUsers
- | Flag::InviteByLinkOrAdd
- | Flag::PinMessages
- | Flag::ManageCall
- | (isCreator ? Flag::AddAdmins : Flag(0)));
- }
- bool ChatData::allowsForwarding() const {
- return !(flags() & Flag::NoForwards);
- }
- bool ChatData::canEditInformation() const {
- return amIn() && !amRestricted(ChatRestriction::ChangeInfo);
- }
- bool ChatData::canEditPermissions() const {
- return amIn()
- && (amCreator() || (adminRights() & ChatAdminRight::BanUsers));
- }
- bool ChatData::canEditUsername() const {
- return amCreator()
- && (flags() & Flag::CanSetUsername);
- }
- bool ChatData::canEditPreHistoryHidden() const {
- return amCreator();
- }
- bool ChatData::canDeleteMessages() const {
- return amCreator()
- || (adminRights() & ChatAdminRight::DeleteMessages);
- }
- bool ChatData::canAddMembers() const {
- return amIn() && !amRestricted(ChatRestriction::AddParticipants);
- }
- bool ChatData::canAddAdmins() const {
- return amIn() && amCreator();
- }
- bool ChatData::canBanMembers() const {
- return amCreator()
- || (adminRights() & ChatAdminRight::BanUsers);
- }
- bool ChatData::anyoneCanAddMembers() const {
- return !(defaultRestrictions() & ChatRestriction::AddParticipants);
- }
- void ChatData::setName(const QString &newName) {
- updateNameDelayed(newName.isEmpty() ? name() : newName, {}, {});
- }
- void ChatData::applyEditAdmin(not_null<UserData*> user, bool isAdmin) {
- if (isAdmin) {
- admins.emplace(user);
- } else {
- admins.remove(user);
- }
- session().changes().peerUpdated(this, UpdateFlag::Admins);
- }
- void ChatData::invalidateParticipants() {
- participants.clear();
- admins.clear();
- setAdminRights(ChatAdminRights());
- //setDefaultRestrictions(ChatRestrictions());
- invitedByMe.clear();
- botStatus = 0;
- session().changes().peerUpdated(
- this,
- UpdateFlag::Members | UpdateFlag::Admins);
- }
- void ChatData::setFlags(ChatDataFlags which) {
- const auto wasIn = amIn();
- _flags.set(which);
- if (wasIn && !amIn()) {
- crl::on_main(&session(), [=] {
- if (!amIn()) {
- Core::App().closeChatFromWindows(this);
- }
- });
- }
- }
- void ChatData::setInviteLink(const QString &newInviteLink) {
- _inviteLink = newInviteLink;
- }
- bool ChatData::canHaveInviteLink() const {
- return amCreator()
- || (adminRights() & ChatAdminRight::InviteByLinkOrAdd);
- }
- void ChatData::setAdminRights(ChatAdminRights rights) {
- if (rights == adminRights()) {
- return;
- }
- _adminRights.set(rights);
- if (!canHaveInviteLink()) {
- setPendingRequestsCount(0, std::vector<UserId>{});
- }
- session().changes().peerUpdated(
- this,
- UpdateFlag::Rights | UpdateFlag::Admins | UpdateFlag::BannedUsers);
- }
- void ChatData::setDefaultRestrictions(ChatRestrictions rights) {
- if (rights == defaultRestrictions()) {
- return;
- }
- _defaultRestrictions.set(rights);
- session().changes().peerUpdated(this, UpdateFlag::Rights);
- }
- void ChatData::refreshBotStatus() {
- if (participants.empty()) {
- botStatus = 0;
- } else {
- const auto bot = ranges::none_of(participants, &UserData::isBot);
- botStatus = bot ? -1 : 2;
- }
- }
- auto ChatData::applyUpdateVersion(int version) -> UpdateStatus {
- if (_version > version) {
- return UpdateStatus::TooOld;
- } else if (_version + 1 < version) {
- invalidateParticipants();
- session().api().requestFullPeer(this);
- return UpdateStatus::Skipped;
- }
- setVersion(version);
- return UpdateStatus::Good;
- }
- ChannelData *ChatData::getMigrateToChannel() const {
- return _migratedTo;
- }
- void ChatData::setMigrateToChannel(ChannelData *channel) {
- if (_migratedTo != channel) {
- _migratedTo = channel;
- if (channel->amIn()) {
- session().changes().peerUpdated(this, UpdateFlag::Migration);
- }
- }
- }
- void ChatData::setGroupCall(
- const MTPInputGroupCall &call,
- TimeId scheduleDate,
- bool rtmp) {
- if (migrateTo()) {
- return;
- }
- call.match([&](const MTPDinputGroupCall &data) {
- if (_call && _call->id() == data.vid().v) {
- return;
- } else if (!_call && !data.vid().v) {
- return;
- } else if (!data.vid().v) {
- clearGroupCall();
- return;
- }
- const auto hasCall = (_call != nullptr);
- if (hasCall) {
- owner().unregisterGroupCall(_call.get());
- }
- _call = std::make_unique<Data::GroupCall>(
- this,
- data.vid().v,
- data.vaccess_hash().v,
- scheduleDate,
- rtmp);
- owner().registerGroupCall(_call.get());
- session().changes().peerUpdated(this, UpdateFlag::GroupCall);
- addFlags(Flag::CallActive);
- });
- }
- void ChatData::clearGroupCall() {
- if (!_call) {
- return;
- } else if (const auto group = migrateTo(); group && !group->groupCall()) {
- group->migrateCall(base::take(_call));
- } else {
- owner().unregisterGroupCall(_call.get());
- _call = nullptr;
- }
- session().changes().peerUpdated(this, UpdateFlag::GroupCall);
- removeFlags(Flag::CallActive | Flag::CallNotEmpty);
- }
- void ChatData::setGroupCallDefaultJoinAs(PeerId peerId) {
- _callDefaultJoinAs = peerId;
- }
- PeerId ChatData::groupCallDefaultJoinAs() const {
- return _callDefaultJoinAs;
- }
- void ChatData::setBotCommands(const std::vector<Data::BotCommands> &list) {
- if (_botCommands.update(list)) {
- owner().botCommandsChanged(this);
- }
- }
- void ChatData::setPendingRequestsCount(
- int count,
- const QVector<MTPlong> &recentRequesters) {
- setPendingRequestsCount(count, ranges::views::all(
- recentRequesters
- ) | ranges::views::transform([&](const MTPlong &value) {
- return UserId(value);
- }) | ranges::to_vector);
- }
- void ChatData::setPendingRequestsCount(
- int count,
- std::vector<UserId> recentRequesters) {
- if (_pendingRequestsCount != count
- || _recentRequesters != recentRequesters) {
- _pendingRequestsCount = count;
- _recentRequesters = std::move(recentRequesters);
- session().changes().peerUpdated(this, UpdateFlag::PendingRequests);
- }
- }
- void ChatData::setAllowedReactions(Data::AllowedReactions value) {
- if (_allowedReactions != value) {
- const auto enabled = [](const Data::AllowedReactions &allowed) {
- return (allowed.type != Data::AllowedReactionsType::Some)
- || !allowed.some.empty()
- || allowed.paidEnabled;
- };
- const auto was = enabled(_allowedReactions);
- _allowedReactions = std::move(value);
- const auto now = enabled(_allowedReactions);
- if (was != now) {
- owner().reactions().updateAllInHistory(this, now);
- }
- session().changes().peerUpdated(this, UpdateFlag::Reactions);
- }
- }
- const Data::AllowedReactions &ChatData::allowedReactions() const {
- return _allowedReactions;
- }
- namespace Data {
- void ApplyChatUpdate(
- not_null<ChatData*> chat,
- const MTPDupdateChatParticipants &update) {
- ApplyChatUpdate(chat, update.vparticipants());
- }
- void ApplyChatUpdate(
- not_null<ChatData*> chat,
- const MTPDupdateChatParticipantAdd &update) {
- if (chat->applyUpdateVersion(update.vversion().v)
- != ChatData::UpdateStatus::Good) {
- return;
- } else if (chat->count < 0) {
- return;
- }
- const auto user = chat->owner().userLoaded(update.vuser_id().v);
- const auto session = &chat->session();
- 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);
- if (UserId(update.vinviter_id()) == session->userId()) {
- chat->invitedByMe.insert(user);
- } else {
- chat->invitedByMe.remove(user);
- }
- ++chat->count;
- if (user->isBot()) {
- chat->botStatus = 2;
- if (!user->botInfo->inited) {
- session->api().requestFullPeer(user);
- }
- }
- }
- session->changes().peerUpdated(chat, UpdateFlag::Members);
- }
- void ApplyChatUpdate(
- not_null<ChatData*> chat,
- const MTPDupdateChatParticipantDelete &update) {
- if (chat->applyUpdateVersion(update.vversion().v)
- != ChatData::UpdateStatus::Good) {
- return;
- } else if (chat->count <= 0) {
- return;
- }
- const auto user = chat->owner().userLoaded(update.vuser_id().v);
- 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(ChatAdminRights());
- }
- if (const auto history = chat->owner().historyLoaded(chat)) {
- if (history->lastKeyboardFrom == user->id) {
- history->clearLastKeyboard();
- }
- }
- if (chat->botStatus > 0 && user->isBot()) {
- chat->refreshBotStatus();
- }
- }
- chat->session().changes().peerUpdated(chat, UpdateFlag::Members);
- }
- void ApplyChatUpdate(
- not_null<ChatData*> chat,
- const MTPDupdateChatParticipantAdmin &update) {
- if (chat->applyUpdateVersion(update.vversion().v)
- != ChatData::UpdateStatus::Good) {
- return;
- }
- const auto session = &chat->session();
- const auto user = chat->owner().userLoaded(update.vuser_id().v);
- if (!user) {
- chat->invalidateParticipants();
- return;
- }
- if (user->isSelf()) {
- chat->setAdminRights(mtpIsTrue(update.vis_admin())
- ? chat->defaultAdminRights(user).flags
- : ChatAdminRights());
- }
- if (mtpIsTrue(update.vis_admin())) {
- if (chat->noParticipantInfo()) {
- session->api().requestFullPeer(chat);
- } else {
- chat->admins.emplace(user);
- }
- } else {
- chat->admins.erase(user);
- }
- session->changes().peerUpdated(chat, UpdateFlag::Admins);
- }
- void ApplyChatUpdate(
- not_null<ChatData*> chat,
- const MTPDupdateChatDefaultBannedRights &update) {
- if (chat->applyUpdateVersion(update.vversion().v)
- != ChatData::UpdateStatus::Good) {
- return;
- }
- chat->setDefaultRestrictions(ChatRestrictionsInfo(
- update.vdefault_banned_rights()).flags);
- }
- void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
- ApplyChatUpdate(chat, update.vparticipants());
- if (const auto call = update.vcall()) {
- chat->setGroupCall(*call);
- } else {
- chat->clearGroupCall();
- }
- if (const auto as = update.vgroupcall_default_join_as()) {
- chat->setGroupCallDefaultJoinAs(peerFromMTP(*as));
- } else {
- chat->setGroupCallDefaultJoinAs(0);
- }
- chat->setMessagesTTL(update.vttl_period().value_or_empty());
- if (const auto info = update.vbot_info()) {
- auto &&commands = ranges::views::all(
- info->v
- ) | ranges::views::transform(Data::BotCommandsFromTL);
- chat->setBotCommands(std::move(commands) | ranges::to_vector);
- } else {
- chat->setBotCommands({});
- }
- using Flag = ChatDataFlag;
- const auto mask = Flag::CanSetUsername;
- chat->setFlags((chat->flags() & ~mask)
- | (update.is_can_set_username() ? Flag::CanSetUsername : Flag()));
- if (const auto photo = update.vchat_photo()) {
- chat->setUserpicPhoto(*photo);
- } else {
- chat->setUserpicPhoto(MTP_photoEmpty(MTP_long(0)));
- }
- if (const auto invite = update.vexported_invite()) {
- chat->session().api().inviteLinks().setMyPermanent(chat, *invite);
- } else {
- chat->session().api().inviteLinks().clearMyPermanent(chat);
- }
- if (const auto pinned = update.vpinned_msg_id()) {
- SetTopPinnedMessageId(chat, pinned->v);
- }
- chat->checkFolder(update.vfolder_id().value_or_empty());
- chat->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
- chat->setTranslationDisabled(update.is_translations_disabled());
- const auto reactionsLimit = update.vreactions_limit().value_or_empty();
- if (const auto allowed = update.vavailable_reactions()) {
- const auto paidEnabled = false;
- auto parsed = Data::Parse(*allowed, reactionsLimit, paidEnabled);
- chat->setAllowedReactions(std::move(parsed));
- } else {
- chat->setAllowedReactions({ .maxCount = reactionsLimit });
- }
- chat->fullUpdated();
- chat->setAbout(qs(update.vabout()));
- chat->setPendingRequestsCount(
- update.vrequests_pending().value_or_empty(),
- update.vrecent_requesters().value_or_empty());
- chat->owner().notifySettings().apply(chat, update.vnotify_settings());
- }
- void ApplyChatUpdate(
- not_null<ChatData*> chat,
- const MTPChatParticipants &participants) {
- const auto session = &chat->session();
- participants.match([&](const MTPDchatParticipantsForbidden &data) {
- if (const auto self = data.vself_participant()) {
- // self->
- }
- chat->count = -1;
- chat->invalidateParticipants();
- }, [&](const MTPDchatParticipants &data) {
- const auto status = chat->applyUpdateVersion(data.vversion().v);
- 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.
- chat->setVersion(data.vversion().v);
- const auto &list = data.vparticipants().v;
- chat->count = list.size();
- chat->participants.clear();
- chat->invitedByMe.clear();
- chat->admins.clear();
- chat->setAdminRights(ChatAdminRights());
- const auto selfUserId = session->userId();
- for (const auto &participant : list) {
- const auto userId = participant.match([&](const auto &data) {
- return data.vuser_id().v;
- });
- 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 UserId(0);
- }, [&](const auto &data) {
- return UserId(data.vinviter_id());
- });
- 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(
- chat->defaultAdminRights(user).flags);
- }
- }, [](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();
- session->changes().peerUpdated(
- chat,
- UpdateFlag::Members | UpdateFlag::Admins);
- });
- }
- } // namespace Data
|