From 4a9f584d48a3d5ff475e5e9b49041056bf2d49ce Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 7 May 2026 16:52:52 +0400 Subject: [PATCH] Store recent inline bot per-account. --- Telegram/CMakeLists.txt | 2 + .../chat_helpers/field_autocomplete.cpp | 23 +++---- Telegram/SourceFiles/config.h | 2 - .../data/components/recent_inline_bots.cpp | 69 +++++++++++++++++++ .../data/components/recent_inline_bots.h | 36 ++++++++++ Telegram/SourceFiles/data/data_session.cpp | 1 - .../SourceFiles/history/history_widget.cpp | 13 +--- .../view/history_view_chat_section.cpp | 13 +--- .../view/history_view_scheduled_section.cpp | 13 +--- Telegram/SourceFiles/main/main_session.cpp | 2 + Telegram/SourceFiles/main/main_session.h | 5 ++ .../media/stories/media_stories_reply.cpp | 13 +--- Telegram/SourceFiles/settings.cpp | 2 - Telegram/SourceFiles/settings.h | 4 -- .../business/settings_shortcut_messages.cpp | 13 +--- .../SourceFiles/storage/storage_account.cpp | 18 +++-- 16 files changed, 146 insertions(+), 83 deletions(-) create mode 100644 Telegram/SourceFiles/data/components/recent_inline_bots.cpp create mode 100644 Telegram/SourceFiles/data/components/recent_inline_bots.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 1167fbcdcc..215baeeaf9 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -590,6 +590,8 @@ PRIVATE data/components/passkeys.h data/components/promo_suggestions.cpp data/components/promo_suggestions.h + data/components/recent_inline_bots.cpp + data/components/recent_inline_bots.h data/components/recent_peers.cpp data/components/recent_peers.h data/components/recent_shared_media_gifts.cpp diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 407ff1f866..ce52852f3b 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/field_autocomplete.h" #include "data/business/data_shortcut_messages.h" +#include "data/components/recent_inline_bots.h" #include "data/components/top_peers.h" #include "data/data_document.h" #include "data/data_document_media.h" @@ -65,7 +66,7 @@ namespace { template inline int indexOfInFirstN(const T &v, const U &elem, int last) { - for (auto b = v.cbegin(), i = b, e = b + std::max(int(v.size()), last) + for (auto b = v.cbegin(), i = b, e = b + std::min(int(v.size()), last) ; i != e ; ++i) { if (i->user == elem) { @@ -464,7 +465,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { ? _session->topGuestChatBots().list() : std::vector>(); int maxListSize = _addInlineBots - ? (cRecentInlineBots().size() + int(guestChatBots.size())) + ? (_session->recentInlineBots().list().size() + int(guestChatBots.size())) : 0; if (_chat) { maxListSize += (_chat->participants.empty() ? _chat->lastAuthors.size() : _chat->participants.size()); @@ -507,6 +508,9 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { const auto pushMentionRow = [&]( not_null user, MentionRow::Source source) { + if (containsMentionUser(user)) { + return; + } mrows.push_back({ user, source }); }; const auto markMentionCandidateIfExists = [&]( @@ -521,7 +525,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { bool listAllSuggestions = _filter.isEmpty(); if (_addInlineBots) { - for (const auto user : cRecentInlineBots()) { + for (const auto user : _session->recentInlineBots().list()) { if (user->isInaccessible() || (!listAllSuggestions && filterNotPassedByUsername(user))) { @@ -1401,14 +1405,9 @@ void FieldAutocomplete::Inner::mousePressEvent(QMouseEvent *e) { } else { const auto &row = _mrows->at(_sel); switch (row.source) { - case MentionRow::Source::InlineRecent: { - RecentInlineBots &recent(cRefRecentInlineBots()); - int32 index = recent.indexOf(row.user); - if (index >= 0) { - recent.remove(index); - writeRecent = true; - } - } break; + case MentionRow::Source::InlineRecent: + _session->recentInlineBots().remove(row.user); + break; case MentionRow::Source::GuestChatTopPeer: _session->topGuestChatBots().remove(row.user); break; @@ -1818,7 +1817,7 @@ void InitFieldAutocomplete( && cRecentSearchHashtags().isEmpty()) { peer->session().local().readRecentHashtagsAndBots(); } else if (parsed.query[0] == '@' - && cRecentInlineBots().isEmpty()) { + && peer->session().recentInlineBots().list().empty()) { peer->session().local().readRecentHashtagsAndBots(); } else if (parsed.query[0] == '/' && peer->isUser() diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 5dc8b7ce76..5d861a7412 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -17,8 +17,6 @@ enum { LocalEncryptNoPwdIterCount = 4, // key derivation iteration count without pwd (not secure anyway) LocalEncryptSaltSize = 32, // 256 bit - RecentInlineBotsLimit = 10, - AutoSearchTimeout = 900, // 0.9 secs PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request diff --git a/Telegram/SourceFiles/data/components/recent_inline_bots.cpp b/Telegram/SourceFiles/data/components/recent_inline_bots.cpp new file mode 100644 index 0000000000..fcbe0f1734 --- /dev/null +++ b/Telegram/SourceFiles/data/components/recent_inline_bots.cpp @@ -0,0 +1,69 @@ +/* +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/components/recent_inline_bots.h" + +#include "config.h" +#include "data/data_user.h" +#include "main/main_session.h" +#include "storage/storage_account.h" + +namespace Data { +namespace { + +constexpr auto kLimit = 10; + +} // namespace + +RecentInlineBots::RecentInlineBots(not_null session) +: _session(session) { +} + +const std::vector> &RecentInlineBots::list() const { + _session->local().readRecentHashtagsAndBots(); + return _list; +} + +rpl::producer<> RecentInlineBots::updates() const { + return _updates.events(); +} + +void RecentInlineBots::bump(not_null user) { + _session->local().readRecentHashtagsAndBots(); + + if (!_list.empty() && _list.front() == user) { + return; + } + auto i = ranges::find(_list, user); + if (i == end(_list)) { + if (int(_list.size()) >= kLimit) { + _list.pop_back(); + } + _list.insert(begin(_list), user); + } else { + ranges::rotate(begin(_list), i, i + 1); + } + _updates.fire({}); + + _session->local().writeRecentHashtagsAndBots(); +} + +void RecentInlineBots::remove(not_null user) { + const auto i = ranges::find(_list, user); + if (i != end(_list)) { + _list.erase(i); + _updates.fire({}); + _session->local().writeRecentHashtagsAndBots(); + } +} + +void RecentInlineBots::applyLocal( + std::vector> list) { + _list = std::move(list); +} + +} // namespace Data diff --git a/Telegram/SourceFiles/data/components/recent_inline_bots.h b/Telegram/SourceFiles/data/components/recent_inline_bots.h new file mode 100644 index 0000000000..739512c0c1 --- /dev/null +++ b/Telegram/SourceFiles/data/components/recent_inline_bots.h @@ -0,0 +1,36 @@ +/* +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 + +class UserData; + +namespace Main { +class Session; +} // namespace Main + +namespace Data { + +class RecentInlineBots final { +public: + explicit RecentInlineBots(not_null session); + + [[nodiscard]] const std::vector> &list() const; + [[nodiscard]] rpl::producer<> updates() const; + + void bump(not_null user); + void remove(not_null user); + void applyLocal(std::vector> list); + +private: + const not_null _session; + std::vector> _list; + rpl::event_stream<> _updates; + +}; + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 4d30915673..315f2c0791 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -438,7 +438,6 @@ void Session::clear() { base::take(_nonChannelMessages); _messageByRandomId.clear(); _sentMessagesData.clear(); - cSetRecentInlineBots(RecentInlineBots()); cSetRecentStickers(RecentStickerPack()); HistoryView::Element::ClearGlobal(); _contactsNoChatsList.clear(); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index f443ea9ba6..e56e7797ea 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -68,6 +68,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/call_delayed.h" #include "data/business/data_shortcut_messages.h" #include "data/components/credits.h" +#include "data/components/recent_inline_bots.h" #include "data/components/scheduled_messages.h" #include "data/components/sponsored_messages.h" #include "data/notify/data_notify_settings.h" @@ -8224,17 +8225,7 @@ void HistoryWidget::sendInlineResult(InlineBots::ResultSelected result) { clearFieldText(); saveDraftWithTextNow(); - auto &bots = cRefRecentInlineBots(); - const auto index = bots.indexOf(result.bot); - if (index) { - if (index > 0) { - bots.removeAt(index); - } else if (bots.size() >= RecentInlineBotsLimit) { - bots.resize(RecentInlineBotsLimit - 1); - } - bots.push_front(result.bot); - session().local().writeRecentHashtagsAndBots(); - } + session().recentInlineBots().bump(result.bot); hideSelectorControlsAnimated(); diff --git a/Telegram/SourceFiles/history/view/history_view_chat_section.cpp b/Telegram/SourceFiles/history/view/history_view_chat_section.cpp index 2dd10dc302..f7640bd575 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_chat_section.cpp @@ -64,6 +64,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "main/main_session_settings.h" #include "menu/menu_timecode_action.h" +#include "data/components/recent_inline_bots.h" #include "data/components/scheduled_messages.h" #include "data/data_histories.h" #include "data/data_saved_messages.h" @@ -1832,17 +1833,7 @@ void ChatWidget::sendInlineResult( //_saveDraftStart = crl::now(); //onDraftSave(); - auto &bots = cRefRecentInlineBots(); - const auto index = bots.indexOf(bot); - if (index) { - if (index > 0) { - bots.removeAt(index); - } else if (bots.size() >= RecentInlineBotsLimit) { - bots.resize(RecentInlineBotsLimit - 1); - } - bots.push_front(bot); - bot->session().local().writeRecentHashtagsAndBots(); - } + bot->session().recentInlineBots().bump(bot); finishSending(); } diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 97dea95670..3ff764bc49 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -42,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/tabbed_selector.h" #include "main/main_session.h" #include "mainwindow.h" +#include "data/components/recent_inline_bots.h" #include "data/components/scheduled_messages.h" #include "data/data_document.h" #include "data/data_file_origin.h" @@ -914,17 +915,7 @@ void ScheduledWidget::sendInlineResult( //_saveDraftStart = crl::now(); //onDraftSave(); - auto &bots = cRefRecentInlineBots(); - const auto index = bots.indexOf(bot); - if (index) { - if (index > 0) { - bots.removeAt(index); - } else if (bots.size() >= RecentInlineBotsLimit) { - bots.resize(RecentInlineBotsLimit - 1); - } - bots.push_front(bot); - bot->session().local().writeRecentHashtagsAndBots(); - } + bot->session().recentInlineBots().bump(bot); _composeControls->hidePanelsAnimated(); _composeControls->focus(); diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index a07a2dde9a..cedcddfd13 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/components/location_pickers.h" #include "data/components/passkeys.h" #include "data/components/promo_suggestions.h" +#include "data/components/recent_inline_bots.h" #include "data/components/recent_peers.h" #include "data/components/recent_shared_media_gifts.h" #include "data/components/scheduled_messages.h" @@ -125,6 +126,7 @@ Session::Session( , _topGuestChatBots(std::make_unique( this, Data::TopPeerType::BotGuestChat)) +, _recentInlineBots(std::make_unique(this)) , _factchecks(std::make_unique(this)) , _locationPickers(std::make_unique()) , _credits(std::make_unique(this)) diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h index 825960d4dc..f1f3a0eee1 100644 --- a/Telegram/SourceFiles/main/main_session.h +++ b/Telegram/SourceFiles/main/main_session.h @@ -33,6 +33,7 @@ namespace Data { class Session; class Changes; class GiftAuctions; +class RecentInlineBots; class RecentPeers; class RecentSharedMediaGifts; class ScheduledMessages; @@ -162,6 +163,9 @@ public: [[nodiscard]] Data::TopPeers &topGuestChatBots() const { return *_topGuestChatBots; } + [[nodiscard]] Data::RecentInlineBots &recentInlineBots() const { + return *_recentInlineBots; + } [[nodiscard]] Data::Factchecks &factchecks() const { return *_factchecks; } @@ -320,6 +324,7 @@ private: const std::unique_ptr _topPeers; const std::unique_ptr _topBotApps; const std::unique_ptr _topGuestChatBots; + const std::unique_ptr _recentInlineBots; const std::unique_ptr _factchecks; const std::unique_ptr _locationPickers; const std::unique_ptr _credits; diff --git a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp index b745473553..c1fae9a2ee 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/stickers/data_custom_emoji.h" #include "data/data_changes.h" #include "data/data_chat_participant_status.h" +#include "data/components/recent_inline_bots.h" #include "data/data_document.h" #include "data/data_group_call.h" #include "data/data_message_reaction_id.h" @@ -481,17 +482,7 @@ void ReplyArea::sendInlineResult( action, localMessageId); - auto &bots = cRefRecentInlineBots(); - const auto index = bots.indexOf(bot); - if (index) { - if (index > 0) { - bots.removeAt(index); - } else if (bots.size() >= RecentInlineBotsLimit) { - bots.resize(RecentInlineBotsLimit - 1); - } - bots.push_front(bot); - bot->session().local().writeRecentHashtagsAndBots(); - } + bot->session().recentInlineBots().bump(bot); finishSending(); _controls->clear(); } diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 7b5e9ba868..c77c212e4f 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -49,8 +49,6 @@ RecentStickerPack gRecentStickers; RecentHashtagPack gRecentWriteHashtags, gRecentSearchHashtags; -RecentInlineBots gRecentInlineBots; - bool gPasswordRecovered = false; int32 gPasscodeBadTries = 0; crl::time gPasscodeLastTry = 0; diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index f39138e3a9..4a29e70d3c 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -99,10 +99,6 @@ typedef QList> RecentHashtagPack; DeclareRefSetting(RecentHashtagPack, RecentWriteHashtags); DeclareSetting(RecentHashtagPack, RecentSearchHashtags); -class UserData; -typedef QVector RecentInlineBots; -DeclareRefSetting(RecentInlineBots, RecentInlineBots); - DeclareSetting(bool, PasswordRecovered); DeclareSetting(int32, PasscodeBadTries); diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp index aa47d3bc3f..9d2e0d355e 100644 --- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp +++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/file_utilities.h" #include "core/mime_type.h" #include "data/business/data_shortcut_messages.h" +#include "data/components/recent_inline_bots.h" #include "data/data_chat_participant_status.h" #include "data/data_message_reaction_id.h" #include "data/data_premium_limits.h" @@ -1538,17 +1539,7 @@ void ShortcutMessages::sendInlineResult( //_saveDraftStart = crl::now(); //onDraftSave(); - auto &bots = cRefRecentInlineBots(); - const auto index = bots.indexOf(bot); - if (index) { - if (index > 0) { - bots.removeAt(index); - } else if (bots.size() >= RecentInlineBotsLimit) { - bots.resize(RecentInlineBotsLimit - 1); - } - bots.push_front(bot); - bot->session().local().writeRecentHashtagsAndBots(); - } + bot->session().recentInlineBots().bump(bot); finishSending(); } diff --git a/Telegram/SourceFiles/storage/storage_account.cpp b/Telegram/SourceFiles/storage/storage_account.cpp index e928e20d54..3c6ebacb35 100644 --- a/Telegram/SourceFiles/storage/storage_account.cpp +++ b/Telegram/SourceFiles/storage/storage_account.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "core/core_settings.h" #include "core/file_location.h" +#include "data/components/recent_inline_bots.h" #include "data/components/recent_peers.h" #include "settings/settings_recent_searches.h" #include "data/components/top_peers.h" @@ -2767,12 +2768,12 @@ void Account::readSavedGifs() { void Account::writeRecentHashtagsAndBots() { const auto &write = cRecentWriteHashtags(); const auto &search = cRecentSearchHashtags(); - const auto &bots = cRecentInlineBots(); + const auto &bots = _owner->session().recentInlineBots().list(); - if (write.isEmpty() && search.isEmpty() && bots.isEmpty()) { + if (write.isEmpty() && search.isEmpty() && bots.empty()) { readRecentHashtagsAndBots(); } - if (write.isEmpty() && search.isEmpty() && bots.isEmpty()) { + if (write.isEmpty() && search.isEmpty() && bots.empty()) { if (_recentHashtagsAndBotsKey) { ClearKey(_recentHashtagsAndBotsKey, _basePath); _recentHashtagsAndBotsKey = 0; @@ -2784,7 +2785,7 @@ void Account::writeRecentHashtagsAndBots() { _recentHashtagsAndBotsKey = GenerateKey(_basePath); writeMapQueued(); } - quint32 size = sizeof(quint32) * 3, writeCnt = 0, searchCnt = 0, botsCnt = cRecentInlineBots().size(); + quint32 size = sizeof(quint32) * 3, writeCnt = 0, searchCnt = 0, botsCnt = bots.size(); for (auto i = write.cbegin(), e = write.cend(); i != e; ++i) { if (!i->first.isEmpty()) { size += Serialize::stringSize(i->first) + sizeof(quint16); @@ -2838,7 +2839,7 @@ void Account::readRecentHashtagsAndBots() { quint16 count; RecentHashtagPack write, search; - RecentInlineBots bots; + std::vector> bots; if (writeCount) { write.reserve(writeCount); for (uint32 i = 0; i < writeCount; ++i) { @@ -2871,11 +2872,14 @@ void Account::readRecentHashtagsAndBots() { && peer->asUser()->isBot() && !peer->asUser()->botInfo->inlinePlaceholder.isEmpty() && !peer->asUser()->username().isEmpty()) { - bots.push_back(peer->asUser()); + const auto user = peer->asUser(); + if (ranges::find(bots, not_null{ user }) == end(bots)) { + bots.push_back(user); + } } } } - cSetRecentInlineBots(bots); + _owner->session().recentInlineBots().applyLocal(std::move(bots)); } }