diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index a56460043..629c2dbb1 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -1033,6 +1033,7 @@ PRIVATE
     winrc/Telegram.rc
     winrc/Telegram.manifest
     langs/lang.strings
+    langs/cloud_lang.strings
     numbers.txt
 )
 
diff --git a/Telegram/Resources/langs/cloud_lang.strings b/Telegram/Resources/langs/cloud_lang.strings
index ad20ff187..c4c4a7ab9 100644
--- a/Telegram/Resources/langs/cloud_lang.strings
+++ b/Telegram/Resources/langs/cloud_lang.strings
@@ -6,6 +6,11 @@ For license and copyright information please follow this link:
 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 
+"cloud_lng_badge_psa_covid" = "Covid-19";
+"cloud_lng_about_psa_covid" = "This channel provides you with a public service announcement in relation to the ongoing pandemics.\nTo remove this channel from your chats list,\nright click it and select 'Hide'.";
+"cloud_lng_forwarded_psa_covid" = "Covid-19 Notification from {channel}";
+"cloud_lng_tooltip_psa_covid" = "This message provides you with a public service announcement in relation to the ongoing pandemics. Learn more about this initiative at: https://telegram.org/blog/coronavirus";
+
 "cloud_lng_passport_in_ar" = "Arabic";
 "cloud_lng_passport_in_az" = "Azerbaijani";
 "cloud_lng_passport_in_bg" = "Bulgarian";
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 81b5f1cd9..e4f15ab4b 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -600,6 +600,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_proxy_sponsor" = "Proxy sponsor";
 "lng_proxy_sponsor_about" = "This channel is shown by your proxy server.\nTo remove this channel from your chats list,\ndisable the proxy in Telegram Settings.";
 "lng_proxy_sponsor_warning" = "This proxy may display a sponsored channel in your chat list. This doesn't reveal any of your Telegram traffic.";
+"lng_badge_psa_default" = "PSA";
+"lng_about_psa_default" = "This channel provides you with a public service announcement.\nTo remove this channel from your chats list,\nright click it and select 'Hide'.";
 
 "lng_settings_blocked_users" = "Blocked users";
 "lng_settings_no_blocked_users" = "None";
@@ -1126,6 +1128,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_forwarded" = "Forwarded from {user}";
 "lng_forwarded_date" = "Original: {date}";
 "lng_forwarded_channel" = "Forwarded from {channel}";
+"lng_forwarded_psa_default" = "Forwarded from {channel}";
 "lng_forwarded_via" = "Forwarded from {user} via {inline_bot}";
 "lng_forwarded_channel_via" = "Forwarded from {channel} via {inline_bot}";
 "lng_forwarded_signed" = "{channel} ({user})";
diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl
index f6d91c04a..728c4bd3d 100644
--- a/Telegram/Resources/tl/api.tl
+++ b/Telegram/Resources/tl/api.tl
@@ -1138,7 +1138,7 @@ messageInteractionCounters#ad4fc9bd msg_id:int views:int forwards:int = MessageI
 stats.broadcastStats#bdf78394 period:StatsDateRangeDays followers:StatsAbsValueAndPrev views_per_post:StatsAbsValueAndPrev shares_per_post:StatsAbsValueAndPrev enabled_notifications:StatsPercentValue growth_graph:StatsGraph followers_graph:StatsGraph mute_graph:StatsGraph top_hours_graph:StatsGraph interactions_graph:StatsGraph iv_interactions_graph:StatsGraph views_by_source_graph:StatsGraph new_followers_by_source_graph:StatsGraph languages_graph:StatsGraph recent_message_interactions:Vector<MessageInteractionCounters> = stats.BroadcastStats;
 
 help.promoDataEmpty#98f6ac75 expires:int = help.PromoData;
-help.promoData#8f6adccf flags:# proxy:flags.0?true psa:flags.1?true expires:int peer:Peer chats:Vector<Chat> users:Vector<User> psa_message:flags.2?string = help.PromoData;
+help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer chats:Vector<Chat> users:Vector<User> psa_type:flags.1?string psa_message:flags.2?string = help.PromoData;
 
 ---functions---
 
@@ -1421,6 +1421,7 @@ help.getSupportName#d360e72c = help.SupportName;
 help.getUserInfo#38a08d3 user_id:InputUser = help.UserInfo;
 help.editUserInfo#66b91b70 user_id:InputUser message:string entities:Vector<MessageEntity> = help.UserInfo;
 help.getPromoData#c0977421 = help.PromoData;
+help.hidePromoData#1e251c95 peer:InputPeer = Bool;
 
 channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
 channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
@@ -1494,6 +1495,7 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua
 folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
 folders.deleteFolder#1c295881 folder_id:int = Updates;
 
+stats.getBroadcastStats#ab42441a flags:# dark:flags.0?true channel:InputChannel = stats.BroadcastStats;
 stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
 
 // LAYER 113
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index b44fb5f77..cc66e1223 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -342,13 +342,16 @@ void ApiWrap::topPromotionDone(const MTPhelp_PromoData &proxy) {
 		_topPromotionNextRequestTime);
 
 	proxy.match([&](const MTPDhelp_promoDataEmpty &data) {
-		_session->data().setTopPromoted(nullptr);
+		_session->data().setTopPromoted(nullptr, false, QString());
 	}, [&](const MTPDhelp_promoData &data) {
 		_session->data().processChats(data.vchats());
 		_session->data().processUsers(data.vusers());
 		const auto peerId = peerFromMTP(data.vpeer());
 		const auto peer = _session->data().peer(peerId);
-		_session->data().setTopPromoted(peer);
+		_session->data().setTopPromoted(
+			peer,
+			data.vpsa_type().value_or_empty(),
+			data.vpsa_message().value_or_empty());
 		if (const auto history = _session->data().historyLoaded(peer)) {
 			history->owner().histories().requestDialogEntry(history);
 		}
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index fd7ccb5e9..c78ac4e52 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -3713,21 +3713,28 @@ MessageIdsList Session::takeMimeForwardIds() {
 	return std::move(_mimeForwardIds);
 }
 
-void Session::setTopPromoted(PeerData *promoted) {
-	if (_topPromoted != promoted) {
-		if (const auto history = historyLoaded(_topPromoted)) {
-			history->cacheTopPromoted(false);
+void Session::setTopPromoted(
+		PeerData *promoted,
+		const QString &type,
+		const QString &message) {
+	const auto changed = (_topPromoted != promoted);
+	const auto history = promoted ? this->history(promoted).get() : nullptr;
+	if (changed
+		|| (history && history->topPromotionMessage() != message)) {
+		if (changed) {
+			if (const auto history = historyLoaded(_topPromoted)) {
+				history->cacheTopPromoted(false, QString(), QString());
+			}
 		}
 		const auto old = std::exchange(_topPromoted, promoted);
-		if (_topPromoted) {
-			const auto history = this->history(_topPromoted);
-			history->cacheTopPromoted(true);
+		if (history) {
+			history->cacheTopPromoted(true, type, message);
 			history->requestChatListMessage();
 			Notify::peerUpdatedDelayed(
 				_topPromoted,
 				Notify::PeerUpdate::Flag::ChannelPromotedChanged);
 		}
-		if (old) {
+		if (changed && old) {
 			Notify::peerUpdatedDelayed(
 				old,
 				Notify::PeerUpdate::Flag::ChannelPromotedChanged);
diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h
index a43a96947..9a5cd1b28 100644
--- a/Telegram/SourceFiles/data/data_session.h
+++ b/Telegram/SourceFiles/data/data_session.h
@@ -678,7 +678,10 @@ public:
 	void setMimeForwardIds(MessageIdsList &&list);
 	MessageIdsList takeMimeForwardIds();
 
-	void setTopPromoted(PeerData *promoted);
+	void setTopPromoted(
+		PeerData *promoted,
+		const QString &type,
+		const QString &message);
 	PeerData *topPromoted() const;
 
 	bool updateWallpapers(const MTPaccount_WallPapers &data);
diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp
index 4359237ec..64780db7f 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp
@@ -86,9 +86,16 @@ void Entry::cachePinnedIndex(FilterId filterId, int index) {
 	}
 }
 
-void Entry::cacheTopPromoted(bool promoted) {
-	if (_isTopPromoted != promoted) {
+void Entry::cacheTopPromoted(
+		bool promoted,
+		const QString &type,
+		const QString &message) {
+	if (_isTopPromoted != promoted
+		|| _topPromotedType != type
+		|| _topPromotedMessage != message) {
 		_isTopPromoted = promoted;
+		_topPromotedType = type;
+		_topPromotedMessage = message;
 		updateChatListSortPosition();
 		updateChatListEntry();
 		if (!_isTopPromoted) {
@@ -97,6 +104,18 @@ void Entry::cacheTopPromoted(bool promoted) {
 	}
 }
 
+bool Entry::isTopPromoted() const {
+	return _isTopPromoted;
+}
+
+QString Entry::topPromotionType() const {
+	return _topPromotedType;
+}
+
+QString Entry::topPromotionMessage() const {
+	return _topPromotedMessage;
+}
+
 bool Entry::needUpdateInChatList() const {
 	return inChatList() || shouldBeInChatList();
 }
diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h
index 219106552..100bcd152 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_entry.h
+++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h
@@ -120,10 +120,13 @@ public:
 		return lookupPinnedIndex(filterId) != 0;
 	}
 	void cachePinnedIndex(FilterId filterId, int index);
-	bool isTopPromoted() const {
-		return _isTopPromoted;
-	}
-	void cacheTopPromoted(bool promoted);
+	void cacheTopPromoted(
+		bool promoted,
+		const QString &type,
+		const QString &message);
+	[[nodiscard]] bool isTopPromoted() const;
+	[[nodiscard]] QString topPromotionType() const;
+	[[nodiscard]] QString topPromotionMessage() const;
 	[[nodiscard]] uint64 sortKeyInChatList(FilterId filterId) const {
 		return filterId
 			? computeSortPosition(filterId)
@@ -211,6 +214,8 @@ private:
 	uint64 _sortKeyInChatList = 0;
 	uint64 _sortKeyByDate = 0;
 	base::flat_map<FilterId, int> _pinnedIndex;
+	QString _topPromotedMessage;
+	QString _topPromotedType;
 	bool _isTopPromoted = false;
 	TimeId _timeId = 0;
 
diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp
index 861c5cebb..3b0f07207 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp
@@ -35,6 +35,7 @@ namespace {
 
 // Show all dates that are in the last 20 hours in time format.
 constexpr int kRecentlyInSeconds = 20 * 3600;
+const auto kPsaBadgePrefix = "cloud_lng_badge_psa_";
 
 bool ShowUserBotIcon(not_null<UserData*> user) {
 	return user->isBot() && !user->isSupport();
@@ -299,7 +300,16 @@ void paintRow(
 	const auto promoted = (history && history->useTopPromotion())
 		&& !(flags & (Flag::SearchResult/* | Flag::FeedSearchResult*/)); // #feed
 	if (promoted) {
-		const auto text = tr::lng_proxy_sponsor(tr::now);
+		const auto type = history->topPromotionType();
+		const auto custom = type.isEmpty()
+			? QString()
+			: Lang::Current().getNonDefaultValue(
+				kPsaBadgePrefix + type.toUtf8());
+		const auto text = type.isEmpty()
+			? tr::lng_proxy_sponsor(tr::now)
+			: custom.isEmpty()
+			? tr::lng_badge_psa_default(tr::now)
+			: custom;
 		PaintRowTopRight(p, text, rectForName, active, selected);
 	} else if (from/* && !(flags & Flag::FeedSearchResult)*/) { // #feed
 		if (const auto chatTypeIcon = ChatTypeIcon(from, active, selected)) {
diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h
index 265b0eb68..f58eeaa32 100644
--- a/Telegram/SourceFiles/history/history.h
+++ b/Telegram/SourceFiles/history/history.h
@@ -330,7 +330,7 @@ public:
 	void setForwardDraft(MessageIdsList &&items);
 
 	History *migrateSibling() const;
-	bool useTopPromotion() const;
+	[[nodiscard]] bool useTopPromotion() const;
 	int fixedOnTopIndex() const override;
 	void updateChatListExistence() override;
 	bool shouldBeInChatList() const override;
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index b59eddc9d..a88bd645a 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -131,6 +131,7 @@ constexpr auto kCommonModifiers = 0
 	| Qt::ShiftModifier
 	| Qt::MetaModifier
 	| Qt::ControlModifier;
+const auto kPsaAboutPrefix = "cloud_lng_about_psa_";
 
 ApiWrap::RequestMessageDataCallback replyEditMessageDataCallback() {
 	return [](ChannelData *channel, MsgId msgId) {
@@ -2262,17 +2263,29 @@ void HistoryWidget::updateControlsVisibility() {
 
 void HistoryWidget::refreshAboutTopPromotion() {
 	if (_history->useTopPromotion()) {
-		if (!_aboutTopPromotion) {
+		const auto type = _history->topPromotionType();
+		const auto custom = type.isEmpty()
+			? QString()
+			: Lang::Current().getNonDefaultValue(
+				kPsaAboutPrefix + type.toUtf8());
+		const auto text = type.isEmpty()
+			? tr::lng_proxy_sponsor_about(tr::now)
+			: custom.isEmpty()
+			? tr::lng_about_psa_default(tr::now)
+			: custom;
+		if (!_aboutTopPromotion || _aboutTopPromotionText != text) {
+			_aboutTopPromotionText = text;
 			_aboutTopPromotion = object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
 				this,
 				object_ptr<Ui::FlatLabel>(
 					this,
-					tr::lng_proxy_sponsor_about(tr::now),
+					_aboutTopPromotionText,
 					st::historyAboutProxy),
 				st::historyAboutProxyPadding);
 		}
 		_aboutTopPromotion->show();
 	} else {
+		_aboutTopPromotionText = QString();
 		_aboutTopPromotion.destroy();
 	}
 }
diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h
index ace45e25b..eab1ba9ca 100644
--- a/Telegram/SourceFiles/history/history_widget.h
+++ b/Telegram/SourceFiles/history/history_widget.h
@@ -735,6 +735,7 @@ private:
 	object_ptr<Ui::FlatButton> _muteUnmute;
 	object_ptr<Ui::FlatButton> _discuss;
 	object_ptr<Ui::RpWidget> _aboutTopPromotion = { nullptr };
+	QString _aboutTopPromotionText;
 	object_ptr<Ui::IconButton> _attachToggle;
 	object_ptr<Ui::EmojiButton> _tabbedSelectorToggle;
 	object_ptr<Ui::IconButton> _botKeyboardShow;