Support no-topics recipient state in bot-forums.

This commit is contained in:
John Preston
2026-02-13 16:10:50 +04:00
parent 2e538b5d38
commit 4c2d72d00b
19 changed files with 304 additions and 190 deletions
+1
View File
@@ -459,6 +459,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_contacts_not_found" = "No contacts found"; "lng_contacts_not_found" = "No contacts found";
"lng_topics_not_found" = "No topics found."; "lng_topics_not_found" = "No topics found.";
"lng_forum_all_messages" = "All Messages"; "lng_forum_all_messages" = "All Messages";
"lng_forum_create_new_topic" = "Create New Thread";
"lng_dlg_search_for_messages" = "Search for messages"; "lng_dlg_search_for_messages" = "Search for messages";
"lng_update_telegram" = "Update Telegram"; "lng_update_telegram" = "Update Telegram";
"lng_dlg_search_in" = "Search messages in"; "lng_dlg_search_in" = "Search messages in";
+48 -26
View File
@@ -3529,7 +3529,6 @@ void ApiWrap::forwardMessages(
if (shared) { if (shared) {
++shared->requestsLeft; ++shared->requestsLeft;
} }
const auto requestType = Data::Histories::RequestType::Send;
const auto idsCopy = localIds; const auto idsCopy = localIds;
const auto scheduled = action.options.scheduled; const auto scheduled = action.options.scheduled;
const auto starsPaid = std::min( const auto starsPaid = std::min(
@@ -3540,57 +3539,80 @@ void ApiWrap::forwardMessages(
action.options.starsApproved -= starsPaid; action.options.starsApproved -= starsPaid;
oneFlags |= SendFlag::f_allow_paid_stars; oneFlags |= SendFlag::f_allow_paid_stars;
} }
histories.sendRequest(history, requestType, [=](Fn<void()> finish) { auto buildMessage = [=](
history->sendRequestId = request(MTPmessages_ForwardMessages( not_null<History*> history,
MTP_flags(oneFlags), FullReplyTo replyTo)
-> Data::Histories::PreparedMessage {
const auto kGeneralId = Data::ForumTopic::kGeneralId;
const auto realTopMsgId = (replyTo.topicRootId == kGeneralId)
? MsgId(0)
: replyTo.topicRootId;
auto flags = oneFlags;
if (realTopMsgId) {
flags |= SendFlag::f_top_msg_id;
} else {
flags &= ~SendFlag::f_top_msg_id;
}
return MTPmessages_ForwardMessages(
MTP_flags(flags),
forwardFrom->input(), forwardFrom->input(),
MTP_vector<MTPint>(ids), MTP_vector<MTPint>(ids),
MTP_vector<MTPlong>(randomIds), MTP_vector<MTPlong>(randomIds),
peer->input(), history->peer->input(),
MTP_int(topMsgId), MTP_int(realTopMsgId),
(action.options.suggest (action.options.suggest
? ReplyToForMTP(history, action.replyTo) ? ReplyToForMTP(history, replyTo)
: monoforumPeer : monoforumPeer
? MTP_inputReplyToMonoForum(monoforumPeer->input()) ? MTP_inputReplyToMonoForum(
monoforumPeer->input())
: MTPInputReplyTo()), : MTPInputReplyTo()),
MTP_int(action.options.scheduled), MTP_int(action.options.scheduled),
MTP_int(action.options.scheduleRepeatPeriod), MTP_int(action.options.scheduleRepeatPeriod),
(sendAs ? sendAs->input() : MTP_inputPeerEmpty()), (sendAs
Data::ShortcutIdToMTP(_session, action.options.shortcutId), ? sendAs->input()
: MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(
&history->session(),
action.options.shortcutId),
MTP_long(action.options.effectId), MTP_long(action.options.effectId),
MTPint(), // video_timestamp MTPint(),
MTP_long(starsPaid), MTP_long(starsPaid),
Api::SuggestToMTP(action.options.suggest) Api::SuggestToMTP(action.options.suggest));
)).done([=](const MTPUpdates &result) { };
histories.sendPreparedMessage(
history,
FullReplyTo{ .topicRootId = topicRootId },
uint64(0),
std::move(buildMessage),
[=](const MTPUpdates &result, const MTP::Response &) {
if (!scheduled) { if (!scheduled) {
this->updates().checkForSentToScheduled(result); _session->api().updates().checkForSentToScheduled(
result);
} }
applyUpdates(result);
if (shared && !--shared->requestsLeft) { if (shared && !--shared->requestsLeft) {
shared->callback(); shared->callback();
} }
finish(); if (peer->isSelf() && _session->premium()) {
if (peer->isSelf() && session().premium()) {
ProcessRecentSelfForwards( ProcessRecentSelfForwards(
_session, _session,
result, result,
peer->id, peer->id,
forwardFrom->id); forwardFrom->id);
} }
}).fail([=](const MTP::Error &error) { },
[=](const MTP::Error &error, const MTP::Response &) {
if (idsCopy) { if (idsCopy) {
for (const auto &[randomId, itemId] : *idsCopy) { for (const auto &[randomId, itemId] : *idsCopy) {
sendMessageFail(error, peer, randomId, itemId); _session->api().sendMessageFail(
error,
peer,
randomId,
itemId);
} }
} else { } else {
sendMessageFail(error, peer); _session->api().sendMessageFail(error, peer);
} }
finish(); });
}).afterRequest(
history->sendRequestId
).send();
return history->sendRequestId;
});
ids.resize(0); ids.resize(0);
randomIds.resize(0); randomIds.resize(0);
@@ -1096,10 +1096,16 @@ auto ChooseTopicBoxController::Row::generateNameWords() const
return _topic->chatListNameWords(); return _topic->chatListNameWords();
} }
ChooseTopicBoxController::AllMessagesRow::AllMessagesRow() QString ChooseTopicBoxController::AllMessagesRow::name() const {
: PeerListRow(PeerListRowId(0)) { return _userCreatesTopics
const auto name = tr::lng_forum_all_messages(tr::now); ? tr::lng_forum_create_new_topic(tr::now)
const auto words = TextUtilities::PrepareSearchWords(name); : tr::lng_forum_all_messages(tr::now);
}
ChooseTopicBoxController::AllMessagesRow::AllMessagesRow(bool userCreatesTopics)
: PeerListRow(PeerListRowId(0))
, _userCreatesTopics(userCreatesTopics) {
const auto words = TextUtilities::PrepareSearchWords(name());
for (const auto &word : words) { for (const auto &word : words) {
_nameWords.emplace(word); _nameWords.emplace(word);
_nameFirstLetters.emplace(word[0]); _nameFirstLetters.emplace(word[0]);
@@ -1107,23 +1113,26 @@ ChooseTopicBoxController::AllMessagesRow::AllMessagesRow()
} }
QString ChooseTopicBoxController::AllMessagesRow::generateName() { QString ChooseTopicBoxController::AllMessagesRow::generateName() {
return tr::lng_forum_all_messages(tr::now); return name();
} }
QString ChooseTopicBoxController::AllMessagesRow::generateShortName() { QString ChooseTopicBoxController::AllMessagesRow::generateShortName() {
return tr::lng_forum_all_messages(tr::now); return name();
} }
auto ChooseTopicBoxController::AllMessagesRow::generatePaintUserpicCallback( auto ChooseTopicBoxController::AllMessagesRow::generatePaintUserpicCallback(
bool forceRound) bool forceRound)
-> PaintRoundImageCallback { -> PaintRoundImageCallback {
return []( return [userCreatesTopics = _userCreatesTopics](
Painter &p, Painter &p,
int x, int x,
int y, int y,
int outerWidth, int outerWidth,
int size) { int size) {
st::menuIconChats.paintInCenter( const auto &icon = userCreatesTopics
? st::menuIconDiscussion
: st::menuIconChats;
icon.paintInCenter(
p, p,
QRect(x, y - st::lineWidth, size, size)); QRect(x, y - st::lineWidth, size, size));
}; };
@@ -1206,8 +1215,10 @@ void ChooseTopicBoxController::refreshRows(bool initial) {
if (_forum->bot() if (_forum->bot()
&& !delegate()->peerListFindRow(PeerListRowId(0)) && !delegate()->peerListFindRow(PeerListRowId(0))
&& (!_filter || _filter(_forum->history()))) { && (!_filter || _filter(_forum->history()))) {
const auto userCreatesTopics = Data::IsBotUserCreatesTopics(
_forum->peer());
delegate()->peerListAppendRow( delegate()->peerListAppendRow(
std::make_unique<AllMessagesRow>()); std::make_unique<AllMessagesRow>(userCreatesTopics));
added = true; added = true;
} }
for (const auto &row : _forum->topicsList()->indexed()->all()) { for (const auto &row : _forum->topicsList()->indexed()->all()) {
@@ -393,7 +393,7 @@ private:
class AllMessagesRow final : public PeerListRow { class AllMessagesRow final : public PeerListRow {
public: public:
AllMessagesRow(); explicit AllMessagesRow(bool userCreatesTopics);
QString generateName() override; QString generateName() override;
QString generateShortName() override; QString generateShortName() override;
@@ -406,8 +406,11 @@ private:
-> const base::flat_set<QString> & override; -> const base::flat_set<QString> & override;
private: private:
[[nodiscard]] QString name() const;
base::flat_set<QChar> _nameFirstLetters; base::flat_set<QChar> _nameFirstLetters;
base::flat_set<QString> _nameWords; base::flat_set<QString> _nameWords;
bool _userCreatesTopics = false;
}; };
+149 -88
View File
@@ -1683,6 +1683,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
std::optional<TimeId> videoTimestamp) { std::optional<TimeId> videoTimestamp) {
struct State final { struct State final {
base::flat_set<mtpRequestId> requests; base::flat_set<mtpRequestId> requests;
mtpRequestId nextRequestKey = 0;
}; };
const auto state = std::make_shared<State>(); const auto state = std::make_shared<State>();
return [=]( return [=](
@@ -1732,13 +1733,6 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
for (const auto &fullId : existingIds) { for (const auto &fullId : existingIds) {
mtpMsgIds.push_back(MTP_int(fullId.msg)); mtpMsgIds.push_back(MTP_int(fullId.msg));
} }
const auto generateRandom = [&] {
auto result = QVector<MTPlong>(existingIds.size());
for (auto &value : result) {
value = base::RandomValue<MTPlong>();
}
return result;
};
auto &api = history->session().api(); auto &api = history->session().api();
auto &histories = history->owner().histories(); auto &histories = history->owner().histories();
const auto donePhraseArgs = CreateForwardedMessagePhraseArgs( const auto donePhraseArgs = CreateForwardedMessagePhraseArgs(
@@ -1747,103 +1741,170 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
const auto showRecentForwardsToSelf = result.size() == 1 const auto showRecentForwardsToSelf = result.size() == 1
&& result.front()->peer()->isSelf() && result.front()->peer()->isSelf()
&& history->session().premium(); && history->session().premium();
const auto requestType = Data::Histories::RequestType::Send;
for (const auto thread : result) { for (const auto thread : result) {
const auto peer = thread->peer();
const auto threadHistory = thread->owningHistory();
const auto forum = threadHistory->asForum();
const auto needNewTopic = forum
&& forum->bot()
&& Data::IsBotUserCreatesTopics(peer)
&& !thread->asTopic();
const auto effectiveThread = [&]() -> not_null<Data::Thread*> {
if (needNewTopic) {
const auto topic = forum->reserveNewBotTopic();
Assert(topic != nullptr);
return topic;
}
return thread;
}();
if (!comment.text.isEmpty()) { if (!comment.text.isEmpty()) {
auto message = Api::MessageToSend( auto message = Api::MessageToSend(
Api::SendAction(thread, options)); Api::SendAction(effectiveThread, options));
message.textWithTags = comment; message.textWithTags = comment;
message.action.clearDraft = false; message.action.clearDraft = false;
api.sendMessage(std::move(message)); api.sendMessage(std::move(message));
} }
const auto topicRootId = thread->topicRootId();
const auto sublistPeer = thread->maybeSublistPeer(); const auto topicRootId = effectiveThread->topicRootId();
const auto kGeneralId = Data::ForumTopic::kGeneralId; const auto sublistPeer = needNewTopic
const auto topMsgId = (topicRootId == kGeneralId) ? nullptr
? MsgId(0) : thread->maybeSublistPeer();
: topicRootId; const auto fromPeer = history->peer;
const auto peer = thread->peer(); const auto msgCount = int(existingIds.size());
const auto threadHistory = thread->owningHistory();
const auto starsPaid = std::min( const auto starsPaid = std::min(
peer->starsPerMessageChecked(), peer->starsPerMessageChecked(),
options.starsApproved); options.starsApproved);
if (starsPaid) { if (starsPaid) {
options.starsApproved -= starsPaid; options.starsApproved -= starsPaid;
} }
histories.sendRequest(threadHistory, requestType, [=]( const auto sendFlags = commonSendFlags
Fn<void()> finish) { | (ShouldSendSilent(peer, options)
const auto session = &threadHistory->session(); ? Flag::f_silent
auto &api = session->api(); : Flag(0))
const auto sendFlags = commonSendFlags | (options.shortcutId
| (topMsgId ? Flag::f_top_msg_id : Flag(0)) ? Flag::f_quick_reply_shortcut
| (ShouldSendSilent(peer, options) : Flag(0))
? Flag::f_silent | (starsPaid ? Flag::f_allow_paid_stars : Flag())
: Flag(0)) | (sublistPeer ? Flag::f_reply_to : Flag())
| (options.shortcutId | (options.suggest ? Flag::f_suggested_post : Flag())
? Flag::f_quick_reply_shortcut | (options.effectId ? Flag::f_effect : Flag());
: Flag(0)) auto buildMessage = [=](
| (starsPaid ? Flag::f_allow_paid_stars : Flag()) not_null<History*> history,
| (sublistPeer ? Flag::f_reply_to : Flag()) FullReplyTo replyTo)
| (options.suggest ? Flag::f_suggested_post : Flag()) -> Data::Histories::PreparedMessage {
| (options.effectId ? Flag::f_effect : Flag()); const auto kGeneralId
threadHistory->sendRequestId = api.request( = Data::ForumTopic::kGeneralId;
MTPmessages_ForwardMessages( const auto realTopMsgId
MTP_flags(sendFlags), = (replyTo.topicRootId == kGeneralId)
history->peer->input(), ? MsgId(0)
MTP_vector<MTPint>(mtpMsgIds), : replyTo.topicRootId;
MTP_vector<MTPlong>(generateRandom()), auto flags = sendFlags;
peer->input(), if (realTopMsgId) {
MTP_int(topMsgId), flags |= Flag::f_top_msg_id;
(sublistPeer } else {
? MTP_inputReplyToMonoForum(sublistPeer->input()) flags &= ~Flag::f_top_msg_id;
: MTPInputReplyTo()), }
MTP_int(options.scheduled), auto randoms = QVector<MTPlong>(msgCount);
MTP_int(options.scheduleRepeatPeriod), for (auto &value : randoms) {
MTP_inputPeerEmpty(), // send_as value = base::RandomValue<MTPlong>();
Data::ShortcutIdToMTP(session, options.shortcutId), }
MTP_long(options.effectId), return MTPmessages_ForwardMessages(
MTP_int(videoTimestamp.value_or(0)), MTP_flags(flags),
MTP_long(starsPaid), fromPeer->input(),
Api::SuggestToMTP(options.suggest) MTP_vector<MTPint>(mtpMsgIds),
)).done([=](const MTPUpdates &updates, mtpRequestId reqId) { MTP_vector<MTPlong>(randoms),
threadHistory->session().api().applyUpdates(updates); history->peer->input(),
if (showRecentForwardsToSelf) { MTP_int(realTopMsgId),
ApiWrap::ProcessRecentSelfForwards( (sublistPeer
&threadHistory->session(), ? MTP_inputReplyToMonoForum(
updates, sublistPeer->input())
peer->id, : MTPInputReplyTo()),
history->peer->id); MTP_int(options.scheduled),
} MTP_int(options.scheduleRepeatPeriod),
state->requests.remove(reqId); MTP_inputPeerEmpty(),
if (state->requests.empty()) { Data::ShortcutIdToMTP(
if (show->valid()) { &history->session(),
auto phrase = rpl::variable<TextWithEntities>( options.shortcutId),
ChatHelpers::ForwardedMessagePhrase( MTP_long(options.effectId),
donePhraseArgs)).current(); MTP_int(videoTimestamp.value_or(0)),
if (!phrase.empty()) { MTP_long(starsPaid),
show->showToast(std::move(phrase)); Api::SuggestToMTP(options.suggest));
} };
show->hideLayer(); const auto requestDone = [=](
const MTPUpdates &updates,
mtpRequestId requestKey) {
if (showRecentForwardsToSelf) {
ApiWrap::ProcessRecentSelfForwards(
&threadHistory->session(),
updates,
peer->id,
history->peer->id);
}
state->requests.remove(requestKey);
if (state->requests.empty()) {
if (show->valid()) {
auto phrase = rpl::variable<
TextWithEntities>(
ChatHelpers::ForwardedMessagePhrase(
donePhraseArgs)).current();
if (!phrase.empty()) {
show->showToast(std::move(phrase));
} }
show->hideLayer();
} }
finish(); }
}).fail([=](const MTP::Error &error) { };
const auto type = error.type(); const auto requestFail = [=](
if (type.startsWith(u"ALLOW_PAYMENT_REQUIRED_"_q)) { const MTP::Error &error,
show->showToast(u"Payment requirements changed. " mtpRequestId requestKey) {
"Please, try again."_q); const auto type = error.type();
} else if (type == u"VOICE_MESSAGES_FORBIDDEN"_q) { if (type.startsWith(
show->showToast( u"ALLOW_PAYMENT_REQUIRED_"_q)) {
tr::lng_restricted_send_voice_messages( show->showToast(
tr::now, u"Payment requirements changed. "
lt_user, "Please, try again."_q);
peer->name())); } else if (type
== u"VOICE_MESSAGES_FORBIDDEN"_q) {
show->showToast(
tr::lng_restricted_send_voice_messages(
tr::now,
lt_user,
peer->name()));
}
state->requests.remove(requestKey);
if (state->requests.empty()) {
if (show->valid()) {
show->hideLayer();
} }
finish(); }
}).afterRequest(threadHistory->sendRequestId).send(); };
return threadHistory->sendRequestId; const auto requestKey = ++state->nextRequestKey;
}); state->requests.insert(requestKey);
state->requests.insert(threadHistory->sendRequestId); histories.sendPreparedMessage(
threadHistory,
FullReplyTo{ .topicRootId = topicRootId },
uint64(0),
std::move(buildMessage),
[=](const MTPUpdates &updates,
const MTP::Response &) {
requestDone(updates, requestKey);
},
[=](const MTP::Error &error,
const MTP::Response &) {
requestFail(error, requestKey);
});
}
if (state->requests.empty()) {
if (show->valid()) {
auto phrase = rpl::variable<TextWithEntities>(
ChatHelpers::ForwardedMessagePhrase(
donePhraseArgs)).current();
if (!phrase.empty()) {
show->showToast(std::move(phrase));
}
show->hideLayer();
}
} }
}; };
} }
+9
View File
@@ -527,6 +527,15 @@ MsgId Forum::reserveCreatingId(
return result; return result;
} }
ForumTopic *Forum::reserveNewBotTopic() {
const auto &colors = ForumTopicColorIds();
const auto colorId = colors[base::RandomIndex(colors.size())];
return topicFor(reserveCreatingId(
tr::lng_bot_new_chat(tr::now),
colorId,
DocumentId()));
}
void Forum::discardCreatingId(MsgId rootId) { void Forum::discardCreatingId(MsgId rootId) {
Expects(creating(rootId)); Expects(creating(rootId));
+1
View File
@@ -87,6 +87,7 @@ public:
void discardCreatingId(MsgId rootId); void discardCreatingId(MsgId rootId);
[[nodiscard]] bool creating(MsgId rootId) const; [[nodiscard]] bool creating(MsgId rootId) const;
void created(MsgId rootId, MsgId realId); void created(MsgId rootId, MsgId realId);
[[nodiscard]] ForumTopic *reserveNewBotTopic();
void clearAllUnreadMentions(); void clearAllUnreadMentions();
void clearAllUnreadReactions(); void clearAllUnreadReactions();
+2 -1
View File
@@ -112,7 +112,8 @@ public:
MTPmessages_SendMessage, MTPmessages_SendMessage,
MTPmessages_SendMedia, MTPmessages_SendMedia,
MTPmessages_SendInlineBotResult, MTPmessages_SendInlineBotResult,
MTPmessages_SendMultiMedia>; MTPmessages_SendMultiMedia,
MTPmessages_ForwardMessages>;
int sendPreparedMessage( int sendPreparedMessage(
not_null<History*> history, not_null<History*> history,
FullReplyTo replyTo, FullReplyTo replyTo,
+2 -2
View File
@@ -2231,9 +2231,9 @@ std::optional<uint8> ColorIndexFromColor(const MTPPeerColor *color) {
}); });
} }
bool IsBotCanManageTopics(not_null<PeerData*> peer) { bool IsBotUserCreatesTopics(not_null<PeerData*> peer) {
if (const auto user = peer->asUser()) { if (const auto user = peer->asUser()) {
return user->botInfo && user->botInfo->canManageTopics; return user->botInfo && user->botInfo->userCreatesTopics;
} }
return false; return false;
} }
+1 -1
View File
@@ -678,6 +678,6 @@ void SetTopPinnedMessageId(
[[nodiscard]] uint64 BackgroundEmojiIdFromColor(const MTPPeerColor *color); [[nodiscard]] uint64 BackgroundEmojiIdFromColor(const MTPPeerColor *color);
[[nodiscard]] std::optional<uint8> ColorIndexFromColor(const MTPPeerColor *); [[nodiscard]] std::optional<uint8> ColorIndexFromColor(const MTPPeerColor *);
[[nodiscard]] bool IsBotCanManageTopics(not_null<PeerData*>); [[nodiscard]] bool IsBotUserCreatesTopics(not_null<PeerData*>);
} // namespace Data } // namespace Data
+22 -22
View File
@@ -564,6 +564,28 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
result->setStarsPerMessage(0); result->setStarsPerMessage(0);
} }
if (!minimal) {
result->setBotInfoVersion(data.vbot_info_version().value_or(-1));
if (const auto info = result->botInfo.get()) {
info->readsAllHistory = data.is_bot_chat_history();
if (info->cantJoinGroups != data.is_bot_nochats()) {
info->cantJoinGroups = data.is_bot_nochats();
flags |= UpdateFlag::BotCanBeInvited;
}
if (const auto value = data.vbot_inline_placeholder()) {
info->inlinePlaceholder = '_' + qs(*value);
} else {
info->inlinePlaceholder = QString();
}
info->supportsAttachMenu = data.is_bot_attach_menu();
info->supportsBusiness = data.is_bot_business();
info->canEditInformation = data.is_bot_can_edit();
info->activeUsers = data.vbot_active_users().value_or_empty();
info->hasMainApp = data.is_bot_has_main_app();
info->userCreatesTopics = data.is_bot_forum_can_manage_topics();
}
}
using Flag = UserDataFlag; using Flag = UserDataFlag;
const auto flagsMask = Flag::Deleted const auto flagsMask = Flag::Deleted
| Flag::Verified | Flag::Verified
@@ -739,28 +761,6 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
result->setEmojiStatus(EmojiStatusId()); result->setEmojiStatus(EmojiStatusId());
} }
if (!minimal) { if (!minimal) {
if (const auto botInfoVersion = data.vbot_info_version()) {
result->setBotInfoVersion(botInfoVersion->v);
result->botInfo->readsAllHistory = data.is_bot_chat_history();
if (result->botInfo->cantJoinGroups != data.is_bot_nochats()) {
result->botInfo->cantJoinGroups = data.is_bot_nochats();
flags |= UpdateFlag::BotCanBeInvited;
}
if (const auto placeholder = data.vbot_inline_placeholder()) {
result->botInfo->inlinePlaceholder = '_' + qs(*placeholder);
} else {
result->botInfo->inlinePlaceholder = QString();
}
result->botInfo->supportsAttachMenu = data.is_bot_attach_menu();
result->botInfo->supportsBusiness = data.is_bot_business();
result->botInfo->canEditInformation = data.is_bot_can_edit();
result->botInfo->activeUsers = data.vbot_active_users().value_or_empty();
result->botInfo->hasMainApp = data.is_bot_has_main_app();
result->botInfo->canManageTopics
= data.is_bot_forum_can_manage_topics();
} else {
result->setBotInfoVersion(-1);
}
result->setIsContact(data.is_contact() result->setIsContact(data.is_contact()
|| data.is_mutual_contact()); || data.is_mutual_contact());
} }
+1 -1
View File
@@ -98,7 +98,7 @@ struct BotInfo {
bool canManageEmojiStatus : 1 = false; bool canManageEmojiStatus : 1 = false;
bool supportsBusiness : 1 = false; bool supportsBusiness : 1 = false;
bool hasMainApp : 1 = false; bool hasMainApp : 1 = false;
bool canManageTopics : 1 = false; bool userCreatesTopics : 1 = false;
private: private:
std::unique_ptr<Data::Forum> _forum; std::unique_ptr<Data::Forum> _forum;
+12 -11
View File
@@ -4719,12 +4719,12 @@ void HistoryWidget::hideSelectorControlsAnimated() {
} }
Api::SendAction HistoryWidget::prepareSendAction( Api::SendAction HistoryWidget::prepareSendAction(
Api::SendOptions options) const { Api::SendOptions options) {
auto result = Api::SendAction(_history, options); auto result = Api::SendAction(_history, options);
result.replyTo = replyTo(); result.replyTo = replyTo();
if (const auto forum = _history->asForum()) { if (const auto forum = _history->asForum()) {
if (Data::IsBotCanManageTopics(_history->peer)) { if (forum->bot() && Data::IsBotUserCreatesTopics(_history->peer)) {
const auto readyRootId = [&]() -> MsgId { const auto readyRootId = [&]() -> MsgId {
if (const auto id = result.replyTo.messageId) { if (const auto id = result.replyTo.messageId) {
if (const auto item = session().data().message(id)) { if (const auto item = session().data().message(id)) {
@@ -4737,14 +4737,15 @@ Api::SendAction HistoryWidget::prepareSendAction(
result.replyTo.topicRootId = readyRootId; result.replyTo.topicRootId = readyRootId;
} else { } else {
if (!_creatingBotTopic) { if (!_creatingBotTopic) {
const auto &colors = Data::ForumTopicColorIds(); _creatingBotTopic = forum->reserveNewBotTopic();
const auto colorId auto draft = _history->forwardDraft(MsgId(0), PeerId());
= colors[base::RandomIndex(colors.size())]; if (!draft.ids.empty()) {
_creatingBotTopic = forum->topicFor( _history->setForwardDraft(MsgId(0), PeerId(), {});
forum->reserveCreatingId( _history->setForwardDraft(
tr::lng_bot_new_chat(tr::now), _creatingBotTopic->rootId(),
colorId, PeerId(),
DocumentId())); std::move(draft));
}
} }
result = Api::SendAction(_creatingBotTopic, options); result = Api::SendAction(_creatingBotTopic, options);
result.replyTo.topicRootId = _creatingBotTopic->rootId(); result.replyTo.topicRootId = _creatingBotTopic->rootId();
@@ -6302,7 +6303,7 @@ void HistoryWidget::updateFieldPlaceholder() {
} }
} else if (const auto user = peer->asUser()) { } else if (const auto user = peer->asUser()) {
if (const auto &info = user->botInfo) { if (const auto &info = user->botInfo) {
if (info->forum() && !info->canManageTopics) { if (info->forum() && !info->userCreatesTopics) {
return tr::lng_bot_off_thread_ph(); return tr::lng_bot_off_thread_ph();
} }
} }
@@ -422,7 +422,7 @@ private:
void messageDataReceived(not_null<PeerData*> peer, MsgId msgId); void messageDataReceived(not_null<PeerData*> peer, MsgId msgId);
[[nodiscard]] Api::SendAction prepareSendAction( [[nodiscard]] Api::SendAction prepareSendAction(
Api::SendOptions options) const; Api::SendOptions options);
void sendVoice(const VoiceToSend &data); void sendVoice(const VoiceToSend &data);
void send(Api::SendOptions options); void send(Api::SendOptions options);
void sendWithModifiers(Qt::KeyboardModifiers modifiers); void sendWithModifiers(Qt::KeyboardModifiers modifiers);
@@ -636,7 +636,7 @@ bool AboutView::aboveHistory() const {
return true; return true;
} }
const auto info = _history->peer->asUser()->botInfo.get(); const auto info = _history->peer->asUser()->botInfo.get();
return !(info->canManageTopics return !(info->userCreatesTopics
&& info->startToken.isEmpty() && info->startToken.isEmpty()
&& (!_history->isEmpty() || _history->lastMessage())); && (!_history->isEmpty() || _history->lastMessage()));
} }
@@ -696,7 +696,7 @@ bool AboutView::refresh() {
_version = 0; _version = 0;
return false; return false;
} else if (_history->peer->isForum() } else if (_history->peer->isForum()
&& info->canManageTopics && info->userCreatesTopics
&& info->startToken.isEmpty() && info->startToken.isEmpty()
&& (!_history->isEmpty() || _history->lastMessage())) { && (!_history->isEmpty() || _history->lastMessage())) {
if (_item) { if (_item) {
@@ -453,7 +453,7 @@ void SubsectionTabs::startFillingSlider(
).append(' ').append(peer->shortName()), ).append(' ').append(peer->shortName()),
}); });
} }
// } else if (Data::IsBotCanManageTopics(item.thread->peer())) { // } else if (Data::IsBotUserCreatesTopics(item.thread->peer())) {
// sections.push_back({ // sections.push_back({
// .text = { tr::lng_bot_new_chat(tr::now) }, // .text = { tr::lng_bot_new_chat(tr::now) },
// }); // });
+24 -21
View File
@@ -654,28 +654,31 @@ bool MainWidget::sendPaths(
bool MainWidget::filesOrForwardDrop( bool MainWidget::filesOrForwardDrop(
not_null<Data::Thread*> thread, not_null<Data::Thread*> thread,
not_null<const QMimeData*> data) { not_null<const QMimeData*> data,
if (const auto forum = thread->asForum()) { bool forumResolved) {
Window::ShowDropMediaBox( if (!forumResolved) {
_controller, if (const auto forum = thread->asForum()) {
Core::ShareMimeMediaData(data), Window::ShowDropMediaBox(
forum); _controller,
if (_hider) { Core::ShareMimeMediaData(data),
_hider->startHide(); forum);
clearHider(_hider); if (_hider) {
_hider->startHide();
clearHider(_hider);
}
return true;
} else if (const auto history = thread->asHistory()
; history && history->peer->monoforum()) {
Window::ShowDropMediaBox(
_controller,
Core::ShareMimeMediaData(data),
history->peer->monoforum());
if (_hider) {
_hider->startHide();
clearHider(_hider);
}
return true;
} }
return true;
} else if (const auto history = thread->asHistory()
; history && history->peer->monoforum()) {
Window::ShowDropMediaBox(
_controller,
Core::ShareMimeMediaData(data),
history->peer->monoforum());
if (_hider) {
_hider->startHide();
clearHider(_hider);
}
return true;
} }
if (data->hasFormat(u"application/x-td-forward"_q)) { if (data->hasFormat(u"application/x-td-forward"_q)) {
auto draft = Data::ForwardDraft{ auto draft = Data::ForwardDraft{
+2 -1
View File
@@ -161,7 +161,8 @@ public:
const QString &text) const; const QString &text) const;
bool filesOrForwardDrop( bool filesOrForwardDrop(
not_null<Data::Thread*> thread, not_null<Data::Thread*> thread,
not_null<const QMimeData*> data); not_null<const QMimeData*> data,
bool forumResolved = false);
void sendBotCommand(Bot::SendCommandRequest request); void sendBotCommand(Bot::SendCommandRequest request);
void hideSingleUseKeyboard(FullMsgId replyToId); void hideSingleUseKeyboard(FullMsgId replyToId);
@@ -3278,7 +3278,7 @@ base::weak_qptr<Ui::BoxContent> ShowDropMediaBox(
navigation navigation
](not_null<Data::Thread*> thread) mutable { ](not_null<Data::Thread*> thread) mutable {
const auto content = navigation->parentController()->content(); const auto content = navigation->parentController()->content();
if (!content->filesOrForwardDrop(thread, data.get())) { if (!content->filesOrForwardDrop(thread, data.get(), true)) {
return; return;
} else if (const auto strong = *weak) { } else if (const auto strong = *weak) {
strong->closeBox(); strong->closeBox();
@@ -3318,7 +3318,7 @@ base::weak_qptr<Ui::BoxContent> ShowDropMediaBox(
navigation navigation
](not_null<Data::SavedSublist*> sublist) mutable { ](not_null<Data::SavedSublist*> sublist) mutable {
const auto content = navigation->parentController()->content(); const auto content = navigation->parentController()->content();
if (!content->filesOrForwardDrop(sublist, data.get())) { if (!content->filesOrForwardDrop(sublist, data.get(), true)) {
return; return;
} else if (const auto strong = *weak) { } else if (const auto strong = *weak) {
strong->closeBox(); strong->closeBox();