feat(core): support paid media
This commit is contained in:
parent
55f3e535c2
commit
323bdb2ad9
10 changed files with 182 additions and 46 deletions
|
@ -221,6 +221,22 @@ export async function _normalizeInputMedia(
|
|||
}
|
||||
}
|
||||
|
||||
if (media.type === 'paid') {
|
||||
let medias: tl.TypeInputMedia[]
|
||||
|
||||
if (Array.isArray(media.media)) {
|
||||
medias = await Promise.all(media.media.map((m) => _normalizeInputMedia(client, m, params)))
|
||||
} else {
|
||||
medias = [await _normalizeInputMedia(client, media.media, params)]
|
||||
}
|
||||
|
||||
return {
|
||||
_: 'inputMediaPaidMedia',
|
||||
starsAmount: Long.isLong(media.starsAmount) ? media.starsAmount : Long.fromNumber(media.starsAmount),
|
||||
extendedMedia: medias,
|
||||
}
|
||||
}
|
||||
|
||||
let inputFile: tl.TypeInputFile | undefined = undefined
|
||||
let thumb: tl.TypeInputFile | undefined = undefined
|
||||
let mime = 'application/octet-stream'
|
||||
|
|
|
@ -50,6 +50,7 @@ export async function uploadMedia(
|
|||
case 'inputMediaInvoice':
|
||||
case 'inputMediaPoll':
|
||||
case 'inputMediaDice':
|
||||
case 'inputMediaPaidMedia':
|
||||
throw new MtArgumentError("This media can't be uploaded")
|
||||
}
|
||||
|
||||
|
|
|
@ -70,8 +70,16 @@ export async function sendMedia(
|
|||
file: media,
|
||||
}
|
||||
}
|
||||
const { peer, replyTo, scheduleDate, chainId, quickReplyShortcut } = await _processCommonSendParameters(
|
||||
client,
|
||||
chatId,
|
||||
params,
|
||||
)
|
||||
|
||||
const inputMedia = await _normalizeInputMedia(client, media, params)
|
||||
const inputMedia = await _normalizeInputMedia(client, media, {
|
||||
progressCallback: params.progressCallback,
|
||||
uploadPeer: peer,
|
||||
})
|
||||
|
||||
const [message, entities] = await _normalizeInputText(
|
||||
client,
|
||||
|
@ -81,11 +89,6 @@ export async function sendMedia(
|
|||
)
|
||||
|
||||
const replyMarkup = BotKeyboard._convertToTl(params.replyMarkup)
|
||||
const { peer, replyTo, scheduleDate, chainId, quickReplyShortcut } = await _processCommonSendParameters(
|
||||
client,
|
||||
chatId,
|
||||
params,
|
||||
)
|
||||
|
||||
const randomId = randomLong()
|
||||
const res = await _maybeInvokeWithBusinessConnection(
|
||||
|
|
42
packages/core/src/highlevel/types/media/extended-media.ts
Normal file
42
packages/core/src/highlevel/types/media/extended-media.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { tl } from '@mtcute/tl'
|
||||
|
||||
import { makeInspectable } from '../../utils/inspectable.js'
|
||||
import { memoizeGetters } from '../../utils/memoize.js'
|
||||
import { Thumbnail } from './thumbnail.js'
|
||||
|
||||
export class ExtendedMediaPreview {
|
||||
constructor(public readonly raw: tl.RawMessageExtendedMediaPreview) {}
|
||||
|
||||
/**
|
||||
* Width of the preview, in pixels (if available, else 0)
|
||||
*/
|
||||
get width(): number {
|
||||
return this.raw.w ?? 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Height of the preview, in pixels (if available, else 0)
|
||||
*/
|
||||
get height(): number {
|
||||
return this.raw.h ?? 0
|
||||
}
|
||||
|
||||
get thumbnail(): Thumbnail | null {
|
||||
if (!this.raw.thumb) {
|
||||
return null
|
||||
}
|
||||
|
||||
return new Thumbnail(this.raw, this.raw.thumb)
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is a video, the duration of the video,
|
||||
* in seconds (if available, else 0)
|
||||
*/
|
||||
get videoDuration(): number {
|
||||
return this.raw.videoDuration ?? 0
|
||||
}
|
||||
}
|
||||
|
||||
memoizeGetters(ExtendedMediaPreview, ['thumbnail'])
|
||||
makeInspectable(ExtendedMediaPreview)
|
|
@ -2,10 +2,12 @@ export * from './audio.js'
|
|||
export * from './contact.js'
|
||||
export * from './dice.js'
|
||||
export * from './document.js'
|
||||
export * from './extended-media.js'
|
||||
export * from './game.js'
|
||||
export * from './input-media/index.js'
|
||||
export * from './invoice.js'
|
||||
export * from './location.js'
|
||||
export * from './paid-media.js'
|
||||
export * from './photo.js'
|
||||
export * from './poll.js'
|
||||
export * from './sticker.js'
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
InputMediaGeoLive,
|
||||
InputMediaInvoice,
|
||||
InputMediaLike,
|
||||
InputMediaPaidMedia,
|
||||
InputMediaPhoto,
|
||||
InputMediaPoll,
|
||||
InputMediaQuiz,
|
||||
|
@ -280,6 +281,13 @@ export function webpage(url: string, params: OmitTypeAndFile<InputMediaWebpage,
|
|||
return ret
|
||||
}
|
||||
|
||||
export function paid(params: OmitTypeAndFile<InputMediaPaidMedia>): InputMediaPaidMedia {
|
||||
const ret = params as tl.Mutable<InputMediaPaidMedia>
|
||||
ret.type = 'paid'
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a document to be sent, which subtype
|
||||
* is inferred automatically by file contents.
|
||||
|
|
|
@ -578,6 +578,20 @@ export interface InputMediaWebpage extends CaptionMixin {
|
|||
url: string
|
||||
}
|
||||
|
||||
export interface InputMediaPaidMedia extends CaptionMixin {
|
||||
type: 'paid'
|
||||
|
||||
/**
|
||||
* Media to be sent
|
||||
*/
|
||||
media: MaybeArray<InputMediaLike>
|
||||
|
||||
/**
|
||||
* Amount of stars that should be paid for the media
|
||||
*/
|
||||
starsAmount: number | tl.Long
|
||||
}
|
||||
|
||||
/**
|
||||
* Input media that can be sent somewhere.
|
||||
*
|
||||
|
@ -606,4 +620,5 @@ export type InputMediaLike =
|
|||
| InputMediaQuiz
|
||||
| InputMediaStory
|
||||
| InputMediaWebpage
|
||||
| InputMediaPaidMedia
|
||||
| tl.TypeInputMedia
|
||||
|
|
|
@ -5,7 +5,7 @@ import { makeInspectable } from '../../utils/index.js'
|
|||
import { memoizeGetters } from '../../utils/memoize.js'
|
||||
import { WebDocument } from '../files/web-document.js'
|
||||
import type { MessageMedia } from '../messages/message-media.js'
|
||||
import { Thumbnail } from './thumbnail.js'
|
||||
import { ExtendedMediaPreview } from './extended-media.js'
|
||||
|
||||
/**
|
||||
* Information about invoice's extended media.
|
||||
|
@ -15,43 +15,6 @@ import { Thumbnail } from './thumbnail.js'
|
|||
*/
|
||||
export type InvoiceExtendedMediaState = 'none' | 'preview' | 'full'
|
||||
|
||||
export class InvoiceExtendedMediaPreview {
|
||||
constructor(public readonly raw: tl.RawMessageExtendedMediaPreview) {}
|
||||
|
||||
/**
|
||||
* Width of the preview, in pixels (if available, else 0)
|
||||
*/
|
||||
get width(): number {
|
||||
return this.raw.w ?? 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Height of the preview, in pixels (if available, else 0)
|
||||
*/
|
||||
get height(): number {
|
||||
return this.raw.h ?? 0
|
||||
}
|
||||
|
||||
get thumbnail(): Thumbnail | null {
|
||||
if (!this.raw.thumb) {
|
||||
return null
|
||||
}
|
||||
|
||||
return new Thumbnail(this.raw, this.raw.thumb)
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is a video, the duration of the video,
|
||||
* in seconds (if available, else 0)
|
||||
*/
|
||||
get videoDuration(): number {
|
||||
return this.raw.videoDuration ?? 0
|
||||
}
|
||||
}
|
||||
|
||||
memoizeGetters(InvoiceExtendedMediaPreview, ['thumbnail'])
|
||||
makeInspectable(InvoiceExtendedMediaPreview)
|
||||
|
||||
/**
|
||||
* An invoice
|
||||
*/
|
||||
|
@ -152,12 +115,12 @@ export class Invoice {
|
|||
* Only available if {@link extendedMediaState} is `preview`.
|
||||
* Otherwise, throws an error.
|
||||
*/
|
||||
get extendedMediaPreview(): InvoiceExtendedMediaPreview {
|
||||
get extendedMediaPreview(): ExtendedMediaPreview {
|
||||
if (this.raw.extendedMedia?._ !== 'messageExtendedMediaPreview') {
|
||||
throw new MtArgumentError('No extended media preview available')
|
||||
}
|
||||
|
||||
return new InvoiceExtendedMediaPreview(this.raw.extendedMedia)
|
||||
return new ExtendedMediaPreview(this.raw.extendedMedia)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
73
packages/core/src/highlevel/types/media/paid-media.ts
Normal file
73
packages/core/src/highlevel/types/media/paid-media.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import { tl } from '@mtcute/tl'
|
||||
|
||||
import { makeInspectable } from '../../utils/inspectable.js'
|
||||
import { memoizeGetters } from '../../utils/memoize.js'
|
||||
import { MessageMedia } from '../messages/message-media.js'
|
||||
import { ExtendedMediaPreview } from './extended-media.js'
|
||||
|
||||
export class PaidMedia {
|
||||
readonly type = 'paid' as const
|
||||
|
||||
constructor(
|
||||
readonly raw: tl.RawMessageMediaPaidMedia,
|
||||
private readonly _extendedMedia: MessageMedia[],
|
||||
) {}
|
||||
|
||||
/** Whether this media was paid for */
|
||||
get isPaid(): boolean {
|
||||
return this._extendedMedia !== undefined
|
||||
}
|
||||
|
||||
/** Price of the media (in Telegram Stars) */
|
||||
get price(): tl.Long {
|
||||
return this.raw.starsAmount
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the available media previews.
|
||||
*
|
||||
* If the media is already paid for, this will return an empty array.
|
||||
*/
|
||||
get previews(): ExtendedMediaPreview[] {
|
||||
const res: ExtendedMediaPreview[] = []
|
||||
|
||||
this.raw.extendedMedia.forEach((m) => {
|
||||
if (m._ !== 'messageExtendedMediaPreview') return
|
||||
|
||||
res.push(new ExtendedMediaPreview(m))
|
||||
})
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the available full media.
|
||||
*
|
||||
* If the media is not paid for, this will return an empty array.
|
||||
*/
|
||||
get medias(): MessageMedia[] {
|
||||
return this._extendedMedia
|
||||
}
|
||||
|
||||
/**
|
||||
* Input media TL object generated from this object,
|
||||
* to be used inside {@link InputMediaLike} and
|
||||
* {@link TelegramClient.sendMedia}.
|
||||
*
|
||||
* Will throw if the media is not paid for.
|
||||
*/
|
||||
get inputMedia(): tl.TypeInputMedia {
|
||||
if (!this.isPaid) {
|
||||
throw new Error('Cannot get input media for non-paid media')
|
||||
}
|
||||
|
||||
return {
|
||||
_: 'inputMediaPaidMedia',
|
||||
starsAmount: this.raw.starsAmount,
|
||||
extendedMedia: this._extendedMedia.map((m) => m!.inputMedia),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
makeInspectable(PaidMedia, undefined, ['inputMedia'])
|
||||
memoizeGetters(PaidMedia, ['previews', 'inputMedia'])
|
|
@ -9,6 +9,7 @@ import { parseDocument } from '../media/document-utils.js'
|
|||
import { Game } from '../media/game.js'
|
||||
import { Invoice } from '../media/invoice.js'
|
||||
import { LiveLocation, Location } from '../media/location.js'
|
||||
import { PaidMedia } from '../media/paid-media.js'
|
||||
import { Photo } from '../media/photo.js'
|
||||
import { Poll } from '../media/poll.js'
|
||||
import { Sticker } from '../media/sticker.js'
|
||||
|
@ -37,6 +38,7 @@ export type MessageMedia =
|
|||
| Poll
|
||||
| Invoice
|
||||
| MediaStory
|
||||
| PaidMedia
|
||||
| null
|
||||
export type MessageMediaType = Exclude<MessageMedia, null>['type']
|
||||
|
||||
|
@ -96,6 +98,17 @@ export function _messageMediaFromTl(peers: PeersIndex | null, m: tl.TypeMessageM
|
|||
|
||||
return new MediaStory(m, peers)
|
||||
}
|
||||
case 'messageMediaPaidMedia': {
|
||||
const extended: MessageMedia[] = []
|
||||
|
||||
m.extendedMedia.forEach((e) => {
|
||||
if (e._ !== 'messageExtendedMedia') return
|
||||
|
||||
extended.push(_messageMediaFromTl(peers, e.media))
|
||||
})
|
||||
|
||||
return new PaidMedia(m, extended)
|
||||
}
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue