feat: basic inline queries support (only articles for now)
This commit is contained in:
parent
8bb23cd464
commit
3336f295ee
12 changed files with 705 additions and 86 deletions
|
@ -15,6 +15,7 @@ import { signIn } from './methods/auth/sign-in'
|
|||
import { signUp } from './methods/auth/sign-up'
|
||||
import { startTest } from './methods/auth/start-test'
|
||||
import { start } from './methods/auth/start'
|
||||
import { answerInlineQuery } from './methods/bots/answer-inline-query'
|
||||
import { addChatMembers } from './methods/chats/add-chat-members'
|
||||
import { archiveChats } from './methods/chats/archive-chats'
|
||||
import { createChannel } from './methods/chats/create-channel'
|
||||
|
@ -104,6 +105,7 @@ import {
|
|||
FileDownloadParameters,
|
||||
InputChatPermissions,
|
||||
InputFileLike,
|
||||
InputInlineResult,
|
||||
InputMediaLike,
|
||||
InputPeerLike,
|
||||
MaybeDynamic,
|
||||
|
@ -376,13 +378,108 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
|
||||
/**
|
||||
* Whether to "catch up" (load missed updates).
|
||||
* Note: you should register your handlers
|
||||
* before calling `start()`
|
||||
* Only applicable if the saved session already
|
||||
* contained authorization and updates state.
|
||||
*
|
||||
* Defaults to true.
|
||||
* Note: you should register your handlers
|
||||
* before calling `start()`, otherwise they will
|
||||
* not be called.
|
||||
*
|
||||
* Note: In case the storage was not properly
|
||||
* closed the last time, "catching up" might
|
||||
* result in duplicate updates.
|
||||
*
|
||||
* Defaults to `false`.
|
||||
*/
|
||||
catchUp?: boolean
|
||||
}): Promise<User>
|
||||
/**
|
||||
* Answer an inline query.
|
||||
*
|
||||
* @param queryId Inline query ID
|
||||
* @param results Results of the query
|
||||
* @param params Additional parameters
|
||||
*/
|
||||
answerInlineQuery(
|
||||
queryId: tl.Long,
|
||||
results: InputInlineResult[],
|
||||
params?: {
|
||||
/**
|
||||
* Maximum number of time in seconds that the results of the
|
||||
* query may be cached on the server for.
|
||||
*
|
||||
* Defaults to `300`
|
||||
*/
|
||||
cacheTime?: number
|
||||
|
||||
/**
|
||||
* Whether the results should be displayed as a gallery instead
|
||||
* of a vertical list. Only applicable to some media types.
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
gallery?: boolean
|
||||
|
||||
/**
|
||||
* Whether the results should only be cached on the server
|
||||
* for the user who sent the query.
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
private?: boolean
|
||||
|
||||
/**
|
||||
* Next pagination offset (up to 64 bytes).
|
||||
*
|
||||
* When user has reached the end of the current results,
|
||||
* it will re-send the inline query with the same text, but
|
||||
* with `offset` set to this value.
|
||||
*
|
||||
* If omitted or empty string is provided, it is assumed that
|
||||
* there are no more results.
|
||||
*/
|
||||
nextOffset?: string
|
||||
|
||||
/**
|
||||
* If passed, clients will display a button before any other results,
|
||||
* that when clicked switches the user to a private chat with the bot
|
||||
* and sends the bot `/start ${parameter}`.
|
||||
*
|
||||
* An example from the Bot API docs:
|
||||
*
|
||||
* An inline bot that sends YouTube videos can ask the user to connect
|
||||
* the bot to their YouTube account to adapt search results accordingly.
|
||||
* To do this, it displays a "Connect your YouTube account" button above
|
||||
* the results, or even before showing any. The user presses the button,
|
||||
* switches to a private chat with the bot and, in doing so, passes a start
|
||||
* parameter that instructs the bot to return an oauth link. Once done, the
|
||||
* bot can offer a switch_inline button so that the user can easily return to
|
||||
* the chat where they wanted to use the bot's inline capabilities
|
||||
*/
|
||||
switchPm?: {
|
||||
/**
|
||||
* Text of the button
|
||||
*/
|
||||
text: string
|
||||
|
||||
/**
|
||||
* Parameter for `/start` command
|
||||
*/
|
||||
parameter: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse mode to use when parsing inline message text.
|
||||
* Defaults to current default parse mode (if any).
|
||||
*
|
||||
* Passing `null` will explicitly disable formatting.
|
||||
*
|
||||
* **Note**: inline results themselves *can not* have markup
|
||||
* entities, only the messages that are sent once a result is clicked.
|
||||
*/
|
||||
parseMode?: string | null
|
||||
}
|
||||
): Promise<void>
|
||||
/**
|
||||
* Add new members to a group, supergroup or channel.
|
||||
*
|
||||
|
@ -1883,6 +1980,7 @@ export class TelegramClient extends BaseTelegramClient {
|
|||
signUp = signUp
|
||||
startTest = startTest
|
||||
start = start
|
||||
answerInlineQuery = answerInlineQuery
|
||||
addChatMembers = addChatMembers
|
||||
archiveChats = archiveChats
|
||||
createChannel = createChannel
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
Message,
|
||||
ReplyMarkup,
|
||||
InputMediaLike,
|
||||
InputInlineResult,
|
||||
TakeoutSession,
|
||||
StickerSet
|
||||
} from '../types'
|
||||
|
|
112
packages/client/src/methods/bots/answer-inline-query.ts
Normal file
112
packages/client/src/methods/bots/answer-inline-query.ts
Normal file
|
@ -0,0 +1,112 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { BotInline, InputInlineResult } from '../../types'
|
||||
|
||||
/**
|
||||
* Answer an inline query.
|
||||
*
|
||||
* @param queryId Inline query ID
|
||||
* @param results Results of the query
|
||||
* @param params Additional parameters
|
||||
* @internal
|
||||
*/
|
||||
export async function answerInlineQuery(
|
||||
this: TelegramClient,
|
||||
queryId: tl.Long,
|
||||
results: InputInlineResult[],
|
||||
params?: {
|
||||
/**
|
||||
* Maximum number of time in seconds that the results of the
|
||||
* query may be cached on the server for.
|
||||
*
|
||||
* Defaults to `300`
|
||||
*/
|
||||
cacheTime?: number
|
||||
|
||||
/**
|
||||
* Whether the results should be displayed as a gallery instead
|
||||
* of a vertical list. Only applicable to some media types.
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
gallery?: boolean
|
||||
|
||||
/**
|
||||
* Whether the results should only be cached on the server
|
||||
* for the user who sent the query.
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
private?: boolean
|
||||
|
||||
/**
|
||||
* Next pagination offset (up to 64 bytes).
|
||||
*
|
||||
* When user has reached the end of the current results,
|
||||
* it will re-send the inline query with the same text, but
|
||||
* with `offset` set to this value.
|
||||
*
|
||||
* If omitted or empty string is provided, it is assumed that
|
||||
* there are no more results.
|
||||
*/
|
||||
nextOffset?: string
|
||||
|
||||
/**
|
||||
* If passed, clients will display a button before any other results,
|
||||
* that when clicked switches the user to a private chat with the bot
|
||||
* and sends the bot `/start ${parameter}`.
|
||||
*
|
||||
* An example from the Bot API docs:
|
||||
*
|
||||
* An inline bot that sends YouTube videos can ask the user to connect
|
||||
* the bot to their YouTube account to adapt search results accordingly.
|
||||
* To do this, it displays a "Connect your YouTube account" button above
|
||||
* the results, or even before showing any. The user presses the button,
|
||||
* switches to a private chat with the bot and, in doing so, passes a start
|
||||
* parameter that instructs the bot to return an oauth link. Once done, the
|
||||
* bot can offer a switch_inline button so that the user can easily return to
|
||||
* the chat where they wanted to use the bot's inline capabilities
|
||||
*/
|
||||
switchPm?: {
|
||||
/**
|
||||
* Text of the button
|
||||
*/
|
||||
text: string
|
||||
|
||||
/**
|
||||
* Parameter for `/start` command
|
||||
*/
|
||||
parameter: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse mode to use when parsing inline message text.
|
||||
* Defaults to current default parse mode (if any).
|
||||
*
|
||||
* Passing `null` will explicitly disable formatting.
|
||||
*
|
||||
* **Note**: inline results themselves *can not* have markup
|
||||
* entities, only the messages that are sent once a result is clicked.
|
||||
*/
|
||||
parseMode?: string | null
|
||||
}
|
||||
): Promise<void> {
|
||||
if (!params) params = {}
|
||||
|
||||
const tlResults = await Promise.all(results.map(it => BotInline._convertToTl(this, it, params!.parseMode)))
|
||||
|
||||
await this.call({
|
||||
_: 'messages.setInlineBotResults',
|
||||
queryId,
|
||||
results: tlResults,
|
||||
cacheTime: params.cacheTime ?? 300,
|
||||
gallery: params.gallery,
|
||||
private: params.private,
|
||||
nextOffset: params.nextOffset,
|
||||
switchPm: params.switchPm ? {
|
||||
_: 'inlineBotSwitchPM',
|
||||
text: params.switchPm.text,
|
||||
startParam: params.switchPm.parameter
|
||||
} : undefined
|
||||
})
|
||||
}
|
|
@ -1 +1,3 @@
|
|||
export * from './keyboards'
|
||||
export * from './inline-query'
|
||||
export * from './input'
|
||||
|
|
111
packages/client/src/types/bots/inline-query.ts
Normal file
111
packages/client/src/types/bots/inline-query.ts
Normal file
|
@ -0,0 +1,111 @@
|
|||
import { makeInspectable } from '@mtcute/client/src/types/utils'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { PeerType, User } from '../peers'
|
||||
import { TelegramClient } from '../../client'
|
||||
import { Location } from '../media'
|
||||
import { InputInlineResult } from './input'
|
||||
|
||||
const PEER_TYPE_MAP: Record<tl.TypeInlineQueryPeerType['_'], PeerType> = {
|
||||
inlineQueryPeerTypeBroadcast: 'channel',
|
||||
inlineQueryPeerTypeChat: 'group',
|
||||
inlineQueryPeerTypeMegagroup: 'supergroup',
|
||||
inlineQueryPeerTypePM: 'user',
|
||||
inlineQueryPeerTypeSameBotPM: 'bot',
|
||||
}
|
||||
|
||||
export class InlineQuery {
|
||||
readonly client: TelegramClient
|
||||
readonly raw: tl.RawUpdateBotInlineQuery
|
||||
|
||||
/** Map of users in this message. Mainly for internal use */
|
||||
readonly _users: Record<number, tl.TypeUser>
|
||||
|
||||
constructor(
|
||||
client: TelegramClient,
|
||||
raw: tl.RawUpdateBotInlineQuery,
|
||||
users: Record<number, tl.TypeUser>
|
||||
) {
|
||||
this.client = client
|
||||
this.raw = raw
|
||||
this._users = users
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique query ID
|
||||
*/
|
||||
get id(): tl.Long {
|
||||
return this.raw.queryId
|
||||
}
|
||||
|
||||
private _user?: User
|
||||
/**
|
||||
* User who sent this query
|
||||
*/
|
||||
get user(): User {
|
||||
if (!this._user) {
|
||||
this._user = new User(this.client, this._users[this.raw.userId])
|
||||
}
|
||||
|
||||
return this._user
|
||||
}
|
||||
|
||||
/**
|
||||
* Text of the query (0-512 characters)
|
||||
*/
|
||||
get query(): string {
|
||||
return this.raw.query
|
||||
}
|
||||
|
||||
private _location?: Location
|
||||
/**
|
||||
* Attached geolocation.
|
||||
*
|
||||
* Only used in case the bot requested user location
|
||||
*/
|
||||
get location(): Location | null {
|
||||
if (this.raw.geo?._ !== 'geoPoint') return null
|
||||
|
||||
if (!this._location) {
|
||||
this._location = new Location(this.raw.geo)
|
||||
}
|
||||
|
||||
return this._location
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline query scroll offset, controlled by the bot
|
||||
*/
|
||||
get offset(): string {
|
||||
return this.raw.offset
|
||||
}
|
||||
|
||||
/**
|
||||
* Peer type from which this query was sent.
|
||||
*
|
||||
* Can be:
|
||||
* - `bot`: Query was sent in this bot's PM
|
||||
* - `user`: Query was sent in somebody's PM
|
||||
* - `group`: Query was sent in a legacy group
|
||||
* - `supergroup`: Query was sent in a supergroup
|
||||
* - `channel`: Query was sent in a channel
|
||||
* - `null`, in case this information is not available
|
||||
*/
|
||||
get peerType(): PeerType | null {
|
||||
return this.raw.peerType ? PEER_TYPE_MAP[this.raw.peerType._] : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Answer to this inline query
|
||||
*
|
||||
* @param results Inline results
|
||||
* @param params Additional parameters
|
||||
*/
|
||||
async answer(
|
||||
results: InputInlineResult[],
|
||||
params: Parameters<TelegramClient['answerInlineQuery']>[2]
|
||||
): Promise<void> {
|
||||
return this.client.answerInlineQuery(this.raw.queryId, results, params)
|
||||
}
|
||||
}
|
||||
|
||||
makeInspectable(InlineQuery)
|
2
packages/client/src/types/bots/input/index.ts
Normal file
2
packages/client/src/types/bots/input/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from './input-inline-message'
|
||||
export * from './input-inline-result'
|
65
packages/client/src/types/bots/input/input-inline-message.ts
Normal file
65
packages/client/src/types/bots/input/input-inline-message.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { tl } from '@mtcute/tl'
|
||||
import { BotKeyboard, ReplyMarkup } from '../keyboards'
|
||||
import { TelegramClient } from '../../../client'
|
||||
|
||||
export interface InputInlineMessageText {
|
||||
type: 'text'
|
||||
|
||||
/**
|
||||
* Text of the message
|
||||
*/
|
||||
text: string
|
||||
|
||||
/**
|
||||
* Text markup entities.
|
||||
* If passed, parse mode is ignored
|
||||
*/
|
||||
entities?: tl.TypeMessageEntity[]
|
||||
|
||||
/**
|
||||
* Message reply markup
|
||||
*/
|
||||
replyMarkup?: ReplyMarkup
|
||||
|
||||
/**
|
||||
* Whether to disable links preview in this message
|
||||
*/
|
||||
disableWebPreview?: boolean
|
||||
}
|
||||
|
||||
export type InputInlineMessage =
|
||||
| InputInlineMessageText
|
||||
|
||||
export namespace BotInlineMessage {
|
||||
export function text (
|
||||
text: string,
|
||||
params?: Omit<InputInlineMessageText, 'type' | 'text'>,
|
||||
): InputInlineMessageText {
|
||||
return {
|
||||
type: 'text',
|
||||
text,
|
||||
...(
|
||||
params || {}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
export async function _convertToTl (
|
||||
client: TelegramClient,
|
||||
obj: InputInlineMessage,
|
||||
parseMode?: string | null,
|
||||
): Promise<tl.TypeInputBotInlineMessage> {
|
||||
if (obj.type === 'text') {
|
||||
const [message, entities] = await client['_parseEntities'](obj.text, parseMode, obj.entities)
|
||||
|
||||
return {
|
||||
_: 'inputBotInlineMessageText',
|
||||
message,
|
||||
entities,
|
||||
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup)
|
||||
}
|
||||
}
|
||||
|
||||
return obj as never
|
||||
}
|
||||
}
|
149
packages/client/src/types/bots/input/input-inline-result.ts
Normal file
149
packages/client/src/types/bots/input/input-inline-result.ts
Normal file
|
@ -0,0 +1,149 @@
|
|||
import { tl } from '@mtcute/tl'
|
||||
import { BotInlineMessage, InputInlineMessage } from './input-inline-message'
|
||||
import { TelegramClient } from '../../../client'
|
||||
|
||||
interface BaseInputInlineResult {
|
||||
/**
|
||||
* Unique ID of the result
|
||||
*/
|
||||
id: string
|
||||
|
||||
/**
|
||||
* Message to send when the result is selected.
|
||||
*
|
||||
* By default, is automatically generated,
|
||||
* and details about how it is generated can be found
|
||||
* in subclasses' description
|
||||
*/
|
||||
message?: InputInlineMessage
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an input article.
|
||||
*
|
||||
* If `message` is not provided, a {@link InputInlineMessageText} is created
|
||||
* with web preview enabled and text generated as follows:
|
||||
* ```
|
||||
* {{#if url}}
|
||||
* <a href="{{url}}"><b>{{title}}</b></a>
|
||||
* {{else}}
|
||||
* <b>{{title}}</b>
|
||||
* {{/if}}
|
||||
* {{#if description}}
|
||||
* {{description}}
|
||||
* {{/if}}
|
||||
* ```
|
||||
* > Handlebars syntax is used. HTML tags are used to signify entities,
|
||||
* > but in fact raw TL entity objects are created
|
||||
*/
|
||||
export interface InputInlineResultArticle extends BaseInputInlineResult {
|
||||
type: 'article'
|
||||
|
||||
/**
|
||||
* Title of the result (must not be empty)
|
||||
*/
|
||||
title: string
|
||||
|
||||
/**
|
||||
* Description of the result
|
||||
*/
|
||||
description?: string
|
||||
|
||||
/**
|
||||
* URL of the article
|
||||
*/
|
||||
url?: string
|
||||
|
||||
/**
|
||||
* Whether to prevent article URL from
|
||||
* displaying by the client
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
hideUrl?: boolean
|
||||
|
||||
/**
|
||||
* Article thumbnail URL (only jpeg).
|
||||
*/
|
||||
thumb?: string | tl.RawInputWebDocument
|
||||
}
|
||||
|
||||
export type InputInlineResult = InputInlineResultArticle
|
||||
|
||||
export namespace BotInline {
|
||||
export function article(
|
||||
params: Omit<InputInlineResultArticle, 'type'>
|
||||
): InputInlineResultArticle {
|
||||
return {
|
||||
type: 'article',
|
||||
...params,
|
||||
}
|
||||
}
|
||||
|
||||
export async function _convertToTl(
|
||||
client: TelegramClient,
|
||||
obj: InputInlineResult,
|
||||
parseMode?: string | null
|
||||
): Promise<tl.TypeInputBotInlineResult> {
|
||||
if (obj.type === 'article') {
|
||||
let sendMessage: tl.TypeInputBotInlineMessage
|
||||
if (obj.message) {
|
||||
sendMessage = await BotInlineMessage._convertToTl(client, obj.message, parseMode)
|
||||
} else {
|
||||
let message = obj.title
|
||||
const entities: tl.TypeMessageEntity[] = [
|
||||
{
|
||||
_: 'messageEntityBold',
|
||||
offset: 0,
|
||||
length: message.length
|
||||
}
|
||||
]
|
||||
|
||||
if (obj.url) {
|
||||
entities.push({
|
||||
_: 'messageEntityTextUrl',
|
||||
url: obj.url,
|
||||
offset: 0,
|
||||
length: message.length
|
||||
})
|
||||
}
|
||||
|
||||
if (obj.description) {
|
||||
message += '\n' + obj.description
|
||||
}
|
||||
|
||||
sendMessage = {
|
||||
_: 'inputBotInlineMessageText',
|
||||
message,
|
||||
entities
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
_: 'inputBotInlineResult',
|
||||
id: obj.id,
|
||||
type: obj.type,
|
||||
title: obj.title,
|
||||
description: obj.description,
|
||||
url: obj.hideUrl ? undefined : obj.url,
|
||||
content: obj.url && obj.hideUrl ? {
|
||||
_: 'inputWebDocument',
|
||||
url: obj.url,
|
||||
mimeType: 'text/html',
|
||||
size: 0,
|
||||
attributes: []
|
||||
} : undefined,
|
||||
thumb: typeof obj.thumb === 'string' ? {
|
||||
_: 'inputWebDocument',
|
||||
size: 0,
|
||||
url: obj.thumb,
|
||||
mimeType: 'image/jpeg',
|
||||
attributes: [],
|
||||
} : obj.thumb,
|
||||
sendMessage
|
||||
}
|
||||
}
|
||||
|
||||
return obj as never
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ interface BaseInputMedia {
|
|||
|
||||
/**
|
||||
* Caption entities of the media.
|
||||
* If passed, {@link caption} is ignored
|
||||
* If passed, parse mode is ignored
|
||||
*/
|
||||
entities?: tl.TypeMessageEntity[]
|
||||
|
||||
|
|
|
@ -1,9 +1,35 @@
|
|||
import { ChatMemberUpdateHandler, NewMessageHandler, RawUpdateHandler } from './handler'
|
||||
import {
|
||||
ChatMemberUpdateHandler,
|
||||
InlineQueryHandler,
|
||||
NewMessageHandler,
|
||||
RawUpdateHandler,
|
||||
UpdateHandler,
|
||||
} from './handler'
|
||||
import { filters, UpdateFilter } from './filters'
|
||||
import { Message } from '@mtcute/client'
|
||||
import { InlineQuery, Message } from '@mtcute/client'
|
||||
import { ChatMemberUpdate } from './updates'
|
||||
|
||||
function _create<T extends UpdateHandler>(
|
||||
type: T['type'],
|
||||
filter: any,
|
||||
handler?: any
|
||||
): T {
|
||||
if (handler) {
|
||||
return {
|
||||
type,
|
||||
check: filter,
|
||||
callback: handler
|
||||
} as any
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
callback: filter
|
||||
} as any
|
||||
}
|
||||
|
||||
export namespace handlers {
|
||||
|
||||
/**
|
||||
* Create a {@link RawUpdateHandler}
|
||||
*
|
||||
|
@ -25,18 +51,7 @@ export namespace handlers {
|
|||
): RawUpdateHandler
|
||||
|
||||
export function rawUpdate(filter: any, handler?: any): RawUpdateHandler {
|
||||
if (handler) {
|
||||
return {
|
||||
type: 'raw',
|
||||
check: filter,
|
||||
callback: handler
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'raw',
|
||||
callback: filter
|
||||
}
|
||||
return _create('raw', filter, handler)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,18 +78,7 @@ export namespace handlers {
|
|||
filter: any,
|
||||
handler?: any
|
||||
): NewMessageHandler {
|
||||
if (handler) {
|
||||
return {
|
||||
type: 'new_message',
|
||||
check: filter,
|
||||
callback: handler,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'new_message',
|
||||
callback: filter,
|
||||
}
|
||||
return _create('new_message', filter, handler)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,17 +105,33 @@ export namespace handlers {
|
|||
filter: any,
|
||||
handler?: any
|
||||
): ChatMemberUpdateHandler {
|
||||
if (handler) {
|
||||
return {
|
||||
type: 'chat_member',
|
||||
check: filter,
|
||||
callback: handler,
|
||||
}
|
||||
return _create('chat_member', filter, handler)
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'chat_member',
|
||||
callback: filter,
|
||||
}
|
||||
/**
|
||||
* Create an inline query handler
|
||||
*
|
||||
* @param handler Inline query handler
|
||||
*/
|
||||
export function inlineQuery(
|
||||
handler: InlineQueryHandler['callback']
|
||||
): InlineQueryHandler
|
||||
|
||||
/**
|
||||
* Create an inline query with a filter
|
||||
*
|
||||
* @param filter Inline query update filter
|
||||
* @param handler Inline query handler
|
||||
*/
|
||||
export function inlineQuery<Mod>(
|
||||
filter: UpdateFilter<InlineQuery, Mod>,
|
||||
handler: InlineQueryHandler<filters.Modify<InlineQuery, Mod>>['callback']
|
||||
): InlineQueryHandler
|
||||
|
||||
export function inlineQuery(
|
||||
filter: any,
|
||||
handler?: any
|
||||
): InlineQueryHandler {
|
||||
return _create('inline_query', filter, handler)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import { Message, MtCuteArgumentError, TelegramClient } from '@mtcute/client'
|
||||
import {
|
||||
InlineQuery,
|
||||
Message,
|
||||
MtCuteArgumentError,
|
||||
TelegramClient,
|
||||
} from '@mtcute/client'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import {
|
||||
ContinuePropagation,
|
||||
|
@ -7,7 +12,7 @@ import {
|
|||
StopPropagation,
|
||||
} from './propagation'
|
||||
import {
|
||||
ChatMemberUpdateHandler,
|
||||
ChatMemberUpdateHandler, InlineQueryHandler,
|
||||
NewMessageHandler,
|
||||
RawUpdateHandler,
|
||||
UpdateHandler,
|
||||
|
@ -18,6 +23,54 @@ import { ChatMemberUpdate } from './updates'
|
|||
|
||||
const noop = () => {}
|
||||
|
||||
type ParserFunction = (
|
||||
client: TelegramClient,
|
||||
upd: tl.TypeUpdate | tl.TypeMessage,
|
||||
users: Record<number, tl.TypeUser>,
|
||||
chats: Record<number, tl.TypeChat>
|
||||
) => any
|
||||
type UpdateParser = [Exclude<UpdateHandler['type'], 'raw'>, ParserFunction]
|
||||
|
||||
const baseMessageParser: ParserFunction = (
|
||||
client: TelegramClient,
|
||||
upd,
|
||||
users,
|
||||
chats
|
||||
) =>
|
||||
new Message(
|
||||
client,
|
||||
tl.isAnyMessage(upd) ? upd : (upd as any).message,
|
||||
users,
|
||||
chats
|
||||
)
|
||||
|
||||
const newMessageParser: UpdateParser = ['new_message', baseMessageParser]
|
||||
const editMessageParser: UpdateParser = ['edit_message', baseMessageParser]
|
||||
const chatMemberParser: UpdateParser = [
|
||||
'chat_member',
|
||||
(client, upd, users, chats) =>
|
||||
new ChatMemberUpdate(client, upd as any, users, chats),
|
||||
]
|
||||
|
||||
const PARSERS: Partial<
|
||||
Record<(tl.TypeUpdate | tl.TypeMessage)['_'], UpdateParser>
|
||||
> = {
|
||||
message: newMessageParser,
|
||||
messageEmpty: newMessageParser,
|
||||
messageService: newMessageParser,
|
||||
updateNewMessage: newMessageParser,
|
||||
updateNewChannelMessage: newMessageParser,
|
||||
updateNewScheduledMessage: newMessageParser,
|
||||
updateEditMessage: editMessageParser,
|
||||
updateEditChannelMessage: editMessageParser,
|
||||
updateChatParticipant: chatMemberParser,
|
||||
updateChannelParticipant: chatMemberParser,
|
||||
updateBotInlineQuery: [
|
||||
'inline_query',
|
||||
(client, upd, users) => new InlineQuery(client, upd as any, users),
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* The dispatcher
|
||||
*/
|
||||
|
@ -115,36 +168,10 @@ export class Dispatcher {
|
|||
if (!this._client) return
|
||||
|
||||
const isRawMessage = tl.isAnyMessage(update)
|
||||
|
||||
let message: Message | null = null
|
||||
if (
|
||||
update._ === 'updateNewMessage' ||
|
||||
update._ === 'updateNewChannelMessage' ||
|
||||
update._ === 'updateNewScheduledMessage' ||
|
||||
update._ === 'updateEditMessage' ||
|
||||
update._ === 'updateEditChannelMessage' ||
|
||||
isRawMessage
|
||||
) {
|
||||
message = new Message(
|
||||
this._client,
|
||||
isRawMessage ? update : (update as any).message,
|
||||
users,
|
||||
chats
|
||||
)
|
||||
}
|
||||
|
||||
let chatMember: ChatMemberUpdate | null = null
|
||||
if (
|
||||
update._ === 'updateChatParticipant' ||
|
||||
update._ === 'updateChannelParticipant'
|
||||
) {
|
||||
chatMember = new ChatMemberUpdate(
|
||||
this._client,
|
||||
update,
|
||||
users,
|
||||
chats
|
||||
)
|
||||
}
|
||||
const pair = PARSERS[update._]
|
||||
const parsed = pair
|
||||
? pair[1](this._client, update, users, chats)
|
||||
: undefined
|
||||
|
||||
outer: for (const grp of this._groupsOrder) {
|
||||
for (const handler of this._groups[grp]) {
|
||||
|
@ -168,19 +195,12 @@ export class Dispatcher {
|
|||
chats
|
||||
)
|
||||
} else if (
|
||||
handler.type === 'new_message' &&
|
||||
message &&
|
||||
pair &&
|
||||
handler.type === pair[0] &&
|
||||
(!handler.check ||
|
||||
(await handler.check(message, this._client)))
|
||||
(await handler.check(parsed, this._client)))
|
||||
) {
|
||||
result = await handler.callback(message, this._client)
|
||||
} else if (
|
||||
handler.type === 'chat_member' &&
|
||||
chatMember &&
|
||||
(!handler.check ||
|
||||
(await handler.check(chatMember, this._client)))
|
||||
) {
|
||||
result = await handler.callback(chatMember, this._client)
|
||||
result = await handler.callback(parsed, this._client)
|
||||
} else continue
|
||||
|
||||
if (result === ContinuePropagation) continue
|
||||
|
@ -407,7 +427,7 @@ export class Dispatcher {
|
|||
}
|
||||
|
||||
/**
|
||||
* Register a chat member update filter without any filters.
|
||||
* Register a chat member update handler without any filters.
|
||||
*
|
||||
* @param handler Update handler
|
||||
* @param group Handler group index
|
||||
|
@ -437,4 +457,36 @@ export class Dispatcher {
|
|||
onChatMemberUpdate(filter: any, handler?: any, group?: number): void {
|
||||
this._addKnownHandler('chatMemberUpdate', filter, handler, group)
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an inline query handler without any filters.
|
||||
*
|
||||
* @param handler Update handler
|
||||
* @param group Handler group index
|
||||
* @internal
|
||||
*/
|
||||
onInlineQuery(
|
||||
handler: InlineQueryHandler['callback'],
|
||||
group?: number
|
||||
): void
|
||||
|
||||
/**
|
||||
* Register an inline query handler with a given filter
|
||||
*
|
||||
* @param filter Update filter
|
||||
* @param handler Update handler
|
||||
* @param group Handler group index
|
||||
*/
|
||||
onInlineQuery<Mod>(
|
||||
filter: UpdateFilter<InlineQuery, Mod>,
|
||||
handler: InlineQueryHandler<
|
||||
filters.Modify<InlineQuery, Mod>
|
||||
>['callback'],
|
||||
group?: number
|
||||
): void
|
||||
|
||||
/** @internal */
|
||||
onInlineQuery(filter: any, handler?: any, group?: number): void {
|
||||
this._addKnownHandler('inlineQuery', filter, handler, group)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { MaybeAsync, Message, TelegramClient } from '@mtcute/client'
|
||||
import { MaybeAsync, Message, TelegramClient, InlineQuery } from '@mtcute/client'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { PropagationSymbol } from './propagation'
|
||||
import { ChatMemberUpdate } from './updates'
|
||||
|
@ -39,12 +39,19 @@ export type NewMessageHandler<T = Message> = ParsedUpdateHandler<
|
|||
'new_message',
|
||||
T
|
||||
>
|
||||
export type EditMessageHandler<T = Message> = ParsedUpdateHandler<
|
||||
'edit_message',
|
||||
T
|
||||
>
|
||||
export type ChatMemberUpdateHandler<T = ChatMemberUpdate> = ParsedUpdateHandler<
|
||||
'chat_member',
|
||||
T
|
||||
>
|
||||
export type InlineQueryHandler<T = InlineQuery> = ParsedUpdateHandler<'inline_query', T>
|
||||
|
||||
export type UpdateHandler =
|
||||
| RawUpdateHandler
|
||||
| NewMessageHandler
|
||||
| EditMessageHandler
|
||||
| ChatMemberUpdateHandler
|
||||
| InlineQueryHandler
|
||||
|
|
Loading…
Reference in a new issue