mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Track bot commands separately in different chats.
This commit is contained in:
parent
93d99d6173
commit
b930bc0e6d
14 changed files with 149 additions and 94 deletions
|
@ -442,23 +442,24 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
|
|||
} else if (_type == Type::BotCommands) {
|
||||
bool listAllSuggestions = _filter.isEmpty();
|
||||
bool hasUsername = _filter.indexOf('@') > 0;
|
||||
base::flat_set<not_null<UserData*>> bots;
|
||||
base::flat_map<
|
||||
not_null<UserData*>,
|
||||
not_null<const std::vector<BotCommand>*>> bots;
|
||||
int32 cnt = 0;
|
||||
if (_chat) {
|
||||
if (_chat->noParticipantInfo()) {
|
||||
_chat->session().api().requestFullPeer(_chat);
|
||||
} else if (!_chat->participants.empty()) {
|
||||
const auto &commands = _chat->botCommands();
|
||||
for (const auto user : _chat->participants) {
|
||||
if (!user->isBot()) {
|
||||
continue;
|
||||
} else if (!user->botInfo->inited) {
|
||||
user->session().api().requestFullPeer(user);
|
||||
}
|
||||
if (user->botInfo->commands.empty()) {
|
||||
continue;
|
||||
const auto i = commands.find(peerToUser(user->id));
|
||||
if (i != end(commands)) {
|
||||
bots.emplace(user, &i->second);
|
||||
cnt += i->second.size();
|
||||
}
|
||||
bots.emplace(user);
|
||||
cnt += user->botInfo->commands.size();
|
||||
}
|
||||
}
|
||||
} else if (_user && _user->isBot()) {
|
||||
|
@ -466,24 +467,23 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
|
|||
_user->session().api().requestFullPeer(_user);
|
||||
}
|
||||
cnt = _user->botInfo->commands.size();
|
||||
bots.emplace(_user);
|
||||
bots.emplace(_user, &_user->botInfo->commands);
|
||||
} else if (_channel && _channel->isMegagroup()) {
|
||||
if (_channel->mgInfo->bots.empty()) {
|
||||
if (!_channel->mgInfo->botStatus) {
|
||||
_channel->session().api().requestBots(_channel);
|
||||
}
|
||||
} else {
|
||||
const auto &commands = _channel->mgInfo->botCommands();
|
||||
for (const auto user : _channel->mgInfo->bots) {
|
||||
if (!user->isBot()) {
|
||||
continue;
|
||||
} else if (!user->botInfo->inited) {
|
||||
user->session().api().requestFullPeer(user);
|
||||
}
|
||||
if (user->botInfo->commands.empty()) {
|
||||
continue;
|
||||
const auto i = commands.find(peerToUser(user->id));
|
||||
if (i != end(commands)) {
|
||||
bots.emplace(user, &i->second);
|
||||
cnt += i->second.size();
|
||||
}
|
||||
bots.emplace(user);
|
||||
cnt += user->botInfo->commands.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -504,16 +504,12 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
|
|||
for (const auto &user : _chat->lastAuthors) {
|
||||
if (!user->isBot()) {
|
||||
continue;
|
||||
} else if (!bots.contains(user)) {
|
||||
continue;
|
||||
} else if (!user->botInfo->inited) {
|
||||
user->session().api().requestFullPeer(user);
|
||||
}
|
||||
if (user->botInfo->commands.empty()) {
|
||||
const auto i = bots.find(user);
|
||||
if (i == end(bots)) {
|
||||
continue;
|
||||
}
|
||||
bots.remove(user);
|
||||
for (const auto &command : user->botInfo->commands) {
|
||||
for (const auto &command : *i->second) {
|
||||
if (!listAllSuggestions) {
|
||||
auto toFilter = (hasUsername || botStatus == 0 || botStatus == 2)
|
||||
? command.command + '@' + user->username
|
||||
|
@ -524,12 +520,13 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
|
|||
}
|
||||
brows.push_back(make(user, command));
|
||||
}
|
||||
bots.erase(i);
|
||||
}
|
||||
}
|
||||
if (!bots.empty()) {
|
||||
for (auto i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
|
||||
const auto user = *i;
|
||||
for (const auto &command : user->botInfo->commands) {
|
||||
const auto user = i->first;
|
||||
for (const auto &command : *i->second) {
|
||||
if (!listAllSuggestions) {
|
||||
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? command.command + '@' + user->username : command.command;
|
||||
if (!toFilter.startsWith(_filter, Qt::CaseInsensitive)/* || toFilter.size() == _filter.size()*/) continue;
|
||||
|
|
|
@ -46,6 +46,10 @@ void MegagroupInfo::setLocation(const ChannelLocation &location) {
|
|||
_location = location;
|
||||
}
|
||||
|
||||
bool MegagroupInfo::updateBotCommands(const MTPVector<MTPBotInfo> &data) {
|
||||
return Data::UpdateBotCommands(_botCommands, data);
|
||||
}
|
||||
|
||||
ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id)
|
||||
: PeerData(owner, id)
|
||||
, inputChannel(
|
||||
|
@ -803,15 +807,6 @@ void ApplyChannelUpdate(
|
|||
const auto chat = channel->owner().chat(migratedFrom->v);
|
||||
Data::ApplyMigration(chat, channel);
|
||||
}
|
||||
for (const auto &item : update.vbot_info().v) {
|
||||
auto &owner = channel->owner();
|
||||
item.match([&](const MTPDbotInfo &info) {
|
||||
if (const auto user = owner.userLoaded(info.vuser_id().v)) {
|
||||
user->setBotInfo(item);
|
||||
session->api().fullPeerUpdated().notify(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
channel->setAbout(qs(update.vabout()));
|
||||
channel->setMembersCount(update.vparticipants_count().value_or_empty());
|
||||
channel->setAdminsCount(update.vadmins_count().value_or_empty());
|
||||
|
@ -867,6 +862,9 @@ void ApplyChannelUpdate(
|
|||
SetTopPinnedMessageId(channel, pinned->v);
|
||||
}
|
||||
if (channel->isMegagroup()) {
|
||||
if (channel->mgInfo->updateBotCommands(update.vbot_info())) {
|
||||
channel->owner().botCommandsChanged(channel);
|
||||
}
|
||||
const auto stickerSet = update.vstickerset();
|
||||
const auto set = stickerSet ? &stickerSet->c_stickerSet() : nullptr;
|
||||
const auto newSetId = (set ? set->vid().v : 0);
|
||||
|
|
|
@ -56,6 +56,12 @@ public:
|
|||
const ChannelLocation *getLocation() const;
|
||||
void setLocation(const ChannelLocation &location);
|
||||
|
||||
bool updateBotCommands(const MTPVector<MTPBotInfo> &data);
|
||||
[[nodiscard]] auto botCommands() const
|
||||
-> const base::flat_map<UserId, std::vector<BotCommand>> & {
|
||||
return _botCommands;
|
||||
}
|
||||
|
||||
std::deque<not_null<UserData*>> lastParticipants;
|
||||
base::flat_map<not_null<UserData*>, Admin> lastAdmins;
|
||||
base::flat_map<not_null<UserData*>, Restricted> lastRestricted;
|
||||
|
@ -82,6 +88,7 @@ public:
|
|||
private:
|
||||
ChatData *_migratedFrom = nullptr;
|
||||
ChannelLocation _location;
|
||||
base::flat_map<UserId, std::vector<BotCommand>> _botCommands;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -246,6 +246,12 @@ PeerId ChatData::groupCallDefaultJoinAs() const {
|
|||
return _callDefaultJoinAs;
|
||||
}
|
||||
|
||||
void ChatData::setBotCommands(const MTPVector<MTPBotInfo> &data) {
|
||||
if (Data::UpdateBotCommands(_botCommands, data)) {
|
||||
owner().botCommandsChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Data {
|
||||
|
||||
void ApplyChatUpdate(
|
||||
|
@ -393,15 +399,9 @@ void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
|
|||
|
||||
chat->setMessagesTTL(update.vttl_period().value_or_empty());
|
||||
if (const auto info = update.vbot_info()) {
|
||||
for (const auto &item : info->v) {
|
||||
item.match([&](const MTPDbotInfo &data) {
|
||||
const auto userId = data.vuser_id().v;
|
||||
if (const auto bot = chat->owner().userLoaded(userId)) {
|
||||
bot->setBotInfo(item);
|
||||
chat->session().api().fullPeerUpdated().notify(bot);
|
||||
}
|
||||
});
|
||||
}
|
||||
chat->setBotCommands(*info);
|
||||
} else {
|
||||
chat->setBotCommands(MTP_vector<MTPBotInfo>());
|
||||
}
|
||||
chat->setFullFlags(update.vflags().v);
|
||||
if (const auto photo = update.vchat_photo()) {
|
||||
|
|
|
@ -173,6 +173,12 @@ public:
|
|||
void setGroupCallDefaultJoinAs(PeerId peerId);
|
||||
[[nodiscard]] PeerId groupCallDefaultJoinAs() const;
|
||||
|
||||
void setBotCommands(const MTPVector<MTPBotInfo> &data);
|
||||
[[nodiscard]] auto botCommands() const
|
||||
-> const base::flat_map<UserId, std::vector<BotCommand>> & {
|
||||
return _botCommands;
|
||||
}
|
||||
|
||||
// Still public data members.
|
||||
const MTPint inputChat;
|
||||
|
||||
|
@ -198,6 +204,7 @@ private:
|
|||
|
||||
std::unique_ptr<Data::GroupCall> _call;
|
||||
PeerId _callDefaultJoinAs = 0;
|
||||
base::flat_map<UserId, std::vector<BotCommand>> _botCommands;
|
||||
|
||||
ChannelData *_migratedTo = nullptr;
|
||||
rpl::lifetime _lifetime;
|
||||
|
|
|
@ -84,6 +84,75 @@ PeerId FakePeerIdForJustName(const QString &name) {
|
|||
: base::crc32(name.constData(), name.size() * sizeof(QChar)));
|
||||
}
|
||||
|
||||
bool UpdateBotCommands(
|
||||
std::vector<BotCommand> &commands,
|
||||
const MTPVector<MTPBotCommand> &data) {
|
||||
const auto &v = data.v;
|
||||
commands.reserve(v.size());
|
||||
auto result = false;
|
||||
auto index = 0;
|
||||
for (const auto &command : v) {
|
||||
command.match([&](const MTPDbotCommand &data) {
|
||||
const auto command = qs(data.vcommand());
|
||||
const auto description = qs(data.vdescription());
|
||||
if (commands.size() <= index) {
|
||||
commands.push_back({
|
||||
.command = command,
|
||||
.description = description,
|
||||
});
|
||||
result = true;
|
||||
} else {
|
||||
auto &entry = commands[index];
|
||||
if (entry.command != command
|
||||
|| entry.description != description) {
|
||||
entry.command = command;
|
||||
entry.description = description;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
++index;
|
||||
});
|
||||
}
|
||||
if (index < commands.size()) {
|
||||
result = true;
|
||||
}
|
||||
commands.resize(index);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool UpdateBotCommands(
|
||||
base::flat_map<UserId, std::vector<BotCommand>> &commands,
|
||||
const MTPVector<MTPBotInfo> &data) {
|
||||
auto result = false;
|
||||
auto filled = base::flat_set<UserId>();
|
||||
filled.reserve(data.v.size());
|
||||
for (const auto &item : data.v) {
|
||||
item.match([&](const MTPDbotInfo &data) {
|
||||
const auto id = UserId(data.vuser_id().v);
|
||||
if (!filled.emplace(id).second) {
|
||||
LOG(("API Error: Two BotInfo for a single bot."));
|
||||
return;
|
||||
}
|
||||
if (data.vcommands().v.isEmpty()) {
|
||||
if (commands.remove(id)) {
|
||||
result = true;
|
||||
}
|
||||
} else if (UpdateBotCommands(commands[id], data.vcommands())) {
|
||||
result = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto i = begin(commands); i != end(commands);) {
|
||||
if (filled.contains(i->first)) {
|
||||
++i;
|
||||
} else {
|
||||
i = commands.erase(i);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
PeerClickHandler::PeerClickHandler(not_null<PeerData*> peer)
|
||||
|
|
|
@ -22,6 +22,11 @@ using ChatRestriction = MTPDchatBannedRights::Flag;
|
|||
using ChatAdminRights = MTPDchatAdminRights::Flags;
|
||||
using ChatRestrictions = MTPDchatBannedRights::Flags;
|
||||
|
||||
struct BotCommand {
|
||||
QString command;
|
||||
QString description;
|
||||
};
|
||||
|
||||
namespace Ui {
|
||||
class EmptyUserpic;
|
||||
} // namespace Ui
|
||||
|
@ -100,6 +105,13 @@ struct UnavailableReason {
|
|||
[[nodiscard]] TimeId ChatBannedRightsUntilDate(
|
||||
const MTPChatBannedRights &rights);
|
||||
|
||||
bool UpdateBotCommands(
|
||||
std::vector<BotCommand> &commands,
|
||||
const MTPVector<MTPBotCommand> &data);
|
||||
bool UpdateBotCommands(
|
||||
base::flat_map<UserId, std::vector<BotCommand>> &commands,
|
||||
const MTPVector<MTPBotInfo> &data);
|
||||
|
||||
} // namespace Data
|
||||
|
||||
class PeerClickHandler : public ClickHandler {
|
||||
|
|
|
@ -1088,11 +1088,11 @@ rpl::producer<not_null<UserData*>> Session::userIsBotChanges() const {
|
|||
return _userIsBotChanges.events();
|
||||
}
|
||||
|
||||
void Session::botCommandsChanged(not_null<UserData*> user) {
|
||||
_botCommandsChanges.fire_copy(user);
|
||||
void Session::botCommandsChanged(not_null<PeerData*> peer) {
|
||||
_botCommandsChanges.fire_copy(peer);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<UserData*>> Session::botCommandsChanges() const {
|
||||
rpl::producer<not_null<PeerData*>> Session::botCommandsChanges() const {
|
||||
return _botCommandsChanges.events();
|
||||
}
|
||||
|
||||
|
|
|
@ -215,8 +215,8 @@ public:
|
|||
|
||||
void userIsBotChanged(not_null<UserData*> user);
|
||||
[[nodiscard]] rpl::producer<not_null<UserData*>> userIsBotChanges() const;
|
||||
void botCommandsChanged(not_null<UserData*> user);
|
||||
[[nodiscard]] rpl::producer<not_null<UserData*>> botCommandsChanges() const;
|
||||
void botCommandsChanged(not_null<PeerData*> peer);
|
||||
[[nodiscard]] rpl::producer<not_null<PeerData*>> botCommandsChanges() const;
|
||||
|
||||
struct ItemVisibilityQuery {
|
||||
not_null<HistoryItem*> item;
|
||||
|
@ -831,7 +831,7 @@ private:
|
|||
rpl::event_stream<Data::Folder*> _chatsListLoadedEvents;
|
||||
rpl::event_stream<Data::Folder*> _chatsListChanged;
|
||||
rpl::event_stream<not_null<UserData*>> _userIsBotChanges;
|
||||
rpl::event_stream<not_null<UserData*>> _botCommandsChanges;
|
||||
rpl::event_stream<not_null<PeerData*>> _botCommandsChanges;
|
||||
base::Observable<ItemVisibilityQuery> _queryItemVisibility;
|
||||
rpl::event_stream<IdChange> _itemIdChanges;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemLayoutChanges;
|
||||
|
|
|
@ -129,38 +129,9 @@ void UserData::setBotInfo(const MTPBotInfo &info) {
|
|||
botInfo->description = desc;
|
||||
botInfo->text = Ui::Text::String(st::msgMinWidth);
|
||||
}
|
||||
|
||||
auto &v = d.vcommands().v;
|
||||
botInfo->commands.reserve(v.size());
|
||||
auto changedCommands = false;
|
||||
int32 j = 0;
|
||||
for (const auto &command : v) {
|
||||
command.match([&](const MTPDbotCommand &data) {
|
||||
const auto command = qs(data.vcommand());
|
||||
const auto description = qs(data.vdescription());
|
||||
if (botInfo->commands.size() <= j) {
|
||||
botInfo->commands.push_back({
|
||||
.command = command,
|
||||
.description = description,
|
||||
});
|
||||
changedCommands = true;
|
||||
} else {
|
||||
if (botInfo->commands[j].command != command) {
|
||||
botInfo->commands[j].command = command;
|
||||
changedCommands = true;
|
||||
}
|
||||
if (botInfo->commands[j].description != description) {
|
||||
botInfo->commands[j].description = description;
|
||||
changedCommands = true;
|
||||
}
|
||||
}
|
||||
++j;
|
||||
});
|
||||
}
|
||||
while (j < botInfo->commands.size()) {
|
||||
botInfo->commands.pop_back();
|
||||
changedCommands = true;
|
||||
}
|
||||
const auto changedCommands = Data::UpdateBotCommands(
|
||||
botInfo->commands,
|
||||
d.vcommands());
|
||||
|
||||
botInfo->inited = true;
|
||||
|
||||
|
|
|
@ -10,11 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_peer.h"
|
||||
#include "dialogs/dialogs_key.h"
|
||||
|
||||
struct BotCommand {
|
||||
QString command;
|
||||
QString description;
|
||||
};
|
||||
|
||||
struct BotInfo {
|
||||
bool inited = false;
|
||||
bool readsAllHistory = false;
|
||||
|
|
|
@ -489,9 +489,9 @@ HistoryWidget::HistoryWidget(
|
|||
}, lifetime());
|
||||
|
||||
session().data().botCommandsChanges(
|
||||
) | rpl::filter([=](not_null<UserData*> user) {
|
||||
return _peer && (_peer == user || !_peer->isUser());
|
||||
}) | rpl::start_with_next([=](not_null<UserData*> user) {
|
||||
) | rpl::filter([=](not_null<PeerData*> peer) {
|
||||
return _peer && (_peer == peer);
|
||||
}) | rpl::start_with_next([=] {
|
||||
if (_fieldAutocomplete->clearFilteredBotCommands()) {
|
||||
checkFieldAutocomplete();
|
||||
}
|
||||
|
|
|
@ -1234,10 +1234,9 @@ void ComposeControls::initAutocomplete() {
|
|||
_field->rawTextEdit()->installEventFilter(_autocomplete.get());
|
||||
|
||||
_window->session().data().botCommandsChanges(
|
||||
) | rpl::filter([=](not_null<UserData*> user) {
|
||||
const auto peer = _history ? _history->peer.get() : nullptr;
|
||||
return peer && (peer == user || !peer->isUser());
|
||||
}) | rpl::start_with_next([=](not_null<UserData*> user) {
|
||||
) | rpl::filter([=](not_null<PeerData*> peer) {
|
||||
return _history && (_history->peer == peer);
|
||||
}) | rpl::start_with_next([=] {
|
||||
if (_autocomplete->clearFilteredBotCommands()) {
|
||||
checkAutocomplete();
|
||||
}
|
||||
|
|
|
@ -515,10 +515,10 @@ void ActionsFiller::addBotCommandActions(not_null<UserData*> user) {
|
|||
if (!user->isBot()) {
|
||||
return QString();
|
||||
}
|
||||
for_const (auto &data, user->botInfo->commands) {
|
||||
auto isSame = data.command.compare(
|
||||
for (const auto &data : user->botInfo->commands) {
|
||||
const auto isSame = !data.command.compare(
|
||||
command,
|
||||
Qt::CaseInsensitive) == 0;
|
||||
Qt::CaseInsensitive);
|
||||
if (isSame) {
|
||||
return data.command;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue