| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357 |
- /*
- 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_channel.h"
- #include "api/api_global_privacy.h"
- #include "data/components/credits.h"
- #include "data/data_changes.h"
- #include "data/data_channel_admins.h"
- #include "data/data_user.h"
- #include "data/data_chat.h"
- #include "data/data_session.h"
- #include "data/data_stories.h"
- #include "data/data_folder.h"
- #include "data/data_forum.h"
- #include "data/data_forum_icons.h"
- #include "data/data_histories.h"
- #include "data/data_group_call.h"
- #include "data/data_message_reactions.h"
- #include "data/data_wall_paper.h"
- #include "data/notify/data_notify_settings.h"
- #include "main/main_session.h"
- #include "main/session/send_as_peers.h"
- #include "base/unixtime.h"
- #include "core/application.h"
- #include "history/history.h"
- #include "api/api_chat_invite.h"
- #include "api/api_invite_links.h"
- #include "apiwrap.h"
- #include "storage/storage_account.h"
- #include "ui/unread_badge.h"
- #include "window/notifications_manager.h"
- namespace {
- using UpdateFlag = Data::PeerUpdate::Flag;
- } // namespace
- MegagroupInfo::MegagroupInfo() = default;
- MegagroupInfo::~MegagroupInfo() = default;
- ChatData *MegagroupInfo::getMigrateFromChat() const {
- return _migratedFrom;
- }
- void MegagroupInfo::setMigrateFromChat(ChatData *chat) {
- _migratedFrom = chat;
- }
- const ChannelLocation *MegagroupInfo::getLocation() const {
- return _location.address.isEmpty() ? nullptr : &_location;
- }
- void MegagroupInfo::setLocation(const ChannelLocation &location) {
- _location = location;
- }
- Data::ChatBotCommands::Changed MegagroupInfo::setBotCommands(
- const std::vector<Data::BotCommands> &list) {
- return _botCommands.update(list);
- }
- void MegagroupInfo::ensureForum(not_null<ChannelData*> that) {
- if (!_forum) {
- const auto history = that->owner().history(that);
- _forum = std::make_unique<Data::Forum>(history);
- history->forumChanged(nullptr);
- }
- }
- Data::Forum *MegagroupInfo::forum() const {
- return _forum.get();
- }
- std::unique_ptr<Data::Forum> MegagroupInfo::takeForumData() {
- if (auto result = base::take(_forum)) {
- result->history()->forumChanged(result.get());
- return result;
- }
- return nullptr;
- }
- ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id)
- : PeerData(owner, id)
- , inputChannel(
- MTP_inputChannel(MTP_long(peerToChannel(id).bare), MTP_long(0)))
- , _ptsWaiter(&owner->session().updates()) {
- }
- void ChannelData::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();
- });
- }
- void ChannelData::setName(
- const QString &newName,
- const QString &newUsername) {
- updateNameDelayed(newName.isEmpty() ? name() : newName, {}, newUsername);
- }
- void ChannelData::setUsername(const QString &username) {
- _username.setUsername(username);
- }
- void ChannelData::setUsernames(const Data::Usernames &newUsernames) {
- const auto wasUsername = username();
- const auto wasUsernames = usernames();
- _username.setUsernames(newUsernames);
- const auto nowUsername = username();
- const auto nowUsernames = usernames();
- session().changes().peerUpdated(
- this,
- UpdateFlag()
- | ((wasUsername != nowUsername)
- ? UpdateFlag::Username
- : UpdateFlag())
- | (!ranges::equal(wasUsernames, nowUsernames)
- ? UpdateFlag::Usernames
- : UpdateFlag()));
- }
- QString ChannelData::username() const {
- return _username.username();
- }
- QString ChannelData::editableUsername() const {
- return _username.editableUsername();
- }
- const std::vector<QString> &ChannelData::usernames() const {
- return _username.usernames();
- }
- bool ChannelData::isUsernameEditable(QString username) const {
- return _username.isEditable(username);
- }
- void ChannelData::setAccessHash(uint64 accessHash) {
- access = accessHash;
- input = MTP_inputPeerChannel(
- MTP_long(peerToChannel(id).bare),
- MTP_long(accessHash));
- inputChannel = MTP_inputChannel(
- MTP_long(peerToChannel(id).bare),
- MTP_long(accessHash));
- }
- void ChannelData::setFlags(ChannelDataFlags which) {
- const auto diff = flags() ^ which;
- if ((which & Flag::Megagroup) && !mgInfo) {
- mgInfo = std::make_unique<MegagroupInfo>();
- }
- // Let Data::Forum live till the end of _flags.set.
- // That way the data can be used in changes handler.
- // Example: render frame for forum auto-closing animation.
- const auto taken = ((diff & Flag::Forum) && !(which & Flag::Forum))
- ? mgInfo->takeForumData()
- : nullptr;
- const auto wasIn = amIn();
- if ((diff & Flag::Forum) && (which & Flag::Forum)) {
- mgInfo->ensureForum(this);
- }
- _flags.set(which);
- if (diff & (Flag::Left | Flag::Forbidden)) {
- if (const auto chat = getMigrateFromChat()) {
- session().changes().peerUpdated(chat, UpdateFlag::Migration);
- session().changes().peerUpdated(this, UpdateFlag::Migration);
- }
- if (wasIn && !amIn()) {
- crl::on_main(&session(), [=] {
- if (!amIn()) {
- Core::App().closeChatFromWindows(this);
- }
- });
- }
- }
- if (diff & (Flag::Forum
- | Flag::CallNotEmpty
- | Flag::SimilarExpanded
- | Flag::Signatures
- | Flag::SignatureProfiles)) {
- if (const auto history = this->owner().historyLoaded(this)) {
- if (diff & Flag::CallNotEmpty) {
- history->updateChatListEntry();
- }
- if (diff & Flag::Forum) {
- Core::App().notifications().clearFromHistory(history);
- history->updateChatListEntryHeight();
- if (history->inChatList()) {
- if (const auto forum = this->forum()) {
- forum->preloadTopics();
- }
- }
- }
- if (diff & Flag::SimilarExpanded) {
- if (const auto item = history->joinedMessageInstance()) {
- history->owner().requestItemResize(item);
- }
- }
- if (diff & Flag::SignatureProfiles) {
- history->forceFullResize();
- }
- if (diff & (Flag::Signatures | Flag::SignatureProfiles)) {
- session().changes().peerUpdated(this, UpdateFlag::Rights);
- }
- }
- }
- if (const auto raw = taken.get()) {
- owner().forumIcons().clearUserpicsReset(raw);
- }
- }
- void ChannelData::addFlags(ChannelDataFlags which) {
- setFlags(flags() | which);
- }
- void ChannelData::removeFlags(ChannelDataFlags which) {
- setFlags(flags() & ~which);
- }
- void ChannelData::setInviteLink(const QString &newInviteLink) {
- _inviteLink = newInviteLink;
- }
- bool ChannelData::canHaveInviteLink() const {
- return amCreator()
- || (adminRights() & AdminRight::InviteByLinkOrAdd);
- }
- void ChannelData::setLocation(const MTPChannelLocation &data) {
- if (!mgInfo) {
- return;
- }
- const auto was = mgInfo->getLocation();
- const auto wasValue = was ? *was : ChannelLocation();
- data.match([&](const MTPDchannelLocation &data) {
- data.vgeo_point().match([&](const MTPDgeoPoint &point) {
- mgInfo->setLocation({
- qs(data.vaddress()),
- Data::LocationPoint(point)
- });
- }, [&](const MTPDgeoPointEmpty &) {
- mgInfo->setLocation(ChannelLocation());
- });
- }, [&](const MTPDchannelLocationEmpty &) {
- mgInfo->setLocation(ChannelLocation());
- });
- const auto now = mgInfo->getLocation();
- const auto nowValue = now ? *now : ChannelLocation();
- if (was != now || (was && wasValue != nowValue)) {
- session().changes().peerUpdated(
- this,
- UpdateFlag::ChannelLocation);
- }
- }
- const ChannelLocation *ChannelData::getLocation() const {
- return mgInfo ? mgInfo->getLocation() : nullptr;
- }
- void ChannelData::setLinkedChat(ChannelData *linked) {
- if (_linkedChat != linked) {
- _linkedChat = linked;
- if (const auto history = owner().historyLoaded(this)) {
- history->forceFullResize();
- }
- session().changes().peerUpdated(this, UpdateFlag::ChannelLinkedChat);
- }
- }
- ChannelData *ChannelData::linkedChat() const {
- return _linkedChat.value_or(nullptr);
- }
- bool ChannelData::linkedChatKnown() const {
- return _linkedChat.has_value();
- }
- void ChannelData::setMembersCount(int newMembersCount) {
- if (_membersCount != newMembersCount) {
- if (isMegagroup()
- && canViewMembers()
- && !mgInfo->lastParticipants.empty()) {
- mgInfo->lastParticipantsStatus
- |= MegagroupInfo::LastParticipantsCountOutdated;
- mgInfo->lastParticipantsCount = membersCount();
- }
- _membersCount = newMembersCount;
- session().changes().peerUpdated(this, UpdateFlag::Members);
- }
- }
- void ChannelData::setAdminsCount(int newAdminsCount) {
- if (_adminsCount != newAdminsCount) {
- _adminsCount = newAdminsCount;
- session().changes().peerUpdated(this, UpdateFlag::Admins);
- }
- }
- void ChannelData::setRestrictedCount(int newRestrictedCount) {
- if (_restrictedCount != newRestrictedCount) {
- _restrictedCount = newRestrictedCount;
- session().changes().peerUpdated(this, UpdateFlag::BannedUsers);
- }
- }
- void ChannelData::setKickedCount(int newKickedCount) {
- if (_kickedCount != newKickedCount) {
- _kickedCount = newKickedCount;
- session().changes().peerUpdated(this, UpdateFlag::BannedUsers);
- }
- }
- void ChannelData::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 ChannelData::setPendingRequestsCount(
- int count,
- std::vector<UserId> recentRequesters) {
- if (_pendingRequestsCount != count
- || _recentRequesters != recentRequesters) {
- _pendingRequestsCount = count;
- _recentRequesters = std::move(recentRequesters);
- session().changes().peerUpdated(this, UpdateFlag::PendingRequests);
- }
- }
- ChatRestrictionsInfo ChannelData::KickedRestrictedRights(
- not_null<PeerData*> participant) {
- using Flag = ChatRestriction;
- const auto flags = Flag::ViewMessages
- | Flag::SendStickers
- | Flag::SendGifs
- | Flag::SendGames
- | Flag::SendInline
- | Flag::SendPhotos
- | Flag::SendVideos
- | Flag::SendVideoMessages
- | Flag::SendMusic
- | Flag::SendVoiceMessages
- | Flag::SendFiles
- | Flag::SendOther
- | Flag::EmbedLinks;
- return ChatRestrictionsInfo(
- (participant->isUser() ? flags : Flag::ViewMessages),
- std::numeric_limits<int32>::max());
- }
- void ChannelData::applyEditAdmin(
- not_null<UserData*> user,
- ChatAdminRightsInfo oldRights,
- ChatAdminRightsInfo newRights,
- const QString &rank) {
- if (mgInfo) {
- // If rights are empty - still add participant? TODO check
- if (!base::contains(mgInfo->lastParticipants, user)) {
- mgInfo->lastParticipants.push_front(user);
- setMembersCount(membersCount() + 1);
- if (user->isBot() && !mgInfo->bots.contains(user)) {
- mgInfo->bots.insert(user);
- if (mgInfo->botStatus != 0 && mgInfo->botStatus < 2) {
- mgInfo->botStatus = 2;
- }
- }
- }
- // If rights are empty - still remove restrictions? TODO check
- if (mgInfo->lastRestricted.contains(user)) {
- mgInfo->lastRestricted.remove(user);
- if (restrictedCount() > 0) {
- setRestrictedCount(restrictedCount() - 1);
- }
- }
- auto userId = peerToUser(user->id);
- auto it = mgInfo->lastAdmins.find(user);
- if (newRights.flags) {
- auto lastAdmin = MegagroupInfo::Admin { newRights };
- lastAdmin.canEdit = true;
- if (it == mgInfo->lastAdmins.cend()) {
- mgInfo->lastAdmins.emplace(user, lastAdmin);
- setAdminsCount(adminsCount() + 1);
- } else {
- it->second = lastAdmin;
- }
- Data::ChannelAdminChanges(this).add(userId, rank);
- } else {
- if (it != mgInfo->lastAdmins.cend()) {
- mgInfo->lastAdmins.erase(it);
- if (adminsCount() > 0) {
- setAdminsCount(adminsCount() - 1);
- }
- }
- Data::ChannelAdminChanges(this).remove(userId);
- }
- }
- if (oldRights.flags && !newRights.flags) {
- // We removed an admin.
- if (adminsCount() > 1) {
- setAdminsCount(adminsCount() - 1);
- }
- if (!isMegagroup() && user->isBot() && membersCount() > 1) {
- // Removing bot admin removes it from channel.
- setMembersCount(membersCount() - 1);
- }
- } else if (!oldRights.flags && newRights.flags) {
- // We added an admin.
- setAdminsCount(adminsCount() + 1);
- updateFullForced();
- }
- session().changes().peerUpdated(this, UpdateFlag::Admins);
- }
- void ChannelData::applyEditBanned(
- not_null<PeerData*> participant,
- ChatRestrictionsInfo oldRights,
- ChatRestrictionsInfo newRights) {
- auto flags = UpdateFlag::BannedUsers | UpdateFlag::None;
- auto isKicked = newRights.flags & ChatRestriction::ViewMessages;
- auto isRestricted = !isKicked && newRights.flags;
- const auto user = participant->asUser();
- if (mgInfo && user) {
- // If rights are empty - still remove admin? TODO check
- if (mgInfo->lastAdmins.contains(user)) {
- mgInfo->lastAdmins.remove(user);
- if (adminsCount() > 1) {
- setAdminsCount(adminsCount() - 1);
- } else {
- flags |= UpdateFlag::Admins;
- }
- }
- auto it = mgInfo->lastRestricted.find(user);
- if (isRestricted) {
- if (it == mgInfo->lastRestricted.cend()) {
- mgInfo->lastRestricted.emplace(
- user,
- MegagroupInfo::Restricted { newRights });
- setRestrictedCount(restrictedCount() + 1);
- } else {
- it->second.rights = newRights;
- }
- } else {
- if (it != mgInfo->lastRestricted.cend()) {
- mgInfo->lastRestricted.erase(it);
- if (restrictedCount() > 0) {
- setRestrictedCount(restrictedCount() - 1);
- }
- }
- if (isKicked) {
- auto i = ranges::find(
- mgInfo->lastParticipants,
- not_null{ user });
- if (i != mgInfo->lastParticipants.end()) {
- mgInfo->lastParticipants.erase(i);
- }
- if (membersCount() > 1) {
- setMembersCount(membersCount() - 1);
- } else {
- mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated;
- mgInfo->lastParticipantsCount = 0;
- }
- setKickedCount(kickedCount() + 1);
- if (mgInfo->bots.contains(user)) {
- mgInfo->bots.remove(user);
- if (mgInfo->bots.empty() && mgInfo->botStatus > 0) {
- mgInfo->botStatus = -1;
- }
- }
- flags |= UpdateFlag::Members;
- owner().removeMegagroupParticipant(this, user);
- }
- }
- Data::ChannelAdminChanges(this).remove(peerToUser(user->id));
- } else if (!mgInfo) {
- if (isKicked) {
- if (user && membersCount() > 1) {
- setMembersCount(membersCount() - 1);
- flags |= UpdateFlag::Members;
- }
- setKickedCount(kickedCount() + 1);
- }
- }
- session().changes().peerUpdated(this, flags);
- }
- void ChannelData::setViewAsMessagesFlag(bool enabled) {
- if (viewForumAsMessages() == enabled) {
- return;
- }
- setFlags((flags() & ~Flag::ViewAsMessages)
- | (enabled ? Flag::ViewAsMessages : Flag()));
- }
- void ChannelData::markForbidden() {
- owner().processChat(MTP_channelForbidden(
- MTP_flags(isMegagroup()
- ? MTPDchannelForbidden::Flag::f_megagroup
- : MTPDchannelForbidden::Flag::f_broadcast),
- MTP_long(peerToChannel(id).bare),
- MTP_long(access),
- MTP_string(name()),
- MTPint()));
- }
- bool ChannelData::isGroupAdmin(not_null<UserData*> user) const {
- if (auto info = mgInfo.get()) {
- return info->admins.contains(peerToUser(user->id));
- }
- return false;
- }
- bool ChannelData::lastParticipantsRequestNeeded() const {
- if (!mgInfo || !canViewMembers()) {
- return false;
- } else if (mgInfo->lastParticipantsCount == membersCount()) {
- mgInfo->lastParticipantsStatus
- &= ~MegagroupInfo::LastParticipantsCountOutdated;
- }
- return mgInfo->lastParticipants.empty()
- || !(mgInfo->lastParticipantsStatus
- & MegagroupInfo::LastParticipantsOnceReceived)
- || (mgInfo->lastParticipantsStatus
- & MegagroupInfo::LastParticipantsCountOutdated);
- }
- auto ChannelData::unavailableReasons() const
- -> const std::vector<Data::UnavailableReason> & {
- return _unavailableReasons;
- }
- void ChannelData::setUnavailableReasonsList(
- std::vector<Data::UnavailableReason> &&reasons) {
- _unavailableReasons = std::move(reasons);
- }
- void ChannelData::setAvailableMinId(MsgId availableMinId) {
- if (_availableMinId != availableMinId) {
- _availableMinId = availableMinId;
- }
- }
- bool ChannelData::canBanMembers() const {
- return amCreator()
- || (adminRights() & AdminRight::BanUsers);
- }
- bool ChannelData::canPostMessages() const {
- return amCreator()
- || (adminRights() & AdminRight::PostMessages);
- }
- bool ChannelData::canEditMessages() const {
- return amCreator()
- || (adminRights() & AdminRight::EditMessages);
- }
- bool ChannelData::canDeleteMessages() const {
- return amCreator()
- || (adminRights() & AdminRight::DeleteMessages);
- }
- bool ChannelData::canPostStories() const {
- return amCreator()
- || (adminRights() & AdminRight::PostStories);
- }
- bool ChannelData::canEditStories() const {
- return amCreator()
- || (adminRights() & AdminRight::EditStories);
- }
- bool ChannelData::canDeleteStories() const {
- return amCreator()
- || (adminRights() & AdminRight::DeleteStories);
- }
- bool ChannelData::canPostPaidMedia() const {
- return canPostMessages() && (flags() & Flag::PaidMediaAllowed);
- }
- bool ChannelData::anyoneCanAddMembers() const {
- return !(defaultRestrictions() & Restriction::AddParticipants);
- }
- bool ChannelData::hiddenPreHistory() const {
- return (flags() & Flag::PreHistoryHidden);
- }
- bool ChannelData::canAddMembers() const {
- return isMegagroup()
- ? !amRestricted(ChatRestriction::AddParticipants)
- : ((adminRights() & AdminRight::InviteByLinkOrAdd) || amCreator());
- }
- bool ChannelData::canAddAdmins() const {
- return amCreator()
- || (adminRights() & AdminRight::AddAdmins);
- }
- bool ChannelData::allowsForwarding() const {
- return !(flags() & Flag::NoForwards);
- }
- bool ChannelData::canViewMembers() const {
- return (flags() & Flag::CanViewParticipants)
- && (!(flags() & Flag::ParticipantsHidden)
- || amCreator()
- || hasAdminRights());
- }
- bool ChannelData::canViewAdmins() const {
- return (isMegagroup() || hasAdminRights() || amCreator());
- }
- bool ChannelData::canViewBanned() const {
- return (hasAdminRights() || amCreator());
- }
- bool ChannelData::canEditInformation() const {
- return isMegagroup()
- ? !amRestricted(Restriction::ChangeInfo)
- : ((adminRights() & AdminRight::ChangeInfo) || amCreator());
- }
- bool ChannelData::canEditPermissions() const {
- return isMegagroup()
- && !isGigagroup()
- && ((adminRights() & AdminRight::BanUsers) || amCreator());
- }
- bool ChannelData::canEditSignatures() const {
- return isChannel() && canEditInformation();
- }
- bool ChannelData::canEditPreHistoryHidden() const {
- return isMegagroup()
- && ((adminRights() & AdminRight::BanUsers) || amCreator())
- && (!isPublic() || canEditUsername());
- }
- bool ChannelData::canEditUsername() const {
- return amCreator()
- && (flags() & Flag::CanSetUsername);
- }
- bool ChannelData::canEditStickers() const {
- return (flags() & Flag::CanSetStickers);
- }
- bool ChannelData::canEditEmoji() const {
- return amCreator() || (adminRights() & ChatAdminRight::ChangeInfo);
- }
- bool ChannelData::canDelete() const {
- constexpr auto kDeleteChannelMembersLimit = 1000;
- return amCreator()
- && (membersCount() <= kDeleteChannelMembersLimit);
- }
- bool ChannelData::canEditLastAdmin(not_null<UserData*> user) const {
- // Duplicated in ParticipantsAdditionalData::canEditAdmin :(
- if (mgInfo) {
- auto i = mgInfo->lastAdmins.find(user);
- if (i != mgInfo->lastAdmins.cend()) {
- return i->second.canEdit;
- }
- return (user != mgInfo->creator);
- }
- return false;
- }
- bool ChannelData::canEditAdmin(not_null<UserData*> user) const {
- // Duplicated in ParticipantsAdditionalData::canEditAdmin :(
- if (user->isSelf()) {
- return false;
- } else if (amCreator()) {
- return true;
- } else if (!canEditLastAdmin(user)) {
- return false;
- }
- return adminRights() & AdminRight::AddAdmins;
- }
- bool ChannelData::canRestrictParticipant(
- not_null<PeerData*> participant) const {
- // Duplicated in ParticipantsAdditionalData::canRestrictParticipant :(
- if (participant->isSelf()) {
- return false;
- } else if (amCreator()) {
- return true;
- } else if (const auto user = participant->asUser()) {
- if (!canEditLastAdmin(user)) {
- return false;
- }
- }
- return adminRights() & AdminRight::BanUsers;
- }
- void ChannelData::setBotVerifyDetails(Ui::BotVerifyDetails details) {
- if (!details) {
- if (_botVerifyDetails) {
- _botVerifyDetails = nullptr;
- session().changes().peerUpdated(this, UpdateFlag::VerifyInfo);
- }
- } else if (!_botVerifyDetails) {
- _botVerifyDetails = std::make_unique<Ui::BotVerifyDetails>(details);
- session().changes().peerUpdated(this, UpdateFlag::VerifyInfo);
- } else if (*_botVerifyDetails != details) {
- *_botVerifyDetails = details;
- session().changes().peerUpdated(this, UpdateFlag::VerifyInfo);
- }
- }
- void ChannelData::setBotVerifyDetailsIcon(DocumentId iconId) {
- if (!iconId) {
- setBotVerifyDetails({});
- } else {
- auto info = _botVerifyDetails
- ? *_botVerifyDetails
- : Ui::BotVerifyDetails();
- info.iconId = iconId;
- setBotVerifyDetails(info);
- }
- }
- void ChannelData::setAdminRights(ChatAdminRights rights) {
- if (rights == adminRights()) {
- return;
- }
- _adminRights.set(rights);
- if (!canHaveInviteLink()) {
- setPendingRequestsCount(0, std::vector<UserId>{});
- }
- if (isMegagroup()) {
- const auto self = session().user();
- if (hasAdminRights()) {
- if (!amCreator()) {
- auto me = MegagroupInfo::Admin{
- ChatAdminRightsInfo{ rights } };
- me.canEdit = false;
- mgInfo->lastAdmins.emplace(self, me);
- }
- mgInfo->lastRestricted.remove(self);
- } else {
- mgInfo->lastAdmins.remove(self);
- }
- }
- session().changes().peerUpdated(
- this,
- UpdateFlag::Rights | UpdateFlag::Admins | UpdateFlag::BannedUsers);
- }
- void ChannelData::setRestrictions(ChatRestrictionsInfo rights) {
- if (rights.flags == restrictions() && rights.until == _restrictedUntil) {
- return;
- }
- _restrictedUntil = rights.until;
- _restrictions.set(rights.flags);
- if (isMegagroup()) {
- const auto self = session().user();
- if (hasRestrictions()) {
- if (!amCreator()) {
- auto me = MegagroupInfo::Restricted { rights };
- mgInfo->lastRestricted.emplace(self, me);
- }
- mgInfo->lastAdmins.remove(self);
- Data::ChannelAdminChanges(this).remove(session().userId());
- } else {
- mgInfo->lastRestricted.remove(self);
- }
- }
- session().changes().peerUpdated(
- this,
- UpdateFlag::Rights | UpdateFlag::Admins | UpdateFlag::BannedUsers);
- }
- void ChannelData::setDefaultRestrictions(ChatRestrictions rights) {
- if (rights == defaultRestrictions()) {
- return;
- }
- _defaultRestrictions.set(rights);
- session().changes().peerUpdated(this, UpdateFlag::Rights);
- }
- ChatData *ChannelData::getMigrateFromChat() const {
- if (const auto info = mgInfo.get()) {
- return info->getMigrateFromChat();
- }
- return nullptr;
- }
- void ChannelData::setMigrateFromChat(ChatData *chat) {
- Expects(mgInfo != nullptr);
- const auto info = mgInfo.get();
- if (chat != info->getMigrateFromChat()) {
- info->setMigrateFromChat(chat);
- if (amIn()) {
- session().changes().peerUpdated(this, UpdateFlag::Migration);
- }
- }
- }
- int ChannelData::slowmodeSeconds() const {
- if (const auto info = mgInfo.get()) {
- return info->slowmodeSeconds;
- }
- return 0;
- }
- void ChannelData::setSlowmodeSeconds(int seconds) {
- if (!mgInfo || slowmodeSeconds() == seconds) {
- return;
- }
- mgInfo->slowmodeSeconds = seconds;
- session().changes().peerUpdated(this, UpdateFlag::Slowmode);
- }
- TimeId ChannelData::slowmodeLastMessage() const {
- return (hasAdminRights()
- || amCreator()
- || unrestrictedByBoosts()
- || !mgInfo)
- ? 0
- : mgInfo->slowmodeLastMessage;
- }
- void ChannelData::growSlowmodeLastMessage(TimeId when) {
- const auto info = mgInfo.get();
- const auto now = base::unixtime::now();
- accumulate_min(when, now);
- if (!info) {
- return;
- } else if (info->slowmodeLastMessage > now) {
- info->slowmodeLastMessage = when;
- } else if (info->slowmodeLastMessage >= when) {
- return;
- } else {
- info->slowmodeLastMessage = when;
- }
- session().changes().peerUpdated(this, UpdateFlag::Slowmode);
- }
- int ChannelData::starsPerMessage() const {
- if (const auto info = mgInfo.get()) {
- return info->_starsPerMessage;
- }
- return 0;
- }
- void ChannelData::setStarsPerMessage(int stars) {
- if (mgInfo && starsPerMessage() != stars) {
- mgInfo->_starsPerMessage = stars;
- session().changes().peerUpdated(this, UpdateFlag::StarsPerMessage);
- }
- checkTrustedPayForMessage();
- }
- int ChannelData::peerGiftsCount() const {
- return _peerGiftsCount;
- }
- void ChannelData::setPeerGiftsCount(int count) {
- if (_peerGiftsCount != count) {
- _peerGiftsCount = count;
- session().changes().peerUpdated(this, UpdateFlag::PeerGifts);
- }
- }
- int ChannelData::boostsApplied() const {
- if (const auto info = mgInfo.get()) {
- return info->boostsApplied;
- }
- return 0;
- }
- int ChannelData::boostsUnrestrict() const {
- if (const auto info = mgInfo.get()) {
- return info->boostsUnrestrict;
- }
- return 0;
- }
- bool ChannelData::unrestrictedByBoosts() const {
- if (const auto info = mgInfo.get()) {
- return (info->boostsUnrestrict > 0)
- && (info->boostsApplied >= info->boostsUnrestrict);
- }
- return 0;
- }
- rpl::producer<bool> ChannelData::unrestrictedByBoostsValue() const {
- return mgInfo
- ? mgInfo->unrestrictedByBoostsChanges.events_starting_with(
- unrestrictedByBoosts())
- : (rpl::single(false) | rpl::type_erased());
- }
- void ChannelData::setBoostsUnrestrict(int applied, int unrestrict) {
- if (const auto info = mgInfo.get()) {
- if (info->boostsApplied == applied
- && info->boostsUnrestrict == unrestrict) {
- return;
- }
- const auto wasUnrestricted = unrestrictedByBoosts();
- info->boostsApplied = applied;
- info->boostsUnrestrict = unrestrict;
- const auto nowUnrestricted = unrestrictedByBoosts();
- if (wasUnrestricted != nowUnrestricted) {
- info->unrestrictedByBoostsChanges.fire_copy(nowUnrestricted);
- session().changes().peerUpdated(
- this,
- UpdateFlag::Rights | UpdateFlag::Slowmode);
- }
- }
- }
- void ChannelData::setInvitePeek(const QString &hash, TimeId expires) {
- if (!_invitePeek) {
- _invitePeek = std::make_unique<InvitePeek>();
- }
- _invitePeek->hash = hash;
- _invitePeek->expires = expires;
- }
- void ChannelData::clearInvitePeek() {
- _invitePeek = nullptr;
- }
- TimeId ChannelData::invitePeekExpires() const {
- return _invitePeek ? _invitePeek->expires : 0;
- }
- QString ChannelData::invitePeekHash() const {
- return _invitePeek ? _invitePeek->hash : QString();
- }
- void ChannelData::privateErrorReceived() {
- if (const auto expires = invitePeekExpires()) {
- const auto hash = invitePeekHash();
- for (const auto &window : session().windows()) {
- clearInvitePeek();
- Api::CheckChatInvite(window, hash, this);
- return;
- }
- _invitePeek->expires = base::unixtime::now();
- } else {
- markForbidden();
- }
- }
- void ChannelData::migrateCall(std::unique_ptr<Data::GroupCall> call) {
- Expects(_call == nullptr);
- Expects(call != nullptr);
- _call = std::move(call);
- _call->setPeer(this);
- session().changes().peerUpdated(this, UpdateFlag::GroupCall);
- addFlags(Flag::CallActive);
- }
- void ChannelData::setGroupCall(
- const MTPInputGroupCall &call,
- TimeId scheduleDate,
- bool rtmp) {
- 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 ChannelData::clearGroupCall() {
- if (!_call) {
- return;
- }
- owner().unregisterGroupCall(_call.get());
- _call = nullptr;
- session().changes().peerUpdated(this, UpdateFlag::GroupCall);
- removeFlags(Flag::CallActive | Flag::CallNotEmpty);
- }
- void ChannelData::setGroupCallDefaultJoinAs(PeerId peerId) {
- _callDefaultJoinAs = peerId;
- }
- PeerId ChannelData::groupCallDefaultJoinAs() const {
- return _callDefaultJoinAs;
- }
- void ChannelData::setAllowedReactions(Data::AllowedReactions value) {
- if (_allowedReactions != value) {
- if (value.paidEnabled) {
- session().api().globalPrivacy().loadPaidReactionShownPeer();
- }
- 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 &ChannelData::allowedReactions() const {
- return _allowedReactions;
- }
- bool ChannelData::hasActiveStories() const {
- return flags() & Flag::HasActiveStories;
- }
- bool ChannelData::hasUnreadStories() const {
- return flags() & Flag::HasUnreadStories;
- }
- void ChannelData::setStoriesState(StoriesState state) {
- Expects(state != StoriesState::Unknown);
- const auto was = flags();
- switch (state) {
- case StoriesState::None:
- _flags.remove(Flag::HasActiveStories | Flag::HasUnreadStories);
- break;
- case StoriesState::HasRead:
- _flags.set(
- (flags() & ~Flag::HasUnreadStories) | Flag::HasActiveStories);
- break;
- case StoriesState::HasUnread:
- _flags.add(Flag::HasActiveStories | Flag::HasUnreadStories);
- break;
- }
- if (flags() != was) {
- if (const auto history = owner().historyLoaded(this)) {
- history->updateChatListEntryPostponed();
- }
- session().changes().peerUpdated(this, UpdateFlag::StoriesState);
- }
- }
- void ChannelData::processTopics(const MTPVector<MTPForumTopic> &topics) {
- if (const auto forum = this->forum()) {
- forum->applyReceivedTopics(topics);
- }
- }
- int ChannelData::levelHint() const {
- return _levelHint;
- }
- void ChannelData::updateLevelHint(int levelHint) {
- _levelHint = levelHint;
- }
- TimeId ChannelData::subscriptionUntilDate() const {
- return _subscriptionUntilDate;
- }
- void ChannelData::updateSubscriptionUntilDate(TimeId subscriptionUntilDate) {
- _subscriptionUntilDate = subscriptionUntilDate;
- }
- namespace Data {
- void ApplyMigration(
- not_null<ChatData*> chat,
- not_null<ChannelData*> channel) {
- Expects(channel->isMegagroup());
- chat->setMigrateToChannel(channel);
- channel->setMigrateFromChat(chat);
- }
- void ApplyChannelUpdate(
- not_null<ChannelData*> channel,
- const MTPDupdateChatDefaultBannedRights &update) {
- channel->setDefaultRestrictions(ChatRestrictionsInfo(
- update.vdefault_banned_rights()).flags);
- }
- void ApplyChannelUpdate(
- not_null<ChannelData*> channel,
- const MTPDchannelFull &update) {
- const auto session = &channel->session();
- if (channel->isMegagroup()) {
- const auto suggestions = update.vpending_suggestions().value_or_empty();
- channel->owner().setSuggestToGigagroup(
- channel,
- ranges::contains(
- suggestions,
- "convert_to_gigagroup"_q,
- &MTPstring::v));
- }
- channel->setAvailableMinId(update.vavailable_min_id().value_or_empty());
- auto canViewAdmins = channel->canViewAdmins();
- auto canViewMembers = channel->canViewMembers();
- auto canEditStickers = channel->canEditStickers();
- if (const auto call = update.vcall()) {
- channel->setGroupCall(*call);
- } else {
- channel->clearGroupCall();
- }
- if (const auto as = update.vgroupcall_default_join_as()) {
- channel->setGroupCallDefaultJoinAs(peerFromMTP(*as));
- } else {
- channel->setGroupCallDefaultJoinAs(0);
- }
- channel->setMessagesTTL(update.vttl_period().value_or_empty());
- using Flag = ChannelDataFlag;
- const auto mask = Flag::CanSetUsername
- | Flag::CanViewParticipants
- | Flag::CanSetStickers
- | Flag::PreHistoryHidden
- | Flag::AntiSpam
- | Flag::Location
- | Flag::ParticipantsHidden
- | Flag::CanGetStatistics
- | Flag::ViewAsMessages
- | Flag::CanViewRevenue
- | Flag::PaidMediaAllowed
- | Flag::CanViewCreditsRevenue
- | Flag::StargiftsAvailable
- | Flag::PaidMessagesAvailable;
- channel->setFlags((channel->flags() & ~mask)
- | (update.is_can_set_username() ? Flag::CanSetUsername : Flag())
- | (update.is_can_view_participants()
- ? Flag::CanViewParticipants
- : Flag())
- | (update.is_can_set_stickers() ? Flag::CanSetStickers : Flag())
- | (update.is_hidden_prehistory() ? Flag::PreHistoryHidden : Flag())
- | (update.is_antispam() ? Flag::AntiSpam : Flag())
- | (update.vlocation() ? Flag::Location : Flag())
- | (update.is_participants_hidden()
- ? Flag::ParticipantsHidden
- : Flag())
- | (update.is_can_view_stats() ? Flag::CanGetStatistics : Flag())
- | (update.is_view_forum_as_messages()
- ? Flag::ViewAsMessages
- : Flag())
- | (update.is_paid_media_allowed() ? Flag::PaidMediaAllowed : Flag())
- | (update.is_can_view_revenue() ? Flag::CanViewRevenue : Flag())
- | (update.is_can_view_stars_revenue()
- ? Flag::CanViewCreditsRevenue
- : Flag())
- | (update.is_stargifts_available()
- ? Flag::StargiftsAvailable
- : Flag())
- | (update.is_paid_messages_available()
- ? Flag::PaidMessagesAvailable
- : Flag()));
- channel->setUserpicPhoto(update.vchat_photo());
- if (const auto migratedFrom = update.vmigrated_from_chat_id()) {
- channel->addFlags(Flag::Megagroup);
- const auto chat = channel->owner().chat(migratedFrom->v);
- Data::ApplyMigration(chat, channel);
- }
- channel->setAbout(qs(update.vabout()));
- channel->setMembersCount(update.vparticipants_count().value_or_empty());
- channel->setAdminsCount(update.vadmins_count().value_or_empty());
- channel->setRestrictedCount(update.vbanned_count().value_or_empty());
- channel->setKickedCount(update.vkicked_count().value_or_empty());
- channel->setSlowmodeSeconds(update.vslowmode_seconds().value_or_empty());
- channel->setPeerGiftsCount(update.vstargifts_count().value_or_empty());
- if (const auto next = update.vslowmode_next_send_date()) {
- channel->growSlowmodeLastMessage(
- next->v - channel->slowmodeSeconds());
- }
- if (const auto invite = update.vexported_invite()) {
- channel->session().api().inviteLinks().setMyPermanent(
- channel,
- *invite);
- } else {
- channel->session().api().inviteLinks().clearMyPermanent(channel);
- }
- if (const auto location = update.vlocation()) {
- channel->setLocation(*location);
- } else {
- channel->setLocation(MTP_channelLocationEmpty());
- }
- if (const auto chat = update.vlinked_chat_id()) {
- channel->setLinkedChat(channel->owner().channelLoaded(chat->v));
- } else {
- channel->setLinkedChat(nullptr);
- }
- if (const auto history = channel->owner().historyLoaded(channel)) {
- if (const auto available = update.vavailable_min_id()) {
- history->clearUpTill(available->v);
- }
- const auto folderId = update.vfolder_id().value_or_empty();
- const auto folder = folderId
- ? channel->owner().folderLoaded(folderId)
- : nullptr;
- auto &histories = channel->owner().histories();
- if (folder && history->folder() != folder) {
- // If history folder is unknown or not synced, request both.
- histories.requestDialogEntry(history);
- histories.requestDialogEntry(folder);
- } else if (!history->folderKnown()
- || channel->pts() != update.vpts().v) {
- histories.requestDialogEntry(history);
- } else {
- history->applyDialogFields(
- history->folder(),
- update.vunread_count().v,
- update.vread_inbox_max_id().v,
- update.vread_outbox_max_id().v);
- }
- }
- if (const auto pinned = update.vpinned_msg_id()) {
- SetTopPinnedMessageId(channel, pinned->v);
- }
- if (channel->isMegagroup()) {
- auto commands = ranges::views::all(
- update.vbot_info().v
- ) | ranges::views::transform(
- Data::BotCommandsFromTL
- ) | ranges::to_vector;
- if (channel->mgInfo->setBotCommands(std::move(commands))) {
- channel->owner().botCommandsChanged(channel);
- }
- const auto stickerSet = update.vstickerset();
- const auto sset = stickerSet ? &stickerSet->c_stickerSet() : nullptr;
- const auto newStickerSetId = (sset ? sset->vid().v : 0);
- const auto oldStickerSetId = channel->mgInfo->stickerSet.id;
- const auto stickersChanged = (canEditStickers != channel->canEditStickers())
- || (oldStickerSetId != newStickerSetId);
- if (oldStickerSetId != newStickerSetId) {
- channel->mgInfo->stickerSet = StickerSetIdentifier{
- .id = sset ? sset->vid().v : 0,
- .accessHash = sset ? sset->vaccess_hash().v : 0,
- };
- }
- if (stickersChanged) {
- session->changes().peerUpdated(channel, UpdateFlag::StickersSet);
- }
- const auto emojiSet = update.vemojiset();
- const auto eset = emojiSet ? &emojiSet->c_stickerSet() : nullptr;
- const auto newEmojiSetId = (eset ? eset->vid().v : 0);
- const auto oldEmojiSetId = channel->mgInfo->emojiSet.id;
- const auto emojiChanged = (oldEmojiSetId != newEmojiSetId);
- if (oldEmojiSetId != newEmojiSetId) {
- channel->mgInfo->emojiSet = StickerSetIdentifier{
- .id = eset ? eset->vid().v : 0,
- .accessHash = eset ? eset->vaccess_hash().v : 0,
- };
- }
- if (emojiChanged) {
- session->changes().peerUpdated(channel, UpdateFlag::EmojiSet);
- }
- channel->setBoostsUnrestrict(
- update.vboosts_applied().value_or_empty(),
- update.vboosts_unrestrict().value_or_empty());
- }
- channel->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
- channel->setTranslationDisabled(update.is_translations_disabled());
- const auto reactionsLimit = update.vreactions_limit().value_or_empty();
- if (const auto allowed = update.vavailable_reactions()) {
- auto parsed = Data::Parse(
- *allowed,
- reactionsLimit,
- update.is_paid_reactions_available());
- channel->setAllowedReactions(std::move(parsed));
- } else {
- channel->setAllowedReactions({
- .maxCount = reactionsLimit,
- .paidEnabled = update.is_paid_reactions_available(),
- });
- }
- channel->setBotVerifyDetails(
- ParseBotVerifyDetails(update.vbot_verification()));
- channel->owner().stories().apply(channel, update.vstories());
- channel->fullUpdated();
- channel->setPendingRequestsCount(
- update.vrequests_pending().value_or_empty(),
- update.vrecent_requesters().value_or_empty());
- if (canViewAdmins != channel->canViewAdmins()
- || canViewMembers != channel->canViewMembers()) {
- session->changes().peerUpdated(channel, UpdateFlag::Rights);
- }
- channel->owner().notifySettings().apply(
- channel,
- update.vnotify_settings());
- if (update.vstats_dc()) {
- channel->owner().applyStatsDcId(channel, update.vstats_dc()->v);
- }
- if (const auto sendAs = update.vdefault_send_as()) {
- session->sendAsPeers().setChosen(channel, peerFromMTP(*sendAs));
- } else {
- session->sendAsPeers().setChosen(channel, PeerId());
- }
- if (const auto paper = update.vwallpaper()) {
- channel->setWallPaper(
- Data::WallPaper::Create(&channel->session(), *paper));
- } else {
- channel->setWallPaper({});
- }
- // For clearUpTill() call.
- channel->owner().sendHistoryChangeNotifications();
- }
- } // namespace Data
|