avoid using namespaces in favor of esm (#21)
This commit is contained in:
commit
d313743e10
38 changed files with 2662 additions and 2608 deletions
|
@ -8,7 +8,7 @@ import { assertTypeIs } from '../../../utils/type-assertions.js'
|
||||||
import { ITelegramClient } from '../../client.types.js'
|
import { ITelegramClient } from '../../client.types.js'
|
||||||
import { isUploadedFile } from '../../types/files/uploaded-file.js'
|
import { isUploadedFile } from '../../types/files/uploaded-file.js'
|
||||||
import { UploadFileLike } from '../../types/files/utils.js'
|
import { UploadFileLike } from '../../types/files/utils.js'
|
||||||
import { InputMediaLike } from '../../types/media/input-media.js'
|
import { InputMediaLike } from '../../types/media/input-media/types.js'
|
||||||
import { fileIdToInputDocument, fileIdToInputPhoto } from '../../utils/convert-file-id.js'
|
import { fileIdToInputDocument, fileIdToInputPhoto } from '../../utils/convert-file-id.js'
|
||||||
import { extractFileName } from '../../utils/file-utils.js'
|
import { extractFileName } from '../../utils/file-utils.js'
|
||||||
import { normalizeDate } from '../../utils/misc-utils.js'
|
import { normalizeDate } from '../../utils/misc-utils.js'
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { randomLong } from '../../../utils/long-utils.js'
|
import { randomLong } from '../../../utils/long-utils.js'
|
||||||
import { ITelegramClient } from '../../client.types.js'
|
import { ITelegramClient } from '../../client.types.js'
|
||||||
import { InputMediaLike } from '../../types/media/input-media.js'
|
import { InputMediaLike } from '../../types/media/input-media/types.js'
|
||||||
import { Message } from '../../types/messages/message.js'
|
import { Message } from '../../types/messages/message.js'
|
||||||
import { InputPeerLike, PeersIndex } from '../../types/peers/index.js'
|
import { InputPeerLike, PeersIndex } from '../../types/peers/index.js'
|
||||||
import { assertIsUpdatesGroup } from '../../updates/utils.js'
|
import { assertIsUpdatesGroup } from '../../updates/utils.js'
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { randomLong } from '../../../utils/long-utils.js'
|
import { randomLong } from '../../../utils/long-utils.js'
|
||||||
import { ITelegramClient } from '../../client.types.js'
|
import { ITelegramClient } from '../../client.types.js'
|
||||||
import { BotKeyboard, ReplyMarkup } from '../../types/bots/keyboards.js'
|
import { BotKeyboard, ReplyMarkup } from '../../types/bots/keyboards/index.js'
|
||||||
import { InputMediaLike } from '../../types/media/input-media.js'
|
import { InputMediaLike } from '../../types/media/input-media/types.js'
|
||||||
import { Message } from '../../types/messages/message.js'
|
import { Message } from '../../types/messages/message.js'
|
||||||
import { InputText } from '../../types/misc/entities.js'
|
import { InputText } from '../../types/misc/entities.js'
|
||||||
import { InputPeerLike } from '../../types/peers/index.js'
|
import { InputPeerLike } from '../../types/peers/index.js'
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { MtTypeAssertionError } from '../../../types/errors.js'
|
||||||
import { randomLong } from '../../../utils/long-utils.js'
|
import { randomLong } from '../../../utils/long-utils.js'
|
||||||
import { getMarkedPeerId } from '../../../utils/peer-utils.js'
|
import { getMarkedPeerId } from '../../../utils/peer-utils.js'
|
||||||
import { ITelegramClient } from '../../client.types.js'
|
import { ITelegramClient } from '../../client.types.js'
|
||||||
import { BotKeyboard, ReplyMarkup } from '../../types/bots/keyboards.js'
|
import { BotKeyboard, ReplyMarkup } from '../../types/bots/keyboards/index.js'
|
||||||
import { Message } from '../../types/messages/message.js'
|
import { Message } from '../../types/messages/message.js'
|
||||||
import { InputText } from '../../types/misc/entities.js'
|
import { InputText } from '../../types/misc/entities.js'
|
||||||
import { InputPeerLike, PeersIndex } from '../../types/peers/index.js'
|
import { InputPeerLike, PeersIndex } from '../../types/peers/index.js'
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
import { tl } from '@mtcute/tl'
|
|
||||||
|
|
||||||
import { InputPeerLike } from '../peers/index.js'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper constants and builder functions for methods
|
|
||||||
* related to bot commands.
|
|
||||||
*
|
|
||||||
* You can learn more about bot command scopes in
|
|
||||||
* [Bot API docs](https://core.telegram.org/bots/api#botcommandscope)
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
||||||
export namespace BotCommands {
|
|
||||||
/**
|
|
||||||
* Intermediate bot scope, that is converted to
|
|
||||||
* TL type `BotCommandScope` by the respective functions.
|
|
||||||
*
|
|
||||||
* Used to avoid manually resolving peers.
|
|
||||||
*/
|
|
||||||
export type IntermediateScope =
|
|
||||||
| {
|
|
||||||
type: 'peer' | 'peer_admins'
|
|
||||||
peer: InputPeerLike
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'member'
|
|
||||||
chat: InputPeerLike
|
|
||||||
user: InputPeerLike
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default commands scope.
|
|
||||||
*
|
|
||||||
* Used if no commands with a narrower scope are available.
|
|
||||||
*/
|
|
||||||
export const default_: tl.RawBotCommandScopeDefault = {
|
|
||||||
_: 'botCommandScopeDefault',
|
|
||||||
} as const
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scope that covers all private chats
|
|
||||||
*/
|
|
||||||
export const allPrivate: tl.RawBotCommandScopeUsers = {
|
|
||||||
_: 'botCommandScopeUsers',
|
|
||||||
} as const
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scope that covers all group chats (both legacy and supergroups)
|
|
||||||
*/
|
|
||||||
export const allGroups: tl.RawBotCommandScopeChats = {
|
|
||||||
_: 'botCommandScopeChats',
|
|
||||||
} as const
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scope that covers all group chat administrators (both legacy and supergroups)
|
|
||||||
*/
|
|
||||||
export const allGroupAdmins: tl.RawBotCommandScopeChatAdmins = {
|
|
||||||
_: 'botCommandScopeChatAdmins',
|
|
||||||
} as const
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scope that covers a specific peer (a single user in PMs,
|
|
||||||
* or all users of a legacy group or a supergroup)
|
|
||||||
*/
|
|
||||||
export function peer(peer: InputPeerLike): IntermediateScope {
|
|
||||||
return {
|
|
||||||
type: 'peer',
|
|
||||||
peer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scope that covers admins in a specific group
|
|
||||||
*/
|
|
||||||
export function groupAdmins(peer: InputPeerLike): IntermediateScope {
|
|
||||||
return {
|
|
||||||
type: 'peer_admins',
|
|
||||||
peer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scope that covers a specific user in a specific group
|
|
||||||
*/
|
|
||||||
export function groupMember(chat: InputPeerLike, user: InputPeerLike): IntermediateScope {
|
|
||||||
return {
|
|
||||||
type: 'member',
|
|
||||||
chat,
|
|
||||||
user,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to create a bot command object
|
|
||||||
*
|
|
||||||
* @param command Bot command (without slash)
|
|
||||||
* @param description Command description
|
|
||||||
*/
|
|
||||||
export function cmd(command: string, description: string): tl.RawBotCommand {
|
|
||||||
return {
|
|
||||||
_: 'botCommand',
|
|
||||||
command,
|
|
||||||
description,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import * as BotCommands from './inner.js'
|
||||||
|
|
||||||
|
export {
|
||||||
|
/**
|
||||||
|
* Helper constants and builder functions for methods
|
||||||
|
* related to bot commands.
|
||||||
|
*
|
||||||
|
* You can learn more about bot command scopes in
|
||||||
|
* [Bot API docs](https://core.telegram.org/bots/api#botcommandscope)
|
||||||
|
*/
|
||||||
|
BotCommands,
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
import { InputPeerLike } from '../../peers/index.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intermediate bot scope, that is converted to
|
||||||
|
* TL type `BotCommandScope` by the respective functions.
|
||||||
|
*
|
||||||
|
* Used to avoid manually resolving peers.
|
||||||
|
*/
|
||||||
|
export type IntermediateScope =
|
||||||
|
| {
|
||||||
|
type: 'peer' | 'peer_admins'
|
||||||
|
peer: InputPeerLike
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'member'
|
||||||
|
chat: InputPeerLike
|
||||||
|
user: InputPeerLike
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default commands scope.
|
||||||
|
*
|
||||||
|
* Used if no commands with a narrower scope are available.
|
||||||
|
*/
|
||||||
|
export const default_: tl.RawBotCommandScopeDefault = {
|
||||||
|
_: 'botCommandScopeDefault',
|
||||||
|
} as const
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope that covers all private chats
|
||||||
|
*/
|
||||||
|
export const allPrivate: tl.RawBotCommandScopeUsers = {
|
||||||
|
_: 'botCommandScopeUsers',
|
||||||
|
} as const
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope that covers all group chats (both legacy and supergroups)
|
||||||
|
*/
|
||||||
|
export const allGroups: tl.RawBotCommandScopeChats = {
|
||||||
|
_: 'botCommandScopeChats',
|
||||||
|
} as const
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope that covers all group chat administrators (both legacy and supergroups)
|
||||||
|
*/
|
||||||
|
export const allGroupAdmins: tl.RawBotCommandScopeChatAdmins = {
|
||||||
|
_: 'botCommandScopeChatAdmins',
|
||||||
|
} as const
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope that covers a specific peer (a single user in PMs,
|
||||||
|
* or all users of a legacy group or a supergroup)
|
||||||
|
*/
|
||||||
|
export function peer(peer: InputPeerLike): IntermediateScope {
|
||||||
|
return {
|
||||||
|
type: 'peer',
|
||||||
|
peer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope that covers admins in a specific group
|
||||||
|
*/
|
||||||
|
export function groupAdmins(peer: InputPeerLike): IntermediateScope {
|
||||||
|
return {
|
||||||
|
type: 'peer_admins',
|
||||||
|
peer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope that covers a specific user in a specific group
|
||||||
|
*/
|
||||||
|
export function groupMember(chat: InputPeerLike, user: InputPeerLike): IntermediateScope {
|
||||||
|
return {
|
||||||
|
type: 'member',
|
||||||
|
chat,
|
||||||
|
user,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to create a bot command object
|
||||||
|
*
|
||||||
|
* @param command Bot command (without slash)
|
||||||
|
* @param description Command description
|
||||||
|
*/
|
||||||
|
export function cmd(command: string, description: string): tl.RawBotCommand {
|
||||||
|
return {
|
||||||
|
_: 'botCommand',
|
||||||
|
command,
|
||||||
|
description,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
export * from './command-scope.js'
|
export * from './command-scope/index.js'
|
||||||
export * from './game-high-score.js'
|
export * from './game-high-score.js'
|
||||||
export * from './input/index.js'
|
export * from './inline-message/index.js'
|
||||||
export * from './keyboard-builder.js'
|
export * from './inline-result/index.js'
|
||||||
export * from './keyboards.js'
|
export * from './keyboards/index.js'
|
||||||
|
|
|
@ -0,0 +1,204 @@
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
import { assertNever } from '../../../../types/utils.js'
|
||||||
|
import { ITelegramClient } from '../../../client.types.js'
|
||||||
|
import { _normalizeInputText } from '../../../methods/misc/normalize-text.js'
|
||||||
|
import { InputText } from '../../../types/misc/entities.js'
|
||||||
|
import { InputMediaGeoLive } from '../../media/index.js'
|
||||||
|
import { BotKeyboard } from '../keyboards/index.js'
|
||||||
|
import {
|
||||||
|
InputInlineMessage,
|
||||||
|
InputInlineMessageContact,
|
||||||
|
InputInlineMessageGame,
|
||||||
|
InputInlineMessageGeo,
|
||||||
|
InputInlineMessageGeoLive,
|
||||||
|
InputInlineMessageMedia,
|
||||||
|
InputInlineMessageText,
|
||||||
|
InputInlineMessageVenue,
|
||||||
|
InputInlineMessageWebpage,
|
||||||
|
} from './types.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a text inline message
|
||||||
|
*
|
||||||
|
* @param text Message text
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function text(
|
||||||
|
text: InputText,
|
||||||
|
params: Omit<InputInlineMessageText, 'type' | 'text'> = {},
|
||||||
|
): InputInlineMessageText {
|
||||||
|
const ret = params as tl.Mutable<InputInlineMessageText>
|
||||||
|
ret.type = 'text'
|
||||||
|
ret.text = text
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline message containing
|
||||||
|
* media from the result
|
||||||
|
*/
|
||||||
|
export function media(params: Omit<InputInlineMessageMedia, 'type'> = {}): InputInlineMessageMedia {
|
||||||
|
const ret = params as tl.Mutable<InputInlineMessageMedia>
|
||||||
|
ret.type = 'media'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline message containing a geolocation
|
||||||
|
*
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function geo(params: Omit<InputInlineMessageGeo, 'type'>): InputInlineMessageGeo {
|
||||||
|
const ret = params as tl.Mutable<InputInlineMessageGeo>
|
||||||
|
ret.type = 'geo'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline message containing a live geolocation
|
||||||
|
*
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function geoLive(params: Omit<InputInlineMessageGeoLive, 'type'>): InputInlineMessageGeoLive {
|
||||||
|
const ret = params as tl.Mutable<InputInlineMessageGeoLive>
|
||||||
|
ret.type = 'geo_live'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline message containing a venue
|
||||||
|
*/
|
||||||
|
export function venue(params: Omit<InputInlineMessageVenue, 'type'>): InputInlineMessageVenue {
|
||||||
|
const ret = params as tl.Mutable<InputInlineMessageVenue>
|
||||||
|
ret.type = 'venue'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline message containing a game
|
||||||
|
* from the inline result
|
||||||
|
*/
|
||||||
|
export function game(params: Omit<InputInlineMessageGame, 'type'>): InputInlineMessageGame {
|
||||||
|
const ret = params as tl.Mutable<InputInlineMessageGame>
|
||||||
|
ret.type = 'game'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline message containing a contact
|
||||||
|
*/
|
||||||
|
export function contact(params: Omit<InputInlineMessageContact, 'type'>): InputInlineMessageContact {
|
||||||
|
const ret = params as tl.Mutable<InputInlineMessageContact>
|
||||||
|
ret.type = 'contact'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline message containing a webpage
|
||||||
|
*/
|
||||||
|
export function webpage(params: Omit<InputInlineMessageWebpage, 'type'>): InputInlineMessageWebpage {
|
||||||
|
const ret = params as tl.Mutable<InputInlineMessageWebpage>
|
||||||
|
ret.type = 'webpage'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export async function _convertToTl(
|
||||||
|
client: ITelegramClient,
|
||||||
|
obj: InputInlineMessage,
|
||||||
|
): Promise<tl.TypeInputBotInlineMessage> {
|
||||||
|
switch (obj.type) {
|
||||||
|
case 'text': {
|
||||||
|
const [message, entities] = await _normalizeInputText(client, obj.text)
|
||||||
|
|
||||||
|
return {
|
||||||
|
_: 'inputBotInlineMessageText',
|
||||||
|
message,
|
||||||
|
entities,
|
||||||
|
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
||||||
|
invertMedia: obj.invertMedia,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'media': {
|
||||||
|
const [message, entities] = await _normalizeInputText(client, obj.text)
|
||||||
|
|
||||||
|
return {
|
||||||
|
_: 'inputBotInlineMessageMediaAuto',
|
||||||
|
message,
|
||||||
|
entities,
|
||||||
|
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
||||||
|
invertMedia: obj.invertMedia,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'geo':
|
||||||
|
case 'geo_live':
|
||||||
|
return {
|
||||||
|
_: 'inputBotInlineMessageMediaGeo',
|
||||||
|
geoPoint: {
|
||||||
|
_: 'inputGeoPoint',
|
||||||
|
lat: obj.latitude,
|
||||||
|
long: obj.longitude,
|
||||||
|
},
|
||||||
|
// fields will be `undefined` if this is a `geo`
|
||||||
|
heading: (obj as InputMediaGeoLive).heading,
|
||||||
|
period: (obj as InputMediaGeoLive).period,
|
||||||
|
proximityNotificationRadius: (obj as InputMediaGeoLive).proximityNotificationRadius,
|
||||||
|
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
||||||
|
}
|
||||||
|
case 'venue':
|
||||||
|
return {
|
||||||
|
_: 'inputBotInlineMessageMediaVenue',
|
||||||
|
geoPoint: {
|
||||||
|
_: 'inputGeoPoint',
|
||||||
|
lat: obj.latitude,
|
||||||
|
long: obj.longitude,
|
||||||
|
},
|
||||||
|
title: obj.title,
|
||||||
|
address: obj.address,
|
||||||
|
provider: obj.source?.provider ?? '',
|
||||||
|
venueId: obj.source?.id ?? '',
|
||||||
|
venueType: obj.source?.type ?? '',
|
||||||
|
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
||||||
|
}
|
||||||
|
case 'game':
|
||||||
|
return {
|
||||||
|
_: 'inputBotInlineMessageGame',
|
||||||
|
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
||||||
|
}
|
||||||
|
case 'contact':
|
||||||
|
return {
|
||||||
|
_: 'inputBotInlineMessageMediaContact',
|
||||||
|
phoneNumber: obj.phone,
|
||||||
|
firstName: obj.firstName,
|
||||||
|
lastName: obj.lastName ?? '',
|
||||||
|
vcard: obj.vcard ?? '',
|
||||||
|
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
||||||
|
}
|
||||||
|
case 'webpage': {
|
||||||
|
const [message, entities] = await _normalizeInputText(client, obj.text)
|
||||||
|
|
||||||
|
return {
|
||||||
|
_: 'inputBotInlineMessageMediaWebPage',
|
||||||
|
message,
|
||||||
|
entities,
|
||||||
|
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
||||||
|
invertMedia: obj.invertMedia,
|
||||||
|
forceLargeMedia: obj.size === 'large',
|
||||||
|
forceSmallMedia: obj.size === 'small',
|
||||||
|
optional: !obj.required,
|
||||||
|
url: obj.url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assertNever(obj)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
export * from './types.js'
|
||||||
|
|
||||||
|
import * as BotInlineMessage from './factories.js'
|
||||||
|
|
||||||
|
export { BotInlineMessage }
|
147
packages/core/src/highlevel/types/bots/inline-message/types.ts
Normal file
147
packages/core/src/highlevel/types/bots/inline-message/types.ts
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
import {
|
||||||
|
InputMediaContact,
|
||||||
|
InputMediaGeo,
|
||||||
|
InputMediaGeoLive,
|
||||||
|
InputMediaVenue,
|
||||||
|
InputMediaWebpage,
|
||||||
|
} from '../../media/index.js'
|
||||||
|
import { InputText } from '../../misc/entities.js'
|
||||||
|
import { ReplyMarkup } from '../index.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline message containing only text
|
||||||
|
*/
|
||||||
|
export interface InputInlineMessageText {
|
||||||
|
type: 'text'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text of the message
|
||||||
|
*/
|
||||||
|
text: InputText
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message reply markup
|
||||||
|
*/
|
||||||
|
replyMarkup?: ReplyMarkup
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to disable links preview in this message
|
||||||
|
*/
|
||||||
|
disableWebPreview?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to invert media position.
|
||||||
|
*
|
||||||
|
* Currently only supported for web previews and makes the
|
||||||
|
* client render the preview above the caption and not below.
|
||||||
|
*/
|
||||||
|
invertMedia?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline message containing media, which is automatically
|
||||||
|
* inferred from the result itself.
|
||||||
|
*/
|
||||||
|
export interface InputInlineMessageMedia {
|
||||||
|
type: 'media'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caption for the media
|
||||||
|
*/
|
||||||
|
text?: InputText
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message reply markup
|
||||||
|
*/
|
||||||
|
replyMarkup?: ReplyMarkup
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to invert media position.
|
||||||
|
*
|
||||||
|
* Currently only supported for web previews and makes the
|
||||||
|
* client render the preview above the caption and not below.
|
||||||
|
*/
|
||||||
|
invertMedia?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline message containing a geolocation
|
||||||
|
*/
|
||||||
|
export interface InputInlineMessageGeo extends InputMediaGeo {
|
||||||
|
/**
|
||||||
|
* Message's reply markup
|
||||||
|
*/
|
||||||
|
replyMarkup?: ReplyMarkup
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline message containing a live geolocation
|
||||||
|
*/
|
||||||
|
export interface InputInlineMessageGeoLive extends InputMediaGeoLive {
|
||||||
|
/**
|
||||||
|
* Message's reply markup
|
||||||
|
*/
|
||||||
|
replyMarkup?: ReplyMarkup
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline message containing a venue
|
||||||
|
*/
|
||||||
|
export interface InputInlineMessageVenue extends InputMediaVenue {
|
||||||
|
/**
|
||||||
|
* Message's reply markup
|
||||||
|
*/
|
||||||
|
replyMarkup?: ReplyMarkup
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline message containing a game
|
||||||
|
*/
|
||||||
|
export interface InputInlineMessageGame {
|
||||||
|
type: 'game'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message's reply markup
|
||||||
|
*/
|
||||||
|
replyMarkup?: ReplyMarkup
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline message containing a contact
|
||||||
|
*/
|
||||||
|
export interface InputInlineMessageContact extends InputMediaContact {
|
||||||
|
/**
|
||||||
|
* Message's reply markup
|
||||||
|
*/
|
||||||
|
replyMarkup?: ReplyMarkup
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InputInlineMessageWebpage extends InputMediaWebpage {
|
||||||
|
/**
|
||||||
|
* Text of the message
|
||||||
|
*/
|
||||||
|
text: InputText
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message reply markup
|
||||||
|
*/
|
||||||
|
replyMarkup?: ReplyMarkup
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to invert media position.
|
||||||
|
*
|
||||||
|
* Currently only supported for web previews and makes the
|
||||||
|
* client render the preview above the caption and not below.
|
||||||
|
*/
|
||||||
|
invertMedia?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InputInlineMessage =
|
||||||
|
| InputInlineMessageText
|
||||||
|
| InputInlineMessageMedia
|
||||||
|
| InputInlineMessageGeo
|
||||||
|
| InputInlineMessageGeoLive
|
||||||
|
| InputInlineMessageVenue
|
||||||
|
| InputInlineMessageGame
|
||||||
|
| InputInlineMessageContact
|
||||||
|
| InputInlineMessageWebpage
|
|
@ -0,0 +1,552 @@
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
import { MtArgumentError } from '../../../../types/errors.js'
|
||||||
|
import { ITelegramClient } from '../../../client.types.js'
|
||||||
|
import { fileIdToInputDocument, fileIdToInputPhoto } from '../../../utils/convert-file-id.js'
|
||||||
|
import { extractFileName } from '../../../utils/file-utils.js'
|
||||||
|
import { BotInlineMessage } from '../inline-message/index.js'
|
||||||
|
import {
|
||||||
|
InputInlineResult,
|
||||||
|
InputInlineResultArticle,
|
||||||
|
InputInlineResultAudio,
|
||||||
|
InputInlineResultContact,
|
||||||
|
InputInlineResultFile,
|
||||||
|
InputInlineResultGame,
|
||||||
|
InputInlineResultGeo,
|
||||||
|
InputInlineResultGif,
|
||||||
|
InputInlineResultPhoto,
|
||||||
|
InputInlineResultSticker,
|
||||||
|
InputInlineResultVenue,
|
||||||
|
InputInlineResultVideo,
|
||||||
|
InputInlineResultVoice,
|
||||||
|
} from './types.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline result containing an article
|
||||||
|
*
|
||||||
|
* @param id Inline result ID
|
||||||
|
* @param params Article
|
||||||
|
*/
|
||||||
|
export function article(id: string, params: Omit<InputInlineResultArticle, 'type' | 'id'>): InputInlineResultArticle {
|
||||||
|
const ret = params as tl.Mutable<InputInlineResultArticle>
|
||||||
|
ret.id = id
|
||||||
|
ret.type = 'article'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline result containing a GIF
|
||||||
|
*
|
||||||
|
* @param id Inline result ID
|
||||||
|
* @param media GIF animation
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function gif(
|
||||||
|
id: string,
|
||||||
|
media: string | tl.RawInputWebDocument | tl.RawInputDocument,
|
||||||
|
params: Omit<InputInlineResultGif, 'type' | 'id' | 'media'> = {},
|
||||||
|
): InputInlineResultGif {
|
||||||
|
const ret = params as tl.Mutable<InputInlineResultGif>
|
||||||
|
ret.id = id
|
||||||
|
ret.type = 'gif'
|
||||||
|
ret.media = media
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline result containing a video
|
||||||
|
*
|
||||||
|
* @param id Inline result ID
|
||||||
|
* @param media Video
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function video(
|
||||||
|
id: string,
|
||||||
|
media: string | tl.RawInputWebDocument | tl.RawInputDocument,
|
||||||
|
params: Omit<InputInlineResultVideo, 'type' | 'id' | 'media'>,
|
||||||
|
): InputInlineResultVideo {
|
||||||
|
const ret = params as tl.Mutable<InputInlineResultVideo>
|
||||||
|
ret.id = id
|
||||||
|
ret.type = 'video'
|
||||||
|
ret.media = media
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline result containing an audio file
|
||||||
|
*
|
||||||
|
* @param id Inline result ID
|
||||||
|
* @param media Audio file
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function audio(
|
||||||
|
id: string,
|
||||||
|
media: string | tl.RawInputWebDocument | tl.RawInputDocument,
|
||||||
|
params: Omit<InputInlineResultAudio, 'type' | 'id' | 'media'>,
|
||||||
|
): InputInlineResultAudio {
|
||||||
|
const ret = params as tl.Mutable<InputInlineResultAudio>
|
||||||
|
ret.id = id
|
||||||
|
ret.type = 'audio'
|
||||||
|
ret.media = media
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline result containing a voice note
|
||||||
|
*
|
||||||
|
* @param id Inline result ID
|
||||||
|
* @param media Voice note
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function voice(
|
||||||
|
id: string,
|
||||||
|
media: string | tl.RawInputWebDocument | tl.RawInputDocument,
|
||||||
|
params: Omit<InputInlineResultVoice, 'type' | 'id' | 'media'>,
|
||||||
|
): InputInlineResultVoice {
|
||||||
|
const ret = params as tl.Mutable<InputInlineResultVoice>
|
||||||
|
ret.id = id
|
||||||
|
ret.type = 'voice'
|
||||||
|
ret.media = media
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline result containing a photo
|
||||||
|
*
|
||||||
|
* @param id Inline result ID
|
||||||
|
* @param media Photo
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function photo(
|
||||||
|
id: string,
|
||||||
|
media: string | tl.RawInputWebDocument | tl.RawInputPhoto,
|
||||||
|
params: Omit<InputInlineResultPhoto, 'type' | 'id' | 'media'> = {},
|
||||||
|
): InputInlineResultPhoto {
|
||||||
|
const ret = params as tl.Mutable<InputInlineResultPhoto>
|
||||||
|
ret.id = id
|
||||||
|
ret.type = 'photo'
|
||||||
|
ret.media = media
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline result containing a sticker
|
||||||
|
*
|
||||||
|
* @param id Inline result ID
|
||||||
|
* @param media Sticker
|
||||||
|
*/
|
||||||
|
export function sticker(id: string, media: string | tl.RawInputDocument): InputInlineResultSticker {
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
type: 'sticker',
|
||||||
|
media,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline result containing a document
|
||||||
|
* (only PDF and ZIP are supported when using URL)
|
||||||
|
*
|
||||||
|
* @param id Inline result ID
|
||||||
|
* @param media Document
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function file(
|
||||||
|
id: string,
|
||||||
|
media: string | tl.RawInputWebDocument | tl.RawInputDocument,
|
||||||
|
params: Omit<InputInlineResultFile, 'type' | 'id' | 'media'>,
|
||||||
|
): InputInlineResultFile {
|
||||||
|
const ret = params as tl.Mutable<InputInlineResultFile>
|
||||||
|
ret.id = id
|
||||||
|
ret.type = 'file'
|
||||||
|
ret.media = media
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline result containing a geolocation
|
||||||
|
*
|
||||||
|
* @param id Inline result ID
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function geo(id: string, params: Omit<InputInlineResultGeo, 'type' | 'id'>): InputInlineResultGeo {
|
||||||
|
const ret = params as tl.Mutable<InputInlineResultGeo>
|
||||||
|
ret.id = id
|
||||||
|
ret.type = 'geo'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline result containing a venue
|
||||||
|
*
|
||||||
|
* @param id Inline result ID
|
||||||
|
* @param params Venue parameters
|
||||||
|
*/
|
||||||
|
export function venue(id: string, params: Omit<InputInlineResultVenue, 'type' | 'id'>): InputInlineResultVenue {
|
||||||
|
const ret = params as tl.Mutable<InputInlineResultVenue>
|
||||||
|
ret.id = id
|
||||||
|
ret.type = 'venue'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline result containing a contact
|
||||||
|
*
|
||||||
|
* @param id Inline result ID
|
||||||
|
* @param params Contact parameters
|
||||||
|
*/
|
||||||
|
export function contact(id: string, params: Omit<InputInlineResultContact, 'type' | 'id'>): InputInlineResultContact {
|
||||||
|
const ret = params as tl.Mutable<InputInlineResultContact>
|
||||||
|
ret.id = id
|
||||||
|
ret.type = 'contact'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline result containing a game
|
||||||
|
*
|
||||||
|
* @param id Inline result ID
|
||||||
|
* @param shortName Short name of the game
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function game(
|
||||||
|
id: string,
|
||||||
|
shortName: string,
|
||||||
|
params: Omit<InputInlineResultGame, 'type' | 'id' | 'shortName'> = {},
|
||||||
|
): InputInlineResultGame {
|
||||||
|
const ret = params as tl.Mutable<InputInlineResultGame>
|
||||||
|
ret.id = id
|
||||||
|
ret.type = 'game'
|
||||||
|
ret.shortName = shortName
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export async function _convertToTl(
|
||||||
|
client: ITelegramClient,
|
||||||
|
results: InputInlineResult[],
|
||||||
|
): Promise<[boolean, tl.TypeInputBotInlineResult[]]> {
|
||||||
|
const normalizeThumb = (obj: InputInlineResult, fallback?: string): tl.RawInputWebDocument | undefined => {
|
||||||
|
if (obj.type !== 'voice' && obj.type !== 'audio' && obj.type !== 'sticker' && obj.type !== 'game') {
|
||||||
|
if (!obj.thumb || typeof obj.thumb === 'string') {
|
||||||
|
if (!obj.thumb && !fallback) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
_: 'inputWebDocument',
|
||||||
|
size: 0,
|
||||||
|
url: obj.thumb || fallback!,
|
||||||
|
mimeType: obj.type === 'gif' ? obj.thumbMime ?? obj.mime ?? 'video/mp4' : 'image/jpeg',
|
||||||
|
attributes: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj.thumb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const items: tl.TypeInputBotInlineResult[] = []
|
||||||
|
|
||||||
|
let isGallery = false
|
||||||
|
let forceVertical = false
|
||||||
|
|
||||||
|
for (const obj of results) {
|
||||||
|
switch (obj.type) {
|
||||||
|
case 'article': {
|
||||||
|
forceVertical = true
|
||||||
|
|
||||||
|
let sendMessage: tl.TypeInputBotInlineMessage
|
||||||
|
|
||||||
|
if (obj.message) {
|
||||||
|
sendMessage = await BotInlineMessage._convertToTl(client, obj.message)
|
||||||
|
} 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
_: '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' ? normalizeThumb(obj) : obj.thumb,
|
||||||
|
sendMessage,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case 'game': {
|
||||||
|
let sendMessage: tl.TypeInputBotInlineMessage
|
||||||
|
|
||||||
|
if (obj.message) {
|
||||||
|
sendMessage = await BotInlineMessage._convertToTl(client, obj.message)
|
||||||
|
|
||||||
|
if (sendMessage._ !== 'inputBotInlineMessageGame') {
|
||||||
|
throw new MtArgumentError('game inline result must contain a game inline message')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendMessage = {
|
||||||
|
_: 'inputBotInlineMessageGame',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
_: 'inputBotInlineResultGame',
|
||||||
|
id: obj.id,
|
||||||
|
shortName: obj.shortName,
|
||||||
|
sendMessage,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case 'gif':
|
||||||
|
case 'photo':
|
||||||
|
case 'sticker':
|
||||||
|
isGallery = true
|
||||||
|
break
|
||||||
|
case 'audio':
|
||||||
|
case 'contact':
|
||||||
|
case 'voice':
|
||||||
|
forceVertical = true
|
||||||
|
}
|
||||||
|
|
||||||
|
let sendMessage: tl.TypeInputBotInlineMessage
|
||||||
|
|
||||||
|
if (obj.message) {
|
||||||
|
sendMessage = await BotInlineMessage._convertToTl(client, obj.message)
|
||||||
|
} else if (obj.type === 'venue') {
|
||||||
|
if (obj.latitude && obj.longitude) {
|
||||||
|
sendMessage = {
|
||||||
|
_: 'inputBotInlineMessageMediaVenue',
|
||||||
|
title: obj.title,
|
||||||
|
address: obj.address,
|
||||||
|
geoPoint: {
|
||||||
|
_: 'inputGeoPoint',
|
||||||
|
lat: obj.latitude,
|
||||||
|
long: obj.longitude,
|
||||||
|
},
|
||||||
|
provider: '',
|
||||||
|
venueId: '',
|
||||||
|
venueType: '',
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new MtArgumentError('message or location (lat&lon) bust be supplied for venue inline result')
|
||||||
|
}
|
||||||
|
} else if (obj.type === 'video' && obj.isEmbed && typeof obj.media === 'string') {
|
||||||
|
sendMessage = {
|
||||||
|
_: 'inputBotInlineMessageText',
|
||||||
|
message: obj.media,
|
||||||
|
}
|
||||||
|
} else if (obj.type === 'geo') {
|
||||||
|
sendMessage = {
|
||||||
|
_: 'inputBotInlineMessageMediaGeo',
|
||||||
|
geoPoint: {
|
||||||
|
_: 'inputGeoPoint',
|
||||||
|
lat: obj.latitude,
|
||||||
|
long: obj.longitude,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if (obj.type === 'contact') {
|
||||||
|
sendMessage = {
|
||||||
|
_: 'inputBotInlineMessageMediaContact',
|
||||||
|
phoneNumber: obj.phone,
|
||||||
|
firstName: obj.firstName,
|
||||||
|
lastName: obj.lastName ?? '',
|
||||||
|
vcard: '',
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendMessage = {
|
||||||
|
_: 'inputBotInlineMessageMediaAuto',
|
||||||
|
message: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let media: tl.TypeInputWebDocument | tl.TypeInputDocument | tl.TypeInputPhoto | undefined = undefined
|
||||||
|
|
||||||
|
if (obj.type !== 'geo' && obj.type !== 'venue' && obj.type !== 'contact') {
|
||||||
|
if (typeof obj.media === 'string') {
|
||||||
|
// file id or url
|
||||||
|
if (obj.media.match(/^https?:\/\//)) {
|
||||||
|
if (obj.type === 'sticker') {
|
||||||
|
throw new MtArgumentError('sticker inline result cannot contain a URL')
|
||||||
|
}
|
||||||
|
|
||||||
|
let mime: string
|
||||||
|
if (obj.type === 'video') mime = 'video/mp4'
|
||||||
|
else if (obj.type === 'audio') {
|
||||||
|
mime = obj.mime ?? 'audio/mpeg'
|
||||||
|
} else if (obj.type === 'gif') {
|
||||||
|
mime = obj.mime ?? 'video/mp4'
|
||||||
|
} else if (obj.type === 'voice') mime = 'audio/ogg'
|
||||||
|
else if (obj.type === 'file') {
|
||||||
|
if (!obj.mime) {
|
||||||
|
throw new MtArgumentError('MIME type must be specified for file inline result')
|
||||||
|
}
|
||||||
|
|
||||||
|
mime = obj.mime
|
||||||
|
} else mime = 'image/jpeg'
|
||||||
|
|
||||||
|
const attributes: tl.TypeDocumentAttribute[] = []
|
||||||
|
|
||||||
|
if (
|
||||||
|
(obj.type === 'video' || obj.type === 'gif' || obj.type === 'photo') &&
|
||||||
|
obj.width &&
|
||||||
|
obj.height
|
||||||
|
) {
|
||||||
|
if (obj.type !== 'photo' && obj.duration) {
|
||||||
|
attributes.push({
|
||||||
|
_: 'documentAttributeVideo',
|
||||||
|
w: obj.width,
|
||||||
|
h: obj.height,
|
||||||
|
duration: obj.duration,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
attributes.push({
|
||||||
|
_: 'documentAttributeImageSize',
|
||||||
|
w: obj.width,
|
||||||
|
h: obj.height,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (obj.type === 'audio' || obj.type === 'voice') {
|
||||||
|
attributes.push({
|
||||||
|
_: 'documentAttributeAudio',
|
||||||
|
voice: obj.type === 'voice',
|
||||||
|
duration: obj.duration ?? 0,
|
||||||
|
title: obj.type === 'audio' ? obj.title : '',
|
||||||
|
performer: obj.type === 'audio' ? obj.performer : '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes.push({
|
||||||
|
_: 'documentAttributeFilename',
|
||||||
|
fileName: extractFileName(obj.media),
|
||||||
|
})
|
||||||
|
|
||||||
|
media = {
|
||||||
|
_: 'inputWebDocument',
|
||||||
|
url: obj.media,
|
||||||
|
mimeType: mime,
|
||||||
|
size: 0,
|
||||||
|
attributes,
|
||||||
|
}
|
||||||
|
} else if (obj.type === 'photo') {
|
||||||
|
media = fileIdToInputPhoto(obj.media)
|
||||||
|
} else {
|
||||||
|
media = fileIdToInputDocument(obj.media)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
media = obj.media
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let title: string | undefined = undefined
|
||||||
|
let description: string | undefined = undefined
|
||||||
|
|
||||||
|
// incredible hacks by durov team.
|
||||||
|
// i honestly don't understand why didn't they just
|
||||||
|
// make a bunch of types, as they normally do,
|
||||||
|
// but whatever.
|
||||||
|
// ref: https://github.com/tdlib/td/blob/master/td/telegram/InlineQueriesManager.cpp
|
||||||
|
if (obj.type === 'contact') {
|
||||||
|
title = obj.lastName?.length ? `${obj.firstName} ${obj.lastName}` : obj.firstName
|
||||||
|
} else if (obj.type !== 'sticker') {
|
||||||
|
title = obj.title
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.type === 'audio') {
|
||||||
|
description = obj.performer
|
||||||
|
} else if (obj.type === 'geo') {
|
||||||
|
description = `${obj.latitude} ${obj.longitude}`
|
||||||
|
} else if (obj.type === 'venue') {
|
||||||
|
description = obj.address
|
||||||
|
} else if (obj.type === 'contact') {
|
||||||
|
description = obj.phone
|
||||||
|
} else if (obj.type !== 'voice' && obj.type !== 'sticker') {
|
||||||
|
description = obj.description
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!media || media._ === 'inputWebDocument') {
|
||||||
|
items.push({
|
||||||
|
_: 'inputBotInlineResult',
|
||||||
|
id: obj.id,
|
||||||
|
type: obj.type,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
content: media,
|
||||||
|
thumb: normalizeThumb(obj, media?.url),
|
||||||
|
sendMessage,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (media._ === 'inputPhoto') {
|
||||||
|
items.push({
|
||||||
|
_: 'inputBotInlineResultPhoto',
|
||||||
|
id: obj.id,
|
||||||
|
type: obj.type,
|
||||||
|
photo: media,
|
||||||
|
sendMessage,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
_: 'inputBotInlineResultDocument',
|
||||||
|
id: obj.id,
|
||||||
|
type: obj.type,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
document: media,
|
||||||
|
sendMessage,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return [isGallery && !forceVertical, items]
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
import * as BotInline from './factories.js'
|
||||||
|
|
||||||
|
export * from './types.js'
|
||||||
|
export { BotInline }
|
501
packages/core/src/highlevel/types/bots/inline-result/types.ts
Normal file
501
packages/core/src/highlevel/types/bots/inline-result/types.ts
Normal file
|
@ -0,0 +1,501 @@
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
import { InputInlineMessage } from '../inline-message/types.js'
|
||||||
|
|
||||||
|
export 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline result containing an 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
|
||||||
|
*
|
||||||
|
* @default `false`
|
||||||
|
*/
|
||||||
|
hideUrl?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Article thumbnail URL (must be jpeg).
|
||||||
|
*/
|
||||||
|
thumb?: string | tl.RawInputWebDocument
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline result containing an animation (silent mp4 or gif).
|
||||||
|
*
|
||||||
|
* If `message` is not provided, {@link InputInlineMessageMedia} is used
|
||||||
|
* with empty caption
|
||||||
|
*/
|
||||||
|
export interface InputInlineResultGif extends BaseInputInlineResult {
|
||||||
|
type: 'gif'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The animation itself.
|
||||||
|
*
|
||||||
|
* Can be a URL, a TDLib and Bot API compatible File ID,
|
||||||
|
* or a TL object representing either of them.
|
||||||
|
*/
|
||||||
|
media: string | tl.RawInputWebDocument | tl.RawInputDocument
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Media MIME type, only applicable to URLs.
|
||||||
|
*
|
||||||
|
* Usually unnecessary, since Telegram automatically infers it.
|
||||||
|
*
|
||||||
|
* @default `video/mp4`
|
||||||
|
*/
|
||||||
|
mime?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title of the result
|
||||||
|
*/
|
||||||
|
title?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title of the result
|
||||||
|
*/
|
||||||
|
description?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animation thumbnail URL, only applicable in case `media` is a URL
|
||||||
|
*
|
||||||
|
* @default `media`
|
||||||
|
*/
|
||||||
|
thumb?: string | tl.RawInputWebDocument
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thumbnail MIME type
|
||||||
|
*
|
||||||
|
* @default `image/jpeg`
|
||||||
|
*/
|
||||||
|
thumbMime?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Width of the animation in pixels
|
||||||
|
*/
|
||||||
|
width?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Height of the animation in pixels
|
||||||
|
*/
|
||||||
|
height?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duration of the animation in seconds
|
||||||
|
*/
|
||||||
|
duration?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline result containing a video (only MP4)
|
||||||
|
*
|
||||||
|
* If `message` is not provided, {@link InputInlineMessageMedia} is used
|
||||||
|
* with empty caption for non-embed videos, {@link InputInlineMessageText}
|
||||||
|
* is used with text containing the URL for embed videos.
|
||||||
|
*/
|
||||||
|
export interface InputInlineResultVideo extends BaseInputInlineResult {
|
||||||
|
type: 'video'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The video itself, or a page containing an embedded video
|
||||||
|
*
|
||||||
|
* Can be a URL, a TDLib and Bot API compatible File ID,
|
||||||
|
* or a TL object representing either of them.
|
||||||
|
*/
|
||||||
|
media: string | tl.RawInputWebDocument | tl.RawInputDocument
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In case `media` is a URL, whether that URL is a link
|
||||||
|
* to an embedded video player.
|
||||||
|
*/
|
||||||
|
isEmbed?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title of the result
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description of the result
|
||||||
|
*/
|
||||||
|
description?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Video thumbnail URL (must be jpeg), only applicable in case `media` is a URL.
|
||||||
|
*
|
||||||
|
* Must be provided explicitly if this is a video loaded by URL.
|
||||||
|
*
|
||||||
|
* @default `media`
|
||||||
|
*/
|
||||||
|
thumb?: string | tl.RawInputWebDocument
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Width of the video in pixels
|
||||||
|
*/
|
||||||
|
width?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Height of the video in pixels
|
||||||
|
*/
|
||||||
|
height?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duration of the video in seconds
|
||||||
|
*/
|
||||||
|
duration?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline result containing an audio file
|
||||||
|
*
|
||||||
|
* If `message` is not provided, {@link InputInlineMessageMedia} is used
|
||||||
|
* with empty caption.
|
||||||
|
*/
|
||||||
|
export interface InputInlineResultAudio extends BaseInputInlineResult {
|
||||||
|
type: 'audio'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The audio itself
|
||||||
|
*
|
||||||
|
* Can be a URL, a TDLib and Bot API compatible File ID,
|
||||||
|
* or a TL object representing either of them.
|
||||||
|
*/
|
||||||
|
media: string | tl.RawInputWebDocument | tl.RawInputDocument
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MIME type of the audio file
|
||||||
|
*
|
||||||
|
* Usually unnecessary, since Telegram infers it automatically.
|
||||||
|
*
|
||||||
|
* @default `audio/mpeg`
|
||||||
|
*/
|
||||||
|
mime?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title of the audio track
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performer of the audio track
|
||||||
|
*/
|
||||||
|
performer?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duration of the audio in seconds
|
||||||
|
*/
|
||||||
|
duration?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline result containing a voice note
|
||||||
|
*
|
||||||
|
* If `message` is not provided, {@link InputInlineMessageMedia} is used
|
||||||
|
* with empty caption.
|
||||||
|
*/
|
||||||
|
export interface InputInlineResultVoice extends BaseInputInlineResult {
|
||||||
|
type: 'voice'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The voice itself (.ogg, preferably encoded with OPUS)
|
||||||
|
*
|
||||||
|
* Can be a URL, a TDLib and Bot API compatible File ID,
|
||||||
|
* or a TL object representing either of them.
|
||||||
|
*/
|
||||||
|
media: string | tl.RawInputWebDocument | tl.RawInputDocument
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title of the result
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duration of the voice note in seconds
|
||||||
|
*/
|
||||||
|
duration?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline result containing a photo
|
||||||
|
*
|
||||||
|
* If `message` is not provided, {@link InputInlineMessageMedia} is used
|
||||||
|
* with empty caption.
|
||||||
|
*/
|
||||||
|
export interface InputInlineResultPhoto extends BaseInputInlineResult {
|
||||||
|
type: 'photo'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The photo itself
|
||||||
|
*
|
||||||
|
* Can be a URL, a TDLib and Bot API compatible File ID,
|
||||||
|
* or a TL object representing either of them.
|
||||||
|
*/
|
||||||
|
media: string | tl.RawInputWebDocument | tl.RawInputPhoto
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title of the result
|
||||||
|
*/
|
||||||
|
title?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description of the result
|
||||||
|
*/
|
||||||
|
description?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Width of the photo in pixels
|
||||||
|
*/
|
||||||
|
width?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Height of the photo in pixels
|
||||||
|
*/
|
||||||
|
height?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Photo thumbnail URL (must be jpeg), only applicable in case `media` is a URL
|
||||||
|
*
|
||||||
|
* @default `media`
|
||||||
|
*/
|
||||||
|
thumb?: string | tl.RawInputWebDocument
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline result containing a sticker
|
||||||
|
*
|
||||||
|
* If `message` is not provided, {@link InputInlineMessageMedia} is used.
|
||||||
|
*/
|
||||||
|
export interface InputInlineResultSticker extends BaseInputInlineResult {
|
||||||
|
type: 'sticker'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The sticker itself. Can't be a URL.
|
||||||
|
*/
|
||||||
|
media: string | tl.RawInputDocument
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline result containing a document
|
||||||
|
*
|
||||||
|
* If `message` is not provided, {@link InputInlineMessageMedia} is used
|
||||||
|
* with empty caption.
|
||||||
|
*/
|
||||||
|
export interface InputInlineResultFile extends BaseInputInlineResult {
|
||||||
|
type: 'file'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file itself. When using URL, only PDF and ZIP are supported.
|
||||||
|
*
|
||||||
|
* Can be a URL, a TDLib and Bot API compatible File ID,
|
||||||
|
* or a TL object representing either of them.
|
||||||
|
*/
|
||||||
|
media: string | tl.RawInputWebDocument | tl.RawInputDocument
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MIME type of the file.
|
||||||
|
*
|
||||||
|
* Due to some Telegram limitation, you can only send
|
||||||
|
* PDF and ZIP files from URL
|
||||||
|
* (`application/pdf` and `application/zip` MIMEs respectively).
|
||||||
|
*
|
||||||
|
* Must be provided if `media` is a URL
|
||||||
|
*/
|
||||||
|
mime?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title of the result
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description of the result
|
||||||
|
*/
|
||||||
|
description?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Photo thumbnail URL (must be jpeg), only applicable in case `media` is a URL
|
||||||
|
*
|
||||||
|
* @default `media`
|
||||||
|
*/
|
||||||
|
thumb?: string | tl.RawInputWebDocument
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline result containing a geolocation.
|
||||||
|
*
|
||||||
|
* If `message` is not passed, a {@link InputInlineMessageGeo} is
|
||||||
|
* used, with the `latitude` and `longitude` parameters set
|
||||||
|
* accordingly
|
||||||
|
*/
|
||||||
|
export interface InputInlineResultGeo extends BaseInputInlineResult {
|
||||||
|
type: 'geo'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title of the result
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Latitude of the geolocation
|
||||||
|
*/
|
||||||
|
latitude: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Longitude of the geolocation
|
||||||
|
*/
|
||||||
|
longitude: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Location thumbnail URL (must be jpeg).
|
||||||
|
*
|
||||||
|
* By default, Telegram generates one based on
|
||||||
|
* the location set by `latitude` and `longitude`
|
||||||
|
*/
|
||||||
|
thumb?: string | tl.RawInputWebDocument
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline result containing a venue.
|
||||||
|
*
|
||||||
|
* If `message` is not passed, {@link BotInlineMessage.venue} is used with
|
||||||
|
* given `latitude` and `longitude` were passed.
|
||||||
|
* If they weren't passed either, an error is thrown.
|
||||||
|
*/
|
||||||
|
export interface InputInlineResultVenue extends BaseInputInlineResult {
|
||||||
|
type: 'venue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title of the venue
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address of the venue
|
||||||
|
*/
|
||||||
|
address: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Latitude of the geolocation
|
||||||
|
*/
|
||||||
|
latitude?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Longitude of the geolocation
|
||||||
|
*/
|
||||||
|
longitude?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Venue thumbnail URL (must be jpeg).
|
||||||
|
*
|
||||||
|
* By default, Telegram generates one based on
|
||||||
|
* the location in the `message`
|
||||||
|
*/
|
||||||
|
thumb?: string | tl.RawInputWebDocument
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline result containing a game.
|
||||||
|
*
|
||||||
|
* If `message` is not passed, {@link InputInlineMessageGame} is used.
|
||||||
|
*
|
||||||
|
* Note that `message` can only be {@link InputInlineMessageGame}
|
||||||
|
*/
|
||||||
|
export interface InputInlineResultGame extends BaseInputInlineResult {
|
||||||
|
type: 'game'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Short name of the game
|
||||||
|
*/
|
||||||
|
shortName: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline result containing a contact.
|
||||||
|
*
|
||||||
|
* If `message` is not passed, {@link InputInlineMessageContact} is used.
|
||||||
|
*/
|
||||||
|
export interface InputInlineResultContact extends BaseInputInlineResult {
|
||||||
|
type: 'contact'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First name of the contact
|
||||||
|
*/
|
||||||
|
firstName: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last name of the contact
|
||||||
|
*/
|
||||||
|
lastName?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phone number of the contact
|
||||||
|
*/
|
||||||
|
phone: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contact thumbnail URL (i.e. their avatar) (must be jpeg)
|
||||||
|
*/
|
||||||
|
thumb?: string | tl.RawInputWebDocument
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InputInlineResult =
|
||||||
|
| InputInlineResultArticle
|
||||||
|
| InputInlineResultGif
|
||||||
|
| InputInlineResultVideo
|
||||||
|
| InputInlineResultAudio
|
||||||
|
| InputInlineResultVoice
|
||||||
|
| InputInlineResultPhoto
|
||||||
|
| InputInlineResultSticker
|
||||||
|
| InputInlineResultFile
|
||||||
|
| InputInlineResultGeo
|
||||||
|
| InputInlineResultVenue
|
||||||
|
| InputInlineResultGame
|
||||||
|
| InputInlineResultContact
|
|
@ -1,2 +0,0 @@
|
||||||
export * from './input-inline-message.js'
|
|
||||||
export * from './input-inline-result.js'
|
|
|
@ -1,340 +0,0 @@
|
||||||
import { tl } from '@mtcute/tl'
|
|
||||||
|
|
||||||
import { assertNever } from '../../../../types/utils.js'
|
|
||||||
import { ITelegramClient } from '../../../client.types.js'
|
|
||||||
import { _normalizeInputText } from '../../../methods/misc/normalize-text.js'
|
|
||||||
import { InputText } from '../../../types/misc/entities.js'
|
|
||||||
import {
|
|
||||||
InputMediaContact,
|
|
||||||
InputMediaGeo,
|
|
||||||
InputMediaGeoLive,
|
|
||||||
InputMediaVenue,
|
|
||||||
InputMediaWebpage,
|
|
||||||
} from '../../media/index.js'
|
|
||||||
import { BotKeyboard, ReplyMarkup } from '../keyboards.js'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inline message containing only text
|
|
||||||
*/
|
|
||||||
export interface InputInlineMessageText {
|
|
||||||
type: 'text'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Text of the message
|
|
||||||
*/
|
|
||||||
text: InputText
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message reply markup
|
|
||||||
*/
|
|
||||||
replyMarkup?: ReplyMarkup
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to disable links preview in this message
|
|
||||||
*/
|
|
||||||
disableWebPreview?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to invert media position.
|
|
||||||
*
|
|
||||||
* Currently only supported for web previews and makes the
|
|
||||||
* client render the preview above the caption and not below.
|
|
||||||
*/
|
|
||||||
invertMedia?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inline message containing media, which is automatically
|
|
||||||
* inferred from the result itself.
|
|
||||||
*/
|
|
||||||
export interface InputInlineMessageMedia {
|
|
||||||
type: 'media'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Caption for the media
|
|
||||||
*/
|
|
||||||
text?: InputText
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message reply markup
|
|
||||||
*/
|
|
||||||
replyMarkup?: ReplyMarkup
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to invert media position.
|
|
||||||
*
|
|
||||||
* Currently only supported for web previews and makes the
|
|
||||||
* client render the preview above the caption and not below.
|
|
||||||
*/
|
|
||||||
invertMedia?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inline message containing a geolocation
|
|
||||||
*/
|
|
||||||
export interface InputInlineMessageGeo extends InputMediaGeo {
|
|
||||||
/**
|
|
||||||
* Message's reply markup
|
|
||||||
*/
|
|
||||||
replyMarkup?: ReplyMarkup
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inline message containing a live geolocation
|
|
||||||
*/
|
|
||||||
export interface InputInlineMessageGeoLive extends InputMediaGeoLive {
|
|
||||||
/**
|
|
||||||
* Message's reply markup
|
|
||||||
*/
|
|
||||||
replyMarkup?: ReplyMarkup
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inline message containing a venue
|
|
||||||
*/
|
|
||||||
export interface InputInlineMessageVenue extends InputMediaVenue {
|
|
||||||
/**
|
|
||||||
* Message's reply markup
|
|
||||||
*/
|
|
||||||
replyMarkup?: ReplyMarkup
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inline message containing a game
|
|
||||||
*/
|
|
||||||
export interface InputInlineMessageGame {
|
|
||||||
type: 'game'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message's reply markup
|
|
||||||
*/
|
|
||||||
replyMarkup?: ReplyMarkup
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inline message containing a contact
|
|
||||||
*/
|
|
||||||
export interface InputInlineMessageContact extends InputMediaContact {
|
|
||||||
/**
|
|
||||||
* Message's reply markup
|
|
||||||
*/
|
|
||||||
replyMarkup?: ReplyMarkup
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InputInlineMessageWebpage extends InputMediaWebpage {
|
|
||||||
/**
|
|
||||||
* Text of the message
|
|
||||||
*/
|
|
||||||
text: InputText
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message reply markup
|
|
||||||
*/
|
|
||||||
replyMarkup?: ReplyMarkup
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to invert media position.
|
|
||||||
*
|
|
||||||
* Currently only supported for web previews and makes the
|
|
||||||
* client render the preview above the caption and not below.
|
|
||||||
*/
|
|
||||||
invertMedia?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type InputInlineMessage =
|
|
||||||
| InputInlineMessageText
|
|
||||||
| InputInlineMessageMedia
|
|
||||||
| InputInlineMessageGeo
|
|
||||||
| InputInlineMessageGeoLive
|
|
||||||
| InputInlineMessageVenue
|
|
||||||
| InputInlineMessageGame
|
|
||||||
| InputInlineMessageContact
|
|
||||||
| InputInlineMessageWebpage
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
||||||
export namespace BotInlineMessage {
|
|
||||||
/**
|
|
||||||
* Create a text inline message
|
|
||||||
*
|
|
||||||
* @param text Message text
|
|
||||||
* @param params
|
|
||||||
*/
|
|
||||||
export function text(
|
|
||||||
text: InputText,
|
|
||||||
params: Omit<InputInlineMessageText, 'type' | 'text'> = {},
|
|
||||||
): InputInlineMessageText {
|
|
||||||
const ret = params as tl.Mutable<InputInlineMessageText>
|
|
||||||
ret.type = 'text'
|
|
||||||
ret.text = text
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an inline message containing
|
|
||||||
* media from the result
|
|
||||||
*/
|
|
||||||
export function media(params: Omit<InputInlineMessageMedia, 'type'> = {}): InputInlineMessageMedia {
|
|
||||||
const ret = params as tl.Mutable<InputInlineMessageMedia>
|
|
||||||
ret.type = 'media'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an inline message containing a geolocation
|
|
||||||
*
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function geo(params: Omit<InputInlineMessageGeo, 'type'>): InputInlineMessageGeo {
|
|
||||||
const ret = params as tl.Mutable<InputInlineMessageGeo>
|
|
||||||
ret.type = 'geo'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an inline message containing a live geolocation
|
|
||||||
*
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function geoLive(params: Omit<InputInlineMessageGeoLive, 'type'>): InputInlineMessageGeoLive {
|
|
||||||
const ret = params as tl.Mutable<InputInlineMessageGeoLive>
|
|
||||||
ret.type = 'geo_live'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an inline message containing a venue
|
|
||||||
*/
|
|
||||||
export function venue(params: Omit<InputInlineMessageVenue, 'type'>): InputInlineMessageVenue {
|
|
||||||
const ret = params as tl.Mutable<InputInlineMessageVenue>
|
|
||||||
ret.type = 'venue'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an inline message containing a game
|
|
||||||
* from the inline result
|
|
||||||
*/
|
|
||||||
export function game(params: Omit<InputInlineMessageGame, 'type'>): InputInlineMessageGame {
|
|
||||||
const ret = params as tl.Mutable<InputInlineMessageGame>
|
|
||||||
ret.type = 'game'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an inline message containing a contact
|
|
||||||
*/
|
|
||||||
export function contact(params: Omit<InputInlineMessageContact, 'type'>): InputInlineMessageContact {
|
|
||||||
const ret = params as tl.Mutable<InputInlineMessageContact>
|
|
||||||
ret.type = 'contact'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an inline message containing a webpage
|
|
||||||
*/
|
|
||||||
export function webpage(params: Omit<InputInlineMessageWebpage, 'type'>): InputInlineMessageWebpage {
|
|
||||||
const ret = params as tl.Mutable<InputInlineMessageWebpage>
|
|
||||||
ret.type = 'webpage'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export async function _convertToTl(
|
|
||||||
client: ITelegramClient,
|
|
||||||
obj: InputInlineMessage,
|
|
||||||
): Promise<tl.TypeInputBotInlineMessage> {
|
|
||||||
switch (obj.type) {
|
|
||||||
case 'text': {
|
|
||||||
const [message, entities] = await _normalizeInputText(client, obj.text)
|
|
||||||
|
|
||||||
return {
|
|
||||||
_: 'inputBotInlineMessageText',
|
|
||||||
message,
|
|
||||||
entities,
|
|
||||||
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
|
||||||
invertMedia: obj.invertMedia,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'media': {
|
|
||||||
const [message, entities] = await _normalizeInputText(client, obj.text)
|
|
||||||
|
|
||||||
return {
|
|
||||||
_: 'inputBotInlineMessageMediaAuto',
|
|
||||||
message,
|
|
||||||
entities,
|
|
||||||
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
|
||||||
invertMedia: obj.invertMedia,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'geo':
|
|
||||||
case 'geo_live':
|
|
||||||
return {
|
|
||||||
_: 'inputBotInlineMessageMediaGeo',
|
|
||||||
geoPoint: {
|
|
||||||
_: 'inputGeoPoint',
|
|
||||||
lat: obj.latitude,
|
|
||||||
long: obj.longitude,
|
|
||||||
},
|
|
||||||
// fields will be `undefined` if this is a `geo`
|
|
||||||
heading: (obj as InputMediaGeoLive).heading,
|
|
||||||
period: (obj as InputMediaGeoLive).period,
|
|
||||||
proximityNotificationRadius: (obj as InputMediaGeoLive).proximityNotificationRadius,
|
|
||||||
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
|
||||||
}
|
|
||||||
case 'venue':
|
|
||||||
return {
|
|
||||||
_: 'inputBotInlineMessageMediaVenue',
|
|
||||||
geoPoint: {
|
|
||||||
_: 'inputGeoPoint',
|
|
||||||
lat: obj.latitude,
|
|
||||||
long: obj.longitude,
|
|
||||||
},
|
|
||||||
title: obj.title,
|
|
||||||
address: obj.address,
|
|
||||||
provider: obj.source?.provider ?? '',
|
|
||||||
venueId: obj.source?.id ?? '',
|
|
||||||
venueType: obj.source?.type ?? '',
|
|
||||||
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
|
||||||
}
|
|
||||||
case 'game':
|
|
||||||
return {
|
|
||||||
_: 'inputBotInlineMessageGame',
|
|
||||||
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
|
||||||
}
|
|
||||||
case 'contact':
|
|
||||||
return {
|
|
||||||
_: 'inputBotInlineMessageMediaContact',
|
|
||||||
phoneNumber: obj.phone,
|
|
||||||
firstName: obj.firstName,
|
|
||||||
lastName: obj.lastName ?? '',
|
|
||||||
vcard: obj.vcard ?? '',
|
|
||||||
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
|
||||||
}
|
|
||||||
case 'webpage': {
|
|
||||||
const [message, entities] = await _normalizeInputText(client, obj.text)
|
|
||||||
|
|
||||||
return {
|
|
||||||
_: 'inputBotInlineMessageMediaWebPage',
|
|
||||||
message,
|
|
||||||
entities,
|
|
||||||
replyMarkup: BotKeyboard._convertToTl(obj.replyMarkup),
|
|
||||||
invertMedia: obj.invertMedia,
|
|
||||||
forceLargeMedia: obj.size === 'large',
|
|
||||||
forceSmallMedia: obj.size === 'small',
|
|
||||||
optional: !obj.required,
|
|
||||||
url: obj.url,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
assertNever(obj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,478 +0,0 @@
|
||||||
import { tl } from '@mtcute/tl'
|
|
||||||
|
|
||||||
import { getPlatform } from '../../../platform.js'
|
|
||||||
import { assertNever } from '../../../types/utils.js'
|
|
||||||
import { toInputUser } from '../../utils/peer-utils.js'
|
|
||||||
import { BotKeyboardBuilder } from './keyboard-builder.js'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reply keyboard markup
|
|
||||||
*/
|
|
||||||
export interface ReplyKeyboardMarkup extends Omit<tl.RawReplyKeyboardMarkup, '_' | 'rows'> {
|
|
||||||
readonly type: 'reply'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Two-dimensional array of buttons
|
|
||||||
*/
|
|
||||||
readonly buttons: tl.TypeKeyboardButton[][]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide previously sent bot keyboard
|
|
||||||
*/
|
|
||||||
export interface ReplyKeyboardHide extends Omit<tl.RawReplyKeyboardHide, '_'> {
|
|
||||||
readonly type: 'reply_hide'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Force the user to send a reply
|
|
||||||
*/
|
|
||||||
export interface ReplyKeyboardForceReply extends Omit<tl.RawReplyKeyboardForceReply, '_'> {
|
|
||||||
readonly type: 'force_reply'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inline keyboard markup
|
|
||||||
*/
|
|
||||||
export interface InlineKeyboardMarkup {
|
|
||||||
readonly type: 'inline'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Two-dimensional array of buttons
|
|
||||||
*/
|
|
||||||
readonly buttons: tl.TypeKeyboardButton[][]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ReplyMarkup =
|
|
||||||
| ReplyKeyboardMarkup
|
|
||||||
| ReplyKeyboardHide
|
|
||||||
| ReplyKeyboardForceReply
|
|
||||||
| InlineKeyboardMarkup
|
|
||||||
| tl.TypeReplyMarkup
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience methods wrapping TL
|
|
||||||
* objects creation for bot keyboard buttons.
|
|
||||||
*
|
|
||||||
* You can also use the type-discriminated objects directly.
|
|
||||||
*
|
|
||||||
* > **Note**: Button creation functions are intended to be used
|
|
||||||
* > with inline reply markup, unless stated otherwise
|
|
||||||
* > in the description.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
||||||
export namespace BotKeyboard {
|
|
||||||
/** Create a keyboard builder */
|
|
||||||
export function builder(maxRowWidth?: number | null): BotKeyboardBuilder {
|
|
||||||
return new BotKeyboardBuilder(maxRowWidth)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an inline keyboard markup
|
|
||||||
*
|
|
||||||
* @param buttons Two-dimensional array of buttons
|
|
||||||
*/
|
|
||||||
export function inline(buttons: tl.TypeKeyboardButton[][]): InlineKeyboardMarkup {
|
|
||||||
return {
|
|
||||||
type: 'inline',
|
|
||||||
buttons,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a reply keyboard markup
|
|
||||||
*
|
|
||||||
* @param buttons Two-dimensional array of buttons
|
|
||||||
* @param params Additional parameters for the keyboard
|
|
||||||
*/
|
|
||||||
export function reply(
|
|
||||||
buttons: tl.TypeKeyboardButton[][],
|
|
||||||
params: Omit<ReplyKeyboardMarkup, 'type' | 'buttons'> = {},
|
|
||||||
): ReplyKeyboardMarkup {
|
|
||||||
const ret = params as tl.Mutable<ReplyKeyboardMarkup>
|
|
||||||
ret.type = 'reply'
|
|
||||||
ret.buttons = buttons
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide the previously sent reply keyboard
|
|
||||||
*
|
|
||||||
* @param selective
|
|
||||||
* Whether to remove the keyboard for specific users only. Targets:
|
|
||||||
* - users that are @mentioned in the text of the Message
|
|
||||||
* - in case this is a reply, sender of the original message
|
|
||||||
*/
|
|
||||||
export function hideReply(selective?: boolean): ReplyKeyboardHide {
|
|
||||||
return {
|
|
||||||
type: 'reply_hide',
|
|
||||||
selective,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Force the user to send a reply
|
|
||||||
*/
|
|
||||||
export function forceReply(params: Omit<ReplyKeyboardForceReply, 'type'> = {}): ReplyKeyboardForceReply {
|
|
||||||
const ret = params as tl.Mutable<ReplyKeyboardForceReply>
|
|
||||||
ret.type = 'force_reply'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a text-only keyboard button.
|
|
||||||
*
|
|
||||||
* Used for reply keyboards, not inline!
|
|
||||||
*
|
|
||||||
* @param text Button text
|
|
||||||
*/
|
|
||||||
export function text(text: string): tl.RawKeyboardButton {
|
|
||||||
return {
|
|
||||||
_: 'keyboardButton',
|
|
||||||
text,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a keyboard button requesting for user's contact.
|
|
||||||
* Available only for private chats.
|
|
||||||
*
|
|
||||||
* Used for reply keyboards, not inline!
|
|
||||||
*
|
|
||||||
* @param text Button text
|
|
||||||
*/
|
|
||||||
export function requestContact(text: string): tl.RawKeyboardButtonRequestPhone {
|
|
||||||
return {
|
|
||||||
_: 'keyboardButtonRequestPhone',
|
|
||||||
text,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a keyboard button requesting for user's geo location.
|
|
||||||
* Available only for private chats.
|
|
||||||
*
|
|
||||||
* Used for reply keyboards, not inline!
|
|
||||||
*
|
|
||||||
* @param text Button text
|
|
||||||
*/
|
|
||||||
export function requestGeo(text: string): tl.RawKeyboardButtonRequestGeoLocation {
|
|
||||||
return {
|
|
||||||
_: 'keyboardButtonRequestGeoLocation',
|
|
||||||
text,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a keyboard button requesting the user to create and send a poll.
|
|
||||||
* Available only for private chats.
|
|
||||||
*
|
|
||||||
* Used for reply keyboards, not inline!
|
|
||||||
*
|
|
||||||
* @param text Button text
|
|
||||||
* @param quiz If set, only quiz polls can be sent
|
|
||||||
*/
|
|
||||||
export function requestPoll(text: string, quiz?: boolean): tl.RawKeyboardButtonRequestPoll {
|
|
||||||
return {
|
|
||||||
_: 'keyboardButtonRequestPoll',
|
|
||||||
text,
|
|
||||||
quiz,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a keyboard button with a link.
|
|
||||||
*
|
|
||||||
* Used for inline keyboards, not reply!
|
|
||||||
*
|
|
||||||
* @param text Button text
|
|
||||||
* @param url URL
|
|
||||||
*/
|
|
||||||
export function url(text: string, url: string): tl.RawKeyboardButtonUrl {
|
|
||||||
return {
|
|
||||||
_: 'keyboardButtonUrl',
|
|
||||||
text,
|
|
||||||
url,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a keyboard button with a link.
|
|
||||||
*
|
|
||||||
* Used for inline keyboards, not reply!
|
|
||||||
*
|
|
||||||
* @param text Button text
|
|
||||||
* @param data Callback data (1-64 bytes). String will be converted to `Buffer`
|
|
||||||
* @param requiresPassword
|
|
||||||
* Whether the user should verify their identity by entering 2FA password.
|
|
||||||
* See more: {@link tl.RawKeyboardButtonCallback#requiresPassword}
|
|
||||||
*/
|
|
||||||
export function callback(
|
|
||||||
text: string,
|
|
||||||
data: string | Uint8Array,
|
|
||||||
requiresPassword?: boolean,
|
|
||||||
): tl.RawKeyboardButtonCallback {
|
|
||||||
return {
|
|
||||||
_: 'keyboardButtonCallback',
|
|
||||||
text,
|
|
||||||
requiresPassword,
|
|
||||||
data: typeof data === 'string' ? getPlatform().utf8Encode(data) : data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button to force a user to switch to inline mode.
|
|
||||||
*
|
|
||||||
* Pressing the button will prompt the user to select
|
|
||||||
* one of their chats, open that chat and insert the bot‘s
|
|
||||||
* username and the specified inline query (if any) in the input field.
|
|
||||||
*
|
|
||||||
* Used for inline keyboards, not reply!
|
|
||||||
*
|
|
||||||
* @param text Button text
|
|
||||||
* @param query Inline query (can be empty or omitted)
|
|
||||||
* @param currentChat
|
|
||||||
* If set, pressing the button will insert the bot's username
|
|
||||||
* and the specified inline query in the current chat's input field
|
|
||||||
*/
|
|
||||||
export function switchInline(text: string, query = '', currentChat?: boolean): tl.RawKeyboardButtonSwitchInline {
|
|
||||||
return {
|
|
||||||
_: 'keyboardButtonSwitchInline',
|
|
||||||
samePeer: currentChat,
|
|
||||||
text,
|
|
||||||
query,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button to start a game
|
|
||||||
*
|
|
||||||
* Used for inline keyboards, not reply!
|
|
||||||
*
|
|
||||||
* **Note**: This type of button must always be
|
|
||||||
* the first button in the first row. ID of the
|
|
||||||
* game is inferred from {@link InputMedia.game},
|
|
||||||
* thus this button should only be used with it.
|
|
||||||
*/
|
|
||||||
export function game(text: string): tl.RawKeyboardButtonGame {
|
|
||||||
return { _: 'keyboardButtonGame', text }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button to pay for a product.
|
|
||||||
*
|
|
||||||
* Used for inline keyboards, not reply!
|
|
||||||
*
|
|
||||||
* **Note**: This type of button must always be
|
|
||||||
* the first button in the first row. Related
|
|
||||||
* invoice is inferred from {@link InputMedia.invoice},
|
|
||||||
* thus this button should only be used with it.
|
|
||||||
*/
|
|
||||||
export function pay(text: string): tl.RawKeyboardButtonBuy {
|
|
||||||
return { _: 'keyboardButtonBuy', text }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button to authorize a user
|
|
||||||
*
|
|
||||||
* Used for inline keyboards, not reply!
|
|
||||||
*
|
|
||||||
* @param text Button label
|
|
||||||
* @param url Authorization URL (see {@link tl.RawInputKeyboardButtonUrlAuth})
|
|
||||||
* @param params
|
|
||||||
*/
|
|
||||||
export function urlAuth(
|
|
||||||
text: string,
|
|
||||||
url: string,
|
|
||||||
params: {
|
|
||||||
/**
|
|
||||||
* Button label when forwarded
|
|
||||||
*/
|
|
||||||
fwdText?: string
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to request the permission for
|
|
||||||
* your bot to send messages to the user
|
|
||||||
*/
|
|
||||||
requestWriteAccess?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bot, which will be used for user authorization.
|
|
||||||
* `url` domain must be the same as the domain linked
|
|
||||||
* with the bot.
|
|
||||||
*
|
|
||||||
* @default current bot
|
|
||||||
*/
|
|
||||||
bot?: tl.TypeInputUser
|
|
||||||
} = {},
|
|
||||||
): tl.RawInputKeyboardButtonUrlAuth {
|
|
||||||
return {
|
|
||||||
_: 'inputKeyboardButtonUrlAuth',
|
|
||||||
text,
|
|
||||||
url,
|
|
||||||
bot: params.bot ?? {
|
|
||||||
_: 'inputUserSelf',
|
|
||||||
},
|
|
||||||
fwdText: params.fwdText,
|
|
||||||
requestWriteAccess: params.requestWriteAccess,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button to open webview
|
|
||||||
*
|
|
||||||
* Used for both inline keyboards and reply ones
|
|
||||||
*
|
|
||||||
* @param text Button label
|
|
||||||
* @param url WebView URL
|
|
||||||
*/
|
|
||||||
export function webView(text: string, url: string): tl.RawKeyboardButtonWebView {
|
|
||||||
return {
|
|
||||||
_: 'keyboardButtonWebView',
|
|
||||||
text,
|
|
||||||
url,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button to open user profile
|
|
||||||
*
|
|
||||||
* @param text Text of the button
|
|
||||||
* @param user User to be opened (use {@link TelegramClient.resolvePeer})
|
|
||||||
*/
|
|
||||||
export function userProfile(text: string, user: tl.TypeInputPeer): tl.RawInputKeyboardButtonUserProfile {
|
|
||||||
return {
|
|
||||||
_: 'inputKeyboardButtonUserProfile',
|
|
||||||
text,
|
|
||||||
userId: toInputUser(user),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button to request a peer from the user
|
|
||||||
*
|
|
||||||
* @param text Text of the button
|
|
||||||
* @param buttonId ID of the button that will later be passed to the service message
|
|
||||||
*/
|
|
||||||
export function requestPeer(
|
|
||||||
text: string,
|
|
||||||
buttonId: number,
|
|
||||||
params: {
|
|
||||||
/**
|
|
||||||
* Peer type, along with filters
|
|
||||||
*/
|
|
||||||
peerType: tl.TypeRequestPeerType
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of peers to be selected
|
|
||||||
*
|
|
||||||
* @default 1
|
|
||||||
*/
|
|
||||||
count?: number
|
|
||||||
},
|
|
||||||
): tl.RawKeyboardButtonRequestPeer {
|
|
||||||
return {
|
|
||||||
_: 'keyboardButtonRequestPeer',
|
|
||||||
text,
|
|
||||||
buttonId,
|
|
||||||
peerType: params.peerType,
|
|
||||||
maxQuantity: params.count ?? 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a button in the keyboard by its text or by predicate
|
|
||||||
*
|
|
||||||
* @param buttons Two-dimensional array of buttons
|
|
||||||
* @param predicate Button text or predicate function
|
|
||||||
*/
|
|
||||||
export function findButton(
|
|
||||||
buttons: tl.TypeKeyboardButton[][],
|
|
||||||
predicate: string | ((btn: tl.TypeKeyboardButton) => boolean),
|
|
||||||
): tl.TypeKeyboardButton | null {
|
|
||||||
if (typeof predicate === 'string') {
|
|
||||||
const text = predicate
|
|
||||||
|
|
||||||
predicate = (btn) => {
|
|
||||||
return 'text' in btn && btn.text === text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const row of buttons) {
|
|
||||||
for (const btn of row) {
|
|
||||||
if (predicate(btn)) {
|
|
||||||
return btn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export function _rowsTo2d(rows: tl.RawKeyboardButtonRow[]): tl.TypeKeyboardButton[][] {
|
|
||||||
return rows.map((it) => it.buttons)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export function _2dToRows(arr: tl.TypeKeyboardButton[][], inline: boolean): tl.RawKeyboardButtonRow[] {
|
|
||||||
return arr.map((row) => {
|
|
||||||
if (!inline) {
|
|
||||||
// le cringe
|
|
||||||
row = row.map((btn) =>
|
|
||||||
btn._ === 'keyboardButtonWebView' ?
|
|
||||||
{
|
|
||||||
...btn,
|
|
||||||
_: 'keyboardButtonSimpleWebView',
|
|
||||||
} :
|
|
||||||
btn,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
_: 'keyboardButtonRow',
|
|
||||||
buttons: row,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export function _convertToTl(obj?: ReplyMarkup): tl.TypeReplyMarkup | undefined {
|
|
||||||
if (!obj) return obj
|
|
||||||
if (tl.isAnyReplyMarkup(obj)) return obj
|
|
||||||
|
|
||||||
switch (obj.type) {
|
|
||||||
case 'reply':
|
|
||||||
return {
|
|
||||||
_: 'replyKeyboardMarkup',
|
|
||||||
resize: obj.resize,
|
|
||||||
singleUse: obj.singleUse,
|
|
||||||
selective: obj.selective,
|
|
||||||
persistent: obj.persistent,
|
|
||||||
placeholder: obj.placeholder,
|
|
||||||
rows: _2dToRows(obj.buttons, false),
|
|
||||||
}
|
|
||||||
case 'reply_hide':
|
|
||||||
return {
|
|
||||||
_: 'replyKeyboardHide',
|
|
||||||
selective: obj.selective,
|
|
||||||
}
|
|
||||||
case 'force_reply':
|
|
||||||
return {
|
|
||||||
_: 'replyKeyboardForceReply',
|
|
||||||
singleUse: obj.singleUse,
|
|
||||||
selective: obj.selective,
|
|
||||||
placeholder: obj.placeholder,
|
|
||||||
}
|
|
||||||
case 'inline':
|
|
||||||
return {
|
|
||||||
_: 'replyInlineMarkup',
|
|
||||||
rows: _2dToRows(obj.buttons, true),
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
assertNever(obj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
import { BotKeyboardBuilder } from './keyboard-builder.js'
|
import { BotKeyboardBuilder } from './builder.js'
|
||||||
|
|
||||||
describe('BotKeyboardBuilder', () => {
|
describe('BotKeyboardBuilder', () => {
|
||||||
describe('#push', () => {
|
describe('#push', () => {
|
|
@ -1,6 +1,6 @@
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import type { InlineKeyboardMarkup, ReplyKeyboardMarkup } from './keyboards.js'
|
import type { InlineKeyboardMarkup, ReplyKeyboardMarkup } from './types.js'
|
||||||
|
|
||||||
export type ButtonLike = tl.TypeKeyboardButton | false | null | undefined | void
|
export type ButtonLike = tl.TypeKeyboardButton | false | null | undefined | void
|
||||||
|
|
427
packages/core/src/highlevel/types/bots/keyboards/factories.ts
Normal file
427
packages/core/src/highlevel/types/bots/keyboards/factories.ts
Normal file
|
@ -0,0 +1,427 @@
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
import { getPlatform } from '../../../../platform.js'
|
||||||
|
import { assertNever } from '../../../../types/utils.js'
|
||||||
|
import { toInputUser } from '../../../utils/peer-utils.js'
|
||||||
|
import { BotKeyboardBuilder } from './builder.js'
|
||||||
|
import {
|
||||||
|
InlineKeyboardMarkup,
|
||||||
|
ReplyKeyboardForceReply,
|
||||||
|
ReplyKeyboardHide,
|
||||||
|
ReplyKeyboardMarkup,
|
||||||
|
ReplyMarkup,
|
||||||
|
} from './types.js'
|
||||||
|
|
||||||
|
/** Create a keyboard builder */
|
||||||
|
export function builder(maxRowWidth?: number | null): BotKeyboardBuilder {
|
||||||
|
return new BotKeyboardBuilder(maxRowWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an inline keyboard markup
|
||||||
|
*
|
||||||
|
* @param buttons Two-dimensional array of buttons
|
||||||
|
*/
|
||||||
|
export function inline(buttons: tl.TypeKeyboardButton[][]): InlineKeyboardMarkup {
|
||||||
|
return {
|
||||||
|
type: 'inline',
|
||||||
|
buttons,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a reply keyboard markup
|
||||||
|
*
|
||||||
|
* @param buttons Two-dimensional array of buttons
|
||||||
|
* @param params Additional parameters for the keyboard
|
||||||
|
*/
|
||||||
|
export function reply(
|
||||||
|
buttons: tl.TypeKeyboardButton[][],
|
||||||
|
params: Omit<ReplyKeyboardMarkup, 'type' | 'buttons'> = {},
|
||||||
|
): ReplyKeyboardMarkup {
|
||||||
|
const ret = params as tl.Mutable<ReplyKeyboardMarkup>
|
||||||
|
ret.type = 'reply'
|
||||||
|
ret.buttons = buttons
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the previously sent reply keyboard
|
||||||
|
*
|
||||||
|
* @param selective
|
||||||
|
* Whether to remove the keyboard for specific users only. Targets:
|
||||||
|
* - users that are @mentioned in the text of the Message
|
||||||
|
* - in case this is a reply, sender of the original message
|
||||||
|
*/
|
||||||
|
export function hideReply(selective?: boolean): ReplyKeyboardHide {
|
||||||
|
return {
|
||||||
|
type: 'reply_hide',
|
||||||
|
selective,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force the user to send a reply
|
||||||
|
*/
|
||||||
|
export function forceReply(params: Omit<ReplyKeyboardForceReply, 'type'> = {}): ReplyKeyboardForceReply {
|
||||||
|
const ret = params as tl.Mutable<ReplyKeyboardForceReply>
|
||||||
|
ret.type = 'force_reply'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a text-only keyboard button.
|
||||||
|
*
|
||||||
|
* Used for reply keyboards, not inline!
|
||||||
|
*
|
||||||
|
* @param text Button text
|
||||||
|
*/
|
||||||
|
export function text(text: string): tl.RawKeyboardButton {
|
||||||
|
return {
|
||||||
|
_: 'keyboardButton',
|
||||||
|
text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a keyboard button requesting for user's contact.
|
||||||
|
* Available only for private chats.
|
||||||
|
*
|
||||||
|
* Used for reply keyboards, not inline!
|
||||||
|
*
|
||||||
|
* @param text Button text
|
||||||
|
*/
|
||||||
|
export function requestContact(text: string): tl.RawKeyboardButtonRequestPhone {
|
||||||
|
return {
|
||||||
|
_: 'keyboardButtonRequestPhone',
|
||||||
|
text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a keyboard button requesting for user's geo location.
|
||||||
|
* Available only for private chats.
|
||||||
|
*
|
||||||
|
* Used for reply keyboards, not inline!
|
||||||
|
*
|
||||||
|
* @param text Button text
|
||||||
|
*/
|
||||||
|
export function requestGeo(text: string): tl.RawKeyboardButtonRequestGeoLocation {
|
||||||
|
return {
|
||||||
|
_: 'keyboardButtonRequestGeoLocation',
|
||||||
|
text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a keyboard button requesting the user to create and send a poll.
|
||||||
|
* Available only for private chats.
|
||||||
|
*
|
||||||
|
* Used for reply keyboards, not inline!
|
||||||
|
*
|
||||||
|
* @param text Button text
|
||||||
|
* @param quiz If set, only quiz polls can be sent
|
||||||
|
*/
|
||||||
|
export function requestPoll(text: string, quiz?: boolean): tl.RawKeyboardButtonRequestPoll {
|
||||||
|
return {
|
||||||
|
_: 'keyboardButtonRequestPoll',
|
||||||
|
text,
|
||||||
|
quiz,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a keyboard button with a link.
|
||||||
|
*
|
||||||
|
* Used for inline keyboards, not reply!
|
||||||
|
*
|
||||||
|
* @param text Button text
|
||||||
|
* @param url URL
|
||||||
|
*/
|
||||||
|
export function url(text: string, url: string): tl.RawKeyboardButtonUrl {
|
||||||
|
return {
|
||||||
|
_: 'keyboardButtonUrl',
|
||||||
|
text,
|
||||||
|
url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a keyboard button with a link.
|
||||||
|
*
|
||||||
|
* Used for inline keyboards, not reply!
|
||||||
|
*
|
||||||
|
* @param text Button text
|
||||||
|
* @param data Callback data (1-64 bytes). String will be converted to `Buffer`
|
||||||
|
* @param requiresPassword
|
||||||
|
* Whether the user should verify their identity by entering 2FA password.
|
||||||
|
* See more: {@link tl.RawKeyboardButtonCallback#requiresPassword}
|
||||||
|
*/
|
||||||
|
export function callback(
|
||||||
|
text: string,
|
||||||
|
data: string | Uint8Array,
|
||||||
|
requiresPassword?: boolean,
|
||||||
|
): tl.RawKeyboardButtonCallback {
|
||||||
|
return {
|
||||||
|
_: 'keyboardButtonCallback',
|
||||||
|
text,
|
||||||
|
requiresPassword,
|
||||||
|
data: typeof data === 'string' ? getPlatform().utf8Encode(data) : data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button to force a user to switch to inline mode.
|
||||||
|
*
|
||||||
|
* Pressing the button will prompt the user to select
|
||||||
|
* one of their chats, open that chat and insert the bot‘s
|
||||||
|
* username and the specified inline query (if any) in the input field.
|
||||||
|
*
|
||||||
|
* Used for inline keyboards, not reply!
|
||||||
|
*
|
||||||
|
* @param text Button text
|
||||||
|
* @param query Inline query (can be empty or omitted)
|
||||||
|
* @param currentChat
|
||||||
|
* If set, pressing the button will insert the bot's username
|
||||||
|
* and the specified inline query in the current chat's input field
|
||||||
|
*/
|
||||||
|
export function switchInline(text: string, query = '', currentChat?: boolean): tl.RawKeyboardButtonSwitchInline {
|
||||||
|
return {
|
||||||
|
_: 'keyboardButtonSwitchInline',
|
||||||
|
samePeer: currentChat,
|
||||||
|
text,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button to start a game
|
||||||
|
*
|
||||||
|
* Used for inline keyboards, not reply!
|
||||||
|
*
|
||||||
|
* **Note**: This type of button must always be
|
||||||
|
* the first button in the first row. ID of the
|
||||||
|
* game is inferred from {@link InputMedia.game},
|
||||||
|
* thus this button should only be used with it.
|
||||||
|
*/
|
||||||
|
export function game(text: string): tl.RawKeyboardButtonGame {
|
||||||
|
return { _: 'keyboardButtonGame', text }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button to pay for a product.
|
||||||
|
*
|
||||||
|
* Used for inline keyboards, not reply!
|
||||||
|
*
|
||||||
|
* **Note**: This type of button must always be
|
||||||
|
* the first button in the first row. Related
|
||||||
|
* invoice is inferred from {@link InputMedia.invoice},
|
||||||
|
* thus this button should only be used with it.
|
||||||
|
*/
|
||||||
|
export function pay(text: string): tl.RawKeyboardButtonBuy {
|
||||||
|
return { _: 'keyboardButtonBuy', text }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button to authorize a user
|
||||||
|
*
|
||||||
|
* Used for inline keyboards, not reply!
|
||||||
|
*
|
||||||
|
* @param text Button label
|
||||||
|
* @param url Authorization URL (see {@link tl.RawInputKeyboardButtonUrlAuth})
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function urlAuth(
|
||||||
|
text: string,
|
||||||
|
url: string,
|
||||||
|
params: {
|
||||||
|
/**
|
||||||
|
* Button label when forwarded
|
||||||
|
*/
|
||||||
|
fwdText?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to request the permission for
|
||||||
|
* your bot to send messages to the user
|
||||||
|
*/
|
||||||
|
requestWriteAccess?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bot, which will be used for user authorization.
|
||||||
|
* `url` domain must be the same as the domain linked
|
||||||
|
* with the bot.
|
||||||
|
*
|
||||||
|
* @default current bot
|
||||||
|
*/
|
||||||
|
bot?: tl.TypeInputUser
|
||||||
|
} = {},
|
||||||
|
): tl.RawInputKeyboardButtonUrlAuth {
|
||||||
|
return {
|
||||||
|
_: 'inputKeyboardButtonUrlAuth',
|
||||||
|
text,
|
||||||
|
url,
|
||||||
|
bot: params.bot ?? {
|
||||||
|
_: 'inputUserSelf',
|
||||||
|
},
|
||||||
|
fwdText: params.fwdText,
|
||||||
|
requestWriteAccess: params.requestWriteAccess,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button to open webview
|
||||||
|
*
|
||||||
|
* Used for both inline keyboards and reply ones
|
||||||
|
*
|
||||||
|
* @param text Button label
|
||||||
|
* @param url WebView URL
|
||||||
|
*/
|
||||||
|
export function webView(text: string, url: string): tl.RawKeyboardButtonWebView {
|
||||||
|
return {
|
||||||
|
_: 'keyboardButtonWebView',
|
||||||
|
text,
|
||||||
|
url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button to open user profile
|
||||||
|
*
|
||||||
|
* @param text Text of the button
|
||||||
|
* @param user User to be opened (use {@link TelegramClient.resolvePeer})
|
||||||
|
*/
|
||||||
|
export function userProfile(text: string, user: tl.TypeInputPeer): tl.RawInputKeyboardButtonUserProfile {
|
||||||
|
return {
|
||||||
|
_: 'inputKeyboardButtonUserProfile',
|
||||||
|
text,
|
||||||
|
userId: toInputUser(user),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button to request a peer from the user
|
||||||
|
*
|
||||||
|
* @param text Text of the button
|
||||||
|
* @param buttonId ID of the button that will later be passed to the service message
|
||||||
|
*/
|
||||||
|
export function requestPeer(
|
||||||
|
text: string,
|
||||||
|
buttonId: number,
|
||||||
|
params: {
|
||||||
|
/**
|
||||||
|
* Peer type, along with filters
|
||||||
|
*/
|
||||||
|
peerType: tl.TypeRequestPeerType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of peers to be selected
|
||||||
|
*
|
||||||
|
* @default 1
|
||||||
|
*/
|
||||||
|
count?: number
|
||||||
|
},
|
||||||
|
): tl.RawKeyboardButtonRequestPeer {
|
||||||
|
return {
|
||||||
|
_: 'keyboardButtonRequestPeer',
|
||||||
|
text,
|
||||||
|
buttonId,
|
||||||
|
peerType: params.peerType,
|
||||||
|
maxQuantity: params.count ?? 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a button in the keyboard by its text or by predicate
|
||||||
|
*
|
||||||
|
* @param buttons Two-dimensional array of buttons
|
||||||
|
* @param predicate Button text or predicate function
|
||||||
|
*/
|
||||||
|
export function findButton(
|
||||||
|
buttons: tl.TypeKeyboardButton[][],
|
||||||
|
predicate: string | ((btn: tl.TypeKeyboardButton) => boolean),
|
||||||
|
): tl.TypeKeyboardButton | null {
|
||||||
|
if (typeof predicate === 'string') {
|
||||||
|
const text = predicate
|
||||||
|
|
||||||
|
predicate = (btn) => {
|
||||||
|
return 'text' in btn && btn.text === text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const row of buttons) {
|
||||||
|
for (const btn of row) {
|
||||||
|
if (predicate(btn)) {
|
||||||
|
return btn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function _rowsTo2d(rows: tl.RawKeyboardButtonRow[]): tl.TypeKeyboardButton[][] {
|
||||||
|
return rows.map((it) => it.buttons)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function _2dToRows(arr: tl.TypeKeyboardButton[][], inline: boolean): tl.RawKeyboardButtonRow[] {
|
||||||
|
return arr.map((row) => {
|
||||||
|
if (!inline) {
|
||||||
|
// le cringe
|
||||||
|
row = row.map((btn) =>
|
||||||
|
btn._ === 'keyboardButtonWebView' ?
|
||||||
|
{
|
||||||
|
...btn,
|
||||||
|
_: 'keyboardButtonSimpleWebView',
|
||||||
|
} :
|
||||||
|
btn,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
_: 'keyboardButtonRow',
|
||||||
|
buttons: row,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function _convertToTl(obj?: ReplyMarkup): tl.TypeReplyMarkup | undefined {
|
||||||
|
if (!obj) return obj
|
||||||
|
if (tl.isAnyReplyMarkup(obj)) return obj
|
||||||
|
|
||||||
|
switch (obj.type) {
|
||||||
|
case 'reply':
|
||||||
|
return {
|
||||||
|
_: 'replyKeyboardMarkup',
|
||||||
|
resize: obj.resize,
|
||||||
|
singleUse: obj.singleUse,
|
||||||
|
selective: obj.selective,
|
||||||
|
persistent: obj.persistent,
|
||||||
|
placeholder: obj.placeholder,
|
||||||
|
rows: _2dToRows(obj.buttons, false),
|
||||||
|
}
|
||||||
|
case 'reply_hide':
|
||||||
|
return {
|
||||||
|
_: 'replyKeyboardHide',
|
||||||
|
selective: obj.selective,
|
||||||
|
}
|
||||||
|
case 'force_reply':
|
||||||
|
return {
|
||||||
|
_: 'replyKeyboardForceReply',
|
||||||
|
singleUse: obj.singleUse,
|
||||||
|
selective: obj.selective,
|
||||||
|
placeholder: obj.placeholder,
|
||||||
|
}
|
||||||
|
case 'inline':
|
||||||
|
return {
|
||||||
|
_: 'replyInlineMarkup',
|
||||||
|
rows: _2dToRows(obj.buttons, true),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assertNever(obj)
|
||||||
|
}
|
||||||
|
}
|
16
packages/core/src/highlevel/types/bots/keyboards/index.ts
Normal file
16
packages/core/src/highlevel/types/bots/keyboards/index.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
export * from './types.js'
|
||||||
|
import * as BotKeyboard from './factories.js'
|
||||||
|
|
||||||
|
export {
|
||||||
|
/**
|
||||||
|
* Convenience methods wrapping TL
|
||||||
|
* objects creation for bot keyboard buttons.
|
||||||
|
*
|
||||||
|
* You can also use the type-discriminated objects directly.
|
||||||
|
*
|
||||||
|
* > **Note**: Button creation functions are intended to be used
|
||||||
|
* > with inline reply markup, unless stated otherwise
|
||||||
|
* > in the description.
|
||||||
|
*/
|
||||||
|
BotKeyboard,
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { BotKeyboard } from './keyboards.js'
|
import { BotKeyboard } from './index.js'
|
||||||
|
|
||||||
describe('findButton', () => {
|
describe('findButton', () => {
|
||||||
const kb: tl.TypeKeyboardButton[][] = [
|
const kb: tl.TypeKeyboardButton[][] = [
|
46
packages/core/src/highlevel/types/bots/keyboards/types.ts
Normal file
46
packages/core/src/highlevel/types/bots/keyboards/types.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reply keyboard markup
|
||||||
|
*/
|
||||||
|
export interface ReplyKeyboardMarkup extends Omit<tl.RawReplyKeyboardMarkup, '_' | 'rows'> {
|
||||||
|
readonly type: 'reply'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Two-dimensional array of buttons
|
||||||
|
*/
|
||||||
|
readonly buttons: tl.TypeKeyboardButton[][]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide previously sent bot keyboard
|
||||||
|
*/
|
||||||
|
export interface ReplyKeyboardHide extends Omit<tl.RawReplyKeyboardHide, '_'> {
|
||||||
|
readonly type: 'reply_hide'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force the user to send a reply
|
||||||
|
*/
|
||||||
|
export interface ReplyKeyboardForceReply extends Omit<tl.RawReplyKeyboardForceReply, '_'> {
|
||||||
|
readonly type: 'force_reply'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline keyboard markup
|
||||||
|
*/
|
||||||
|
export interface InlineKeyboardMarkup {
|
||||||
|
readonly type: 'inline'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Two-dimensional array of buttons
|
||||||
|
*/
|
||||||
|
readonly buttons: tl.TypeKeyboardButton[][]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ReplyMarkup =
|
||||||
|
| ReplyKeyboardMarkup
|
||||||
|
| ReplyKeyboardHide
|
||||||
|
| ReplyKeyboardForceReply
|
||||||
|
| InlineKeyboardMarkup
|
||||||
|
| tl.TypeReplyMarkup
|
|
@ -3,7 +3,7 @@ export * from './contact.js'
|
||||||
export * from './dice.js'
|
export * from './dice.js'
|
||||||
export * from './document.js'
|
export * from './document.js'
|
||||||
export * from './game.js'
|
export * from './game.js'
|
||||||
export * from './input-media.js'
|
export * from './input-media/index.js'
|
||||||
export * from './invoice.js'
|
export * from './invoice.js'
|
||||||
export * from './location.js'
|
export * from './location.js'
|
||||||
export * from './photo.js'
|
export * from './photo.js'
|
||||||
|
|
300
packages/core/src/highlevel/types/media/input-media/factories.ts
Normal file
300
packages/core/src/highlevel/types/media/input-media/factories.ts
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
import { InputFileLike } from '../../files/utils.js'
|
||||||
|
import {
|
||||||
|
CaptionMixin,
|
||||||
|
InputMediaAudio,
|
||||||
|
InputMediaAuto,
|
||||||
|
InputMediaContact,
|
||||||
|
InputMediaDice,
|
||||||
|
InputMediaDocument,
|
||||||
|
InputMediaGame,
|
||||||
|
InputMediaGeo,
|
||||||
|
InputMediaGeoLive,
|
||||||
|
InputMediaInvoice,
|
||||||
|
InputMediaLike,
|
||||||
|
InputMediaPhoto,
|
||||||
|
InputMediaPoll,
|
||||||
|
InputMediaQuiz,
|
||||||
|
InputMediaSticker,
|
||||||
|
InputMediaStory,
|
||||||
|
InputMediaVenue,
|
||||||
|
InputMediaVideo,
|
||||||
|
InputMediaVoice,
|
||||||
|
InputMediaWebpage,
|
||||||
|
} from './types.js'
|
||||||
|
|
||||||
|
/** Omit `type` and `file` from the given type */
|
||||||
|
export type OmitTypeAndFile<T extends InputMediaLike, K extends keyof T = never> = Omit<T, 'type' | 'file' | K>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an animation to be sent
|
||||||
|
*
|
||||||
|
* @param file Animation
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function animation(file: InputFileLike, params: OmitTypeAndFile<InputMediaVideo> = {}): InputMediaVideo {
|
||||||
|
const ret = params as tl.Mutable<InputMediaVideo>
|
||||||
|
ret.type = 'video'
|
||||||
|
ret.file = file
|
||||||
|
ret.isAnimated = true
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an audio to be sent
|
||||||
|
*
|
||||||
|
* @param file Audio file
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function audio(file: InputFileLike, params: OmitTypeAndFile<InputMediaAudio> = {}): InputMediaAudio {
|
||||||
|
const ret = params as tl.Mutable<InputMediaAudio>
|
||||||
|
ret.type = 'audio'
|
||||||
|
ret.file = file
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an document to be sent
|
||||||
|
*
|
||||||
|
* @param file Document
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function document(file: InputFileLike, params: OmitTypeAndFile<InputMediaDocument> = {}): InputMediaDocument {
|
||||||
|
const ret = params as tl.Mutable<InputMediaDocument>
|
||||||
|
ret.type = 'document'
|
||||||
|
ret.file = file
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an photo to be sent
|
||||||
|
*
|
||||||
|
* @param file Photo
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function photo(file: InputFileLike, params: OmitTypeAndFile<InputMediaPhoto> = {}): InputMediaPhoto {
|
||||||
|
const ret = params as tl.Mutable<InputMediaPhoto>
|
||||||
|
ret.type = 'photo'
|
||||||
|
ret.file = file
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an video to be sent
|
||||||
|
*
|
||||||
|
* @param file Video
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function video(file: InputFileLike, params: OmitTypeAndFile<InputMediaVideo> = {}): InputMediaVideo {
|
||||||
|
const ret = params as tl.Mutable<InputMediaVideo>
|
||||||
|
ret.type = 'video'
|
||||||
|
ret.file = file
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a voice note to be sent
|
||||||
|
*
|
||||||
|
* @param file Voice note
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function voice(file: InputFileLike, params: OmitTypeAndFile<InputMediaVoice> = {}): InputMediaVoice {
|
||||||
|
const ret = params as tl.Mutable<InputMediaVoice>
|
||||||
|
ret.type = 'voice'
|
||||||
|
ret.file = file
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a sticker to be sent
|
||||||
|
*
|
||||||
|
* @param file Sticker
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function sticker(file: InputFileLike, params: OmitTypeAndFile<InputMediaSticker> = {}): InputMediaSticker {
|
||||||
|
const ret = params as tl.Mutable<InputMediaSticker>
|
||||||
|
ret.type = 'sticker'
|
||||||
|
ret.file = file
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a venue to be sent
|
||||||
|
*
|
||||||
|
* @param params Venue parameters
|
||||||
|
*/
|
||||||
|
export function venue(params: OmitTypeAndFile<InputMediaVenue>): InputMediaVenue {
|
||||||
|
const ret = params as tl.Mutable<InputMediaVenue>
|
||||||
|
ret.type = 'venue'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a geolocation to be sent
|
||||||
|
*
|
||||||
|
* @param latitude Latitude of the location
|
||||||
|
* @param longitude Longitude of the location
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function geo(
|
||||||
|
latitude: number,
|
||||||
|
longitude: number,
|
||||||
|
params: OmitTypeAndFile<InputMediaGeo, 'latitude' | 'longitude'> = {},
|
||||||
|
): InputMediaGeo {
|
||||||
|
const ret = params as tl.Mutable<InputMediaGeo>
|
||||||
|
ret.type = 'geo'
|
||||||
|
ret.latitude = latitude
|
||||||
|
ret.longitude = longitude
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a live geolocation to be sent
|
||||||
|
*
|
||||||
|
* @param latitude Latitude of the current location
|
||||||
|
* @param longitude Longitude of the current location
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function geoLive(
|
||||||
|
latitude: number,
|
||||||
|
longitude: number,
|
||||||
|
params: OmitTypeAndFile<InputMediaGeoLive, 'latitude' | 'longitude'> = {},
|
||||||
|
): InputMediaGeoLive {
|
||||||
|
const ret = params as tl.Mutable<InputMediaGeoLive>
|
||||||
|
ret.type = 'geo_live'
|
||||||
|
ret.latitude = latitude
|
||||||
|
ret.longitude = longitude
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a dice to be sent
|
||||||
|
*
|
||||||
|
* For convenience, known dice emojis are available
|
||||||
|
* as static members of {@link Dice}.
|
||||||
|
*
|
||||||
|
* @param emoji Emoji representing the dice
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function dice(emoji: string, params: CaptionMixin): InputMediaDice {
|
||||||
|
const ret = params as tl.Mutable<InputMediaDice>
|
||||||
|
ret.type = 'dice'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a contact to be sent
|
||||||
|
*
|
||||||
|
* @param params Contact parameters
|
||||||
|
*/
|
||||||
|
export function contact(params: OmitTypeAndFile<InputMediaContact>): InputMediaContact {
|
||||||
|
const ret = params as tl.Mutable<InputMediaContact>
|
||||||
|
ret.type = 'contact'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a game to be sent
|
||||||
|
*
|
||||||
|
* @param game Game short name or TL object representing one
|
||||||
|
*/
|
||||||
|
export function game(game: string | tl.TypeInputGame): InputMediaGame {
|
||||||
|
return {
|
||||||
|
type: 'game',
|
||||||
|
game,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an invoice to be sent
|
||||||
|
*
|
||||||
|
* @param params Invoice parameters
|
||||||
|
*/
|
||||||
|
export function invoice(params: OmitTypeAndFile<InputMediaInvoice>): InputMediaInvoice {
|
||||||
|
const ret = params as tl.Mutable<InputMediaInvoice>
|
||||||
|
ret.type = 'invoice'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a poll to be sent
|
||||||
|
*
|
||||||
|
* @param params Poll parameters
|
||||||
|
*/
|
||||||
|
export function poll(params: OmitTypeAndFile<InputMediaPoll>): InputMediaPoll {
|
||||||
|
const ret = params as tl.Mutable<InputMediaPoll>
|
||||||
|
ret.type = 'poll'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a quiz to be sent
|
||||||
|
*
|
||||||
|
* @param params Quiz parameters
|
||||||
|
*/
|
||||||
|
export function quiz(params: OmitTypeAndFile<InputMediaQuiz>): InputMediaQuiz {
|
||||||
|
const ret = params as tl.Mutable<InputMediaQuiz>
|
||||||
|
ret.type = 'quiz'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a story to be sent
|
||||||
|
*
|
||||||
|
* @param params Story parameters
|
||||||
|
*/
|
||||||
|
export function story(params: OmitTypeAndFile<InputMediaStory>): InputMediaStory {
|
||||||
|
const ret = params as tl.Mutable<InputMediaStory>
|
||||||
|
ret.type = 'story'
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a webpage to be sent
|
||||||
|
*
|
||||||
|
* @param url Webpage URL
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function webpage(url: string, params: OmitTypeAndFile<InputMediaWebpage, 'url'> = {}): InputMediaWebpage {
|
||||||
|
const ret = params as tl.Mutable<InputMediaWebpage>
|
||||||
|
ret.type = 'webpage'
|
||||||
|
ret.url = url
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a document to be sent, which subtype
|
||||||
|
* is inferred automatically by file contents.
|
||||||
|
*
|
||||||
|
* Photo type is only inferred for reused files,
|
||||||
|
* newly uploaded photos with `auto` will be
|
||||||
|
* uploaded as a document
|
||||||
|
*
|
||||||
|
* @param file The media file
|
||||||
|
* @param params Additional parameters
|
||||||
|
*/
|
||||||
|
export function auto(file: InputFileLike, params: OmitTypeAndFile<InputMediaAuto> = {}): InputMediaAuto {
|
||||||
|
const ret = params as tl.Mutable<InputMediaAuto>
|
||||||
|
ret.type = 'auto'
|
||||||
|
ret.file = file
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
import * as InputMedia from './factories.js'
|
||||||
|
export * from './types.js'
|
||||||
|
export { InputMedia }
|
|
@ -1,10 +1,10 @@
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { MaybeArray } from '../../../types/utils.js'
|
import { MaybeArray } from '../../../../types/utils.js'
|
||||||
import { InputText } from '../../types/misc/entities.js'
|
import { InputText } from '../../../types/misc/entities.js'
|
||||||
import { InputFileLike } from '../files/index.js'
|
import { InputFileLike } from '../../files/index.js'
|
||||||
import { InputPeerLike } from '../peers/index.js'
|
import { InputPeerLike } from '../../peers/index.js'
|
||||||
import { VenueSource } from './venue.js'
|
import { VenueSource } from '../venue.js'
|
||||||
|
|
||||||
export interface CaptionMixin {
|
export interface CaptionMixin {
|
||||||
/**
|
/**
|
||||||
|
@ -607,284 +607,3 @@ export type InputMediaLike =
|
||||||
| InputMediaStory
|
| InputMediaStory
|
||||||
| InputMediaWebpage
|
| InputMediaWebpage
|
||||||
| tl.TypeInputMedia
|
| tl.TypeInputMedia
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
||||||
export namespace InputMedia {
|
|
||||||
/** Omit `type` and `file` from the given type */
|
|
||||||
export type OmitTypeAndFile<T extends InputMediaLike, K extends keyof T = never> = Omit<T, 'type' | 'file' | K>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an animation to be sent
|
|
||||||
*
|
|
||||||
* @param file Animation
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function animation(file: InputFileLike, params: OmitTypeAndFile<InputMediaVideo> = {}): InputMediaVideo {
|
|
||||||
const ret = params as tl.Mutable<InputMediaVideo>
|
|
||||||
ret.type = 'video'
|
|
||||||
ret.file = file
|
|
||||||
ret.isAnimated = true
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an audio to be sent
|
|
||||||
*
|
|
||||||
* @param file Audio file
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function audio(file: InputFileLike, params: OmitTypeAndFile<InputMediaAudio> = {}): InputMediaAudio {
|
|
||||||
const ret = params as tl.Mutable<InputMediaAudio>
|
|
||||||
ret.type = 'audio'
|
|
||||||
ret.file = file
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an document to be sent
|
|
||||||
*
|
|
||||||
* @param file Document
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function document(
|
|
||||||
file: InputFileLike,
|
|
||||||
params: OmitTypeAndFile<InputMediaDocument> = {},
|
|
||||||
): InputMediaDocument {
|
|
||||||
const ret = params as tl.Mutable<InputMediaDocument>
|
|
||||||
ret.type = 'document'
|
|
||||||
ret.file = file
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an photo to be sent
|
|
||||||
*
|
|
||||||
* @param file Photo
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function photo(file: InputFileLike, params: OmitTypeAndFile<InputMediaPhoto> = {}): InputMediaPhoto {
|
|
||||||
const ret = params as tl.Mutable<InputMediaPhoto>
|
|
||||||
ret.type = 'photo'
|
|
||||||
ret.file = file
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an video to be sent
|
|
||||||
*
|
|
||||||
* @param file Video
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function video(file: InputFileLike, params: OmitTypeAndFile<InputMediaVideo> = {}): InputMediaVideo {
|
|
||||||
const ret = params as tl.Mutable<InputMediaVideo>
|
|
||||||
ret.type = 'video'
|
|
||||||
ret.file = file
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a voice note to be sent
|
|
||||||
*
|
|
||||||
* @param file Voice note
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function voice(file: InputFileLike, params: OmitTypeAndFile<InputMediaVoice> = {}): InputMediaVoice {
|
|
||||||
const ret = params as tl.Mutable<InputMediaVoice>
|
|
||||||
ret.type = 'voice'
|
|
||||||
ret.file = file
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a sticker to be sent
|
|
||||||
*
|
|
||||||
* @param file Sticker
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function sticker(file: InputFileLike, params: OmitTypeAndFile<InputMediaSticker> = {}): InputMediaSticker {
|
|
||||||
const ret = params as tl.Mutable<InputMediaSticker>
|
|
||||||
ret.type = 'sticker'
|
|
||||||
ret.file = file
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a venue to be sent
|
|
||||||
*
|
|
||||||
* @param params Venue parameters
|
|
||||||
*/
|
|
||||||
export function venue(params: OmitTypeAndFile<InputMediaVenue>): InputMediaVenue {
|
|
||||||
const ret = params as tl.Mutable<InputMediaVenue>
|
|
||||||
ret.type = 'venue'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a geolocation to be sent
|
|
||||||
*
|
|
||||||
* @param latitude Latitude of the location
|
|
||||||
* @param longitude Longitude of the location
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function geo(
|
|
||||||
latitude: number,
|
|
||||||
longitude: number,
|
|
||||||
params: OmitTypeAndFile<InputMediaGeo, 'latitude' | 'longitude'> = {},
|
|
||||||
): InputMediaGeo {
|
|
||||||
const ret = params as tl.Mutable<InputMediaGeo>
|
|
||||||
ret.type = 'geo'
|
|
||||||
ret.latitude = latitude
|
|
||||||
ret.longitude = longitude
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a live geolocation to be sent
|
|
||||||
*
|
|
||||||
* @param latitude Latitude of the current location
|
|
||||||
* @param longitude Longitude of the current location
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function geoLive(
|
|
||||||
latitude: number,
|
|
||||||
longitude: number,
|
|
||||||
params: OmitTypeAndFile<InputMediaGeoLive, 'latitude' | 'longitude'> = {},
|
|
||||||
): InputMediaGeoLive {
|
|
||||||
const ret = params as tl.Mutable<InputMediaGeoLive>
|
|
||||||
ret.type = 'geo_live'
|
|
||||||
ret.latitude = latitude
|
|
||||||
ret.longitude = longitude
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a dice to be sent
|
|
||||||
*
|
|
||||||
* For convenience, known dice emojis are available
|
|
||||||
* as static members of {@link Dice}.
|
|
||||||
*
|
|
||||||
* @param emoji Emoji representing the dice
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function dice(emoji: string, params: CaptionMixin): InputMediaDice {
|
|
||||||
const ret = params as tl.Mutable<InputMediaDice>
|
|
||||||
ret.type = 'dice'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a contact to be sent
|
|
||||||
*
|
|
||||||
* @param params Contact parameters
|
|
||||||
*/
|
|
||||||
export function contact(params: OmitTypeAndFile<InputMediaContact>): InputMediaContact {
|
|
||||||
const ret = params as tl.Mutable<InputMediaContact>
|
|
||||||
ret.type = 'contact'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a game to be sent
|
|
||||||
*
|
|
||||||
* @param game Game short name or TL object representing one
|
|
||||||
*/
|
|
||||||
export function game(game: string | tl.TypeInputGame): InputMediaGame {
|
|
||||||
return {
|
|
||||||
type: 'game',
|
|
||||||
game,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an invoice to be sent
|
|
||||||
*
|
|
||||||
* @param params Invoice parameters
|
|
||||||
*/
|
|
||||||
export function invoice(params: OmitTypeAndFile<InputMediaInvoice>): InputMediaInvoice {
|
|
||||||
const ret = params as tl.Mutable<InputMediaInvoice>
|
|
||||||
ret.type = 'invoice'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a poll to be sent
|
|
||||||
*
|
|
||||||
* @param params Poll parameters
|
|
||||||
*/
|
|
||||||
export function poll(params: OmitTypeAndFile<InputMediaPoll>): InputMediaPoll {
|
|
||||||
const ret = params as tl.Mutable<InputMediaPoll>
|
|
||||||
ret.type = 'poll'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a quiz to be sent
|
|
||||||
*
|
|
||||||
* @param params Quiz parameters
|
|
||||||
*/
|
|
||||||
export function quiz(params: OmitTypeAndFile<InputMediaQuiz>): InputMediaQuiz {
|
|
||||||
const ret = params as tl.Mutable<InputMediaQuiz>
|
|
||||||
ret.type = 'quiz'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a story to be sent
|
|
||||||
*
|
|
||||||
* @param params Story parameters
|
|
||||||
*/
|
|
||||||
export function story(params: OmitTypeAndFile<InputMediaStory>): InputMediaStory {
|
|
||||||
const ret = params as tl.Mutable<InputMediaStory>
|
|
||||||
ret.type = 'story'
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a webpage to be sent
|
|
||||||
*
|
|
||||||
* @param url Webpage URL
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function webpage(url: string, params: OmitTypeAndFile<InputMediaWebpage, 'url'> = {}): InputMediaWebpage {
|
|
||||||
const ret = params as tl.Mutable<InputMediaWebpage>
|
|
||||||
ret.type = 'webpage'
|
|
||||||
ret.url = url
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a document to be sent, which subtype
|
|
||||||
* is inferred automatically by file contents.
|
|
||||||
*
|
|
||||||
* Photo type is only inferred for reused files,
|
|
||||||
* newly uploaded photos with `auto` will be
|
|
||||||
* uploaded as a document
|
|
||||||
*
|
|
||||||
* @param file The media file
|
|
||||||
* @param params Additional parameters
|
|
||||||
*/
|
|
||||||
export function auto(file: InputFileLike, params: OmitTypeAndFile<InputMediaAuto> = {}): InputMediaAuto {
|
|
||||||
const ret = params as tl.Mutable<InputMediaAuto>
|
|
||||||
ret.type = 'auto'
|
|
||||||
ret.file = file
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,7 @@ import { getMarkedPeerId, toggleChannelIdMark } from '../../../utils/peer-utils.
|
||||||
import { assertTypeIsNot } from '../../../utils/type-assertions.js'
|
import { assertTypeIsNot } from '../../../utils/type-assertions.js'
|
||||||
import { makeInspectable } from '../../utils/index.js'
|
import { makeInspectable } from '../../utils/index.js'
|
||||||
import { memoizeGetters } from '../../utils/memoize.js'
|
import { memoizeGetters } from '../../utils/memoize.js'
|
||||||
import { BotKeyboard, ReplyMarkup } from '../bots/keyboards.js'
|
import { BotKeyboard, ReplyMarkup } from '../bots/keyboards/index.js'
|
||||||
import { TextWithEntities } from '../misc/index.js'
|
import { TextWithEntities } from '../misc/index.js'
|
||||||
import { Chat } from '../peers/chat.js'
|
import { Chat } from '../peers/chat.js'
|
||||||
import { parsePeer, Peer } from '../peers/peer.js'
|
import { parsePeer, Peer } from '../peers/peer.js'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export * from './app-config.js'
|
export * from './app-config.js'
|
||||||
export * from './entities.js'
|
export * from './entities.js'
|
||||||
export * from './input-privacy-rule.js'
|
export * from './input-privacy-rule/index.js'
|
||||||
export * from './sticker-set.js'
|
export * from './sticker-set.js'
|
||||||
export * from './takeout-session.js'
|
export * from './takeout-session.js'
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
/* eslint-disable @typescript-eslint/no-namespace */
|
|
||||||
|
|
||||||
import { tl } from '@mtcute/tl'
|
|
||||||
|
|
||||||
import { MaybeArray } from '../../../types/utils.js'
|
|
||||||
import { InputPeerLike } from '../peers/index.js'
|
|
||||||
|
|
||||||
export interface InputPrivacyRuleUsers {
|
|
||||||
allow: boolean
|
|
||||||
users: InputPeerLike[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InputPrivacyRuleChatParticipants {
|
|
||||||
allow: boolean
|
|
||||||
chats: InputPeerLike[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type InputPrivacyRule = InputPrivacyRuleChatParticipants | InputPrivacyRuleUsers | tl.TypeInputPrivacyRule
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helpers for creating {@link InputPrivacyRule}s
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```typescript
|
|
||||||
* const rules = [
|
|
||||||
* PrivacyRule.allow.all,
|
|
||||||
* PrivacyRule.disallow.users([123456789, 'username']),
|
|
||||||
* ]
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export namespace PrivacyRule {
|
|
||||||
export namespace allow {
|
|
||||||
/** Allow all users */
|
|
||||||
export const all: tl.RawInputPrivacyValueAllowAll = { _: 'inputPrivacyValueAllowAll' }
|
|
||||||
/** Allow only contacts */
|
|
||||||
export const contacts: tl.RawInputPrivacyValueAllowContacts = { _: 'inputPrivacyValueAllowContacts' }
|
|
||||||
/** Allow only "close friends" list */
|
|
||||||
export const closeFriends: tl.RawInputPrivacyValueAllowCloseFriends = {
|
|
||||||
_: 'inputPrivacyValueAllowCloseFriends',
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allow only users specified in `users`
|
|
||||||
*
|
|
||||||
* @param users Users to allow
|
|
||||||
*/
|
|
||||||
export function users(users: MaybeArray<InputPeerLike>): InputPrivacyRuleUsers {
|
|
||||||
return {
|
|
||||||
allow: true,
|
|
||||||
users: Array.isArray(users) ? users : [users],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allow only participants of chats specified in `chats`
|
|
||||||
*
|
|
||||||
* @param chats Chats to allow
|
|
||||||
*/
|
|
||||||
export function chatParticipants(chats: MaybeArray<InputPeerLike>): InputPrivacyRuleChatParticipants {
|
|
||||||
return {
|
|
||||||
allow: true,
|
|
||||||
chats: Array.isArray(chats) ? chats : [chats],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace disallow {
|
|
||||||
/** Disallow all users */
|
|
||||||
export const all: tl.RawInputPrivacyValueDisallowAll = { _: 'inputPrivacyValueDisallowAll' }
|
|
||||||
/** Disallow contacts */
|
|
||||||
export const contacts: tl.RawInputPrivacyValueDisallowContacts = { _: 'inputPrivacyValueDisallowContacts' }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disallow users specified in `users`
|
|
||||||
*
|
|
||||||
* @param users Users to disallow
|
|
||||||
*/
|
|
||||||
export function users(users: MaybeArray<InputPeerLike>): InputPrivacyRuleUsers {
|
|
||||||
return {
|
|
||||||
allow: false,
|
|
||||||
users: Array.isArray(users) ? users : [users],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disallow participants of chats specified in `chats`
|
|
||||||
*
|
|
||||||
* @param chats Chats to disallow
|
|
||||||
*/
|
|
||||||
export function chatParticipants(chats: MaybeArray<InputPeerLike>): InputPrivacyRuleChatParticipants {
|
|
||||||
return {
|
|
||||||
allow: false,
|
|
||||||
chats: Array.isArray(chats) ? chats : [chats],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
import { MaybeArray } from '../../../../types/utils.js'
|
||||||
|
import { InputPeerLike } from '../../peers/peer.js'
|
||||||
|
import { InputPrivacyRuleChatParticipants, InputPrivacyRuleUsers } from './types.js'
|
||||||
|
|
||||||
|
/** Allow all users */
|
||||||
|
export const all: tl.RawInputPrivacyValueAllowAll = { _: 'inputPrivacyValueAllowAll' }
|
||||||
|
/** Allow only contacts */
|
||||||
|
export const contacts: tl.RawInputPrivacyValueAllowContacts = { _: 'inputPrivacyValueAllowContacts' }
|
||||||
|
/** Allow only "close friends" list */
|
||||||
|
export const closeFriends: tl.RawInputPrivacyValueAllowCloseFriends = {
|
||||||
|
_: 'inputPrivacyValueAllowCloseFriends',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow only users specified in `users`
|
||||||
|
*
|
||||||
|
* @param users Users to allow
|
||||||
|
*/
|
||||||
|
export function users(users: MaybeArray<InputPeerLike>): InputPrivacyRuleUsers {
|
||||||
|
return {
|
||||||
|
allow: true,
|
||||||
|
users: Array.isArray(users) ? users : [users],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow only participants of chats specified in `chats`
|
||||||
|
*
|
||||||
|
* @param chats Chats to allow
|
||||||
|
*/
|
||||||
|
export function chatParticipants(chats: MaybeArray<InputPeerLike>): InputPrivacyRuleChatParticipants {
|
||||||
|
return {
|
||||||
|
allow: true,
|
||||||
|
chats: Array.isArray(chats) ? chats : [chats],
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
import * as allow from './allow.js'
|
||||||
|
import * as disallow from './disallow.js'
|
||||||
|
|
||||||
|
export { allow, disallow }
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
import { MaybeArray } from '../../../../types/utils.js'
|
||||||
|
import { InputPeerLike } from '../../peers/peer.js'
|
||||||
|
import { InputPrivacyRuleChatParticipants, InputPrivacyRuleUsers } from './types.js'
|
||||||
|
|
||||||
|
/** Disallow all users */
|
||||||
|
export const all: tl.RawInputPrivacyValueDisallowAll = { _: 'inputPrivacyValueDisallowAll' }
|
||||||
|
/** Disallow contacts */
|
||||||
|
export const contacts: tl.RawInputPrivacyValueDisallowContacts = { _: 'inputPrivacyValueDisallowContacts' }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disallow users specified in `users`
|
||||||
|
*
|
||||||
|
* @param users Users to disallow
|
||||||
|
*/
|
||||||
|
export function users(users: MaybeArray<InputPeerLike>): InputPrivacyRuleUsers {
|
||||||
|
return {
|
||||||
|
allow: false,
|
||||||
|
users: Array.isArray(users) ? users : [users],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disallow participants of chats specified in `chats`
|
||||||
|
*
|
||||||
|
* @param chats Chats to disallow
|
||||||
|
*/
|
||||||
|
export function chatParticipants(chats: MaybeArray<InputPeerLike>): InputPrivacyRuleChatParticipants {
|
||||||
|
return {
|
||||||
|
allow: false,
|
||||||
|
chats: Array.isArray(chats) ? chats : [chats],
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
import * as PrivacyRule from './bundle.js'
|
||||||
|
|
||||||
|
export { PrivacyRule }
|
||||||
|
export * from './types.js'
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
import { InputPeerLike } from '../../peers/peer.js'
|
||||||
|
|
||||||
|
export interface InputPrivacyRuleUsers {
|
||||||
|
allow: boolean
|
||||||
|
users: InputPeerLike[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InputPrivacyRuleChatParticipants {
|
||||||
|
allow: boolean
|
||||||
|
chats: InputPeerLike[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InputPrivacyRule = InputPrivacyRuleChatParticipants | InputPrivacyRuleUsers | tl.TypeInputPrivacyRule
|
232
packages/file-id/src/types-inner.ts
Normal file
232
packages/file-id/src/types-inner.ts
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
import Long from 'long'
|
||||||
|
|
||||||
|
export const PERSISTENT_ID_VERSION_OLD = 2
|
||||||
|
export const PERSISTENT_ID_VERSION = 4
|
||||||
|
|
||||||
|
export const WEB_LOCATION_FLAG = 1 << 24
|
||||||
|
export const FILE_REFERENCE_FLAG = 1 << 25
|
||||||
|
|
||||||
|
export const CURRENT_VERSION = 48
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An error occurred while parsing or serializing a File ID
|
||||||
|
*/
|
||||||
|
export class FileIdError extends Error {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A newer version of File ID is provided, which is
|
||||||
|
* currently not supported by the library.
|
||||||
|
*
|
||||||
|
* Feel free to open an issue on Github!
|
||||||
|
*/
|
||||||
|
export class UnsupportedError extends FileIdError {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File ID was invalid, meaning that something did not
|
||||||
|
* add up while parsing the file ID, or the file ID object
|
||||||
|
* contained invalid data.
|
||||||
|
*/
|
||||||
|
export class InvalidFileIdError extends FileIdError {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provided File ID cannot be converted to that TL object.
|
||||||
|
*/
|
||||||
|
export class ConversionError extends FileIdError {
|
||||||
|
constructor(to: string) {
|
||||||
|
super(`Cannot convert given File ID to ${to}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FileType {
|
||||||
|
Thumbnail,
|
||||||
|
ProfilePhoto,
|
||||||
|
Photo,
|
||||||
|
VoiceNote,
|
||||||
|
Video,
|
||||||
|
Document,
|
||||||
|
Encrypted,
|
||||||
|
Temp,
|
||||||
|
Sticker,
|
||||||
|
Audio,
|
||||||
|
Animation,
|
||||||
|
EncryptedThumbnail,
|
||||||
|
Wallpaper,
|
||||||
|
VideoNote,
|
||||||
|
SecureRaw,
|
||||||
|
Secure,
|
||||||
|
Background,
|
||||||
|
DocumentAsFile,
|
||||||
|
Size,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
// naming convention just like in @mtcute/tl
|
||||||
|
|
||||||
|
// additionally, `_` discriminator is used,
|
||||||
|
// so we can interoperate with normal TL objects
|
||||||
|
// like InputFile just by checking `_`
|
||||||
|
|
||||||
|
// for nested types, we don't bother with full type name
|
||||||
|
// for discriminator since it is really only used internally,
|
||||||
|
// so uniqueness is pretty much guaranteed
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This photo is a legacy photo that is
|
||||||
|
* represented simply by a secret number
|
||||||
|
*/
|
||||||
|
export interface RawPhotoSizeSourceLegacy {
|
||||||
|
readonly _: 'legacy'
|
||||||
|
readonly secret: Long
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This photo is a thumbnail, and its size
|
||||||
|
* is provided here as a one-letter string
|
||||||
|
*/
|
||||||
|
export interface RawPhotoSizeSourceThumbnail {
|
||||||
|
readonly _: 'thumbnail'
|
||||||
|
readonly fileType: FileType
|
||||||
|
readonly thumbnailType: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This photo is a profile photo of
|
||||||
|
* some peer, and their ID and access
|
||||||
|
* hash are provided here.
|
||||||
|
*/
|
||||||
|
export interface RawPhotoSizeSourceDialogPhoto {
|
||||||
|
readonly _: 'dialogPhoto'
|
||||||
|
readonly big: boolean
|
||||||
|
readonly id: number
|
||||||
|
readonly accessHash: Long
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This photo is a thumbnail for a a sticker set,
|
||||||
|
* and set's ID and access hash are provided here
|
||||||
|
*/
|
||||||
|
export interface RawPhotoSizeSourceStickerSetThumbnail {
|
||||||
|
readonly _: 'stickerSetThumbnail'
|
||||||
|
readonly id: Long
|
||||||
|
readonly accessHash: Long
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This photo is a legacy photo containing
|
||||||
|
* volume_id, local_id and secret
|
||||||
|
*/
|
||||||
|
export interface RawPhotoSizeSourceFullLegacy {
|
||||||
|
readonly _: 'fullLegacy'
|
||||||
|
readonly volumeId: Long
|
||||||
|
readonly localId: number
|
||||||
|
readonly secret: Long
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This photo is a legacy dialog photo
|
||||||
|
*/
|
||||||
|
export interface RawPhotoSizeSourceDialogPhotoLegacy extends Omit<RawPhotoSizeSourceDialogPhoto, '_'> {
|
||||||
|
readonly _: 'dialogPhotoLegacy'
|
||||||
|
readonly volumeId: Long
|
||||||
|
readonly localId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This photo is a legacy sticker set thumbnail
|
||||||
|
*/
|
||||||
|
export interface RawPhotoSizeSourceStickerSetThumbnailLegacy extends Omit<RawPhotoSizeSourceStickerSetThumbnail, '_'> {
|
||||||
|
readonly _: 'stickerSetThumbnailLegacy'
|
||||||
|
readonly volumeId: Long
|
||||||
|
readonly localId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This photo is a legacy sticker set identified by a version
|
||||||
|
*/
|
||||||
|
export interface RawPhotoSizeSourceStickerSetThumbnailVersion extends Omit<RawPhotoSizeSourceStickerSetThumbnail, '_'> {
|
||||||
|
readonly _: 'stickerSetThumbnailVersion'
|
||||||
|
readonly version: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TypePhotoSizeSource =
|
||||||
|
| RawPhotoSizeSourceLegacy
|
||||||
|
| RawPhotoSizeSourceThumbnail
|
||||||
|
| RawPhotoSizeSourceDialogPhoto
|
||||||
|
| RawPhotoSizeSourceStickerSetThumbnail
|
||||||
|
| RawPhotoSizeSourceFullLegacy
|
||||||
|
| RawPhotoSizeSourceDialogPhotoLegacy
|
||||||
|
| RawPhotoSizeSourceStickerSetThumbnailLegacy
|
||||||
|
| RawPhotoSizeSourceStickerSetThumbnailVersion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An external web file
|
||||||
|
*/
|
||||||
|
export interface RawWebRemoteFileLocation {
|
||||||
|
readonly _: 'web'
|
||||||
|
readonly url: string
|
||||||
|
readonly accessHash: Long
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A photo, that, in addition to ID and access
|
||||||
|
* hash, has its own `source` and detailed
|
||||||
|
* information about photo location on the
|
||||||
|
* servers.
|
||||||
|
*/
|
||||||
|
export interface RawPhotoRemoteFileLocation {
|
||||||
|
readonly _: 'photo'
|
||||||
|
readonly id: Long
|
||||||
|
readonly accessHash: Long
|
||||||
|
readonly source: TypePhotoSizeSource
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A common file that is represented as a pair
|
||||||
|
* of ID and access hash
|
||||||
|
*/
|
||||||
|
export interface RawCommonRemoteFileLocation {
|
||||||
|
readonly _: 'common'
|
||||||
|
readonly id: Long
|
||||||
|
readonly accessHash: Long
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TypeRemoteFileLocation = RawWebRemoteFileLocation | RawPhotoRemoteFileLocation | RawCommonRemoteFileLocation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object representing information about
|
||||||
|
* file location, that was either parsed from
|
||||||
|
* TDLib compatible File ID, or will be parsed
|
||||||
|
* to one.
|
||||||
|
*
|
||||||
|
* This type is supposed to be an intermediate step
|
||||||
|
* between TL objects and string file IDs,
|
||||||
|
* and if you are using `@mtcute/client`, you don't
|
||||||
|
* really need to care about this type at all.
|
||||||
|
*/
|
||||||
|
export interface RawFullRemoteFileLocation {
|
||||||
|
readonly _: 'remoteFileLocation'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DC ID where this file is located
|
||||||
|
*/
|
||||||
|
readonly dcId: number
|
||||||
|
/**
|
||||||
|
* Type of the file
|
||||||
|
*/
|
||||||
|
readonly type: FileType
|
||||||
|
/**
|
||||||
|
* File reference (if any)
|
||||||
|
*/
|
||||||
|
readonly fileReference: Uint8Array | null
|
||||||
|
/**
|
||||||
|
* Context of the file location
|
||||||
|
*/
|
||||||
|
readonly location: TypeRemoteFileLocation
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFileIdLike(obj: unknown): obj is string | RawFullRemoteFileLocation {
|
||||||
|
return (
|
||||||
|
typeof obj === 'string' ||
|
||||||
|
(obj !== null && typeof obj === 'object' && (obj as { _: unknown })._ === 'remoteFileLocation')
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,240 +1,2 @@
|
||||||
import Long from 'long'
|
import * as tdFileId from './types-inner.js'
|
||||||
|
export { tdFileId }
|
||||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
||||||
export namespace tdFileId {
|
|
||||||
export const PERSISTENT_ID_VERSION_OLD = 2
|
|
||||||
export const PERSISTENT_ID_VERSION = 4
|
|
||||||
|
|
||||||
export const WEB_LOCATION_FLAG = 1 << 24
|
|
||||||
export const FILE_REFERENCE_FLAG = 1 << 25
|
|
||||||
|
|
||||||
export const CURRENT_VERSION = 48
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An error occurred while parsing or serializing a File ID
|
|
||||||
*/
|
|
||||||
export class FileIdError extends Error {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A newer version of File ID is provided, which is
|
|
||||||
* currently not supported by the library.
|
|
||||||
*
|
|
||||||
* Feel free to open an issue on Github!
|
|
||||||
*/
|
|
||||||
export class UnsupportedError extends FileIdError {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* File ID was invalid, meaning that something did not
|
|
||||||
* add up while parsing the file ID, or the file ID object
|
|
||||||
* contained invalid data.
|
|
||||||
*/
|
|
||||||
export class InvalidFileIdError extends FileIdError {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provided File ID cannot be converted to that TL object.
|
|
||||||
*/
|
|
||||||
export class ConversionError extends FileIdError {
|
|
||||||
constructor(to: string) {
|
|
||||||
super(`Cannot convert given File ID to ${to}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum FileType {
|
|
||||||
Thumbnail,
|
|
||||||
ProfilePhoto,
|
|
||||||
Photo,
|
|
||||||
VoiceNote,
|
|
||||||
Video,
|
|
||||||
Document,
|
|
||||||
Encrypted,
|
|
||||||
Temp,
|
|
||||||
Sticker,
|
|
||||||
Audio,
|
|
||||||
Animation,
|
|
||||||
EncryptedThumbnail,
|
|
||||||
Wallpaper,
|
|
||||||
VideoNote,
|
|
||||||
SecureRaw,
|
|
||||||
Secure,
|
|
||||||
Background,
|
|
||||||
DocumentAsFile,
|
|
||||||
Size,
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
// naming convention just like in @mtcute/tl
|
|
||||||
|
|
||||||
// additionally, `_` discriminator is used,
|
|
||||||
// so we can interoperate with normal TL objects
|
|
||||||
// like InputFile just by checking `_`
|
|
||||||
|
|
||||||
// for nested types, we don't bother with full type name
|
|
||||||
// for discriminator since it is really only used internally,
|
|
||||||
// so uniqueness is pretty much guaranteed
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This photo is a legacy photo that is
|
|
||||||
* represented simply by a secret number
|
|
||||||
*/
|
|
||||||
export interface RawPhotoSizeSourceLegacy {
|
|
||||||
readonly _: 'legacy'
|
|
||||||
readonly secret: Long
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This photo is a thumbnail, and its size
|
|
||||||
* is provided here as a one-letter string
|
|
||||||
*/
|
|
||||||
export interface RawPhotoSizeSourceThumbnail {
|
|
||||||
readonly _: 'thumbnail'
|
|
||||||
readonly fileType: FileType
|
|
||||||
readonly thumbnailType: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This photo is a profile photo of
|
|
||||||
* some peer, and their ID and access
|
|
||||||
* hash are provided here.
|
|
||||||
*/
|
|
||||||
export interface RawPhotoSizeSourceDialogPhoto {
|
|
||||||
readonly _: 'dialogPhoto'
|
|
||||||
readonly big: boolean
|
|
||||||
readonly id: number
|
|
||||||
readonly accessHash: Long
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This photo is a thumbnail for a a sticker set,
|
|
||||||
* and set's ID and access hash are provided here
|
|
||||||
*/
|
|
||||||
export interface RawPhotoSizeSourceStickerSetThumbnail {
|
|
||||||
readonly _: 'stickerSetThumbnail'
|
|
||||||
readonly id: Long
|
|
||||||
readonly accessHash: Long
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This photo is a legacy photo containing
|
|
||||||
* volume_id, local_id and secret
|
|
||||||
*/
|
|
||||||
export interface RawPhotoSizeSourceFullLegacy {
|
|
||||||
readonly _: 'fullLegacy'
|
|
||||||
readonly volumeId: Long
|
|
||||||
readonly localId: number
|
|
||||||
readonly secret: Long
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This photo is a legacy dialog photo
|
|
||||||
*/
|
|
||||||
export interface RawPhotoSizeSourceDialogPhotoLegacy extends Omit<RawPhotoSizeSourceDialogPhoto, '_'> {
|
|
||||||
readonly _: 'dialogPhotoLegacy'
|
|
||||||
readonly volumeId: Long
|
|
||||||
readonly localId: number
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This photo is a legacy sticker set thumbnail
|
|
||||||
*/
|
|
||||||
export interface RawPhotoSizeSourceStickerSetThumbnailLegacy
|
|
||||||
extends Omit<RawPhotoSizeSourceStickerSetThumbnail, '_'> {
|
|
||||||
readonly _: 'stickerSetThumbnailLegacy'
|
|
||||||
readonly volumeId: Long
|
|
||||||
readonly localId: number
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This photo is a legacy sticker set identified by a version
|
|
||||||
*/
|
|
||||||
export interface RawPhotoSizeSourceStickerSetThumbnailVersion
|
|
||||||
extends Omit<RawPhotoSizeSourceStickerSetThumbnail, '_'> {
|
|
||||||
readonly _: 'stickerSetThumbnailVersion'
|
|
||||||
readonly version: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TypePhotoSizeSource =
|
|
||||||
| RawPhotoSizeSourceLegacy
|
|
||||||
| RawPhotoSizeSourceThumbnail
|
|
||||||
| RawPhotoSizeSourceDialogPhoto
|
|
||||||
| RawPhotoSizeSourceStickerSetThumbnail
|
|
||||||
| RawPhotoSizeSourceFullLegacy
|
|
||||||
| RawPhotoSizeSourceDialogPhotoLegacy
|
|
||||||
| RawPhotoSizeSourceStickerSetThumbnailLegacy
|
|
||||||
| RawPhotoSizeSourceStickerSetThumbnailVersion
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An external web file
|
|
||||||
*/
|
|
||||||
export interface RawWebRemoteFileLocation {
|
|
||||||
readonly _: 'web'
|
|
||||||
readonly url: string
|
|
||||||
readonly accessHash: Long
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A photo, that, in addition to ID and access
|
|
||||||
* hash, has its own `source` and detailed
|
|
||||||
* information about photo location on the
|
|
||||||
* servers.
|
|
||||||
*/
|
|
||||||
export interface RawPhotoRemoteFileLocation {
|
|
||||||
readonly _: 'photo'
|
|
||||||
readonly id: Long
|
|
||||||
readonly accessHash: Long
|
|
||||||
readonly source: TypePhotoSizeSource
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A common file that is represented as a pair
|
|
||||||
* of ID and access hash
|
|
||||||
*/
|
|
||||||
export interface RawCommonRemoteFileLocation {
|
|
||||||
readonly _: 'common'
|
|
||||||
readonly id: Long
|
|
||||||
readonly accessHash: Long
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TypeRemoteFileLocation =
|
|
||||||
| RawWebRemoteFileLocation
|
|
||||||
| RawPhotoRemoteFileLocation
|
|
||||||
| RawCommonRemoteFileLocation
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An object representing information about
|
|
||||||
* file location, that was either parsed from
|
|
||||||
* TDLib compatible File ID, or will be parsed
|
|
||||||
* to one.
|
|
||||||
*
|
|
||||||
* This type is supposed to be an intermediate step
|
|
||||||
* between TL objects and string file IDs,
|
|
||||||
* and if you are using `@mtcute/client`, you don't
|
|
||||||
* really need to care about this type at all.
|
|
||||||
*/
|
|
||||||
export interface RawFullRemoteFileLocation {
|
|
||||||
readonly _: 'remoteFileLocation'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DC ID where this file is located
|
|
||||||
*/
|
|
||||||
readonly dcId: number
|
|
||||||
/**
|
|
||||||
* Type of the file
|
|
||||||
*/
|
|
||||||
readonly type: FileType
|
|
||||||
/**
|
|
||||||
* File reference (if any)
|
|
||||||
*/
|
|
||||||
readonly fileReference: Uint8Array | null
|
|
||||||
/**
|
|
||||||
* Context of the file location
|
|
||||||
*/
|
|
||||||
readonly location: TypeRemoteFileLocation
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isFileIdLike(obj: unknown): obj is string | RawFullRemoteFileLocation {
|
|
||||||
return (
|
|
||||||
typeof obj === 'string' ||
|
|
||||||
(obj !== null && typeof obj === 'object' && (obj as { _: unknown })._ === 'remoteFileLocation')
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue