refactor(client): extracted input file and media normalization to own methods, merged sendPhoto and sendMedia methods
This commit is contained in:
parent
0eb0ac91eb
commit
a67c4ae85e
6 changed files with 283 additions and 444 deletions
|
@ -55,6 +55,8 @@ import { downloadAsBuffer } from './methods/files/download-buffer'
|
|||
import { downloadToFile } from './methods/files/download-file'
|
||||
import { downloadAsIterable } from './methods/files/download-iterable'
|
||||
import { downloadAsStream } from './methods/files/download-stream'
|
||||
import { _normalizeInputFile } from './methods/files/normalize-input-file'
|
||||
import { _normalizeInputMedia } from './methods/files/normalize-input-media'
|
||||
import { uploadFile } from './methods/files/upload-file'
|
||||
import { deleteMessages } from './methods/messages/delete-messages'
|
||||
import { editMessage } from './methods/messages/edit-message'
|
||||
|
@ -70,7 +72,6 @@ import { searchMessages } from './methods/messages/search-messages'
|
|||
import { sendDice } from './methods/messages/send-dice'
|
||||
import { sendLocation } from './methods/messages/send-location'
|
||||
import { sendMedia } from './methods/messages/send-media'
|
||||
import { sendPhoto } from './methods/messages/send-photo'
|
||||
import { sendText } from './methods/messages/send-text'
|
||||
import { unpinMessage } from './methods/messages/unpin-message'
|
||||
import { initTakeoutSession } from './methods/misc/init-takeout-session'
|
||||
|
@ -1600,7 +1601,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
}
|
||||
): Promise<Message>
|
||||
/**
|
||||
* Send a single media.
|
||||
* Send a single media (a photo or a document-based media)
|
||||
*
|
||||
* @param chatId ID of the chat, its username, phone or `"me"` or `"self"`
|
||||
* @param media
|
||||
|
@ -1608,6 +1609,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
* and Bot API compatible File ID, which will be wrapped
|
||||
* in {@link InputMedia.auto}
|
||||
* @param params Additional sending parameters
|
||||
* @see InputMedia
|
||||
*/
|
||||
sendMedia(
|
||||
chatId: InputPeerLike,
|
||||
|
@ -1661,84 +1663,6 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
clearDraft?: boolean
|
||||
}
|
||||
): Promise<Message>
|
||||
/**
|
||||
* Send a single photo
|
||||
*
|
||||
* @param chatId ID of the chat, its username, phone or `"me"` or `"self"`
|
||||
* @param photo Photo contained in the message.
|
||||
* @param params Additional sending parameters
|
||||
*/
|
||||
sendPhoto(
|
||||
chatId: InputPeerLike,
|
||||
photo: InputFileLike,
|
||||
params?: {
|
||||
/**
|
||||
* Caption for the photo
|
||||
*/
|
||||
caption?: string
|
||||
|
||||
/**
|
||||
* Message to reply to. Either a message object or message ID.
|
||||
*/
|
||||
replyTo?: number | Message
|
||||
|
||||
/**
|
||||
* Parse mode to use to parse entities before sending
|
||||
* the message. Defaults to current default parse mode (if any).
|
||||
*
|
||||
* Passing `null` will explicitly disable formatting.
|
||||
*/
|
||||
parseMode?: string | null
|
||||
|
||||
/**
|
||||
* List of formatting entities to use instead of parsing via a
|
||||
* parse mode.
|
||||
*
|
||||
* **Note:** Passing this makes the method ignore {@link parseMode}
|
||||
*/
|
||||
entities?: tl.TypeMessageEntity[]
|
||||
|
||||
/**
|
||||
* Whether to send this message silently.
|
||||
*/
|
||||
silent?: boolean
|
||||
|
||||
/**
|
||||
* If set, the message will be scheduled to this date.
|
||||
* When passing a number, a UNIX time in ms is expected.
|
||||
*/
|
||||
schedule?: Date | number
|
||||
|
||||
/**
|
||||
* For bots: inline or reply markup or an instruction
|
||||
* to hide a reply keyboard or to force a reply.
|
||||
*/
|
||||
replyMarkup?: ReplyMarkup
|
||||
|
||||
/**
|
||||
* Self-Destruct timer.
|
||||
* If set, the photo will self-destruct in a given number
|
||||
* of seconds.
|
||||
*/
|
||||
ttlSeconds?: number
|
||||
|
||||
/**
|
||||
* Function that will be called after some part has been uploaded.
|
||||
* Only used when a file that requires uploading is passed.
|
||||
*
|
||||
* @param uploaded Number of bytes already uploaded
|
||||
* @param total Total file size
|
||||
*/
|
||||
progressCallback?: (uploaded: number, total: number) => void
|
||||
|
||||
/**
|
||||
* Whether to clear draft after sending this message.
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
clearDraft?: boolean
|
||||
}
|
||||
): Promise<Message>
|
||||
/**
|
||||
* Send a text message
|
||||
*
|
||||
|
@ -2024,6 +1948,8 @@ export class TelegramClient extends BaseTelegramClient {
|
|||
downloadToFile = downloadToFile
|
||||
downloadAsIterable = downloadAsIterable
|
||||
downloadAsStream = downloadAsStream
|
||||
protected _normalizeInputFile = _normalizeInputFile
|
||||
protected _normalizeInputMedia = _normalizeInputMedia
|
||||
uploadFile = uploadFile
|
||||
deleteMessages = deleteMessages
|
||||
editMessage = editMessage
|
||||
|
@ -2039,7 +1965,6 @@ export class TelegramClient extends BaseTelegramClient {
|
|||
sendDice = sendDice
|
||||
sendLocation = sendLocation
|
||||
sendMedia = sendMedia
|
||||
sendPhoto = sendPhoto
|
||||
sendText = sendText
|
||||
unpinMessage = unpinMessage
|
||||
initTakeoutSession = initTakeoutSession
|
||||
|
|
49
packages/client/src/methods/files/normalize-input-file.ts
Normal file
49
packages/client/src/methods/files/normalize-input-file.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { InputFileLike, isUploadedFile, MtCuteArgumentError } from '../../types'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { tdFileId } from '@mtcute/file-id'
|
||||
|
||||
/**
|
||||
* Normalize a {@link InputFileLike} to `InputFile`,
|
||||
* uploading it if needed.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export async function _normalizeInputFile(
|
||||
this: TelegramClient,
|
||||
input: InputFileLike,
|
||||
params: {
|
||||
progressCallback?: (uploaded: number, total: number) => void
|
||||
fileName?: string
|
||||
fileSize?: number
|
||||
fileMime?: string
|
||||
}
|
||||
): Promise<tl.TypeInputFile> {
|
||||
if (typeof input === 'object' && tl.isAnyInputMedia(input)) {
|
||||
throw new MtCuteArgumentError(
|
||||
"InputFile can't be created from an InputMedia"
|
||||
)
|
||||
} else if (tdFileId.isFileIdLike(input)) {
|
||||
if (typeof input === 'string' && input.match(/^file:/)) {
|
||||
const uploaded = await this.uploadFile({
|
||||
file: input.substr(5),
|
||||
...params,
|
||||
})
|
||||
return uploaded.inputFile
|
||||
} else {
|
||||
throw new MtCuteArgumentError(
|
||||
"InputFile can't be created from an URL or a File ID"
|
||||
)
|
||||
}
|
||||
} else if (isUploadedFile(input)) {
|
||||
return input.inputFile
|
||||
} else if (typeof input === 'object' && tl.isAnyInputFile(input)) {
|
||||
return input
|
||||
} else {
|
||||
const uploaded = await this.uploadFile({
|
||||
file: input,
|
||||
...params,
|
||||
})
|
||||
return uploaded.inputFile
|
||||
}
|
||||
}
|
171
packages/client/src/methods/files/normalize-input-media.ts
Normal file
171
packages/client/src/methods/files/normalize-input-media.ts
Normal file
|
@ -0,0 +1,171 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import {
|
||||
InputMediaLike,
|
||||
isUploadedFile,
|
||||
MtCuteArgumentError,
|
||||
UploadFileLike,
|
||||
} from '../../types'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import {
|
||||
fileIdToInputDocument,
|
||||
fileIdToInputPhoto,
|
||||
parseFileId,
|
||||
tdFileId,
|
||||
} from '@mtcute/file-id'
|
||||
import { extractFileName } from '../../utils/file-utils'
|
||||
|
||||
/**
|
||||
* Normalize an {@link InputMediaLike} to `InputMedia`,
|
||||
* uploading the file if needed.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export async function _normalizeInputMedia(
|
||||
this: TelegramClient,
|
||||
media: InputMediaLike,
|
||||
params: {
|
||||
progressCallback?: (uploaded: number, total: number) => void
|
||||
}
|
||||
): Promise<tl.TypeInputMedia> {
|
||||
// my condolences to those poor souls who are going to maintain this (myself included)
|
||||
|
||||
let inputFile: tl.TypeInputFile | undefined = undefined
|
||||
let thumb: tl.TypeInputFile | undefined = undefined
|
||||
let mime = 'application/octet-stream'
|
||||
|
||||
const upload = async (file: UploadFileLike): Promise<void> => {
|
||||
const uploaded = await this.uploadFile({
|
||||
file,
|
||||
progressCallback: params.progressCallback,
|
||||
fileName: media.fileName,
|
||||
fileMime:
|
||||
media.type === 'sticker'
|
||||
? media.isAnimated
|
||||
? 'application/x-tgsticker'
|
||||
: 'image/webp'
|
||||
: media.fileMime,
|
||||
fileSize: media.fileSize,
|
||||
})
|
||||
inputFile = uploaded.inputFile
|
||||
mime = uploaded.mime
|
||||
}
|
||||
|
||||
const input = media.file
|
||||
if (tdFileId.isFileIdLike(input)) {
|
||||
if (typeof input === 'string' && input.match(/^https?:\/\//)) {
|
||||
return {
|
||||
_:
|
||||
media.type === 'photo'
|
||||
? 'inputMediaPhotoExternal'
|
||||
: 'inputMediaDocumentExternal',
|
||||
url: input,
|
||||
}
|
||||
} else if (typeof input === 'string' && input.match(/^file:/)) {
|
||||
await upload(input.substr(5))
|
||||
} else {
|
||||
const parsed =
|
||||
typeof input === 'string' ? parseFileId(input) : input
|
||||
|
||||
if (parsed.location._ === 'photo') {
|
||||
return {
|
||||
_: 'inputMediaPhoto',
|
||||
id: fileIdToInputPhoto(parsed),
|
||||
}
|
||||
} else if (parsed.location._ === 'web') {
|
||||
return {
|
||||
_:
|
||||
parsed.type === tdFileId.FileType.Photo
|
||||
? 'inputMediaPhotoExternal'
|
||||
: 'inputMediaDocumentExternal',
|
||||
url: parsed.location.url,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
_: 'inputMediaDocument',
|
||||
id: fileIdToInputDocument(parsed),
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (typeof input === 'object' && tl.isAnyInputMedia(input)) {
|
||||
return input
|
||||
} else if (isUploadedFile(input)) {
|
||||
inputFile = input.inputFile
|
||||
mime = input.mime
|
||||
} else if (typeof input === 'object' && tl.isAnyInputFile(input)) {
|
||||
inputFile = input
|
||||
} else {
|
||||
await upload(input)
|
||||
}
|
||||
|
||||
if (!inputFile) throw new Error('should not happen')
|
||||
|
||||
if (media.type === 'photo') {
|
||||
return {
|
||||
_: 'inputMediaUploadedPhoto',
|
||||
file: inputFile,
|
||||
ttlSeconds: media.ttlSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
if ('thumb' in media && media.thumb) {
|
||||
thumb = await this._normalizeInputFile(media.thumb, {})
|
||||
}
|
||||
|
||||
const attributes: tl.TypeDocumentAttribute[] = []
|
||||
|
||||
if (media.type !== 'voice') {
|
||||
attributes.push({
|
||||
_: 'documentAttributeFilename',
|
||||
fileName:
|
||||
media.fileName ||
|
||||
(typeof media.file === 'string'
|
||||
? extractFileName(media.file)
|
||||
: 'unnamed'),
|
||||
})
|
||||
}
|
||||
|
||||
if (media.type === 'video') {
|
||||
attributes.push({
|
||||
_: 'documentAttributeVideo',
|
||||
duration: media.duration || 0,
|
||||
w: media.width || 0,
|
||||
h: media.height || 0,
|
||||
supportsStreaming: media.supportsStreaming,
|
||||
roundMessage: media.isRound,
|
||||
})
|
||||
if (media.isAnimated)
|
||||
attributes.push({ _: 'documentAttributeAnimated' })
|
||||
}
|
||||
|
||||
if (media.type === 'audio' || media.type === 'voice') {
|
||||
attributes.push({
|
||||
_: 'documentAttributeAudio',
|
||||
voice: media.type === 'voice',
|
||||
duration: media.duration || 0,
|
||||
title: media.type === 'audio' ? media.title : undefined,
|
||||
performer: media.type === 'audio' ? media.performer : undefined,
|
||||
waveform: media.type === 'voice' ? media.waveform : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
if (media.type === 'sticker') {
|
||||
attributes.push({
|
||||
_: 'documentAttributeSticker',
|
||||
stickerset: {
|
||||
_: 'inputStickerSetEmpty',
|
||||
},
|
||||
alt: media.alt ?? '',
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
_: 'inputMediaUploadedDocument',
|
||||
nosoundVideo: media.type === 'video' && media.isAnimated,
|
||||
forceFile: media.type === 'document',
|
||||
file: inputFile,
|
||||
thumb,
|
||||
mimeType: mime,
|
||||
attributes,
|
||||
ttlSeconds: media.ttlSeconds
|
||||
}
|
||||
}
|
|
@ -3,24 +3,14 @@ import {
|
|||
BotKeyboard,
|
||||
InputMediaLike,
|
||||
InputPeerLike,
|
||||
isUploadedFile,
|
||||
Message,
|
||||
MtCuteArgumentError,
|
||||
ReplyMarkup, UploadFileLike,
|
||||
ReplyMarkup,
|
||||
} from '../../types'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { extractFileName } from '../../utils/file-utils'
|
||||
import { normalizeToInputPeer } from '../../utils/peer-utils'
|
||||
import { normalizeDate, randomUlong } from '../../utils/misc-utils'
|
||||
import {
|
||||
fileIdToInputDocument,
|
||||
fileIdToInputPhoto,
|
||||
parseFileId,
|
||||
tdFileId,
|
||||
} from '@mtcute/file-id'
|
||||
|
||||
/**
|
||||
* Send a single media.
|
||||
* Send a single media (a photo or a document-based media)
|
||||
*
|
||||
* @param chatId ID of the chat, its username, phone or `"me"` or `"self"`
|
||||
* @param media
|
||||
|
@ -28,6 +18,7 @@ import {
|
|||
* and Bot API compatible File ID, which will be wrapped
|
||||
* in {@link InputMedia.auto}
|
||||
* @param params Additional sending parameters
|
||||
* @see InputMedia
|
||||
* @internal
|
||||
*/
|
||||
export async function sendMedia(
|
||||
|
@ -92,170 +83,7 @@ export async function sendMedia(
|
|||
}
|
||||
}
|
||||
|
||||
if (media.type === 'photo') {
|
||||
return this.sendPhoto(chatId, media.file, {
|
||||
caption: media.caption,
|
||||
entities: media.entities,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
let inputMedia: tl.TypeInputMedia | null = null
|
||||
|
||||
// my condolences to those poor souls who are going to maintain this (myself included)
|
||||
|
||||
let inputFile: tl.TypeInputFile | undefined = undefined
|
||||
let thumb: tl.TypeInputFile | undefined = undefined
|
||||
let mime = 'application/octet-stream'
|
||||
|
||||
const upload = async (media: InputMediaLike, file: UploadFileLike): Promise<void> => {
|
||||
const uploaded = await this.uploadFile({
|
||||
file,
|
||||
fileName: media.fileName,
|
||||
progressCallback: params!.progressCallback,
|
||||
fileMime:
|
||||
media.type === 'sticker'
|
||||
? media.isAnimated
|
||||
? 'application/x-tgsticker'
|
||||
: 'image/webp'
|
||||
: media.mime,
|
||||
fileSize: media.fileSize
|
||||
})
|
||||
inputFile = uploaded.inputFile
|
||||
mime = uploaded.mime
|
||||
}
|
||||
|
||||
const input = media.file
|
||||
if (tdFileId.isFileIdLike(input)) {
|
||||
if (typeof input === 'string' && input.match(/^https?:\/\//)) {
|
||||
inputMedia = {
|
||||
_: 'inputMediaDocumentExternal',
|
||||
url: input,
|
||||
}
|
||||
} else if (typeof input === 'string' && input.match(/^file:/)) {
|
||||
await upload(media, input.substr(5))
|
||||
} else {
|
||||
const parsed =
|
||||
typeof input === 'string' ? parseFileId(input) : input
|
||||
|
||||
if (parsed.location._ === 'photo') {
|
||||
inputMedia = {
|
||||
_: 'inputMediaPhoto',
|
||||
id: fileIdToInputPhoto(parsed),
|
||||
}
|
||||
} else if (parsed.location._ === 'web') {
|
||||
inputMedia = {
|
||||
_:
|
||||
parsed.type === tdFileId.FileType.Photo
|
||||
? 'inputMediaPhotoExternal'
|
||||
: 'inputMediaDocumentExternal',
|
||||
url: parsed.location.url,
|
||||
}
|
||||
} else {
|
||||
inputMedia = {
|
||||
_: 'inputMediaDocument',
|
||||
id: fileIdToInputDocument(parsed),
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (typeof input === 'object' && tl.isAnyInputMedia(input)) {
|
||||
inputMedia = input
|
||||
} else if (isUploadedFile(input)) {
|
||||
inputFile = input.inputFile
|
||||
mime = input.mime
|
||||
} else if (typeof input === 'object' && tl.isAnyInputFile(input)) {
|
||||
inputFile = input
|
||||
} else {
|
||||
await upload(media, input)
|
||||
}
|
||||
|
||||
if (!inputMedia) {
|
||||
if (!inputFile) throw new Error('should not happen')
|
||||
|
||||
if ('thumb' in media && media.thumb) {
|
||||
const t = media.thumb
|
||||
if (typeof t === 'object' && tl.isAnyInputMedia(t)) {
|
||||
throw new MtCuteArgumentError("Thumbnail can't be InputMedia")
|
||||
} else if (tdFileId.isFileIdLike(t)) {
|
||||
if (typeof t === 'string' && t.match(/^file:/)) {
|
||||
const uploaded = await this.uploadFile({
|
||||
file: t.substr(5),
|
||||
})
|
||||
thumb = uploaded.inputFile
|
||||
} else {
|
||||
throw new MtCuteArgumentError(
|
||||
"Thumbnail can't be a URL or a File ID"
|
||||
)
|
||||
}
|
||||
} else if (isUploadedFile(t)) {
|
||||
thumb = t.inputFile
|
||||
} else if (typeof t === 'object' && tl.isAnyInputFile(t)) {
|
||||
thumb = t
|
||||
} else {
|
||||
const uploaded = await this.uploadFile({
|
||||
file: t,
|
||||
})
|
||||
thumb = uploaded.inputFile
|
||||
}
|
||||
}
|
||||
|
||||
const attributes: tl.TypeDocumentAttribute[] = []
|
||||
|
||||
if (media.type !== 'voice') {
|
||||
attributes.push({
|
||||
_: 'documentAttributeFilename',
|
||||
fileName:
|
||||
media.fileName ||
|
||||
(typeof media.file === 'string'
|
||||
? extractFileName(media.file)
|
||||
: 'unnamed'),
|
||||
})
|
||||
}
|
||||
|
||||
if (media.type === 'video') {
|
||||
attributes.push({
|
||||
_: 'documentAttributeVideo',
|
||||
duration: media.duration || 0,
|
||||
w: media.width || 0,
|
||||
h: media.height || 0,
|
||||
supportsStreaming: media.supportsStreaming,
|
||||
roundMessage: media.isRound,
|
||||
})
|
||||
if (media.isAnimated)
|
||||
attributes.push({ _: 'documentAttributeAnimated' })
|
||||
}
|
||||
|
||||
if (media.type === 'audio' || media.type === 'voice') {
|
||||
attributes.push({
|
||||
_: 'documentAttributeAudio',
|
||||
voice: media.type === 'voice',
|
||||
duration: media.duration || 0,
|
||||
title: media.type === 'audio' ? media.title : undefined,
|
||||
performer: media.type === 'audio' ? media.performer : undefined,
|
||||
waveform: media.type === 'voice' ? media.waveform : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
if (media.type === 'sticker') {
|
||||
attributes.push({
|
||||
_: 'documentAttributeSticker',
|
||||
stickerset: {
|
||||
_: 'inputStickerSetEmpty',
|
||||
},
|
||||
alt: media.alt ?? '',
|
||||
})
|
||||
}
|
||||
|
||||
inputMedia = {
|
||||
_: 'inputMediaUploadedDocument',
|
||||
nosoundVideo: media.type === 'video' && media.isAnimated,
|
||||
forceFile: media.type === 'document',
|
||||
file: inputFile,
|
||||
thumb,
|
||||
mimeType: mime,
|
||||
attributes,
|
||||
}
|
||||
}
|
||||
const inputMedia = await this._normalizeInputMedia(media, params)
|
||||
|
||||
const [message, entities] = await this._parseEntities(
|
||||
media.caption,
|
||||
|
|
|
@ -1,181 +0,0 @@
|
|||
import {
|
||||
InputPeerLike,
|
||||
InputFileLike,
|
||||
Message,
|
||||
BotKeyboard,
|
||||
ReplyMarkup,
|
||||
isUploadedFile,
|
||||
UploadFileLike,
|
||||
} from '../../types'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { TelegramClient } from '../../client'
|
||||
import { normalizeToInputPeer } from '../../utils/peer-utils'
|
||||
import { normalizeDate, randomUlong } from '../../utils/misc-utils'
|
||||
import { fileIdToInputPhoto, tdFileId } from '@mtcute/file-id'
|
||||
|
||||
/**
|
||||
* Send a single photo
|
||||
*
|
||||
* @param chatId ID of the chat, its username, phone or `"me"` or `"self"`
|
||||
* @param photo Photo contained in the message.
|
||||
* @param params Additional sending parameters
|
||||
* @internal
|
||||
*/
|
||||
export async function sendPhoto(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
photo: InputFileLike,
|
||||
params?: {
|
||||
/**
|
||||
* Caption for the photo
|
||||
*/
|
||||
caption?: string
|
||||
|
||||
/**
|
||||
* Message to reply to. Either a message object or message ID.
|
||||
*/
|
||||
replyTo?: number | Message
|
||||
|
||||
/**
|
||||
* Parse mode to use to parse entities before sending
|
||||
* the message. Defaults to current default parse mode (if any).
|
||||
*
|
||||
* Passing `null` will explicitly disable formatting.
|
||||
*/
|
||||
parseMode?: string | null
|
||||
|
||||
/**
|
||||
* List of formatting entities to use instead of parsing via a
|
||||
* parse mode.
|
||||
*
|
||||
* **Note:** Passing this makes the method ignore {@link parseMode}
|
||||
*/
|
||||
entities?: tl.TypeMessageEntity[]
|
||||
|
||||
/**
|
||||
* Whether to send this message silently.
|
||||
*/
|
||||
silent?: boolean
|
||||
|
||||
/**
|
||||
* If set, the message will be scheduled to this date.
|
||||
* When passing a number, a UNIX time in ms is expected.
|
||||
*/
|
||||
schedule?: Date | number
|
||||
|
||||
/**
|
||||
* For bots: inline or reply markup or an instruction
|
||||
* to hide a reply keyboard or to force a reply.
|
||||
*/
|
||||
replyMarkup?: ReplyMarkup
|
||||
|
||||
/**
|
||||
* Self-Destruct timer.
|
||||
* If set, the photo will self-destruct in a given number
|
||||
* of seconds.
|
||||
*/
|
||||
ttlSeconds?: number
|
||||
|
||||
/**
|
||||
* Function that will be called after some part has been uploaded.
|
||||
* Only used when a file that requires uploading is passed.
|
||||
*
|
||||
* @param uploaded Number of bytes already uploaded
|
||||
* @param total Total file size
|
||||
*/
|
||||
progressCallback?: (uploaded: number, total: number) => void
|
||||
|
||||
/**
|
||||
* Whether to clear draft after sending this message.
|
||||
*
|
||||
* Defaults to `false`
|
||||
*/
|
||||
clearDraft?: boolean
|
||||
|
||||
/**
|
||||
* File size. Only used when uploading from streams without
|
||||
* known length.
|
||||
*/
|
||||
fileSize?: number
|
||||
}
|
||||
): Promise<Message> {
|
||||
if (!params) params = {}
|
||||
|
||||
let media: tl.TypeInputMedia
|
||||
|
||||
const upload = async (photo: UploadFileLike) => {
|
||||
const uploaded = await this.uploadFile({
|
||||
file: photo,
|
||||
progressCallback: params!.progressCallback,
|
||||
fileSize: params!.fileSize,
|
||||
})
|
||||
media = {
|
||||
_: 'inputMediaUploadedPhoto',
|
||||
file: uploaded.inputFile,
|
||||
ttlSeconds: params!.ttlSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
if (tdFileId.isFileIdLike(photo)) {
|
||||
if (typeof photo === 'string' && photo.match(/^https?:\/\//)) {
|
||||
media = {
|
||||
_: 'inputMediaPhotoExternal',
|
||||
url: photo,
|
||||
ttlSeconds: params.ttlSeconds,
|
||||
}
|
||||
} else if (typeof photo === 'string' && photo.match(/^file:/)) {
|
||||
await upload(photo.substr(5))
|
||||
} else {
|
||||
const input = fileIdToInputPhoto(photo)
|
||||
media = {
|
||||
_: 'inputMediaPhoto',
|
||||
id: input,
|
||||
}
|
||||
}
|
||||
} else if (typeof photo === 'object' && tl.isAnyInputMedia(photo)) {
|
||||
media = photo
|
||||
} else if (isUploadedFile(photo)) {
|
||||
media = {
|
||||
_: 'inputMediaUploadedPhoto',
|
||||
file: photo.inputFile,
|
||||
ttlSeconds: params.ttlSeconds,
|
||||
}
|
||||
} else if (typeof photo === 'object' && tl.isAnyInputFile(photo)) {
|
||||
media = {
|
||||
_: 'inputMediaUploadedPhoto',
|
||||
file: photo,
|
||||
ttlSeconds: params.ttlSeconds,
|
||||
}
|
||||
} else {
|
||||
await upload(photo)
|
||||
}
|
||||
|
||||
const [message, entities] = await this._parseEntities(
|
||||
params.caption,
|
||||
params.parseMode,
|
||||
params.entities
|
||||
)
|
||||
|
||||
const peer = normalizeToInputPeer(await this.resolvePeer(chatId))
|
||||
const replyMarkup = BotKeyboard._convertToTl(params.replyMarkup)
|
||||
|
||||
const res = await this.call({
|
||||
_: 'messages.sendMedia',
|
||||
media: media!,
|
||||
peer,
|
||||
silent: params.silent,
|
||||
replyToMsgId: params.replyTo
|
||||
? typeof params.replyTo === 'number'
|
||||
? params.replyTo
|
||||
: params.replyTo.id
|
||||
: undefined,
|
||||
randomId: randomUlong(),
|
||||
scheduleDate: normalizeDate(params.schedule),
|
||||
replyMarkup,
|
||||
message,
|
||||
entities,
|
||||
clearDraft: params.clearDraft,
|
||||
})
|
||||
|
||||
return this._findMessageInUpdate(res)
|
||||
}
|
|
@ -20,26 +20,39 @@ interface BaseInputMedia {
|
|||
|
||||
/**
|
||||
* Override file name for the file.
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
fileName?: string
|
||||
|
||||
/**
|
||||
* Override MIME type for the file
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
mime?: string
|
||||
fileMime?: string
|
||||
|
||||
/**
|
||||
* Override file size for the file
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
fileSize?: number
|
||||
|
||||
/**
|
||||
* TTL for the media in seconds.
|
||||
*
|
||||
* Only applicable to some media types
|
||||
*/
|
||||
ttlSeconds?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically detect media type based on file contents.
|
||||
*
|
||||
* Only works for files that are internally documents, i.e.
|
||||
* *does not* infer photos, so use {@link InputMediaPhoto} instead
|
||||
* (except for File IDs, from which photos *are* inferred)
|
||||
* Photo type is only inferred for reused files,
|
||||
* newly uploaded photos with `auto` will be
|
||||
* uploaded as a document
|
||||
*/
|
||||
export interface InputMediaAuto extends BaseInputMedia {
|
||||
type: 'auto'
|
||||
|
@ -57,21 +70,29 @@ export interface InputMediaAudio extends BaseInputMedia {
|
|||
* The thumbnail should be in JPEG format and less than 200 KB in size.
|
||||
* A thumbnail's width and height should not exceed 320 pixels.
|
||||
* Thumbnails can't be reused and can be only uploaded as a new file.
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
thumb?: InputFileLike
|
||||
|
||||
/**
|
||||
* Duration of the audio in seconds
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
duration?: number
|
||||
|
||||
/**
|
||||
* Performer of the audio
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
performer?: string
|
||||
|
||||
/**
|
||||
* Title of the audio
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
title?: string
|
||||
}
|
||||
|
@ -84,11 +105,15 @@ export interface InputMediaVoice extends BaseInputMedia {
|
|||
|
||||
/**
|
||||
* Duration of the voice message in seconds
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
duration?: number
|
||||
|
||||
/**
|
||||
* Waveform of the voice message
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
waveform?: Buffer
|
||||
}
|
||||
|
@ -105,6 +130,8 @@ export interface InputMediaDocument extends BaseInputMedia {
|
|||
* The thumbnail should be in JPEG format and less than 200 KB in size.
|
||||
* A thumbnail's width and height should not exceed 320 pixels.
|
||||
* Thumbnails can't be reused and can be only uploaded as a new file.
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
thumb?: InputFileLike
|
||||
}
|
||||
|
@ -132,11 +159,16 @@ export interface InputMediaSticker extends BaseInputMedia {
|
|||
* format, which is Lottie JSON compressed using GZip
|
||||
*
|
||||
* Defaults to `false`
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
isAnimated?: boolean
|
||||
|
||||
/**
|
||||
* An emoji representing this sticker
|
||||
*
|
||||
* Only applicable to newly uploaded files,
|
||||
* for some reason doesn't work with animated stickers.
|
||||
*/
|
||||
alt?: string
|
||||
}
|
||||
|
@ -153,36 +185,50 @@ export interface InputMediaVideo extends BaseInputMedia {
|
|||
* The thumbnail should be in JPEG format and less than 200 KB in size.
|
||||
* A thumbnail's width and height should not exceed 320 pixels.
|
||||
* Thumbnails can't be reused and can be only uploaded as a new file.
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
thumb?: InputFileLike
|
||||
|
||||
/**
|
||||
* Width of the video in pixels
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
width?: number
|
||||
|
||||
/**
|
||||
* Height of the video in pixels
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
height?: number
|
||||
|
||||
/**
|
||||
* Duration of the video in seconds
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
duration?: number
|
||||
|
||||
/**
|
||||
* Whether the video is suitable for streaming
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
supportsStreaming?: boolean
|
||||
|
||||
/**
|
||||
* Whether this video is an animated GIF
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
isAnimated?: boolean
|
||||
|
||||
/**
|
||||
* Whether this video is a round message (aka video note)
|
||||
*
|
||||
* Only applicable to newly uploaded files.
|
||||
*/
|
||||
isRound?: boolean
|
||||
}
|
||||
|
@ -311,8 +357,9 @@ export namespace InputMedia {
|
|||
* Create a document to be sent, which subtype
|
||||
* is inferred automatically by file contents.
|
||||
*
|
||||
* Only infers photos from the File ID, otherwise
|
||||
* photos will be sent as documents.
|
||||
* Photo type is only inferred for reused files,
|
||||
* newly uploaded photos with `auto` will be
|
||||
* uploaded as a document
|
||||
*/
|
||||
export function auto(
|
||||
file: InputFileLike,
|
||||
|
|
Loading…
Reference in a new issue