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 { signUp } from './methods/auth/sign-up'
|
||||||
import { startTest } from './methods/auth/start-test'
|
import { startTest } from './methods/auth/start-test'
|
||||||
import { start } from './methods/auth/start'
|
import { start } from './methods/auth/start'
|
||||||
|
import { answerInlineQuery } from './methods/bots/answer-inline-query'
|
||||||
import { addChatMembers } from './methods/chats/add-chat-members'
|
import { addChatMembers } from './methods/chats/add-chat-members'
|
||||||
import { archiveChats } from './methods/chats/archive-chats'
|
import { archiveChats } from './methods/chats/archive-chats'
|
||||||
import { createChannel } from './methods/chats/create-channel'
|
import { createChannel } from './methods/chats/create-channel'
|
||||||
|
@ -104,6 +105,7 @@ import {
|
||||||
FileDownloadParameters,
|
FileDownloadParameters,
|
||||||
InputChatPermissions,
|
InputChatPermissions,
|
||||||
InputFileLike,
|
InputFileLike,
|
||||||
|
InputInlineResult,
|
||||||
InputMediaLike,
|
InputMediaLike,
|
||||||
InputPeerLike,
|
InputPeerLike,
|
||||||
MaybeDynamic,
|
MaybeDynamic,
|
||||||
|
@ -376,13 +378,108 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to "catch up" (load missed updates).
|
* Whether to "catch up" (load missed updates).
|
||||||
* Note: you should register your handlers
|
* Only applicable if the saved session already
|
||||||
* before calling `start()`
|
* 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
|
catchUp?: boolean
|
||||||
}): Promise<User>
|
}): 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.
|
* Add new members to a group, supergroup or channel.
|
||||||
*
|
*
|
||||||
|
@ -1883,6 +1980,7 @@ export class TelegramClient extends BaseTelegramClient {
|
||||||
signUp = signUp
|
signUp = signUp
|
||||||
startTest = startTest
|
startTest = startTest
|
||||||
start = start
|
start = start
|
||||||
|
answerInlineQuery = answerInlineQuery
|
||||||
addChatMembers = addChatMembers
|
addChatMembers = addChatMembers
|
||||||
archiveChats = archiveChats
|
archiveChats = archiveChats
|
||||||
createChannel = createChannel
|
createChannel = createChannel
|
||||||
|
|
|
@ -27,6 +27,7 @@ import {
|
||||||
Message,
|
Message,
|
||||||
ReplyMarkup,
|
ReplyMarkup,
|
||||||
InputMediaLike,
|
InputMediaLike,
|
||||||
|
InputInlineResult,
|
||||||
TakeoutSession,
|
TakeoutSession,
|
||||||
StickerSet
|
StickerSet
|
||||||
} from '../types'
|
} 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 './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.
|
* Caption entities of the media.
|
||||||
* If passed, {@link caption} is ignored
|
* If passed, parse mode is ignored
|
||||||
*/
|
*/
|
||||||
entities?: tl.TypeMessageEntity[]
|
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 { filters, UpdateFilter } from './filters'
|
||||||
import { Message } from '@mtcute/client'
|
import { InlineQuery, Message } from '@mtcute/client'
|
||||||
import { ChatMemberUpdate } from './updates'
|
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 {
|
export namespace handlers {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link RawUpdateHandler}
|
* Create a {@link RawUpdateHandler}
|
||||||
*
|
*
|
||||||
|
@ -25,18 +51,7 @@ export namespace handlers {
|
||||||
): RawUpdateHandler
|
): RawUpdateHandler
|
||||||
|
|
||||||
export function rawUpdate(filter: any, handler?: any): RawUpdateHandler {
|
export function rawUpdate(filter: any, handler?: any): RawUpdateHandler {
|
||||||
if (handler) {
|
return _create('raw', filter, handler)
|
||||||
return {
|
|
||||||
type: 'raw',
|
|
||||||
check: filter,
|
|
||||||
callback: handler
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'raw',
|
|
||||||
callback: filter
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,18 +78,7 @@ export namespace handlers {
|
||||||
filter: any,
|
filter: any,
|
||||||
handler?: any
|
handler?: any
|
||||||
): NewMessageHandler {
|
): NewMessageHandler {
|
||||||
if (handler) {
|
return _create('new_message', filter, handler)
|
||||||
return {
|
|
||||||
type: 'new_message',
|
|
||||||
check: filter,
|
|
||||||
callback: handler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'new_message',
|
|
||||||
callback: filter,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,17 +105,33 @@ export namespace handlers {
|
||||||
filter: any,
|
filter: any,
|
||||||
handler?: any
|
handler?: any
|
||||||
): ChatMemberUpdateHandler {
|
): ChatMemberUpdateHandler {
|
||||||
if (handler) {
|
return _create('chat_member', filter, handler)
|
||||||
return {
|
}
|
||||||
type: 'chat_member',
|
|
||||||
check: filter,
|
|
||||||
callback: handler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
/**
|
||||||
type: 'chat_member',
|
* Create an inline query handler
|
||||||
callback: filter,
|
*
|
||||||
}
|
* @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 { tl } from '@mtcute/tl'
|
||||||
import {
|
import {
|
||||||
ContinuePropagation,
|
ContinuePropagation,
|
||||||
|
@ -7,7 +12,7 @@ import {
|
||||||
StopPropagation,
|
StopPropagation,
|
||||||
} from './propagation'
|
} from './propagation'
|
||||||
import {
|
import {
|
||||||
ChatMemberUpdateHandler,
|
ChatMemberUpdateHandler, InlineQueryHandler,
|
||||||
NewMessageHandler,
|
NewMessageHandler,
|
||||||
RawUpdateHandler,
|
RawUpdateHandler,
|
||||||
UpdateHandler,
|
UpdateHandler,
|
||||||
|
@ -18,6 +23,54 @@ import { ChatMemberUpdate } from './updates'
|
||||||
|
|
||||||
const noop = () => {}
|
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
|
* The dispatcher
|
||||||
*/
|
*/
|
||||||
|
@ -115,36 +168,10 @@ export class Dispatcher {
|
||||||
if (!this._client) return
|
if (!this._client) return
|
||||||
|
|
||||||
const isRawMessage = tl.isAnyMessage(update)
|
const isRawMessage = tl.isAnyMessage(update)
|
||||||
|
const pair = PARSERS[update._]
|
||||||
let message: Message | null = null
|
const parsed = pair
|
||||||
if (
|
? pair[1](this._client, update, users, chats)
|
||||||
update._ === 'updateNewMessage' ||
|
: undefined
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
outer: for (const grp of this._groupsOrder) {
|
outer: for (const grp of this._groupsOrder) {
|
||||||
for (const handler of this._groups[grp]) {
|
for (const handler of this._groups[grp]) {
|
||||||
|
@ -168,19 +195,12 @@ export class Dispatcher {
|
||||||
chats
|
chats
|
||||||
)
|
)
|
||||||
} else if (
|
} else if (
|
||||||
handler.type === 'new_message' &&
|
pair &&
|
||||||
message &&
|
handler.type === pair[0] &&
|
||||||
(!handler.check ||
|
(!handler.check ||
|
||||||
(await handler.check(message, this._client)))
|
(await handler.check(parsed, this._client)))
|
||||||
) {
|
) {
|
||||||
result = await handler.callback(message, this._client)
|
result = await handler.callback(parsed, this._client)
|
||||||
} else if (
|
|
||||||
handler.type === 'chat_member' &&
|
|
||||||
chatMember &&
|
|
||||||
(!handler.check ||
|
|
||||||
(await handler.check(chatMember, this._client)))
|
|
||||||
) {
|
|
||||||
result = await handler.callback(chatMember, this._client)
|
|
||||||
} else continue
|
} else continue
|
||||||
|
|
||||||
if (result === ContinuePropagation) 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 handler Update handler
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
|
@ -437,4 +457,36 @@ export class Dispatcher {
|
||||||
onChatMemberUpdate(filter: any, handler?: any, group?: number): void {
|
onChatMemberUpdate(filter: any, handler?: any, group?: number): void {
|
||||||
this._addKnownHandler('chatMemberUpdate', filter, handler, group)
|
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 { tl } from '@mtcute/tl'
|
||||||
import { PropagationSymbol } from './propagation'
|
import { PropagationSymbol } from './propagation'
|
||||||
import { ChatMemberUpdate } from './updates'
|
import { ChatMemberUpdate } from './updates'
|
||||||
|
@ -39,12 +39,19 @@ export type NewMessageHandler<T = Message> = ParsedUpdateHandler<
|
||||||
'new_message',
|
'new_message',
|
||||||
T
|
T
|
||||||
>
|
>
|
||||||
|
export type EditMessageHandler<T = Message> = ParsedUpdateHandler<
|
||||||
|
'edit_message',
|
||||||
|
T
|
||||||
|
>
|
||||||
export type ChatMemberUpdateHandler<T = ChatMemberUpdate> = ParsedUpdateHandler<
|
export type ChatMemberUpdateHandler<T = ChatMemberUpdate> = ParsedUpdateHandler<
|
||||||
'chat_member',
|
'chat_member',
|
||||||
T
|
T
|
||||||
>
|
>
|
||||||
|
export type InlineQueryHandler<T = InlineQuery> = ParsedUpdateHandler<'inline_query', T>
|
||||||
|
|
||||||
export type UpdateHandler =
|
export type UpdateHandler =
|
||||||
| RawUpdateHandler
|
| RawUpdateHandler
|
||||||
| NewMessageHandler
|
| NewMessageHandler
|
||||||
|
| EditMessageHandler
|
||||||
| ChatMemberUpdateHandler
|
| ChatMemberUpdateHandler
|
||||||
|
| InlineQueryHandler
|
||||||
|
|
Loading…
Reference in a new issue