Composer: Fix race conditions when sending messages (#3279)

This commit is contained in:
Alexander Zinchuk 2023-06-12 11:56:00 +02:00
parent e5ad070a27
commit 85bdbd5fa5
9 changed files with 133 additions and 66 deletions

View File

@ -5,15 +5,18 @@ import { getActions, withGlobal } from '../../global';
import type { FC } from '../../lib/teact/teact';
import type { ApiSticker, ApiStickerSet } from '../../api/types';
import type { MessageList } from '../../global/types';
import { EMOJI_SIZE_MODAL, STICKER_SIZE_MODAL, TME_LINK_PREFIX } from '../../config';
import {
selectCanScheduleUntilOnline,
selectChat,
selectCurrentMessageList,
selectIsChatWithSelf, selectIsCurrentUserPremium,
selectIsChatWithSelf,
selectIsCurrentUserPremium,
selectShouldSchedule,
selectStickerSet, selectThreadInfo,
selectStickerSet,
selectThreadInfo,
} from '../../global/selectors';
import renderText from './helpers/renderText';
import { copyTextToClipboard } from '../../util/clipboard';
@ -44,6 +47,7 @@ export type OwnProps = {
};
type StateProps = {
currentMessageList?: MessageList;
canSendStickers?: boolean;
stickerSet?: ApiStickerSet;
canScheduleUntilOnline?: boolean;
@ -66,6 +70,7 @@ const StickerSetModal: FC<OwnProps & StateProps> = ({
isSavedMessages,
isCurrentUserPremium,
shouldUpdateStickerSetOrder,
currentMessageList,
onClose,
}) => {
const {
@ -109,6 +114,9 @@ const StickerSetModal: FC<OwnProps & StateProps> = ({
}, [isOpen, fromSticker, loadStickers, stickerSetShortName, renderingStickerSet]);
const handleSelect = useCallback((sticker: ApiSticker, isSilent?: boolean, isScheduleRequested?: boolean) => {
if (!currentMessageList) {
return;
}
sticker = {
...sticker,
isPreloadedGlobally: true,
@ -117,19 +125,20 @@ const StickerSetModal: FC<OwnProps & StateProps> = ({
if (shouldSchedule || isScheduleRequested) {
requestCalendar((scheduledAt) => {
sendMessage({
sticker, isSilent, scheduledAt,
messageList: currentMessageList, sticker, isSilent, scheduledAt,
});
onClose();
});
} else {
sendMessage({
messageList: currentMessageList,
sticker,
isSilent,
shouldUpdateStickerSetOrder: shouldUpdateStickerSetOrder && isAdded,
});
onClose();
}
}, [onClose, requestCalendar, sendMessage, shouldSchedule, isAdded, shouldUpdateStickerSetOrder]);
}, [currentMessageList, shouldSchedule, requestCalendar, onClose, shouldUpdateStickerSetOrder, isAdded]);
const handleButtonClick = useCallback(() => {
if (renderingStickerSet) {
@ -270,6 +279,7 @@ export default memo(withGlobal<OwnProps>(
stickerSet,
isCurrentUserPremium: selectIsCurrentUserPremium(global),
shouldUpdateStickerSetOrder: global.settings.byKey.shouldUpdateStickerSetOrder,
currentMessageList,
};
},
)(StickerSetModal));

View File

@ -5,8 +5,9 @@ import { getActions, withGlobal } from '../../global';
import type {
ApiContact, ApiError, ApiInviteInfo, ApiPhoto,
} from '../../api/types';
import type { MessageList } from '../../global/types';
import { selectTabState } from '../../global/selectors';
import { selectCurrentMessageList, selectTabState } from '../../global/selectors';
import getReadableErrorText from '../../util/getReadableErrorText';
import { pick } from '../../util/iteratees';
import renderText from '../common/helpers/renderText';
@ -18,10 +19,11 @@ import Button from '../ui/Button';
import Avatar from '../common/Avatar';
type StateProps = {
currentMessageList?: MessageList;
dialogs: (ApiError | ApiInviteInfo | ApiContact)[];
};
const Dialogs: FC<StateProps> = ({ dialogs }) => {
const Dialogs: FC<StateProps> = ({ dialogs, currentMessageList }) => {
const {
dismissDialog,
acceptInviteConfirmation,
@ -115,8 +117,13 @@ const Dialogs: FC<StateProps> = ({ dialogs }) => {
const renderContactRequest = (contactRequest: ApiContact) => {
const handleConfirm = () => {
if (!currentMessageList) {
return;
}
sendMessage({
contact: pick(contactRequest, ['firstName', 'lastName', 'phoneNumber']),
messageList: currentMessageList,
});
closeModal();
};
@ -194,6 +201,7 @@ export default memo(withGlobal(
(global): StateProps => {
return {
dialogs: selectTabState(global).dialogs,
currentMessageList: selectCurrentMessageList(global),
};
},
)(Dialogs));

View File

@ -3,8 +3,9 @@ import React, { memo, useEffect, useRef } from '../../lib/teact/teact';
import { getActions, withGlobal } from '../../global';
import type { ApiSticker, ApiUpdateConnectionStateType } from '../../api/types';
import type { MessageList } from '../../global/types';
import { selectChat } from '../../global/selectors';
import { selectChat, selectCurrentMessageList } from '../../global/selectors';
import { getUserIdDividend } from '../../global/helpers';
import useLastCallback from '../../hooks/useLastCallback';
@ -23,6 +24,7 @@ type StateProps = {
sticker?: ApiSticker;
lastUnreadMessageId?: number;
connectionState?: ApiUpdateConnectionStateType;
currentMessageList?: MessageList;
};
const INTERSECTION_DEBOUNCE_MS = 200;
@ -31,6 +33,7 @@ const ContactGreeting: FC<OwnProps & StateProps> = ({
sticker,
connectionState,
lastUnreadMessageId,
currentMessageList,
}) => {
const {
loadGreetingStickers,
@ -62,11 +65,15 @@ const ContactGreeting: FC<OwnProps & StateProps> = ({
}, [connectionState, markMessageListRead, lastUnreadMessageId]);
const handleStickerSelect = useLastCallback((selectedSticker: ApiSticker) => {
if (!currentMessageList) {
return;
}
selectedSticker = {
...selectedSticker,
isPreloadedGlobally: true,
};
sendMessage({ sticker: selectedSticker });
sendMessage({ sticker: selectedSticker, messageList: currentMessageList });
});
return (
@ -110,6 +117,7 @@ export default memo(withGlobal<OwnProps>(
? chat.lastMessage.id
: undefined,
connectionState: global.connectionState,
currentMessageList: selectCurrentMessageList(global),
};
},
)(ContactGreeting));

View File

@ -6,7 +6,7 @@ import { requestMeasure, requestNextMutation } from '../../../lib/fasterdom/fast
import { getActions, withGlobal } from '../../../global';
import type {
TabState, MessageListType, GlobalState, ApiDraft,
TabState, MessageListType, GlobalState, ApiDraft, MessageList,
} from '../../../global/types';
import type {
ApiAttachment,
@ -159,6 +159,7 @@ type StateProps =
editingMessage?: ApiMessage;
chat?: ApiChat;
draft?: ApiDraft;
currentMessageList?: MessageList;
isChatWithBot?: boolean;
isChatWithSelf?: boolean;
isChannel?: boolean;
@ -243,6 +244,7 @@ const Composer: FC<OwnProps & StateProps> = ({
editingMessage,
chatId,
threadId,
currentMessageList,
messageListType,
draft,
chat,
@ -740,7 +742,7 @@ const Composer: FC<OwnProps & StateProps> = ({
isSilent?: boolean;
scheduledAt?: number;
}) => {
if (connectionState !== 'connectionStateReady') {
if (connectionState !== 'connectionStateReady' || !currentMessageList) {
return;
}
@ -752,6 +754,7 @@ const Composer: FC<OwnProps & StateProps> = ({
if (!checkSlowMode()) return;
sendMessage({
messageList: currentMessageList,
text,
entities,
scheduledAt,
@ -787,7 +790,7 @@ const Composer: FC<OwnProps & StateProps> = ({
});
const handleSend = useLastCallback(async (isSilent = false, scheduledAt?: number) => {
if (connectionState !== 'connectionStateReady') {
if (connectionState !== 'connectionStateReady' || !currentMessageList) {
return;
}
@ -826,6 +829,7 @@ const Composer: FC<OwnProps & StateProps> = ({
if (!checkSlowMode()) return;
sendMessage({
messageList: currentMessageList,
text,
entities,
scheduledAt,
@ -871,7 +875,7 @@ const Composer: FC<OwnProps & StateProps> = ({
});
const handleMessageSchedule = useLastCallback((
args: ScheduledMessageArgs, scheduledAt: number,
args: ScheduledMessageArgs, scheduledAt: number, messageList: MessageList,
) => {
if (args && 'queryId' in args) {
const { id, queryId, isSilent } = args;
@ -880,6 +884,7 @@ const Composer: FC<OwnProps & StateProps> = ({
queryId,
scheduledAt,
isSilent,
messageList,
});
return;
}
@ -894,18 +899,19 @@ const Composer: FC<OwnProps & StateProps> = ({
} else {
sendMessage({
...args,
messageList,
scheduledAt,
});
}
});
useEffectWithPrevDeps(([prevContentToBeScheduled]) => {
if (contentToBeScheduled && contentToBeScheduled !== prevContentToBeScheduled) {
if (currentMessageList && contentToBeScheduled && contentToBeScheduled !== prevContentToBeScheduled) {
requestCalendar((scheduledAt) => {
handleMessageSchedule(contentToBeScheduled, scheduledAt);
handleMessageSchedule(contentToBeScheduled, scheduledAt, currentMessageList);
});
}
}, [contentToBeScheduled, handleMessageSchedule, requestCalendar]);
}, [contentToBeScheduled, currentMessageList, handleMessageSchedule, requestCalendar]);
useEffect(() => {
if (requestedDraftText) {
@ -940,17 +946,21 @@ const Composer: FC<OwnProps & StateProps> = ({
});
const handleGifSelect = useLastCallback((gif: ApiVideo, isSilent?: boolean, isScheduleRequested?: boolean) => {
if (!currentMessageList) {
return;
}
if (shouldSchedule || isScheduleRequested) {
forceShowSymbolMenu();
requestCalendar((scheduledAt) => {
cancelForceShowSymbolMenu();
handleMessageSchedule({ gif, isSilent }, scheduledAt);
handleMessageSchedule({ gif, isSilent }, scheduledAt, currentMessageList);
requestMeasure(() => {
resetComposer(true);
});
});
} else {
sendMessage({ gif, isSilent });
sendMessage({ messageList: currentMessageList, gif, isSilent });
requestMeasure(() => {
resetComposer(true);
});
@ -964,6 +974,10 @@ const Composer: FC<OwnProps & StateProps> = ({
shouldPreserveInput = false,
canUpdateStickerSetsOrder?: boolean,
) => {
if (!currentMessageList) {
return;
}
sticker = {
...sticker,
isPreloadedGlobally: true,
@ -973,13 +987,14 @@ const Composer: FC<OwnProps & StateProps> = ({
forceShowSymbolMenu();
requestCalendar((scheduledAt) => {
cancelForceShowSymbolMenu();
handleMessageSchedule({ sticker, isSilent }, scheduledAt);
handleMessageSchedule({ sticker, isSilent }, scheduledAt, currentMessageList);
requestMeasure(() => {
resetComposer(shouldPreserveInput);
});
});
} else {
sendMessage({
messageList: currentMessageList,
sticker,
isSilent,
shouldUpdateStickerSetOrder: shouldUpdateStickerSetOrder && canUpdateStickerSetsOrder,
@ -993,7 +1008,7 @@ const Composer: FC<OwnProps & StateProps> = ({
const handleInlineBotSelect = useLastCallback((
inlineResult: ApiBotInlineResult | ApiBotInlineMediaResult, isSilent?: boolean, isScheduleRequested?: boolean,
) => {
if (connectionState !== 'connectionStateReady') {
if (connectionState !== 'connectionStateReady' || !currentMessageList) {
return;
}
@ -1003,13 +1018,14 @@ const Composer: FC<OwnProps & StateProps> = ({
id: inlineResult.id,
queryId: inlineResult.queryId,
isSilent,
}, scheduledAt);
}, scheduledAt, currentMessageList);
});
} else {
sendInlineBotResult({
id: inlineResult.id,
queryId: inlineResult.queryId,
isSilent,
messageList: currentMessageList,
});
}
@ -1032,13 +1048,17 @@ const Composer: FC<OwnProps & StateProps> = ({
});
const handlePollSend = useLastCallback((poll: ApiNewPoll) => {
if (!currentMessageList) {
return;
}
if (shouldSchedule) {
requestCalendar((scheduledAt) => {
handleMessageSchedule({ poll }, scheduledAt);
handleMessageSchedule({ poll }, scheduledAt, currentMessageList);
});
closePollModal();
} else {
sendMessage({ poll });
sendMessage({ messageList: currentMessageList, poll });
closePollModal();
}
});
@ -1046,7 +1066,7 @@ const Composer: FC<OwnProps & StateProps> = ({
const sendSilent = useLastCallback((additionalArgs?: ScheduledMessageArgs) => {
if (shouldSchedule) {
requestCalendar((scheduledAt) => {
handleMessageSchedule({ ...additionalArgs, isSilent: true }, scheduledAt);
handleMessageSchedule({ ...additionalArgs, isSilent: true }, scheduledAt, currentMessageList!);
});
} else if (additionalArgs && ('sendCompressed' in additionalArgs || 'sendGrouped' in additionalArgs)) {
const { sendCompressed = false, sendGrouped = false } = additionalArgs;
@ -1162,8 +1182,12 @@ const Composer: FC<OwnProps & StateProps> = ({
if (activeVoiceRecording) {
pauseRecordingVoice();
}
if (!currentMessageList) {
return;
}
requestCalendar((scheduledAt) => {
handleMessageSchedule({}, scheduledAt);
handleMessageSchedule({}, scheduledAt, currentMessageList!);
});
break;
default:
@ -1201,7 +1225,7 @@ const Composer: FC<OwnProps & StateProps> = ({
const handleSendScheduled = useLastCallback(() => {
requestCalendar((scheduledAt) => {
handleMessageSchedule({}, scheduledAt);
handleMessageSchedule({}, scheduledAt, currentMessageList!);
});
});
@ -1210,12 +1234,12 @@ const Composer: FC<OwnProps & StateProps> = ({
});
const handleSendWhenOnline = useLastCallback(() => {
handleMessageSchedule({}, SCHEDULED_WHEN_ONLINE);
handleMessageSchedule({}, SCHEDULED_WHEN_ONLINE, currentMessageList!);
});
const handleSendScheduledAttachments = useLastCallback((sendCompressed: boolean, sendGrouped: boolean) => {
requestCalendar((scheduledAt) => {
handleMessageSchedule({ sendCompressed, sendGrouped }, scheduledAt);
handleMessageSchedule({ sendCompressed, sendGrouped }, scheduledAt, currentMessageList!);
});
});
@ -1656,6 +1680,7 @@ export default memo(withGlobal<OwnProps>(
canSendVoiceByPrivacy,
attachmentSettings: global.attachmentSettings,
slowMode,
currentMessageList,
};
},
)(Composer));

View File

@ -155,6 +155,7 @@ const useEditing = (
}
editMessage({
messageList: { chatId, threadId, type },
text,
entities,
});

View File

@ -3,6 +3,7 @@ import React, { memo, useRef, useCallback } from '../../lib/teact/teact';
import { getActions, withGlobal } from '../../global';
import type { ApiChat, ApiVideo } from '../../api/types';
import type { MessageList } from '../../global/types';
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
import {
@ -39,6 +40,7 @@ type StateProps = {
canScheduleUntilOnline?: boolean;
isSavedMessages?: boolean;
canPostInChat?: boolean;
currentMessageList?: MessageList;
};
const PRELOAD_BACKWARDS = 96; // GIF Search bot results are multiplied by 24
@ -53,6 +55,7 @@ const GifSearch: FC<OwnProps & StateProps> = ({
canScheduleUntilOnline,
isSavedMessages,
canPostInChat,
currentMessageList,
onClose,
}) => {
const {
@ -74,19 +77,28 @@ const GifSearch: FC<OwnProps & StateProps> = ({
const handleGifClick = useCallback((gif: ApiVideo, isSilent?: boolean, shouldSchedule?: boolean) => {
if (canSendGifs) {
if (!currentMessageList) {
return;
}
if (shouldSchedule) {
requestCalendar((scheduledAt) => {
sendMessage({ gif, scheduledAt, isSilent });
sendMessage({
messageList: currentMessageList,
gif,
scheduledAt,
isSilent,
});
});
} else {
sendMessage({ gif, isSilent });
sendMessage({ messageList: currentMessageList, gif, isSilent });
}
}
if (IS_TOUCH_ENV) {
setGifSearchQuery({ query: undefined });
}
}, [canSendGifs, requestCalendar, sendMessage, setGifSearchQuery]);
}, [canSendGifs, currentMessageList, requestCalendar]);
const handleSearchMoreGifs = useCallback(() => {
searchMoreGifs();
@ -167,6 +179,7 @@ export default memo(withGlobal(
isSavedMessages,
canPostInChat,
canScheduleUntilOnline: Boolean(chatId) && selectCanScheduleUntilOnline(global, chatId),
currentMessageList: selectCurrentMessageList(global),
};
},
)(GifSearch));

View File

@ -329,23 +329,20 @@ addActionHandler('switchBotInline', (global, actions, payload): ActionReturnType
addActionHandler('sendInlineBotResult', (global, actions, payload): ActionReturnType => {
const {
id, queryId, isSilent, scheduledAt,
id, queryId, isSilent, scheduledAt, messageList,
tabId = getCurrentTabId(),
} = payload;
const currentMessageList = selectCurrentMessageList(global, tabId);
if (!currentMessageList || !id) {
if (!id) {
return;
}
const { chatId, threadId } = currentMessageList;
const { chatId, threadId } = messageList;
const chat = selectChat(global, chatId)!;
const replyingTo = selectReplyingToId(global, chatId, threadId);
let replyingToTopId: number | undefined;
if (replyingTo && threadId !== MAIN_THREAD_ID) {
replyingToTopId = selectThreadTopMessageId(global, chatId, threadId)!;
}
const replyingToId = selectReplyingToId(global, chatId, threadId);
const replyingToMessage = replyingToId ? selectChatMessage(global, chatId, replyingToId) : undefined;
const replyingToTopId = (chat.isForum || threadId !== MAIN_THREAD_ID)
? selectThreadTopMessageId(global, chatId, threadId)
: replyingToMessage?.replyToTopMessageId || replyingToMessage?.replyToMessageId;
actions.setReplyingToId({ messageId: undefined, tabId });
actions.clearWebPagePreview({ tabId });
@ -354,7 +351,7 @@ addActionHandler('sendInlineBotResult', (global, actions, payload): ActionReturn
chat,
resultId: id,
queryId,
replyingTo,
replyingTo: replyingToId || replyingToTopId,
replyingToTopId,
sendAs: selectSendAs(global, chatId),
isSilent,

View File

@ -235,14 +235,13 @@ addActionHandler('loadMessage', async (global, actions, payload): Promise<void>
});
addActionHandler('sendMessage', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload;
const currentMessageList = selectCurrentMessageList(global, tabId);
const { messageList, tabId = getCurrentTabId() } = payload;
if (!currentMessageList) {
if (!messageList) {
return undefined;
}
const { chatId, threadId, type } = currentMessageList;
const { chatId, threadId, type } = messageList;
payload = omit(payload, ['tabId']);
@ -263,6 +262,7 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType =>
const params = {
...payload,
chat,
currentThreadId: messageList.threadId,
replyingTo: replyingToId,
replyingToTopId,
noWebPage: selectNoWebPage(global, chatId, threadId),
@ -280,7 +280,7 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType =>
sendMessage(global, {
...restParams,
attachment: attachments ? attachments[0] : undefined,
}, tabId);
});
} else if (isGrouped) {
const {
text, entities, attachments, ...commonParams
@ -301,14 +301,14 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType =>
entities: isFirst ? entities : undefined,
attachment: firstAttachment,
groupedId: restAttachments.length > 0 ? groupedId : undefined,
}, tabId);
});
restAttachments.forEach((attachment: ApiAttachment) => {
sendMessage(global, {
...commonParams,
attachment,
groupedId,
}, tabId);
});
});
}
});
@ -323,14 +323,14 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType =>
text,
entities,
replyingTo,
}, tabId);
});
}
attachments?.forEach((attachment: ApiAttachment) => {
sendMessage(global, {
...commonParams,
attachment,
}, tabId);
});
});
}
@ -338,14 +338,15 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType =>
});
addActionHandler('editMessage', (global, actions, payload): ActionReturnType => {
const { text, entities, tabId = getCurrentTabId() } = payload;
const {
messageList, text, entities, tabId = getCurrentTabId(),
} = payload;
const currentMessageList = selectCurrentMessageList(global, tabId);
if (!currentMessageList) {
if (!messageList) {
return;
}
const { chatId, threadId, type: messageListType } = currentMessageList;
const { chatId, threadId, type: messageListType } = messageList;
const chat = selectChat(global, chatId);
const message = selectEditingMessage(global, chatId, threadId, messageListType);
if (!chat || !message) {
@ -760,7 +761,9 @@ addActionHandler('loadExtendedMedia', (global, actions, payload): ActionReturnTy
});
addActionHandler('forwardMessages', (global, actions, payload): ActionReturnType => {
const { isSilent, scheduledAt, tabId = getCurrentTabId() } = payload;
const {
isSilent, scheduledAt, tabId = getCurrentTabId(),
} = payload;
const {
fromChatId, messageIds, toChatId, withMyScore, noAuthors, noCaptions, toThreadId,
@ -806,6 +809,7 @@ addActionHandler('forwardMessages', (global, actions, payload): ActionReturnType
void sendMessage(global, {
chat: toChat,
replyingToTopId: toThreadId,
currentThreadId: toThreadId || MAIN_THREAD_ID,
text,
entities,
sticker,
@ -813,7 +817,7 @@ addActionHandler('forwardMessages', (global, actions, payload): ActionReturnType
isSilent,
scheduledAt,
sendAs,
}, tabId);
});
});
global = getGlobal();
@ -1140,10 +1144,10 @@ async function sendMessage<T extends GlobalState>(global: T, params: {
isSilent?: boolean;
scheduledAt?: number;
sendAs?: ApiChat | ApiUser;
currentThreadId: number;
replyingToTopId?: number;
groupedId?: string;
},
...[tabId = getCurrentTabId()]: TabArgs<T>) {
}) {
let localId: number | undefined;
const progressCallback = params.attachment ? (progress: number, messageLocalId: number) => {
if (!uploadProgressCallbacks.has(messageLocalId)) {
@ -1171,18 +1175,16 @@ async function sendMessage<T extends GlobalState>(global: T, params: {
}
global = getGlobal();
const currentMessageList = selectCurrentMessageList(global, tabId);
if (!currentMessageList) {
if (params.currentThreadId === undefined) {
return;
}
const { threadId } = currentMessageList;
if (!params.replyingTo && threadId !== MAIN_THREAD_ID) {
params.replyingTo = selectThreadTopMessageId(global, params.chat.id, threadId)!;
if (!params.replyingTo && params.currentThreadId !== MAIN_THREAD_ID) {
params.replyingTo = selectThreadTopMessageId(global, params.chat.id, params.currentThreadId)!;
}
if (params.replyingTo && !params.replyingToTopId && threadId !== MAIN_THREAD_ID) {
params.replyingToTopId = selectThreadTopMessageId(global, params.chat.id, threadId)!;
if (params.replyingTo && !params.replyingToTopId && params.currentThreadId !== MAIN_THREAD_ID) {
params.replyingToTopId = selectThreadTopMessageId(global, params.chat.id, params.currentThreadId)!;
}
await callApi('sendMessage', params, progressCallback);

View File

@ -1185,6 +1185,7 @@ export interface ActionPayloads {
contact?: Partial<ApiContact>;
shouldUpdateStickerSetOrder?: boolean;
shouldGroupMessages?: boolean;
messageList: MessageList;
} & WithTabId;
cancelSendingMessage: {
chatId: string;
@ -1216,6 +1217,7 @@ export interface ActionPayloads {
};
};
editMessage: {
messageList: MessageList;
text: string;
entities?: ApiMessageEntity[];
} & WithTabId;
@ -2067,6 +2069,7 @@ export interface ActionPayloads {
sendInlineBotResult: {
id: string;
queryId: string;
messageList: MessageList;
isSilent?: boolean;
scheduledAt?: number;
} & WithTabId;