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