diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 0afd120d0..e4a5c5eff 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -180,6 +180,8 @@ PRIVATE
     boxes/filters/edit_filter_box.h
     boxes/filters/edit_filter_chats_list.cpp
     boxes/filters/edit_filter_chats_list.h
+    boxes/filters/edit_filter_chats_preview.cpp
+    boxes/filters/edit_filter_chats_preview.h
     boxes/filters/edit_filter_links.cpp
     boxes/filters/edit_filter_links.h
     boxes/peers/add_bot_to_chat_box.cpp
@@ -446,6 +448,9 @@ PRIVATE
     core/version.h
     countries/countries_manager.cpp
     countries/countries_manager.h
+    data/business/data_business_chatbots.cpp
+    data/business/data_business_chatbots.h
+    data/business/data_business_common.h
     data/notify/data_notify_settings.cpp
     data/notify/data_notify_settings.h
     data/notify/data_peer_notify_settings.cpp
@@ -1277,6 +1282,8 @@ PRIVATE
     profile/profile_block_widget.h
     profile/profile_cover_drop_area.cpp
     profile/profile_cover_drop_area.h
+    settings/business/settings_business_exceptions.cpp
+    settings/business/settings_business_exceptions.h
     settings/business/settings_chatbots.cpp
     settings/business/settings_chatbots.h
     settings/cloud_password/settings_cloud_password_common.cpp
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 19ff138a1..73b164dee 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2189,6 +2189,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_chatbots_reply" = "Reply to Messages";
 "lng_chatbots_reply_about" = "The bot will be able to view all new incoming messages, but not the messages that had been sent before you added the bot.";
 "lng_chatbots_remove" = "Remove Bot";
+"lng_chatbots_not_found" = "Chatbot not found";
+"lng_chatbots_add" = "Add";
 
 "lng_boost_channel_button" = "Boost Channel";
 "lng_boost_group_button" = "Boost Group";
@@ -4341,6 +4343,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_filters_type_non_contacts" = "Non-Contacts";
 "lng_filters_type_groups" = "Groups";
 "lng_filters_type_channels" = "Channels";
+"lng_filters_type_new" = "New Chats";
+"lng_filters_type_existing" = "Existing Chats";
 "lng_filters_type_bots" = "Bots";
 "lng_filters_type_no_archived" = "Archived";
 "lng_filters_type_no_muted" = "Muted";
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
index 37b946d63..958d01436 100644
--- a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/filters/edit_filter_box.h"
 
 #include "boxes/filters/edit_filter_chats_list.h"
+#include "boxes/filters/edit_filter_chats_preview.h"
 #include "boxes/filters/edit_filter_links.h"
 #include "boxes/premium_limits_box.h"
 #include "chat_helpers/emoji_suggestions_widget.h"
@@ -56,60 +57,6 @@ using Flags = Data::ChatFilter::Flags;
 using ExceptionPeersRef = const base::flat_set<not_null<History*>> &;
 using ExceptionPeersGetter = ExceptionPeersRef(Data::ChatFilter::*)() const;
 
-constexpr auto kAllTypes = {
-	Flag::Contacts,
-	Flag::NonContacts,
-	Flag::Groups,
-	Flag::Channels,
-	Flag::Bots,
-	Flag::NoMuted,
-	Flag::NoRead,
-	Flag::NoArchived,
-};
-
-class FilterChatsPreview final : public Ui::RpWidget {
-public:
-	FilterChatsPreview(
-		not_null<QWidget*> parent,
-		Flags flags,
-		const base::flat_set<not_null<History*>> &peers);
-
-	[[nodiscard]] rpl::producer<Flag> flagRemoved() const;
-	[[nodiscard]] rpl::producer<not_null<History*>> peerRemoved() const;
-
-	void updateData(
-		Flags flags,
-		const base::flat_set<not_null<History*>> &peers);
-
-	int resizeGetHeight(int newWidth) override;
-
-private:
-	using Button = base::unique_qptr<Ui::IconButton>;
-	struct FlagButton {
-		Flag flag = Flag();
-		Button button;
-	};
-	struct PeerButton {
-		not_null<History*> history;
-		Ui::PeerUserpicView userpic;
-		Ui::Text::String name;
-		Button button;
-	};
-
-	void paintEvent(QPaintEvent *e) override;
-
-	void refresh();
-	void removeFlag(Flag flag);
-	void removePeer(not_null<History*> history);
-
-	std::vector<FlagButton> _removeFlag;
-	std::vector<PeerButton> _removePeer;
-
-	rpl::event_stream<Flag> _flagRemoved;
-	rpl::event_stream<not_null<History*>> _peerRemoved;
-
-};
-
 struct NameEditing {
 	not_null<Ui::InputField*> field;
 	bool custom = false;
@@ -167,167 +114,6 @@ not_null<FilterChatsPreview*> SetupChatsPreview(
 	return preview;
 }
 
-FilterChatsPreview::FilterChatsPreview(
-	not_null<QWidget*> parent,
-	Flags flags,
-	const base::flat_set<not_null<History*>> &peers)
-: RpWidget(parent) {
-	updateData(flags, peers);
-}
-
-void FilterChatsPreview::refresh() {
-	resizeToWidth(width());
-}
-
-void FilterChatsPreview::updateData(
-		Flags flags,
-		const base::flat_set<not_null<History*>> &peers) {
-	_removeFlag.clear();
-	_removePeer.clear();
-	const auto makeButton = [&](Fn<void()> handler) {
-		auto result = base::make_unique_q<Ui::IconButton>(
-			this,
-			st::windowFilterSmallRemove);
-		result->setClickedCallback(std::move(handler));
-		return result;
-	};
-	for (const auto flag : kAllTypes) {
-		if (flags & flag) {
-			_removeFlag.push_back({
-				flag,
-				makeButton([=] { removeFlag(flag); }) });
-		}
-	}
-	for (const auto &history : peers) {
-		_removePeer.push_back(PeerButton{
-			.history = history,
-			.button = makeButton([=] { removePeer(history); })
-		});
-	}
-	refresh();
-}
-
-int FilterChatsPreview::resizeGetHeight(int newWidth) {
-	const auto right = st::windowFilterSmallRemoveRight;
-	const auto add = (st::windowFilterSmallItem.height
-		- st::windowFilterSmallRemove.height) / 2;
-	auto top = 0;
-	const auto moveNextButton = [&](not_null<Ui::IconButton*> button) {
-		button->moveToRight(right, top + add, newWidth);
-		top += st::windowFilterSmallItem.height;
-	};
-	for (const auto &[flag, button] : _removeFlag) {
-		moveNextButton(button.get());
-	}
-	for (const auto &[history, userpic, name, button] : _removePeer) {
-		moveNextButton(button.get());
-	}
-	return top;
-}
-
-void FilterChatsPreview::paintEvent(QPaintEvent *e) {
-	auto p = Painter(this);
-	auto top = 0;
-	const auto &st = st::windowFilterSmallItem;
-	const auto iconLeft = st.photoPosition.x();
-	const auto iconTop = st.photoPosition.y();
-	const auto nameLeft = st.namePosition.x();
-	p.setFont(st::windowFilterSmallItem.nameStyle.font);
-	const auto nameTop = st.namePosition.y();
-	for (const auto &[flag, button] : _removeFlag) {
-		PaintFilterChatsTypeIcon(
-			p,
-			flag,
-			iconLeft,
-			top + iconTop,
-			width(),
-			st.photoSize);
-
-		p.setPen(st::contactsNameFg);
-		p.drawTextLeft(
-			nameLeft,
-			top + nameTop,
-			width(),
-			FilterChatsTypeName(flag));
-		top += st.height;
-	}
-	for (auto &[history, userpic, name, button] : _removePeer) {
-		const auto savedMessages = history->peer->isSelf();
-		const auto repliesMessages = history->peer->isRepliesChat();
-		if (savedMessages || repliesMessages) {
-			if (savedMessages) {
-				Ui::EmptyUserpic::PaintSavedMessages(
-					p,
-					iconLeft,
-					top + iconTop,
-					width(),
-					st.photoSize);
-			} else {
-				Ui::EmptyUserpic::PaintRepliesMessages(
-					p,
-					iconLeft,
-					top + iconTop,
-					width(),
-					st.photoSize);
-			}
-			p.setPen(st::contactsNameFg);
-			p.drawTextLeft(
-				nameLeft,
-				top + nameTop,
-				width(),
-				(savedMessages
-					? tr::lng_saved_messages(tr::now)
-					: tr::lng_replies_messages(tr::now)));
-		} else {
-			history->peer->paintUserpicLeft(
-				p,
-				userpic,
-				iconLeft,
-				top + iconTop,
-				width(),
-				st.photoSize);
-			p.setPen(st::contactsNameFg);
-			if (name.isEmpty()) {
-				name.setText(
-					st::msgNameStyle,
-					history->peer->name(),
-					Ui::NameTextOptions());
-			}
-			name.drawLeftElided(
-				p,
-				nameLeft,
-				top + nameTop,
-				button->x() - nameLeft,
-				width());
-		}
-		top += st.height;
-	}
-}
-
-void FilterChatsPreview::removeFlag(Flag flag) {
-	const auto i = ranges::find(_removeFlag, flag, &FlagButton::flag);
-	Assert(i != end(_removeFlag));
-	_removeFlag.erase(i);
-	refresh();
-	_flagRemoved.fire_copy(flag);
-}
-
-void FilterChatsPreview::removePeer(not_null<History*> history) {
-	const auto i = ranges::find(_removePeer, history, &PeerButton::history);
-	Assert(i != end(_removePeer));
-	_removePeer.erase(i);
-	refresh();
-	_peerRemoved.fire_copy(history);
-}
-
-rpl::producer<Flag> FilterChatsPreview::flagRemoved() const {
-	return _flagRemoved.events();
-}
-
-rpl::producer<not_null<History*>> FilterChatsPreview::peerRemoved() const {
-	return _peerRemoved.events();
-}
-
 void EditExceptions(
 		not_null<Window::SessionController*> window,
 		not_null<QObject*> context,
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp
index 989a9867b..25463f1e2 100644
--- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp
@@ -28,6 +28,8 @@ using Flag = Data::ChatFilter::Flag;
 using Flags = Data::ChatFilter::Flags;
 
 constexpr auto kAllTypes = {
+	Flag::NewChats,
+	Flag::ExistingChats,
 	Flag::Contacts,
 	Flag::NonContacts,
 	Flag::Groups,
@@ -119,7 +121,7 @@ PaintRoundImageCallback TypeRow::generatePaintUserpicCallback(
 }
 
 Flag TypeRow::flag() const {
-	return static_cast<Flag>(id() & 0xFF);
+	return static_cast<Flag>(id() & 0xFFFF);
 }
 
 ExceptionRow::ExceptionRow(not_null<History*> history) : Row(history) {
@@ -219,6 +221,8 @@ auto TypeController::rowSelectionChanges() const
 
 [[nodiscard]] QString FilterChatsTypeName(Flag flag) {
 	switch (flag) {
+	case Flag::NewChats: return tr::lng_filters_type_new(tr::now);
+	case Flag::ExistingChats: return tr::lng_filters_type_existing(tr::now);
 	case Flag::Contacts: return tr::lng_filters_type_contacts(tr::now);
 	case Flag::NonContacts:
 		return tr::lng_filters_type_non_contacts(tr::now);
@@ -241,6 +245,8 @@ void PaintFilterChatsTypeIcon(
 		int size) {
 	const auto &color1 = [&]() -> const style::color& {
 		switch (flag) {
+		case Flag::NewChats: return st::historyPeer5UserpicBg;
+		case Flag::ExistingChats: return st::historyPeer8UserpicBg;
 		case Flag::Contacts: return st::historyPeer4UserpicBg;
 		case Flag::NonContacts: return st::historyPeer7UserpicBg;
 		case Flag::Groups: return st::historyPeer2UserpicBg;
@@ -254,6 +260,8 @@ void PaintFilterChatsTypeIcon(
 	}();
 	const auto &color2 = [&]() -> const style::color& {
 		switch (flag) {
+		case Flag::NewChats: return st::historyPeer5UserpicBg2;
+		case Flag::ExistingChats: return st::historyPeer8UserpicBg2;
 		case Flag::Contacts: return st::historyPeer4UserpicBg2;
 		case Flag::NonContacts: return st::historyPeer7UserpicBg2;
 		case Flag::Groups: return st::historyPeer2UserpicBg2;
@@ -267,6 +275,8 @@ void PaintFilterChatsTypeIcon(
 	}();
 	const auto &icon = [&]() -> const style::icon& {
 		switch (flag) {
+		case Flag::NewChats: return st::windowFilterTypeNewChats;
+		case Flag::ExistingChats: return st::windowFilterTypeExistingChats;
 		case Flag::Contacts: return st::windowFilterTypeContacts;
 		case Flag::NonContacts: return st::windowFilterTypeNonContacts;
 		case Flag::Groups: return st::windowFilterTypeGroups;
@@ -469,6 +479,10 @@ object_ptr<Ui::RpWidget> EditFilterChatsListController::prepareTypesList() {
 
 auto EditFilterChatsListController::createRow(not_null<History*> history)
 -> std::unique_ptr<Row> {
+	const auto business = _options & (Flag::NewChats | Flag::ExistingChats);
+	if (business && (history->peer->isSelf() || !history->peer->isUser())) {
+		return nullptr;
+	}
 	return history->inChatList()
 		? std::make_unique<ExceptionRow>(history)
 		: nullptr;
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.cpp
new file mode 100644
index 000000000..3e2efb87e
--- /dev/null
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.cpp
@@ -0,0 +1,199 @@
+/*
+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 "boxes/filters/edit_filter_chats_preview.h"
+
+#include "boxes/filters/edit_filter_chats_list.h"
+#include "data/data_peer.h"
+#include "history/history.h"
+#include "lang/lang_keys.h"
+#include "ui/text/text_options.h"
+#include "ui/widgets/buttons.h"
+#include "ui/painter.h"
+#include "styles/style_chat.h"
+#include "styles/style_window.h"
+
+namespace {
+
+using Flag = Data::ChatFilter::Flag;
+
+constexpr auto kAllTypes = {
+	Flag::NewChats,
+	Flag::ExistingChats,
+	Flag::Contacts,
+	Flag::NonContacts,
+	Flag::Groups,
+	Flag::Channels,
+	Flag::Bots,
+	Flag::NoMuted,
+	Flag::NoRead,
+	Flag::NoArchived,
+};
+
+} // namespace
+
+FilterChatsPreview::FilterChatsPreview(
+	not_null<QWidget*> parent,
+	Flags flags,
+	const base::flat_set<not_null<History*>> &peers)
+: RpWidget(parent) {
+	updateData(flags, peers);
+}
+
+void FilterChatsPreview::refresh() {
+	resizeToWidth(width());
+}
+
+void FilterChatsPreview::updateData(
+		Flags flags,
+		const base::flat_set<not_null<History*>> &peers) {
+	_removeFlag.clear();
+	_removePeer.clear();
+	const auto makeButton = [&](Fn<void()> handler) {
+		auto result = base::make_unique_q<Ui::IconButton>(
+			this,
+			st::windowFilterSmallRemove);
+		result->setClickedCallback(std::move(handler));
+		result->show();
+		return result;
+	};
+	for (const auto flag : kAllTypes) {
+		if (flags & flag) {
+			_removeFlag.push_back({
+				flag,
+				makeButton([=] { removeFlag(flag); }) });
+		}
+	}
+	for (const auto &history : peers) {
+		_removePeer.push_back(PeerButton{
+			.history = history,
+			.button = makeButton([=] { removePeer(history); })
+		});
+	}
+	refresh();
+}
+
+int FilterChatsPreview::resizeGetHeight(int newWidth) {
+	const auto right = st::windowFilterSmallRemoveRight;
+	const auto add = (st::windowFilterSmallItem.height
+		- st::windowFilterSmallRemove.height) / 2;
+	auto top = 0;
+	const auto moveNextButton = [&](not_null<Ui::IconButton*> button) {
+		button->moveToRight(right, top + add, newWidth);
+		top += st::windowFilterSmallItem.height;
+	};
+	for (const auto &[flag, button] : _removeFlag) {
+		moveNextButton(button.get());
+	}
+	for (const auto &[history, userpic, name, button] : _removePeer) {
+		moveNextButton(button.get());
+	}
+	return top;
+}
+
+void FilterChatsPreview::paintEvent(QPaintEvent *e) {
+	auto p = Painter(this);
+	auto top = 0;
+	const auto &st = st::windowFilterSmallItem;
+	const auto iconLeft = st.photoPosition.x();
+	const auto iconTop = st.photoPosition.y();
+	const auto nameLeft = st.namePosition.x();
+	p.setFont(st::windowFilterSmallItem.nameStyle.font);
+	const auto nameTop = st.namePosition.y();
+	for (const auto &[flag, button] : _removeFlag) {
+		PaintFilterChatsTypeIcon(
+			p,
+			flag,
+			iconLeft,
+			top + iconTop,
+			width(),
+			st.photoSize);
+
+		p.setPen(st::contactsNameFg);
+		p.drawTextLeft(
+			nameLeft,
+			top + nameTop,
+			width(),
+			FilterChatsTypeName(flag));
+		top += st.height;
+	}
+	for (auto &[history, userpic, name, button] : _removePeer) {
+		const auto savedMessages = history->peer->isSelf();
+		const auto repliesMessages = history->peer->isRepliesChat();
+		if (savedMessages || repliesMessages) {
+			if (savedMessages) {
+				Ui::EmptyUserpic::PaintSavedMessages(
+					p,
+					iconLeft,
+					top + iconTop,
+					width(),
+					st.photoSize);
+			} else {
+				Ui::EmptyUserpic::PaintRepliesMessages(
+					p,
+					iconLeft,
+					top + iconTop,
+					width(),
+					st.photoSize);
+			}
+			p.setPen(st::contactsNameFg);
+			p.drawTextLeft(
+				nameLeft,
+				top + nameTop,
+				width(),
+				(savedMessages
+					? tr::lng_saved_messages(tr::now)
+					: tr::lng_replies_messages(tr::now)));
+		} else {
+			history->peer->paintUserpicLeft(
+				p,
+				userpic,
+				iconLeft,
+				top + iconTop,
+				width(),
+				st.photoSize);
+			p.setPen(st::contactsNameFg);
+			if (name.isEmpty()) {
+				name.setText(
+					st::msgNameStyle,
+					history->peer->name(),
+					Ui::NameTextOptions());
+			}
+			name.drawLeftElided(
+				p,
+				nameLeft,
+				top + nameTop,
+				button->x() - nameLeft,
+				width());
+		}
+		top += st.height;
+	}
+}
+
+void FilterChatsPreview::removeFlag(Flag flag) {
+	const auto i = ranges::find(_removeFlag, flag, &FlagButton::flag);
+	Assert(i != end(_removeFlag));
+	_removeFlag.erase(i);
+	refresh();
+	_flagRemoved.fire_copy(flag);
+}
+
+void FilterChatsPreview::removePeer(not_null<History*> history) {
+	const auto i = ranges::find(_removePeer, history, &PeerButton::history);
+	Assert(i != end(_removePeer));
+	_removePeer.erase(i);
+	refresh();
+	_peerRemoved.fire_copy(history);
+}
+
+rpl::producer<Flag> FilterChatsPreview::flagRemoved() const {
+	return _flagRemoved.events();
+}
+
+rpl::producer<not_null<History*>> FilterChatsPreview::peerRemoved() const {
+	return _peerRemoved.events();
+}
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.h b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.h
new file mode 100644
index 000000000..c795bc493
--- /dev/null
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.h
@@ -0,0 +1,64 @@
+/*
+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
+*/
+#pragma once
+
+#include "data/data_chat_filters.h"
+#include "ui/rp_widget.h"
+#include "ui/userpic_view.h"
+
+class History;
+
+namespace Ui {
+class IconButton;
+} // namespace Ui
+
+class FilterChatsPreview final : public Ui::RpWidget {
+public:
+	using Flag = Data::ChatFilter::Flag;
+	using Flags = Data::ChatFilter::Flags;
+
+	FilterChatsPreview(
+		not_null<QWidget*> parent,
+		Flags flags,
+		const base::flat_set<not_null<History*>> &peers);
+
+	[[nodiscard]] rpl::producer<Flag> flagRemoved() const;
+	[[nodiscard]] rpl::producer<not_null<History*>> peerRemoved() const;
+
+	void updateData(
+		Flags flags,
+		const base::flat_set<not_null<History*>> &peers);
+
+	int resizeGetHeight(int newWidth) override;
+
+private:
+	using Button = base::unique_qptr<Ui::IconButton>;
+	struct FlagButton {
+		Flag flag = Flag();
+		Button button;
+	};
+	struct PeerButton {
+		not_null<History*> history;
+		Ui::PeerUserpicView userpic;
+		Ui::Text::String name;
+		Button button;
+	};
+
+	void paintEvent(QPaintEvent *e) override;
+
+	void refresh();
+	void removeFlag(Flag flag);
+	void removePeer(not_null<History*> history);
+
+	std::vector<FlagButton> _removeFlag;
+	std::vector<PeerButton> _removePeer;
+
+	rpl::event_stream<Flag> _flagRemoved;
+	rpl::event_stream<not_null<History*>> _peerRemoved;
+
+};
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp
index d60cc030d..5640c11b5 100644
--- a/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp
@@ -982,8 +982,7 @@ bool GoodForExportFilterLink(
 		not_null<Window::SessionController*> window,
 		const Data::ChatFilter &filter) {
 	using Flag = Data::ChatFilter::Flag;
-	const auto listflags = Flag::Chatlist | Flag::HasMyLinks;
-	if (!filter.never().empty() || (filter.flags() & ~listflags)) {
+	if (!filter.never().empty() || (filter.flags() & Flag::RulesMask)) {
 		window->showToast(tr::lng_filters_link_cant(tr::now));
 		return false;
 	}
diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.cpp b/Telegram/SourceFiles/data/business/data_business_chatbots.cpp
new file mode 100644
index 000000000..26dd21687
--- /dev/null
+++ b/Telegram/SourceFiles/data/business/data_business_chatbots.cpp
@@ -0,0 +1,34 @@
+/*
+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/business/data_business_chatbots.h"
+
+namespace Data {
+
+Chatbots::Chatbots(not_null<Session*> session)
+: _session(session) {
+}
+
+Chatbots::~Chatbots() = default;
+
+const ChatbotsSettings &Chatbots::current() const {
+	return _settings.current();
+}
+
+rpl::producer<ChatbotsSettings> Chatbots::changes() const {
+	return _settings.changes();
+}
+
+rpl::producer<ChatbotsSettings> Chatbots::value() const {
+	return _settings.value();
+}
+
+void Chatbots::save(ChatbotsSettings settings) {
+	_settings = settings;
+}
+
+} // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.h b/Telegram/SourceFiles/data/business/data_business_chatbots.h
new file mode 100644
index 000000000..adfe998d2
--- /dev/null
+++ b/Telegram/SourceFiles/data/business/data_business_chatbots.h
@@ -0,0 +1,44 @@
+/*
+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
+*/
+#pragma once
+
+#include "data/business/data_business_common.h"
+
+class UserData;
+
+namespace Data {
+
+class Session;
+
+struct ChatbotsSettings {
+	UserData *bot = nullptr;
+	BusinessExceptions allowed;
+	BusinessExceptions disallowed;
+	bool repliesAllowed = false;
+	bool onlySelected = false;
+};
+
+class Chatbots final {
+public:
+	explicit Chatbots(not_null<Session*> session);
+	~Chatbots();
+
+	[[nodiscard]] const ChatbotsSettings &current() const;
+	[[nodiscard]] rpl::producer<ChatbotsSettings> changes() const;
+	[[nodiscard]] rpl::producer<ChatbotsSettings> value() const;
+
+	void save(ChatbotsSettings settings);
+
+private:
+	const not_null<Session*> _session;
+
+	rpl::variable<ChatbotsSettings> _settings;
+
+};
+
+} // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
new file mode 100644
index 000000000..aed51fdf9
--- /dev/null
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -0,0 +1,31 @@
+/*
+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
+*/
+#pragma once
+
+#include "base/flags.h"
+
+class UserData;
+
+namespace Data {
+
+enum class BusinessChatType {
+	NewChats = (1 << 0),
+	ExistingChats = (1 << 1),
+	Contacts = (1 << 2),
+	NonContacts = (1 << 3),
+};
+inline constexpr bool is_flag_type(BusinessChatType) { return true; }
+
+using BusinessChatTypes = base::flags<BusinessChatType>;
+
+struct BusinessExceptions {
+	BusinessChatTypes types;
+	std::vector<not_null<UserData*>> list;
+};
+
+} // namespace Data
diff --git a/Telegram/SourceFiles/data/data_chat_filters.cpp b/Telegram/SourceFiles/data/data_chat_filters.cpp
index 7e824cc33..237bf5c29 100644
--- a/Telegram/SourceFiles/data/data_chat_filters.cpp
+++ b/Telegram/SourceFiles/data/data_chat_filters.cpp
@@ -163,6 +163,7 @@ ChatFilter ChatFilter::withTitle(const QString &title) const {
 
 ChatFilter ChatFilter::withChatlist(bool chatlist, bool hasMyLinks) const {
 	auto result = *this;
+	result._flags &= Flag::RulesMask;
 	if (chatlist) {
 		result._flags |= Flag::Chatlist;
 		if (hasMyLinks) {
@@ -170,8 +171,6 @@ ChatFilter ChatFilter::withChatlist(bool chatlist, bool hasMyLinks) const {
 		} else {
 			result._flags &= ~Flag::HasMyLinks;
 		}
-	} else {
-		result._flags &= ~(Flag::Chatlist | Flag::HasMyLinks);
 	}
 	return result;
 }
@@ -593,7 +592,7 @@ bool ChatFilters::applyChange(ChatFilter &filter, ChatFilter &&updated) {
 
 	const auto id = filter.id();
 	const auto exceptionsChanged = filter.always() != updated.always();
-	const auto rulesMask = ~(Flag::Chatlist | Flag::HasMyLinks);
+	const auto rulesMask = Flag() | Flag::RulesMask;
 	const auto rulesChanged = exceptionsChanged
 		|| ((filter.flags() & rulesMask) != (updated.flags() & rulesMask))
 		|| (filter.never() != updated.never());
diff --git a/Telegram/SourceFiles/data/data_chat_filters.h b/Telegram/SourceFiles/data/data_chat_filters.h
index 987d55ebe..7b5a96476 100644
--- a/Telegram/SourceFiles/data/data_chat_filters.h
+++ b/Telegram/SourceFiles/data/data_chat_filters.h
@@ -36,9 +36,13 @@ public:
 		NoMuted     = (1 << 5),
 		NoRead      = (1 << 6),
 		NoArchived  = (1 << 7),
+		RulesMask   = ((1 << 8) - 1),
 
 		Chatlist    = (1 << 8),
 		HasMyLinks  = (1 << 9),
+
+		NewChats      = (1 << 10), // Telegram Business exceptions.
+		ExistingChats = (1 << 11),
 	};
 	friend constexpr inline bool is_flag_type(Flag) { return true; };
 	using Flags = base::flags<Flag>;
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index e7ec13242..4efd8fa8b 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/abstract_box.h"
 #include "passport/passport_form_controller.h"
 #include "lang/lang_keys.h" // tr::lng_deleted(tr::now) in user name
+#include "data/business/data_business_chatbots.h"
 #include "data/stickers/data_stickers.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/data_bot_app.h"
@@ -268,7 +269,8 @@ Session::Session(not_null<Main::Session*> session)
 , _notifySettings(std::make_unique<NotifySettings>(this))
 , _customEmojiManager(std::make_unique<CustomEmojiManager>(this))
 , _stories(std::make_unique<Stories>(this))
-, _savedMessages(std::make_unique<SavedMessages>(this)) {
+, _savedMessages(std::make_unique<SavedMessages>(this))
+, _chatbots(std::make_unique<Chatbots>(this)) {
 	_cache->open(_session->local().cacheKey());
 	_bigFileCache->open(_session->local().cacheBigFileKey());
 
diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h
index d391d1d31..4fc7b1db1 100644
--- a/Telegram/SourceFiles/data/data_session.h
+++ b/Telegram/SourceFiles/data/data_session.h
@@ -62,6 +62,7 @@ class NotifySettings;
 class CustomEmojiManager;
 class Stories;
 class SavedMessages;
+class Chatbots;
 struct ReactionId;
 
 struct RepliesReadTillUpdate {
@@ -142,6 +143,9 @@ public:
 	[[nodiscard]] SavedMessages &savedMessages() const {
 		return *_savedMessages;
 	}
+	[[nodiscard]] Chatbots &chatbots() const {
+		return *_chatbots;
+	}
 
 	[[nodiscard]] MsgId nextNonHistoryEntryId() {
 		return ++_nonHistoryEntryId;
@@ -1065,6 +1069,7 @@ private:
 	const std::unique_ptr<CustomEmojiManager> _customEmojiManager;
 	const std::unique_ptr<Stories> _stories;
 	const std::unique_ptr<SavedMessages> _savedMessages;
+	const std::unique_ptr<Chatbots> _chatbots;
 
 	MsgId _nonHistoryEntryId = ServerMaxMsgId.bare + ScheduledMsgIdsRange;
 
diff --git a/Telegram/SourceFiles/settings/business/settings_business_exceptions.cpp b/Telegram/SourceFiles/settings/business/settings_business_exceptions.cpp
new file mode 100644
index 000000000..568aca80f
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_business_exceptions.cpp
@@ -0,0 +1,145 @@
+/*
+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 "settings/business/settings_business_exceptions.h"
+
+#include "boxes/filters/edit_filter_chats_list.h"
+#include "boxes/filters/edit_filter_chats_preview.h"
+#include "data/data_session.h"
+#include "data/data_user.h"
+#include "history/history.h"
+#include "lang/lang_keys.h"
+#include "ui/wrap/vertical_layout.h"
+#include "window/window_session_controller.h"
+
+namespace Settings {
+namespace {
+
+using Flag = Data::ChatFilter::Flag;
+using Flags = Data::ChatFilter::Flags;
+
+[[nodiscard]] Flags TypesToFlags(Data::BusinessChatTypes types) {
+	using Type = Data::BusinessChatType;
+	return ((types & Type::Contacts) ? Flag::Contacts : Flag())
+		| ((types & Type::NonContacts) ? Flag::NonContacts : Flag())
+		| ((types & Type::NewChats) ? Flag::NewChats : Flag())
+		| ((types & Type::ExistingChats) ? Flag::ExistingChats : Flag());
+}
+
+[[nodiscard]] Data::BusinessChatTypes FlagsToTypes(Flags flags) {
+	using Type = Data::BusinessChatType;
+	return ((flags & Flag::Contacts) ? Type::Contacts : Type())
+		| ((flags & Flag::NonContacts) ? Type::NonContacts : Type())
+		| ((flags & Flag::NewChats) ? Type::NewChats : Type())
+		| ((flags & Flag::ExistingChats) ? Type::ExistingChats : Type());
+}
+
+} // namespace
+
+void EditBusinessExceptions(
+		not_null<Window::SessionController*> window,
+		BusinessExceptionsDescriptor &&descriptor) {
+	const auto session = &window->session();
+	const auto options = Flag::ExistingChats
+		| Flag::NewChats
+		| Flag::Contacts
+		| Flag::NonContacts;
+	auto &&peers = descriptor.current.list | ranges::views::transform([=](
+			not_null<UserData*> user) {
+		return user->owner().history(user);
+	});
+	auto controller = std::make_unique<EditFilterChatsListController>(
+		session,
+		(descriptor.allow
+			? tr::lng_filters_include_title()
+			: tr::lng_filters_exclude_title()),
+		options,
+		TypesToFlags(descriptor.current.types) & options,
+		base::flat_set<not_null<History*>>(begin(peers), end(peers)),
+		[=](int count) {
+			return nullptr; AssertIsDebug();
+		});
+	const auto rawController = controller.get();
+	const auto save = descriptor.save;
+	auto initBox = [=](not_null<PeerListBox*> box) {
+		box->setCloseByOutsideClick(false);
+		box->addButton(tr::lng_settings_save(), crl::guard(box, [=] {
+			const auto peers = box->collectSelectedRows();
+			auto &&users = ranges::views::all(
+				peers
+			) | ranges::views::transform([=](not_null<PeerData*> peer) {
+				return not_null(peer->asUser());
+			}) | ranges::to_vector;
+			save(Data::BusinessExceptions{
+				.types = FlagsToTypes(rawController->chosenOptions()),
+				.list = std::move(users),
+			});
+			box->closeBox();
+		}));
+		box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
+	};
+	window->show(
+		Box<PeerListBox>(std::move(controller), std::move(initBox)));
+}
+
+not_null<FilterChatsPreview*> SetupBusinessExceptionsPreview(
+		not_null<Ui::VerticalLayout*> content,
+		not_null<rpl::variable<Data::BusinessExceptions>*> data) {
+	const auto rules = data->current();
+
+	const auto locked = std::make_shared<bool>();
+	auto &&peers = data->current().list | ranges::views::transform([=](
+			not_null<UserData*> user) {
+		return user->owner().history(user);
+	});
+	const auto preview = content->add(object_ptr<FilterChatsPreview>(
+		content,
+		TypesToFlags(data->current().types),
+		base::flat_set<not_null<History*>>(begin(peers), end(peers))));
+
+	preview->flagRemoved(
+	) | rpl::start_with_next([=](Flag flag) {
+		*locked = true;
+		*data = Data::BusinessExceptions{
+			data->current().types & ~FlagsToTypes(flag),
+			data->current().list
+		};
+		*locked = false;
+	}, preview->lifetime());
+
+	preview->peerRemoved(
+	) | rpl::start_with_next([=](not_null<History*> history) {
+		auto list = data->current().list;
+		list.erase(
+			ranges::remove(list, not_null(history->peer->asUser())),
+			end(list));
+
+		*locked = true;
+		*data = Data::BusinessExceptions{
+			data->current().types,
+			std::move(list)
+		};
+		*locked = false;
+	}, preview->lifetime());
+
+	data->changes(
+	) | rpl::filter([=] {
+		return !*locked;
+	}) | rpl::start_with_next([=](const Data::BusinessExceptions &rules) {
+		auto &&peers = rules.list | ranges::views::transform([=](
+				not_null<UserData*> user) {
+			return user->owner().history(user);
+		});
+		preview->updateData(
+			TypesToFlags(rules.types),
+			base::flat_set<not_null<History*>>(begin(peers), end(peers)));
+	}, preview->lifetime());
+
+	return preview;
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_business_exceptions.h b/Telegram/SourceFiles/settings/business/settings_business_exceptions.h
new file mode 100644
index 000000000..e60f1a01b
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_business_exceptions.h
@@ -0,0 +1,37 @@
+/*
+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
+*/
+#pragma once
+
+#include "data/business/data_business_common.h"
+
+class FilterChatsPreview;
+
+namespace Ui {
+class VerticalLayout;
+} // namespace Ui
+
+namespace Window {
+class SessionController;
+} // namespace Window
+
+namespace Settings {
+
+struct BusinessExceptionsDescriptor {
+	Data::BusinessExceptions current;
+	Fn<void(const Data::BusinessExceptions&)> save;
+	bool allow = false;
+};
+void EditBusinessExceptions(
+	not_null<Window::SessionController*> window,
+	BusinessExceptionsDescriptor &&descriptor);
+
+not_null<FilterChatsPreview*> SetupBusinessExceptionsPreview(
+	not_null<Ui::VerticalLayout*> content,
+	not_null<rpl::variable<Data::BusinessExceptions>*> data);
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
index 34969c7d9..d358b5112 100644
--- a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
@@ -7,7 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/business/settings_chatbots.h"
 
+#include "core/application.h"
+#include "data/business/data_business_chatbots.h"
+#include "data/data_session.h"
+#include "data/data_user.h"
 #include "lang/lang_keys.h"
+#include "main/main_session.h"
+#include "settings/business/settings_business_exceptions.h"
 #include "settings/settings_common_session.h"
 #include "ui/text/text_utilities.h"
 #include "ui/widgets/fields/input_field.h"
@@ -15,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/wrap/slide_wrap.h"
 #include "ui/wrap/vertical_layout.h"
 #include "ui/vertical_list.h"
+#include "window/window_session_controller.h"
 #include "styles/style_layers.h"
 #include "styles/style_settings.h"
 
@@ -29,6 +36,7 @@ public:
 	Chatbots(
 		QWidget *parent,
 		not_null<Window::SessionController*> controller);
+	~Chatbots();
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
@@ -42,24 +50,41 @@ public:
 
 private:
 	void setupContent(not_null<Window::SessionController*> controller);
+	void save();
 
 	void showFinished() override {
 		_showFinished.fire({});
 	}
 
+	const not_null<Window::SessionController*> _controller;
+	const not_null<Main::Session*> _session;
+
 	rpl::event_stream<> _showFinished;
 	Ui::RoundRect _bottomSkipRounding;
 
+	rpl::variable<bool> _onlySelected = false;
+	rpl::variable<bool> _repliesAllowed = true;
+	rpl::variable<Data::BusinessExceptions> _allowed;
+	rpl::variable<Data::BusinessExceptions> _disallowed;
+
 };
 
 Chatbots::Chatbots(
 	QWidget *parent,
 	not_null<Window::SessionController*> controller)
 : Section(parent)
+, _controller(controller)
+, _session(&controller->session())
 , _bottomSkipRounding(st::boxRadius, st::boxDividerBg) {
 	setupContent(controller);
 }
 
+Chatbots::~Chatbots() {
+	if (!Core::Quitting()) {
+		save();
+	}
+}
+
 rpl::producer<QString> Chatbots::title() {
 	return tr::lng_chatbots_title();
 }
@@ -69,12 +94,12 @@ void Chatbots::setupContent(
 	using namespace rpl::mappers;
 
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
+	const auto current = controller->session().data().chatbots().current();
 
-	struct State {
-		rpl::variable<bool> onlySelected = false;
-		rpl::variable<bool> replyAllowed = true;
-	};
-	const auto state = content->lifetime().make_state<State>();
+	_onlySelected = current.onlySelected;
+	_repliesAllowed = current.repliesAllowed;
+	_allowed = current.allowed;
+	_disallowed = current.disallowed;
 
 	AddDividerTextWithLottie(content, {
 		.lottie = u"robot"_q,
@@ -93,7 +118,11 @@ void Chatbots::setupContent(
 		object_ptr<Ui::InputField>(
 			content,
 			st::settingsChatbotsUsername,
-			tr::lng_chatbots_placeholder()),
+			tr::lng_chatbots_placeholder(),
+			(current.bot
+				? current.bot->session().createInternalLink(
+					current.bot->username())
+				: QString())),
 		st::settingsChatbotsUsernameMargins);
 
 	Ui::AddDividerText(
@@ -104,7 +133,7 @@ void Chatbots::setupContent(
 	Ui::AddSubsectionTitle(content, tr::lng_chatbots_access_title());
 
 	const auto group = std::make_shared<Ui::RadiobuttonGroup>(
-		state->onlySelected.current() ? kSelectedOnly : kAllExcept);
+		_onlySelected.current() ? kSelectedOnly : kAllExcept);
 	const auto everyone = content->add(
 		object_ptr<Ui::Radiobutton>(
 			content,
@@ -139,8 +168,18 @@ void Chatbots::setupContent(
 		tr::lng_chatbots_exclude_button(),
 		st::settingsChatbotsAdd,
 		{ &st::settingsIconRemove, IconType::Round, &st::windowBgActive });
+	excludeAdd->setClickedCallback([=] {
+		EditBusinessExceptions(_controller, {
+			.current = _disallowed.current(),
+			.save = crl::guard(this, [=](Data::BusinessExceptions value) {
+				_disallowed = std::move(value);
+			}),
+			.allow = false,
+		});
+	});
+	SetupBusinessExceptionsPreview(excludeInner, &_disallowed);
 
-	excludeWrap->toggleOn(state->onlySelected.value() | rpl::map(!_1));
+	excludeWrap->toggleOn(_onlySelected.value() | rpl::map(!_1));
 	excludeWrap->finishAnimating();
 
 	const auto includeWrap = content->add(
@@ -157,12 +196,22 @@ void Chatbots::setupContent(
 		tr::lng_chatbots_include_button(),
 		st::settingsChatbotsAdd,
 		{ &st::settingsIconAdd, IconType::Round, &st::windowBgActive });
+	includeAdd->setClickedCallback([=] {
+		EditBusinessExceptions(_controller, {
+			.current = _allowed.current(),
+			.save = crl::guard(this, [=](Data::BusinessExceptions value) {
+				_allowed = std::move(value);
+			}),
+			.allow = true,
+		});
+	});
+	SetupBusinessExceptionsPreview(includeInner, &_allowed);
 
-	includeWrap->toggleOn(state->onlySelected.value());
+	includeWrap->toggleOn(_onlySelected.value());
 	includeWrap->finishAnimating();
 
 	group->setChangedCallback([=](int value) {
-		state->onlySelected = (value == kSelectedOnly);
+		_onlySelected = (value == kSelectedOnly);
 	});
 
 	Ui::AddSkip(content, st::settingsChatbotsAccessSkip);
@@ -177,9 +226,9 @@ void Chatbots::setupContent(
 		content,
 		tr::lng_chatbots_reply(),
 		st::settingsButtonNoIcon
-	))->toggleOn(state->replyAllowed.value())->toggledChanges(
+	))->toggleOn(_repliesAllowed.value())->toggledChanges(
 	) | rpl::start_with_next([=](bool value) {
-		state->replyAllowed = value;
+		_repliesAllowed = value;
 	}, content->lifetime());
 	Ui::AddSkip(content);
 
@@ -192,6 +241,17 @@ void Chatbots::setupContent(
 	Ui::ResizeFitChild(this, content);
 }
 
+void Chatbots::save() {
+	const auto settings = Data::ChatbotsSettings{
+		.bot = nullptr,
+		.allowed = _allowed.current(),
+		.disallowed = _disallowed.current(),
+		.repliesAllowed = _repliesAllowed.current(),
+		.onlySelected = _onlySelected.current(),
+	};
+	_session->data().chatbots().save(settings);
+}
+
 } // namespace
 
 Type ChatbotsId() {
diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style
index 38621c69b..645c78285 100644
--- a/Telegram/SourceFiles/window/window.style
+++ b/Telegram/SourceFiles/window/window.style
@@ -303,6 +303,8 @@ windowFilterTypeBots: icon {{ "folders/folders_type_bots", historyPeerUserpicFg
 windowFilterTypeNoMuted: icon {{ "folders/folders_type_muted", historyPeerUserpicFg }};
 windowFilterTypeNoArchived: icon {{ "folders/folders_type_archived", historyPeerUserpicFg }};
 windowFilterTypeNoRead: icon {{ "folders/folders_type_read", historyPeerUserpicFg }};
+windowFilterTypeNewChats: icon {{ "folders/folders_unread", historyPeerUserpicFg }};
+windowFilterTypeExistingChats: windowFilterTypeNoRead;
 windowFilterChatsSectionSubtitleHeight: 28px;
 windowFilterChatsSectionSubtitle: FlatLabel(defaultFlatLabel) {
 	style: TextStyle(defaultTextStyle) {