feat(client): support invoices with extended media
This commit is contained in:
parent
1935413aee
commit
e75ac9fa5d
6 changed files with 170 additions and 17 deletions
|
@ -132,6 +132,9 @@ export async function _normalizeInputMedia(
|
||||||
data: JSON.stringify(media.providerData),
|
data: JSON.stringify(media.providerData),
|
||||||
},
|
},
|
||||||
startParam: media.startParam,
|
startParam: media.startParam,
|
||||||
|
extendedMedia: media.extendedMedia
|
||||||
|
? await this._normalizeInputMedia(media.extendedMedia, params)
|
||||||
|
: undefined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -435,6 +435,11 @@ export interface InputMediaInvoice extends CaptionMixin {
|
||||||
* Can be a URL, or a TL object with input web document
|
* Can be a URL, or a TL object with input web document
|
||||||
*/
|
*/
|
||||||
photo?: string | tl.TypeInputWebDocument
|
photo?: string | tl.TypeInputWebDocument
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended media (i.e. media that will be available once the invoice is paid)
|
||||||
|
*/
|
||||||
|
extendedMedia?: InputMediaLike
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -3,7 +3,63 @@ import { tl } from '@mtcute/tl'
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
import { makeInspectable } from '../utils'
|
import { makeInspectable } from '../utils'
|
||||||
import { WebDocument } from '../files/web-document'
|
import { WebDocument } from '../files/web-document'
|
||||||
import { MtArgumentError } from '../errors'
|
import { MtArgumentError, MtTypeAssertionError } from '../errors'
|
||||||
|
import { _messageMediaFromTl, MessageMedia } from '../messages'
|
||||||
|
import { Thumbnail } from './thumbnail'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about invoice's extended media.
|
||||||
|
* - `none`: there is no extended media in this invoice
|
||||||
|
* - `preview`: there is only a preview of this invoice's media ({@link Invoice.extendedMediaPreview})
|
||||||
|
* - `full`: there is a full version of this invoice's media available ({@link Invoice.extendedMedia})
|
||||||
|
*/
|
||||||
|
export type InvoiceExtendedMediaState = 'none' | 'preview' | 'full'
|
||||||
|
|
||||||
|
export class InvoiceExtendedMediaPreview {
|
||||||
|
constructor(
|
||||||
|
public readonly client: TelegramClient,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
private _thumbnail?: Thumbnail
|
||||||
|
get thumbnail(): Thumbnail | null {
|
||||||
|
if (!this.raw.thumb) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._thumbnail) {
|
||||||
|
this._thumbnail = new Thumbnail(
|
||||||
|
this.client,
|
||||||
|
this.raw,
|
||||||
|
this.raw.thumb
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._thumbnail
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this is a video, the duration of the video,
|
||||||
|
* in seconds (if available, else 0)
|
||||||
|
*/
|
||||||
|
get videoDuration(): number {
|
||||||
|
return this.raw.videoDuration ?? 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An invoice
|
* An invoice
|
||||||
|
@ -92,6 +148,58 @@ export class Invoice {
|
||||||
return this.raw.startParam
|
return this.raw.startParam
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this invoice has extended media
|
||||||
|
*/
|
||||||
|
get extendedMediaState(): InvoiceExtendedMediaState {
|
||||||
|
if (!this.raw.extendedMedia) return 'none'
|
||||||
|
if (this.raw.extendedMedia._ === 'messageExtendedMediaPreview')
|
||||||
|
return 'preview'
|
||||||
|
return 'full'
|
||||||
|
}
|
||||||
|
|
||||||
|
private _extendedMediaPreview?: InvoiceExtendedMediaPreview
|
||||||
|
/**
|
||||||
|
* Get the invoice's extended media preview.
|
||||||
|
* Only available if {@link extendedMediaState} is `preview`.
|
||||||
|
* Otherwise, throws an error.
|
||||||
|
*/
|
||||||
|
get extendedMediaPreview(): InvoiceExtendedMediaPreview {
|
||||||
|
if (this.raw.extendedMedia?._ !== 'messageExtendedMediaPreview')
|
||||||
|
throw new MtArgumentError('No extended media preview available')
|
||||||
|
|
||||||
|
if (!this._extendedMediaPreview) {
|
||||||
|
this._extendedMediaPreview = new InvoiceExtendedMediaPreview(
|
||||||
|
this.client,
|
||||||
|
this.raw.extendedMedia
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._extendedMediaPreview
|
||||||
|
}
|
||||||
|
|
||||||
|
private _extendedMedia?: MessageMedia
|
||||||
|
/**
|
||||||
|
* Get the invoice's extended media.
|
||||||
|
* Only available if {@link extendedMediaState} is `full`.
|
||||||
|
* Otherwise, throws an error.
|
||||||
|
*/
|
||||||
|
get extendedMedia(): MessageMedia {
|
||||||
|
if (this.raw.extendedMedia?._ !== "messageExtendedMedia") {
|
||||||
|
throw new MtArgumentError('No extended media available')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._extendedMedia) {
|
||||||
|
this._extendedMedia = _messageMediaFromTl(
|
||||||
|
this.client,
|
||||||
|
null,
|
||||||
|
this.raw.extendedMedia.media
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._extendedMedia
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input media TL object generated from this object,
|
* Input media TL object generated from this object,
|
||||||
* to be used inside {@link InputMediaLike} and
|
* to be used inside {@link InputMediaLike} and
|
||||||
|
@ -108,3 +216,4 @@ export class Invoice {
|
||||||
}
|
}
|
||||||
|
|
||||||
makeInspectable(Invoice, undefined, ['inputMedia'])
|
makeInspectable(Invoice, undefined, ['inputMedia'])
|
||||||
|
makeInspectable(InvoiceExtendedMediaPreview)
|
||||||
|
|
|
@ -54,11 +54,19 @@ export class Thumbnail extends FileLocation {
|
||||||
readonly height: number
|
readonly height: number
|
||||||
|
|
||||||
private _path?: string
|
private _path?: string
|
||||||
private _media: tl.RawPhoto | tl.RawDocument | tl.RawStickerSet
|
private _media:
|
||||||
|
| tl.RawPhoto
|
||||||
|
| tl.RawDocument
|
||||||
|
| tl.RawStickerSet
|
||||||
|
| tl.RawMessageExtendedMediaPreview
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
client: TelegramClient,
|
client: TelegramClient,
|
||||||
media: tl.RawPhoto | tl.RawDocument | tl.RawStickerSet,
|
media:
|
||||||
|
| tl.RawPhoto
|
||||||
|
| tl.RawDocument
|
||||||
|
| tl.RawStickerSet
|
||||||
|
| tl.RawMessageExtendedMediaPreview,
|
||||||
sz: tl.TypePhotoSize | tl.TypeVideoSize
|
sz: tl.TypePhotoSize | tl.TypeVideoSize
|
||||||
) {
|
) {
|
||||||
switch (sz._) {
|
switch (sz._) {
|
||||||
|
@ -99,6 +107,13 @@ export class Thumbnail extends FileLocation {
|
||||||
},
|
},
|
||||||
thumbVersion: media.thumbVersion!,
|
thumbVersion: media.thumbVersion!,
|
||||||
}
|
}
|
||||||
|
} else if (media._ === 'messageExtendedMediaPreview') {
|
||||||
|
// according to tdlib and tdesktop sources, sz can only be photoStrippedSize
|
||||||
|
throw new MtTypeAssertionError(
|
||||||
|
'messageExtendedMediaPreview#thumb',
|
||||||
|
'photoStrippedSize',
|
||||||
|
sz._
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
location = {
|
location = {
|
||||||
_:
|
_:
|
||||||
|
@ -124,7 +139,11 @@ export class Thumbnail extends FileLocation {
|
||||||
client,
|
client,
|
||||||
location,
|
location,
|
||||||
size,
|
size,
|
||||||
media._ === 'stickerSet' ? media.thumbDcId : media.dcId
|
media._ === 'stickerSet'
|
||||||
|
? media.thumbDcId
|
||||||
|
: media._ === 'messageExtendedMediaPreview'
|
||||||
|
? 0
|
||||||
|
: media.dcId
|
||||||
)
|
)
|
||||||
this.raw = sz
|
this.raw = sz
|
||||||
this.width = width
|
this.width = width
|
||||||
|
@ -176,7 +195,8 @@ export class Thumbnail extends FileLocation {
|
||||||
if (
|
if (
|
||||||
this.raw._ !== 'photoSize' &&
|
this.raw._ !== 'photoSize' &&
|
||||||
this.raw._ !== 'photoSizeProgressive' &&
|
this.raw._ !== 'photoSizeProgressive' &&
|
||||||
this.raw._ !== 'videoSize'
|
this.raw._ !== 'videoSize' ||
|
||||||
|
this._media._ === 'messageExtendedMediaPreview' // just for type safety
|
||||||
) {
|
) {
|
||||||
throw new MtArgumentError(
|
throw new MtArgumentError(
|
||||||
`Cannot generate a file ID for "${this.raw.type}"`
|
`Cannot generate a file ID for "${this.raw.type}"`
|
||||||
|
@ -238,7 +258,8 @@ export class Thumbnail extends FileLocation {
|
||||||
if (
|
if (
|
||||||
this.raw._ !== 'photoSize' &&
|
this.raw._ !== 'photoSize' &&
|
||||||
this.raw._ !== 'photoSizeProgressive' &&
|
this.raw._ !== 'photoSizeProgressive' &&
|
||||||
this.raw._ !== 'videoSize'
|
this.raw._ !== 'videoSize' ||
|
||||||
|
this._media._ === 'messageExtendedMediaPreview' // just for type safety
|
||||||
) {
|
) {
|
||||||
throw new MtArgumentError(
|
throw new MtArgumentError(
|
||||||
`Cannot generate a unique file ID for "${this.raw.type}"`
|
`Cannot generate a unique file ID for "${this.raw.type}"`
|
||||||
|
|
|
@ -19,6 +19,9 @@ import {
|
||||||
} from '../media'
|
} from '../media'
|
||||||
import { parseDocument } from '../media/document-utils'
|
import { parseDocument } from '../media/document-utils'
|
||||||
import { Message } from './message'
|
import { Message } from './message'
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { PeersIndex } from '../peers'
|
||||||
|
import { MtTypeAssertionError } from '../errors'
|
||||||
|
|
||||||
/** A media inside of a {@link Message} */
|
/** A media inside of a {@link Message} */
|
||||||
export type MessageMedia =
|
export type MessageMedia =
|
||||||
|
@ -38,41 +41,53 @@ export type MessageMedia =
|
||||||
| Poll
|
| Poll
|
||||||
| Invoice
|
| Invoice
|
||||||
| null
|
| null
|
||||||
|
|
||||||
// todo: successful_payment, connected_website
|
// todo: successful_payment, connected_website
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export function _messageMediaFromTl(
|
export function _messageMediaFromTl(
|
||||||
this: Message,
|
client: TelegramClient,
|
||||||
|
peers: PeersIndex | null,
|
||||||
m: tl.TypeMessageMedia
|
m: tl.TypeMessageMedia
|
||||||
): MessageMedia {
|
): MessageMedia {
|
||||||
switch (m._) {
|
switch (m._) {
|
||||||
case 'messageMediaPhoto':
|
case 'messageMediaPhoto':
|
||||||
if (!(m.photo?._ === 'photo')) return null
|
if (!(m.photo?._ === 'photo')) return null
|
||||||
return new Photo(this.client, m.photo)
|
return new Photo(client, m.photo)
|
||||||
case 'messageMediaDice':
|
case 'messageMediaDice':
|
||||||
return new Dice(m)
|
return new Dice(m)
|
||||||
case 'messageMediaContact':
|
case 'messageMediaContact':
|
||||||
return new Contact(m)
|
return new Contact(m)
|
||||||
case 'messageMediaDocument':
|
case 'messageMediaDocument':
|
||||||
if (!(m.document?._ === 'document')) return null
|
if (!(m.document?._ === 'document')) return null
|
||||||
return parseDocument(this.client, m.document) as MessageMedia
|
return parseDocument(client, m.document) as MessageMedia
|
||||||
case 'messageMediaGeo':
|
case 'messageMediaGeo':
|
||||||
if (!(m.geo._ === 'geoPoint')) return null
|
if (!(m.geo._ === 'geoPoint')) return null
|
||||||
return new Location(this.client, m.geo)
|
return new Location(client, m.geo)
|
||||||
case 'messageMediaGeoLive':
|
case 'messageMediaGeoLive':
|
||||||
if (!(m.geo._ === 'geoPoint')) return null
|
if (!(m.geo._ === 'geoPoint')) return null
|
||||||
return new LiveLocation(this.client, m)
|
return new LiveLocation(client, m)
|
||||||
case 'messageMediaGame':
|
case 'messageMediaGame':
|
||||||
return new Game(this.client, m.game)
|
return new Game(client, m.game)
|
||||||
case 'messageMediaWebPage':
|
case 'messageMediaWebPage':
|
||||||
if (!(m.webpage._ === 'webPage')) return null
|
if (!(m.webpage._ === 'webPage')) return null
|
||||||
return new WebPage(this.client, m.webpage)
|
return new WebPage(client, m.webpage)
|
||||||
case 'messageMediaVenue':
|
case 'messageMediaVenue':
|
||||||
return new Venue(this.client, m)
|
return new Venue(client, m)
|
||||||
case 'messageMediaPoll':
|
case 'messageMediaPoll':
|
||||||
return new Poll(this.client, m.poll, this._peers, m.results)
|
if (!peers) {
|
||||||
|
// should only be possible in extended media
|
||||||
|
// (and afaik polls can't be there)
|
||||||
|
throw new MtTypeAssertionError(
|
||||||
|
"can't create poll without peers index",
|
||||||
|
'PeersIndex',
|
||||||
|
'null'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Poll(client, m.poll, peers, m.results)
|
||||||
case 'messageMediaInvoice':
|
case 'messageMediaInvoice':
|
||||||
return new Invoice(this.client, m)
|
return new Invoice(client, m)
|
||||||
default:
|
default:
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -431,7 +431,7 @@ export class Message {
|
||||||
) {
|
) {
|
||||||
this._media = null
|
this._media = null
|
||||||
} else {
|
} else {
|
||||||
this._media = _messageMediaFromTl.call(this, this.raw.media)
|
this._media = _messageMediaFromTl(this.client, this._peers, this.raw.media)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue