diff --git a/packages/client/scripts/update-types.txt b/packages/client/scripts/update-types.txt index b3346593..64fc4fdf 100644 --- a/packages/client/scripts/update-types.txt +++ b/packages/client/scripts/update-types.txt @@ -7,6 +7,7 @@ chat_member: ChatMemberUpdate = ChatMemberUpdate inline_query = InlineQuery in InlineQueryContext chosen_inline_result = ChosenInlineResult in ChosenInlineResultContext callback_query = CallbackQuery + State in CallbackQueryContext +inline_callback_query = InlineCallbackQuery + State in InlineCallbackQueryContext poll: PollUpdate = PollUpdate poll_vote = PollVoteUpdate user_status: UserStatusUpdate = UserStatusUpdate diff --git a/packages/client/src/methods/bots/answer-callback-query.ts b/packages/client/src/methods/bots/answer-callback-query.ts index 3a9dee66..16a324c2 100644 --- a/packages/client/src/methods/bots/answer-callback-query.ts +++ b/packages/client/src/methods/bots/answer-callback-query.ts @@ -1,6 +1,6 @@ import { BaseTelegramClient, Long } from '@mtcute/core' -import { CallbackQuery } from '../../types/bots/callback-query.js' +import { CallbackQuery } from '../../types/updates/callback-query.js' /** * Send an answer to a callback query. diff --git a/packages/client/src/methods/bots/answer-inline-query.ts b/packages/client/src/methods/bots/answer-inline-query.ts index 2319d676..79021e38 100644 --- a/packages/client/src/methods/bots/answer-inline-query.ts +++ b/packages/client/src/methods/bots/answer-inline-query.ts @@ -1,6 +1,7 @@ import { BaseTelegramClient, Long, tl } from '@mtcute/core' -import { BotInline, InlineQuery, InputInlineResult } from '../../types/bots/index.js' +import { BotInline, InputInlineResult } from '../../types/bots/index.js' +import { InlineQuery } from '../../types/updates/inline-query.js' /** * Answer an inline query. diff --git a/packages/client/src/methods/messages/get-callback-query-message.ts b/packages/client/src/methods/messages/get-callback-query-message.ts index 890a8c79..339c3266 100644 --- a/packages/client/src/methods/messages/get-callback-query-message.ts +++ b/packages/client/src/methods/messages/get-callback-query-message.ts @@ -1,9 +1,9 @@ import { BaseTelegramClient, tl } from '@mtcute/core' import { assertTypeIsNot } from '@mtcute/core/utils.js' -import type { CallbackQuery } from '../../types/bots/callback-query.js' import { Message } from '../../types/messages/message.js' import { InputPeerLike, PeersIndex } from '../../types/peers/index.js' +import type { CallbackQuery } from '../../types/updates/callback-query.js' import { isInputPeerChannel, toInputChannel } from '../../utils/peer-utils.js' import { resolvePeer } from '../users/resolve-peer.js' diff --git a/packages/client/src/types/bots/index.ts b/packages/client/src/types/bots/index.ts index 97447ca3..3c944903 100644 --- a/packages/client/src/types/bots/index.ts +++ b/packages/client/src/types/bots/index.ts @@ -1,7 +1,5 @@ -export * from './callback-query.js' export * from './command-scope.js' export * from './game-high-score.js' -export * from './inline-query.js' export * from './input/index.js' export * from './keyboard-builder.js' export * from './keyboards.js' diff --git a/packages/client/src/types/bots/callback-query.ts b/packages/client/src/types/updates/callback-query.ts similarity index 58% rename from packages/client/src/types/bots/callback-query.ts rename to packages/client/src/types/updates/callback-query.ts index 2c09250a..a3bd9a3c 100644 --- a/packages/client/src/types/bots/callback-query.ts +++ b/packages/client/src/types/updates/callback-query.ts @@ -1,4 +1,4 @@ -import { BasicPeerType, getBasicPeerType, getMarkedPeerId, MtArgumentError, tl } from '@mtcute/core' +import { MtArgumentError, tl } from '@mtcute/core' import { makeInspectable, utf8Decode } from '../../utils/index.js' import { encodeInlineMessageId } from '../../utils/inline-utils.js' @@ -7,11 +7,8 @@ import { Chat } from '../peers/chat.js' import { PeersIndex } from '../peers/peers-index.js' import { User } from '../peers/user.js' -/** - * An incoming callback query, originated from a callback button - * of an inline keyboard. - */ -export class CallbackQuery { +/** Base class for callback queries */ +class BaseCallbackQuery { constructor( readonly raw: tl.RawUpdateBotCallbackQuery | tl.RawUpdateInlineBotCallbackQuery, readonly _peers: PeersIndex, @@ -39,96 +36,6 @@ export class CallbackQuery { return this.raw.chatInstance } - /** - * Whether this callback query originates from - * a button that was attached to a message sent - * *via* the bot (i.e. using inline mode). - * - * If `true`, `messageId` is available and `getMessage` can be used, - * otherwise `inlineMessageId` and `inlineMessageIdStr` are available - */ - get isInline(): boolean { - return this.raw._ === 'updateInlineBotCallbackQuery' - } - - /** - * Identifier of the previously sent inline message, - * that contained the button which was clicked. - * This ID can be used in `TelegramClient.editInlineMessage` - * - * Is only available in case `isInline = true` - */ - get inlineMessageId(): tl.TypeInputBotInlineMessageID { - if (this.raw._ !== 'updateInlineBotCallbackQuery') { - throw new MtArgumentError('Cannot get inline message id for non-inline callback') - } - - return this.raw.msgId - } - - /** - * Identifier of the previously sent inline message, - * that contained the button which was clicked, - * as a TDLib and Bot API compatible string. - * Can be used instead of {@link inlineMessageId} in - * case you want to store it in some storage. - * - * Is only available in case `isInline = true` - */ - get inlineMessageIdStr(): string { - if (this.raw._ !== 'updateInlineBotCallbackQuery') { - throw new MtArgumentError('Cannot get inline message id for non-inline callback') - } - - return encodeInlineMessageId(this.raw.msgId) - } - - /** - * Identifier of the chat where this message was sent - */ - get chatId(): number { - if (this.raw._ !== 'updateBotCallbackQuery') { - throw new MtArgumentError('Cannot get message id for inline callback') - } - - return getMarkedPeerId(this.raw.peer) - } - - /** - * Chat where this message was sent - * - * Only available in case `isInline = false` - */ - get chat(): Chat { - if (this.raw._ !== 'updateBotCallbackQuery') { - throw new MtArgumentError('Cannot get message id for inline callback') - } - - return new Chat(this._peers.get(this.raw.peer)) - } - - /** - * Basic peer type of the chat where this message was sent, - * derived based on {@link chatId} - */ - get chatType(): BasicPeerType { - return getBasicPeerType(this.chatId) - } - - /** - * Identifier of the message sent by the bot - * that contained the button which was clicked. - * - * Is only available in case `isInline = false` - */ - get messageId(): number { - if (this.raw._ !== 'updateBotCallbackQuery') { - throw new MtArgumentError('Cannot get message id for inline callback') - } - - return this.raw.msgId - } - /** * Data that was contained in the callback button, if any * @@ -162,5 +69,70 @@ export class CallbackQuery { } } -memoizeGetters(CallbackQuery, ['user', 'chat', 'dataStr', 'inlineMessageIdStr']) +/** + * A callback query originating from a normal message sent by the bot. + */ +export class CallbackQuery extends BaseCallbackQuery { + constructor( + readonly raw: tl.RawUpdateBotCallbackQuery, + _peers: PeersIndex, + ) { + super(raw, _peers) + } + + /** + * Chat where the originating message was sent + */ + get chat(): Chat { + if (this.raw._ !== 'updateBotCallbackQuery') { + throw new MtArgumentError('Cannot get message id for inline callback') + } + + return new Chat(this._peers.get(this.raw.peer)) + } + + /** + * Identifier of the message containing the button which was clicked. + */ + get messageId(): number { + return this.raw.msgId + } +} + +memoizeGetters(CallbackQuery, ['user', 'dataStr', 'chat']) makeInspectable(CallbackQuery) + +/** + * A callback query originating from an inline message sent by the bot. + */ +export class InlineCallbackQuery extends BaseCallbackQuery { + constructor( + readonly raw: tl.RawUpdateInlineBotCallbackQuery, + _peers: PeersIndex, + ) { + super(raw, _peers) + } + + /** + * Identifier of the previously sent inline message, + * that contained the button which was clicked. + * This ID can be used in `TelegramClient.editInlineMessage` + */ + get inlineMessageId(): tl.TypeInputBotInlineMessageID { + return this.raw.msgId + } + + /** + * Identifier of the previously sent inline message, + * that contained the button which was clicked, + * as a TDLib and Bot API compatible string. + * Can be used instead of {@link inlineMessageId} in + * case you want to store it in some storage. + */ + get inlineMessageIdStr(): string { + return encodeInlineMessageId(this.raw.msgId) + } +} + +memoizeGetters(InlineCallbackQuery, ['user', 'dataStr', 'inlineMessageIdStr']) +makeInspectable(InlineCallbackQuery) diff --git a/packages/client/src/types/updates/index.ts b/packages/client/src/types/updates/index.ts index c5c52172..430d32af 100644 --- a/packages/client/src/types/updates/index.ts +++ b/packages/client/src/types/updates/index.ts @@ -1,8 +1,10 @@ -import type { CallbackQuery, InlineQuery, Message } from '../../types/index.js' +import type { Message } from '../../types/index.js' import { BotChatJoinRequestUpdate } from './bot-chat-join-request.js' import { BotStoppedUpdate } from './bot-stopped.js' +import { CallbackQuery, InlineCallbackQuery } from './callback-query.js' import { ChatJoinRequestUpdate } from './chat-join-request.js' import { ChatMemberUpdate } from './chat-member-update.js' +import { InlineQuery } from './inline-query.js' export type { ChatMemberUpdateType } from './chat-member-update.js' import { ChosenInlineResult } from './chosen-inline-result.js' import { DeleteMessageUpdate } from './delete-message-update.js' @@ -18,12 +20,15 @@ import { UserTypingUpdate } from './user-typing-update.js' export { BotChatJoinRequestUpdate, BotStoppedUpdate, + CallbackQuery, ChatJoinRequestUpdate, ChatMemberUpdate, ChosenInlineResult, DeleteMessageUpdate, DeleteStoryUpdate, HistoryReadUpdate, + InlineCallbackQuery, + InlineQuery, PollUpdate, PollVoteUpdate, PreCheckoutQuery, @@ -42,6 +47,7 @@ export type ParsedUpdate = | { name: 'inline_query'; data: InlineQuery } | { name: 'chosen_inline_result'; data: ChosenInlineResult } | { name: 'callback_query'; data: CallbackQuery } + | { name: 'inline_callback_query'; data: InlineCallbackQuery } | { name: 'poll'; data: PollUpdate } | { name: 'poll_vote'; data: PollVoteUpdate } | { name: 'user_status'; data: UserStatusUpdate } diff --git a/packages/client/src/types/bots/inline-query.ts b/packages/client/src/types/updates/inline-query.ts similarity index 100% rename from packages/client/src/types/bots/inline-query.ts rename to packages/client/src/types/updates/inline-query.ts diff --git a/packages/client/src/types/updates/parse-update.ts b/packages/client/src/types/updates/parse-update.ts index 4dcbaa9f..0fd7a1c9 100644 --- a/packages/client/src/types/updates/parse-update.ts +++ b/packages/client/src/types/updates/parse-update.ts @@ -11,6 +11,7 @@ import { DeleteMessageUpdate, DeleteStoryUpdate, HistoryReadUpdate, + InlineCallbackQuery, InlineQuery, Message, ParsedUpdate, @@ -44,8 +45,9 @@ export function _parseUpdate(update: tl.TypeUpdate, peers: PeersIndex): ParsedUp case 'updateBotInlineSend': return { name: 'chosen_inline_result', data: new ChosenInlineResult(update, peers) } case 'updateBotCallbackQuery': - case 'updateInlineBotCallbackQuery': return { name: 'callback_query', data: new CallbackQuery(update, peers) } + case 'updateInlineBotCallbackQuery': + return { name: 'inline_callback_query', data: new InlineCallbackQuery(update, peers) } case 'updateMessagePoll': return { name: 'poll', data: new PollUpdate(update, peers) } case 'updateMessagePollVote': diff --git a/packages/dispatcher/src/context/callback-query.ts b/packages/dispatcher/src/context/callback-query.ts index c6f9bcfe..ba5bf468 100644 --- a/packages/dispatcher/src/context/callback-query.ts +++ b/packages/dispatcher/src/context/callback-query.ts @@ -1,12 +1,4 @@ -import { - CallbackQuery, - getMarkedPeerId, - MaybeAsync, - Message, - MtArgumentError, - MtMessageNotFoundError, - TelegramClient, -} from '@mtcute/client' +import { CallbackQuery, InlineCallbackQuery, MaybeAsync, Message, TelegramClient } from '@mtcute/client' import { UpdateContext } from './base.js' @@ -34,33 +26,16 @@ export class CallbackQueryContext extends CallbackQuery implements UpdateContext * Get the message containing the callback button being clicked. * * Note that the message may have been deleted, in which case - * `MessageNotFoundError` is thrown. + * `null` will be returned. */ - async getMessage() { - if (this.raw._ !== 'updateBotCallbackQuery') { - throw new MtArgumentError('Cannot get message for inline callback query') - } - - const msg = await this.client.getCallbackQueryMessage(this) - - if (!msg) { - throw new MtMessageNotFoundError(getMarkedPeerId(this.raw.peer), this.raw.msgId, 'Message not found') - } - - return msg + async getMessage(): Promise { + return this.client.getCallbackQueryMessage(this) } /** * Edit the message that contained the callback button that was clicked. */ async editMessage(params: Omit[0], 'messageId'>) { - if (this.raw._ === 'updateInlineBotCallbackQuery') { - return this.client.editInlineMessage({ - messageId: this.raw.msgId, - ...params, - }) - } - return this.client.editMessage({ chatId: this.raw.peer, message: this.raw.msgId, @@ -81,3 +56,34 @@ export class CallbackQueryContext extends CallbackQuery implements UpdateContext return this.editMessage(res) } } + +/** + * Context of an inline-originated callback query update. + * + * This is a subclass of {@link InlineCallbackQuery}, so all its fields are also available. + */ +export class InlineCallbackQueryContext extends InlineCallbackQuery implements UpdateContext { + readonly _name = 'inline_callback_query' + + constructor( + readonly client: TelegramClient, + query: InlineCallbackQuery, + ) { + super(query.raw, query._peers) + } + + /** Answer to this callback query */ + answer(params: Parameters[1]) { + return this.client.answerCallbackQuery(this.id, params) + } + + /** + * Edit the message that contained the callback button that was clicked. + */ + async editMessage(params: Omit[0], 'messageId'>) { + return this.client.editInlineMessage({ + messageId: this.raw.msgId, + ...params, + }) + } +} diff --git a/packages/dispatcher/src/dispatcher.ts b/packages/dispatcher/src/dispatcher.ts index a1a6a5f7..66e3703a 100644 --- a/packages/dispatcher/src/dispatcher.ts +++ b/packages/dispatcher/src/dispatcher.ts @@ -28,6 +28,7 @@ import { CallbackQueryContext, ChatJoinRequestUpdateContext, ChosenInlineResultContext, + InlineCallbackQueryContext, InlineQueryContext, MessageContext, PreCheckoutQueryContext, @@ -46,6 +47,7 @@ import { DeleteStoryHandler, EditMessageHandler, HistoryReadHandler, + InlineCallbackQueryHandler, InlineQueryHandler, MessageGroupHandler, NewMessageHandler, @@ -1359,6 +1361,57 @@ export class Dispatcher { this._addKnownHandler('callback_query', filter, handler, group) } + /** + * Register an inline callback query handler without any filters + * + * @param handler Inline callback query handler + * @param group Handler group index + */ + onInlineCallbackQuery( + handler: InlineCallbackQueryHandler< + InlineCallbackQueryContext, + State extends never ? never : UpdateState + >['callback'], + group?: number, + ): void + + /** + * Register an inline callback query handler with a filter + * + * @param filter Update filter + * @param handler Inline callback query handler + * @param group Handler group index + */ + onInlineCallbackQuery( + filter: UpdateFilter, + handler: InlineCallbackQueryHandler< + filters.Modify, + State extends never ? never : UpdateState + >['callback'], + group?: number, + ): void + + /** + * Register an inline callback query handler with a filter + * + * @param filter Update filter + * @param handler Inline callback query handler + * @param group Handler group index + */ + onInlineCallbackQuery( + filter: UpdateFilter, + handler: InlineCallbackQueryHandler< + filters.Modify, + State extends never ? never : UpdateState + >['callback'], + group?: number, + ): void + + /** @internal */ + onInlineCallbackQuery(filter: any, handler?: any, group?: number): void { + this._addKnownHandler('inline_callback_query', filter, handler, group) + } + /** * Register a poll update handler without any filters * diff --git a/packages/dispatcher/src/filters/text.ts b/packages/dispatcher/src/filters/text.ts index c7714414..2b8f3983 100644 --- a/packages/dispatcher/src/filters/text.ts +++ b/packages/dispatcher/src/filters/text.ts @@ -1,9 +1,11 @@ -import { CallbackQuery, ChosenInlineResult, InlineQuery, Message } from '@mtcute/client' +import { CallbackQuery, ChosenInlineResult, InlineCallbackQuery, InlineQuery, Message } from '@mtcute/client' import { UpdateContextDistributed } from '../context/base.js' import { UpdateFilter } from './types.js' -type UpdatesWithText = UpdateContextDistributed +type UpdatesWithText = UpdateContextDistributed< + Message | InlineQuery | ChosenInlineResult | CallbackQuery | InlineCallbackQuery +> function extractText(obj: UpdatesWithText): string | null { switch (obj._name) { @@ -14,6 +16,7 @@ function extractText(obj: UpdatesWithText): string | null { case 'chosen_inline_result': return obj.id case 'callback_query': + case 'inline_callback_query': if (obj.raw.data) return obj.dataStr } diff --git a/packages/dispatcher/src/filters/updates.ts b/packages/dispatcher/src/filters/updates.ts index 15dce9da..2360c19f 100644 --- a/packages/dispatcher/src/filters/updates.ts +++ b/packages/dispatcher/src/filters/updates.ts @@ -1,4 +1,4 @@ -import { CallbackQuery, ChatMemberUpdate, ChatMemberUpdateType, MaybeArray, UserStatus, UserStatusUpdate } from '@mtcute/client' +import { ChatMemberUpdate, ChatMemberUpdateType, MaybeArray, UserStatus, UserStatusUpdate } from '@mtcute/client' import { UpdateFilter } from './types.js' @@ -56,9 +56,3 @@ export const userStatus: { * regarding current user */ export const chatMemberSelf: UpdateFilter = (upd) => upd.isSelf - -/** - * Create a filter for callback queries that - * originated from an inline message - */ -export const callbackInline: UpdateFilter = (q) => q.isInline diff --git a/packages/dispatcher/src/filters/user.ts b/packages/dispatcher/src/filters/user.ts index 45b2f4f1..e291ebc4 100644 --- a/packages/dispatcher/src/filters/user.ts +++ b/packages/dispatcher/src/filters/user.ts @@ -5,6 +5,7 @@ import { ChosenInlineResult, DeleteStoryUpdate, HistoryReadUpdate, + InlineCallbackQuery, InlineQuery, MaybeArray, Message, @@ -44,6 +45,7 @@ export const userId: { | ChatMemberUpdate | ChosenInlineResult | CallbackQuery + | InlineCallbackQuery | PollVoteUpdate | BotChatJoinRequestUpdate >> @@ -58,6 +60,7 @@ export const userId: { | ChatMemberUpdate | ChosenInlineResult | CallbackQuery + | InlineCallbackQuery | PollVoteUpdate | BotChatJoinRequestUpdate >> diff --git a/packages/dispatcher/src/handler.ts b/packages/dispatcher/src/handler.ts index a55098dc..068869c0 100644 --- a/packages/dispatcher/src/handler.ts +++ b/packages/dispatcher/src/handler.ts @@ -21,6 +21,7 @@ import { CallbackQueryContext, ChatJoinRequestUpdateContext, ChosenInlineResultContext, + InlineCallbackQueryContext, InlineQueryContext, MessageContext, PreCheckoutQueryContext, @@ -59,6 +60,11 @@ export type ChatMemberUpdateHandler> = Parse export type InlineQueryHandler = ParsedUpdateHandler<'inline_query', T> export type ChosenInlineResultHandler = ParsedUpdateHandler<'chosen_inline_result', T> export type CallbackQueryHandler = ParsedUpdateHandler<'callback_query', T, S> +export type InlineCallbackQueryHandler = ParsedUpdateHandler< + 'inline_callback_query', + T, + S +> export type PollUpdateHandler> = ParsedUpdateHandler<'poll', T> export type PollVoteHandler> = ParsedUpdateHandler<'poll_vote', T> export type UserStatusUpdateHandler> = ParsedUpdateHandler<'user_status', T> @@ -87,6 +93,7 @@ export type UpdateHandler = | InlineQueryHandler | ChosenInlineResultHandler | CallbackQueryHandler + | InlineCallbackQueryHandler | PollUpdateHandler | PollVoteHandler | UserStatusUpdateHandler diff --git a/packages/dispatcher/src/state/key.ts b/packages/dispatcher/src/state/key.ts index b82ca6c1..9524ef53 100644 --- a/packages/dispatcher/src/state/key.ts +++ b/packages/dispatcher/src/state/key.ts @@ -45,10 +45,9 @@ export const defaultStateKeyDelegate: StateKeyDelegate = (upd): string | null => } if (upd._name === 'callback_query') { - if (upd.isInline) return null - if (upd.chatType === 'user') return `${upd.user.id}` + if (upd.chat.chatType === 'private') return `${upd.user.id}` - return `${upd.chatId}_${upd.user.id}` + return `${upd.chat.id}_${upd.user.id}` } return null