How Telegram Desktop Implements Push Notifications and Update Mechanisms
Telegram Desktop delivers native push notifications and automatic updates through a layered architecture that separates platform-specific services from cross-platform scheduling logic, using Window::Notifications::System for notification queuing and Core::UpdateChecker for secure binary updates.
Telegram Desktop orchestrates real-time push notifications and seamless automatic updates through a sophisticated C++ architecture that abstracts platform differences behind unified manager interfaces. In the telegramdesktop/tdesktop repository, the implementation cleanly separates policy decisions—such as when to display alerts or download new binaries—from the mechanics of native OS integration. This analysis examines the specific source files, class hierarchies, and data flows that enable notifications across Windows, macOS, and Linux, alongside the cryptographic update verification pipeline.
Push Notification Architecture
The notification system relies on a four-layer stack. The Platform layer invokes native APIs like Windows Runtime toasts, macOS NSUserNotification, or Linux DBus. The Cross-platform manager provides a unified interface through Window::Notifications::System that handles queuing, deduplication, and grouping. The Core scheduler determines timing and visibility rules, while the UI rendering layer either draws custom toasts or delegates to the OS.
The client supports three manager types—Dummy, Default, and Native—defined in Window::Notifications::ManagerType within Telegram/SourceFiles/window/notifications_manager.h. The Default manager renders custom notification widgets in Qt, whereas the Native manager forwards calls to platform-specific implementations.
Notification Flow: From MTProto to Toast
When the MTProto layer receives a new message or reaction, it constructs a Data::ItemNotification object and invokes Window::Notifications::System::schedule(). This method first calls skipNotification() (lines 92–115 in notifications_manager.cpp) to enforce mute settings and rate limits. If the notification passes filters, countTiming() (lines 58–71) calculates display delays based on kMinimalDelay constants.
The scheduler stores the notification in _waitTimer or _waitForAllGroupedTimer. Upon firing, showNext() or showGrouped() assembles a NotificationFields object and dispatches it. For native mode, this calls Platform::Notifications::Create(); for default mode, it instantiates a Notification widget in notifications_manager_default.cpp (lines 355–380).
Platform-specific implementations reside in:
- Windows:
Telegram/SourceFiles/platform/win/notifications_manager_win.hand.cpp(WinRT toast) - macOS:
Telegram/SourceFiles/platform/mac/notifications_manager_mac.hand.cpp(NSUserNotification) - Linux:
Telegram/SourceFiles/platform/linux/notifications_manager_linux.hand.cpp(DBusorg.freedesktop.Notifications)
Custom vs. Native Notifications
Users can force the default manager even when native support exists by enabling kOptionCustomNotification (defined in notifications_manager.cpp, lines 36–46). Conversely, the platform can mandate native notifications via Platform::Notifications::Enforced(), implemented per-OS in the platform-specific files listed above. This dual-mode approach ensures consistent behavior across diverse desktop environments while respecting OS conventions when requested.
Automatic Update Mechanisms
The update system centers on Core::UpdateChecker, instantiated globally within Core::Application. The public entry point Core::UpdateApplication() (defined in Telegram/SourceFiles/core/update_checker.cpp, line 1634) triggers the entire workflow, whether invoked by automated timers or user interaction.
Update Checker Components
The architecture consists of an Updater core handling verification and restart, a Network layer with HttpChecker and MtpChecker subclasses, and UI triggers in settings and intro screens. The system uses reactive streams (rpl::producer) to broadcast states like checking(), progress(), failed(), and ready() to interface elements.
The Update Sequence: Download to Restart
When invoked, UpdateApplication() creates a Core::Updater instance (line 1650). The checker selection logic inspects cAlphaVersion(): if non-zero, it uses MtpChecker for MTProto-based downloads; otherwise, HttpChecker performs an HTTP GET to the update server via start() (line 132).
After HttpChecker::parseResponse() extracts the download URL (lines 44–66), HttpLoader (lines 58–71) spawns a thread to fetch the binary. The downloaded payload undergoes RSA signature verification using OpenSSL (lines 37–42). Upon validation, Updater::unpackDone() (line 1070) extracts the archive, and the application launches the new binary via Core::Launcher before exiting the old process.
Implementation Examples
Scheduling a Message Notification
The following pattern from notifications_manager.cpp demonstrates how incoming MTProto events enter the notification pipeline:
// Called from the MTProto update handler when a new message arrives.
void Window::Notifications::System::schedule(Data::ItemNotification notification) {
if (skipNotification(notification).value == SkipState::Value::Skip) {
return; // Respect mute / settings.
}
const auto when = countTiming(notification.thread, kMinimalDelay);
_waitTimer.callOnce(when);
// The timer will invoke showNext() which builds NotificationFields.
}
Triggering Manual Update Checks
UI elements initiate checks by calling the global update function. The Settings screen uses this pattern from settings_privacy_security.cpp:
// Settings screen button handler (settings_privacy_security.cpp, line 439)
void SettingsPrivacySecurity::checkForUpdates() {
Core::UpdateApplication(); // Starts the whole update flow.
}
Monitoring Download Progress
The updater exposes reactive streams that UI components can observe to display progress bars:
auto updater = Core::UpdateChecker::instance();
updater->checking() | rpl::start_with_next([=] {
ui->statusLabel->setText(tr::lng_update_checking(tr::now));
});
updater->progress() | rpl::start_with_next([=](Core::UpdateChecker::Progress p) {
ui->progressBar->setValue(p.percentage);
});
updater->ready() | rpl::start_with_next([=] {
QMessageBox::information(this, tr::lng_update_ready(tr::now),
tr::lng_update_success(tr::now));
Core::UpdateApplication(); // Restart to apply.
});
Summary
- Notification routing flows from MTProto through
Window::Notifications::System, which applies rate-limiting viaskipNotification()and timing logic viacountTiming()before delegating to platform-specific backends. - Platform abstraction uses
Platform::Notificationswith dedicated implementations for Windows (WinRT), macOS (NSUserNotification), and Linux (DBus) to render native toasts. - Update orchestration relies on
Core::UpdateChecker, which selects betweenHttpCheckerandMtpCheckerbased on alpha build status, then verifies RSA signatures before unpacking. - Security integration ensures that only cryptographically signed binaries pass verification (lines 37–42 in
update_checker.cpp) prior to the restart sequence triggered byunpackDone().
Frequently Asked Questions
How does Telegram Desktop choose between native and custom notifications?
The platform reports capabilities via Platform::Notifications::Supported() and enforcement status via Enforced(). Users can override this with kOptionCustomNotification (defined in notifications_manager.cpp, lines 36–46) to force the Default manager, which renders custom Qt-based toasts instead of native OS notifications.
What initiates an automatic update check in Telegram Desktop?
Core::UpdateApplication() serves as the universal entry point, triggered by UI elements such as the Settings button in settings_privacy_security.cpp (line 439) or the QR intro screen in intro_qr.cpp (line 486). The method instantiates Core::UpdateChecker to begin the network verification sequence.
How does the client suppress notifications for muted chats?
The scheduler calls skipNotification() (lines 92–115 in notifications_manager.cpp) to check mute status and notification settings. For sounds specifically, MaybeSoundFor() (lines 17–31) returns a std::optional<DocumentId> only for unmuted threads, preventing audio playback when appropriate.
What security measures protect Telegram Desktop updates?
Telegram Desktop verifies downloaded binaries using RSA signatures through OpenSSL calls (lines 37–42 in update_checker.cpp) before extraction. The update only proceeds to unpackDone() and subsequent restart if cryptographic validation succeeds, preventing execution of tampered binaries.
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 →