How Telegram Desktop Implements Privacy Settings Using GlobalPrivacy and UserPrivacy Classes
Telegram Desktop implements privacy settings through two dedicated API-wrapper classes—Api::GlobalPrivacy for account-wide flags and Api::UserPrivacy for per-feature rules—both exposing reactive streams via the RPL library to keep the UI synchronized with server state.
The telegramdesktop/tdesktop repository separates privacy concerns into distinct C++ classes that abstract the MTProto API complexity while providing reactive interfaces for the Qt-based UI layer. This architecture ensures that settings like archive-and-mute behavior or last-seen visibility remain consistent across the application without manual polling.
Architecture Overview
Telegram Desktop organizes privacy management into two complementary scopes:
Api::GlobalPrivacymanages account-wide configurations that apply universally, such as auto-archiving new chats, hiding read receipts, premium-only messaging requirements, and disallowed gift types.Api::UserPrivacyhandles granular, per-key privacy rules including phone number visibility, last seen status, call permissions, and story viewership.
Both classes maintain references to Main::Session and use MTP::Sender (via ApiWrap) to communicate with Telegram's servers. They expose state through Reactive Programming Library (RPL) streams, allowing UI components to subscribe to changes rather than querying state manually.
GlobalPrivacy Implementation
Located in Telegram/SourceFiles/api/api_global_privacy.h and .cpp, the GlobalPrivacy class encapsulates the account.getGlobalPrivacySettings and account.setGlobalPrivacySettings MTProto methods.
Reactive State Management
The class stores mutable state in rpl::variable<T> members, providing both instantaneous access and subscription capabilities:
rpl::variable<bool> _archiveAndMute = false;
rpl::variable<UnarchiveOnNewMessage> _unarchiveOnNewMessage = UnarchiveOnNewMessage::None;
rpl::variable<bool> _hideReadTime = false;
rpl::variable<DisallowedGiftTypes> _disallowedGiftTypes;
Public getters return rpl::producer<T> streams that emit the current value upon subscription and subsequent updates:
[[nodiscard]] bool archiveAndMuteCurrent() const;
[[nodiscard]] rpl::producer<bool> archiveAndMute() const;
Loading and Updating Global Settings
The reload() method dispatches account.getGlobalPrivacySettings and invokes apply(const MTPGlobalPrivacySettings &settings) on success, which extracts flags and updates the reactive variables.
For modifications, methods like updateArchiveAndMute(bool value) forward to a private update() function that:
- Constructs an
MTP_flagsbitmap mirroring the server'sglobalPrivacySettingsstructure - Cancels any pending request via stored
mtpRequestId - Sends
account.setGlobalPrivacySettingswith the combined flag set - Updates local
rpl::variablemembers optimistically - Handles rollback on errors such as
PREMIUM_ACCOUNT_REQUIRED
UserPrivacy Implementation
The UserPrivacy class in Telegram/SourceFiles/api/api_user_privacy.h manages the account.getPrivacy and account.setPrivacy methods for specific privacy keys.
Rule Structure and Conversion
Privacy rules use a structured Rule type containing an Option enum (Everyone, Contacts, CloseFriends, Nobody) and two Exceptions lists:
struct Rule {
Option option = Option::Everyone;
Exceptions always;
Exceptions never;
bool ignoreAlways = false;
bool ignoreNever = false;
};
struct Exceptions {
std::vector<not_null<PeerData*>> peers;
bool premiums = false;
bool miniapps = false;
};
The class uses free functions RulesToTL(const Rule&) and TLToRules() to translate between the internal C++ representation and the MTPVector<MTPPrivacyRule> expected by the MTProto API.
Saving and Applying Privacy Rules
The save(Key key, const Rule &rule) method:
- Converts the key to its TL equivalent using
KeyToTL(key) - Translates the rule via
RulesToTL(rule) - Dispatches
account.setPrivacyand stores the request ID in_privacySaveRequests - On success, calls
apply()to update local caches
The apply(mtpTypeId type, const MTPVector<MTPPrivacyRule> &rules, bool allLoaded) function parses server responses, stores the resulting Rule in _privacyValues, and pushes updates to _privacyChanges event streams. If data is incomplete, reload(Key key) fetches the complete rule set.
Reactive Programming Integration
Both classes leverage RPL to drive UI updates without callback hell. The UI layer subscribes to specific privacy streams:
// Global privacy subscription
globalPrivacy->archiveAndMute()
| rpl::start_with_next([](bool enabled) {
// Update toggle switch state
}, lifetime);
// User privacy subscription
userPrivacy->value(Api::UserPrivacy::Key::LastSeen)
| rpl::start_with_next([](const Api::UserPrivacy::Rule &rule) {
// Update visibility label
}, lifetime);
These streams automatically emit cached values immediately, then emit again whenever apply() or update methods modify the underlying state.
Practical Usage Examples
Accessing and modifying global privacy settings:
auto *apiWrap = session->api();
auto global = std::make_unique<Api::GlobalPrivacy>(apiWrap);
// Load current state
global->reload([]{
qInfo() << "Global privacy configuration loaded";
});
// Toggle archive-and-mute
global->updateArchiveAndMute(!global->archiveAndMuteCurrent());
Managing per-key privacy rules:
auto userPrivacy = std::make_unique<Api::UserPrivacy>(apiWrap);
// Subscribe to Last Seen changes
userPrivacy->value(Api::UserPrivacy::Key::LastSeen)
| rpl::start_with_next([](const auto &rule) {
qInfo() << "Visibility setting:" << static_cast<int>(rule.option);
}, lifetime);
// Create rule: visible to contacts only, hide from premium users
Api::UserPrivacy::Rule newRule;
newRule.option = Api::UserPrivacy::Option::Contacts;
newRule.never.premiums = true;
// Persist changes
userPrivacy->save(Api::UserPrivacy::Key::LastSeen, newRule);
Summary
Api::GlobalPrivacyencapsulates account-wide settings viaaccount.getGlobalPrivacySettingsandaccount.setGlobalPrivacySettings, storing state inrpl::variablemembers.Api::UserPrivacymanages granular rules for specific keys (PhoneNumber, LastSeen, Calls) using theaccount.setPrivacyandaccount.getPrivacyMTProto methods.- Both classes support request cancellation through stored
mtpRequestIdvalues and handle optimistic UI updates with server-error rollback. - RPL reactive streams (
rpl::producer) provide the primary interface for UI components, ensuring automatic synchronization when server responses modify local state. - Rule conversion occurs through
RulesToTL()andTLToRules()helper functions that map between C++ structs and MTProto vectors.
Frequently Asked Questions
How does Telegram Desktop handle privacy settings updates when the user is offline?
The implementation stores request IDs in maps like _privacySaveRequests and _privacyRequestIds, allowing cancellation of pending MTProto calls. When connectivity resumes, the reactive streams automatically emit the server-confirmed state upon receipt of the response, while local changes apply optimistically to maintain UI responsiveness.
What is the difference between GlobalPrivacy and UserPrivacy in the tdesktop codebase?
GlobalPrivacy manages boolean flags and enumerations that apply account-wide (archive behavior, read receipt hiding, premium requirements), while UserPrivacy handles complex rule structures with exceptions for specific peers or user categories. The former uses MTPGlobalPrivacySettings, whereas the latter processes MTPVector<MTPPrivacyRule> for individual privacy keys.
How are privacy exceptions (specific users) handled in the API wrapper?
The UserPrivacy::Exceptions struct contains a vector of not_null<PeerData*> pointers representing specific users or chats, plus boolean flags for premium users and mini-apps. These convert to MTPPrivacyValueAllowUsers, MTPPrivacyValueDisallowUsers, MTPPrivacyValueAllowCloseFriends, and similar TL constructors via the RulesToTL() function before transmission to the server.
Where does the UI connect to these privacy classes in the source code?
The settings layer in Telegram/SourceFiles/settings/sections/settings_privacy_security.h instantiates these classes, while Telegram/SourceFiles/boxes/edit_privacy_box.h provides the dialog interface for modifying individual UserPrivacy::Key rules. Both UI components subscribe to the reactive producers exposed by GlobalPrivacy and UserPrivacy to update switches and labels in real-time.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →