Bots: Support reply keyboard in groups and forums (#2477)
This commit is contained in:
parent
b07be031a9
commit
172f03f474
@ -176,13 +176,15 @@ export function buildApiMessageWithChatId(
|
||||
} = mtpMessage.replyTo || {};
|
||||
const isEdited = mtpMessage.editDate && !mtpMessage.editHide;
|
||||
const {
|
||||
inlineButtons, keyboardButtons, keyboardPlaceholder, isKeyboardSingleUse,
|
||||
inlineButtons, keyboardButtons, keyboardPlaceholder, isKeyboardSingleUse, isKeyboardSelective,
|
||||
} = buildReplyButtons(mtpMessage, isInvoiceMedia) || {};
|
||||
const forwardInfo = mtpMessage.fwdFrom && buildApiMessageForwardInfo(mtpMessage.fwdFrom, isChatWithSelf);
|
||||
const { replies, mediaUnread: isMediaUnread, postAuthor } = mtpMessage;
|
||||
const groupedId = mtpMessage.groupedId && String(mtpMessage.groupedId);
|
||||
const isInAlbum = Boolean(groupedId) && !(content.document || content.audio || content.sticker);
|
||||
const shouldHideKeyboardButtons = mtpMessage.replyMarkup instanceof GramJs.ReplyKeyboardHide;
|
||||
const isHideKeyboardSelective = mtpMessage.replyMarkup instanceof GramJs.ReplyKeyboardHide
|
||||
&& mtpMessage.replyMarkup.selective;
|
||||
const isProtected = mtpMessage.noforwards || isInvoiceMedia;
|
||||
const isForwardingAllowed = !mtpMessage.noforwards;
|
||||
const emojiOnlyCount = getEmojiOnlyCountForMessage(content, groupedId);
|
||||
@ -215,8 +217,10 @@ export function buildApiMessageWithChatId(
|
||||
isInAlbum,
|
||||
}),
|
||||
inlineButtons,
|
||||
...(keyboardButtons && { keyboardButtons, keyboardPlaceholder, isKeyboardSingleUse }),
|
||||
...(shouldHideKeyboardButtons && { shouldHideKeyboardButtons }),
|
||||
...(keyboardButtons && {
|
||||
keyboardButtons, keyboardPlaceholder, isKeyboardSingleUse, isKeyboardSelective,
|
||||
}),
|
||||
...(shouldHideKeyboardButtons && { shouldHideKeyboardButtons, isHideKeyboardSelective }),
|
||||
...(mtpMessage.viaBotId && { viaBotId: buildApiPeerId(mtpMessage.viaBotId, 'user') }),
|
||||
...(replies?.comments && { repliesThreadInfo: buildThreadInfo(replies, mtpMessage.id, chatId) }),
|
||||
...(postAuthor && { postAuthorTitle: postAuthor }),
|
||||
@ -1242,6 +1246,7 @@ function buildReplyButtons(message: UniversalMessage, shouldSkipBuyButton?: bool
|
||||
...(replyMarkup instanceof GramJs.ReplyKeyboardMarkup && {
|
||||
keyboardPlaceholder: replyMarkup.placeholder,
|
||||
isKeyboardSingleUse: replyMarkup.singleUse,
|
||||
isKeyboardSelective: replyMarkup.selective,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@ -413,11 +413,13 @@ export interface ApiMessage {
|
||||
keyboardButtons?: ApiKeyboardButtons;
|
||||
keyboardPlaceholder?: string;
|
||||
isKeyboardSingleUse?: boolean;
|
||||
isKeyboardSelective?: boolean;
|
||||
viaBotId?: string;
|
||||
repliesThreadInfo?: ApiThreadInfo;
|
||||
postAuthorTitle?: string;
|
||||
isScheduled?: boolean;
|
||||
shouldHideKeyboardButtons?: boolean;
|
||||
isHideKeyboardSelective?: boolean;
|
||||
isFromScheduled?: boolean;
|
||||
isSilent?: boolean;
|
||||
seenByUserIds?: string[];
|
||||
@ -600,6 +602,7 @@ export type ApiKeyboardButtons = ApiKeyboardButton[][];
|
||||
export type ApiReplyKeyboard = {
|
||||
keyboardPlaceholder?: string;
|
||||
isKeyboardSingleUse?: boolean;
|
||||
isKeyboardSelective?: boolean;
|
||||
} & {
|
||||
[K in 'inlineButtons' | 'keyboardButtons']?: ApiKeyboardButtons;
|
||||
};
|
||||
|
||||
@ -518,6 +518,8 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
||||
const isEmptyTopic = messageIds?.length === 1
|
||||
&& messagesById?.[messageIds[0]]?.content.action?.type === 'topicCreate';
|
||||
|
||||
const isBotInfoEmpty = botInfo && !botInfo.description && !botInfo.gif && !botInfo.photo;
|
||||
|
||||
const className = buildClassName(
|
||||
'MessageList custom-scroll',
|
||||
noAvatars && 'no-avatars',
|
||||
@ -546,7 +548,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
||||
) : botInfo ? (
|
||||
<div className="empty">
|
||||
{isLoadingBotInfo && <span>{lang('Loading')}</span>}
|
||||
{!botInfo && !isLoadingBotInfo && <span>{lang('NoMessages')}</span>}
|
||||
{isBotInfoEmpty && !isLoadingBotInfo && <span>{lang('NoMessages')}</span>}
|
||||
{botInfo && (
|
||||
<div
|
||||
className="bot-info"
|
||||
|
||||
@ -1526,7 +1526,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const chatBot = chatId !== REPLIES_USER_ID ? selectChatBot(global, chatId) : undefined;
|
||||
const isChatWithBot = Boolean(chatBot);
|
||||
const isChatWithSelf = selectIsChatWithSelf(global, chatId);
|
||||
const messageWithActualBotKeyboard = isChatWithBot && selectNewestMessageWithBotKeyboardButtons(global, chatId);
|
||||
const messageWithActualBotKeyboard = selectNewestMessageWithBotKeyboardButtons(global, chatId, threadId);
|
||||
const scheduledIds = selectScheduledIds(global, chatId, threadId);
|
||||
const { language, shouldSuggestStickers, shouldSuggestCustomEmoji } = global.settings.byKey;
|
||||
const baseEmojiKeywords = global.emojiKeywords[BASE_EMOJI_KEYWORD_LANG];
|
||||
|
||||
@ -175,11 +175,10 @@ addActionHandler('clickBotInlineButton', (global, actions, payload): ActionRetur
|
||||
|
||||
addActionHandler('sendBotCommand', (global, actions, payload): ActionReturnType => {
|
||||
const { command, chatId, tabId = getCurrentTabId() } = payload;
|
||||
const { currentUserId } = global;
|
||||
const chat = chatId ? selectChat(global, chatId) : selectCurrentChat(global, tabId);
|
||||
const currentMessageList = selectCurrentMessageList(global, tabId);
|
||||
|
||||
if (!currentUserId || !chat || !currentMessageList) {
|
||||
if (!chat || !currentMessageList) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -188,7 +187,7 @@ addActionHandler('sendBotCommand', (global, actions, payload): ActionReturnType
|
||||
actions.clearWebPagePreview({ tabId });
|
||||
|
||||
void sendBotCommand(
|
||||
chat, currentUserId, command, selectReplyingToId(global, chat.id, threadId), selectSendAs(global, chat.id),
|
||||
chat, threadId, command, selectReplyingToId(global, chat.id, threadId), selectSendAs(global, chat.id),
|
||||
);
|
||||
});
|
||||
|
||||
@ -209,7 +208,7 @@ addActionHandler('restartBot', async (global, actions, payload): Promise<void> =
|
||||
global = getGlobal();
|
||||
global = removeBlockedContact(global, bot.id);
|
||||
setGlobal(global);
|
||||
void sendBotCommand(chat, currentUserId, '/start', undefined, selectSendAs(global, chatId));
|
||||
void sendBotCommand(chat, MAIN_THREAD_ID, '/start', undefined, selectSendAs(global, chatId));
|
||||
});
|
||||
|
||||
addActionHandler('loadTopInlineBots', async (global): Promise<void> => {
|
||||
@ -927,10 +926,11 @@ async function searchInlineBot<T extends GlobalState>(global: T, {
|
||||
}
|
||||
|
||||
async function sendBotCommand(
|
||||
chat: ApiChat, currentUserId: string, command: string, replyingTo?: number, sendAs?: ApiChat | ApiUser,
|
||||
chat: ApiChat, threadId = MAIN_THREAD_ID, command: string, replyingTo?: number, sendAs?: ApiChat | ApiUser,
|
||||
) {
|
||||
await callApi('sendMessage', {
|
||||
chat,
|
||||
replyingToTopId: threadId,
|
||||
text: command,
|
||||
replyingTo,
|
||||
sendAs,
|
||||
|
||||
@ -15,7 +15,7 @@ import {
|
||||
GENERAL_TOPIC_ID, LOCAL_MESSAGE_MIN_ID, REPLIES_USER_ID, SERVICE_NOTIFICATIONS_USER_ID,
|
||||
} from '../../config';
|
||||
import {
|
||||
selectChat, selectChatBot, selectIsChatWithBot, selectIsChatWithSelf,
|
||||
selectChat, selectChatBot, selectIsChatWithSelf,
|
||||
} from './chats';
|
||||
import {
|
||||
selectIsCurrentUserPremium, selectIsUserOrChatContact, selectUser, selectUserStatus,
|
||||
@ -921,7 +921,7 @@ export function selectSelectedMessagesCount<T extends GlobalState>(
|
||||
}
|
||||
|
||||
export function selectNewestMessageWithBotKeyboardButtons<T extends GlobalState>(
|
||||
global: T, chatId: string,
|
||||
global: T, chatId: string, threadId = MAIN_THREAD_ID,
|
||||
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||
): ApiMessage | undefined {
|
||||
const chat = selectChat(global, chatId);
|
||||
@ -929,23 +929,15 @@ export function selectNewestMessageWithBotKeyboardButtons<T extends GlobalState>
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!selectIsChatWithBot(global, chat)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const chatMessages = selectChatMessages(global, chatId);
|
||||
const viewportIds = selectViewportIds(global, chatId, MAIN_THREAD_ID, tabId);
|
||||
const viewportIds = selectViewportIds(global, chatId, threadId, tabId);
|
||||
if (!chatMessages || !viewportIds) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const messageId = findLast(viewportIds, (id) => {
|
||||
return !chatMessages[id].isOutgoing && Boolean(chatMessages[id].keyboardButtons);
|
||||
});
|
||||
const messageId = findLast(viewportIds, (id) => selectShouldDisplayReplyKeyboard(global, chatMessages[id]));
|
||||
|
||||
const replyHideMessageId = findLast(viewportIds, (id) => {
|
||||
return Boolean(chatMessages[id].shouldHideKeyboardButtons);
|
||||
});
|
||||
const replyHideMessageId = findLast(viewportIds, (id) => selectShouldHideReplyKeyboard(global, chatMessages[id]));
|
||||
|
||||
if (messageId && replyHideMessageId && replyHideMessageId > messageId) {
|
||||
return undefined;
|
||||
@ -954,6 +946,46 @@ export function selectNewestMessageWithBotKeyboardButtons<T extends GlobalState>
|
||||
return messageId ? chatMessages[messageId] : undefined;
|
||||
}
|
||||
|
||||
function selectShouldHideReplyKeyboard<T extends GlobalState>(global: T, message: ApiMessage) {
|
||||
const {
|
||||
shouldHideKeyboardButtons,
|
||||
isHideKeyboardSelective,
|
||||
replyToMessageId,
|
||||
isMentioned,
|
||||
} = message;
|
||||
if (!shouldHideKeyboardButtons) return false;
|
||||
|
||||
if (isHideKeyboardSelective) {
|
||||
if (isMentioned) return true;
|
||||
if (!replyToMessageId) return false;
|
||||
|
||||
const replyMessage = selectChatMessage(global, message.chatId, replyToMessageId);
|
||||
return Boolean(replyMessage?.senderId === global.currentUserId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function selectShouldDisplayReplyKeyboard<T extends GlobalState>(global: T, message: ApiMessage) {
|
||||
const {
|
||||
keyboardButtons,
|
||||
shouldHideKeyboardButtons,
|
||||
isKeyboardSelective,
|
||||
isMentioned,
|
||||
replyToMessageId,
|
||||
} = message;
|
||||
if (!keyboardButtons || shouldHideKeyboardButtons) return false;
|
||||
|
||||
if (isKeyboardSelective) {
|
||||
if (isMentioned) return true;
|
||||
if (!replyToMessageId) return false;
|
||||
|
||||
const replyMessage = selectChatMessage(global, message.chatId, replyToMessageId);
|
||||
return Boolean(replyMessage?.senderId === global.currentUserId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function selectCanAutoLoadMedia<T extends GlobalState>(global: T, message: ApiMessage) {
|
||||
const chat = selectChat(global, message.chatId);
|
||||
if (!chat) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user