feat(client): support invoices with extended media

This commit is contained in:
teidesu 2022-10-30 20:51:38 +03:00
parent 1935413aee
commit e75ac9fa5d
6 changed files with 170 additions and 17 deletions

View file

@ -132,6 +132,9 @@ export async function _normalizeInputMedia(
data: JSON.stringify(media.providerData),
},
startParam: media.startParam,
extendedMedia: media.extendedMedia
? await this._normalizeInputMedia(media.extendedMedia, params)
: undefined,
}
}

View file

@ -435,6 +435,11 @@ export interface InputMediaInvoice extends CaptionMixin {
* Can be a URL, or a TL object with input web document
*/
photo?: string | tl.TypeInputWebDocument
/**
* Extended media (i.e. media that will be available once the invoice is paid)
*/
extendedMedia?: InputMediaLike
}
/**

View file

@ -3,7 +3,63 @@ import { tl } from '@mtcute/tl'
import { TelegramClient } from '../../client'
import { makeInspectable } from '../utils'
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
@ -92,6 +148,58 @@ export class Invoice {
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,
* to be used inside {@link InputMediaLike} and
@ -108,3 +216,4 @@ export class Invoice {
}
makeInspectable(Invoice, undefined, ['inputMedia'])
makeInspectable(InvoiceExtendedMediaPreview)

View file

@ -54,11 +54,19 @@ export class Thumbnail extends FileLocation {
readonly height: number
private _path?: string
private _media: tl.RawPhoto | tl.RawDocument | tl.RawStickerSet
private _media:
| tl.RawPhoto
| tl.RawDocument
| tl.RawStickerSet
| tl.RawMessageExtendedMediaPreview
constructor(
client: TelegramClient,
media: tl.RawPhoto | tl.RawDocument | tl.RawStickerSet,
media:
| tl.RawPhoto
| tl.RawDocument
| tl.RawStickerSet
| tl.RawMessageExtendedMediaPreview,
sz: tl.TypePhotoSize | tl.TypeVideoSize
) {
switch (sz._) {
@ -99,6 +107,13 @@ export class Thumbnail extends FileLocation {
},
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 {
location = {
_:
@ -124,7 +139,11 @@ export class Thumbnail extends FileLocation {
client,
location,
size,
media._ === 'stickerSet' ? media.thumbDcId : media.dcId
media._ === 'stickerSet'
? media.thumbDcId
: media._ === 'messageExtendedMediaPreview'
? 0
: media.dcId
)
this.raw = sz
this.width = width
@ -176,7 +195,8 @@ export class Thumbnail extends FileLocation {
if (
this.raw._ !== 'photoSize' &&
this.raw._ !== 'photoSizeProgressive' &&
this.raw._ !== 'videoSize'
this.raw._ !== 'videoSize' ||
this._media._ === 'messageExtendedMediaPreview' // just for type safety
) {
throw new MtArgumentError(
`Cannot generate a file ID for "${this.raw.type}"`
@ -238,7 +258,8 @@ export class Thumbnail extends FileLocation {
if (
this.raw._ !== 'photoSize' &&
this.raw._ !== 'photoSizeProgressive' &&
this.raw._ !== 'videoSize'
this.raw._ !== 'videoSize' ||
this._media._ === 'messageExtendedMediaPreview' // just for type safety
) {
throw new MtArgumentError(
`Cannot generate a unique file ID for "${this.raw.type}"`

View file

@ -19,6 +19,9 @@ import {
} from '../media'
import { parseDocument } from '../media/document-utils'
import { Message } from './message'
import { TelegramClient } from '../../client'
import { PeersIndex } from '../peers'
import { MtTypeAssertionError } from '../errors'
/** A media inside of a {@link Message} */
export type MessageMedia =
@ -38,41 +41,53 @@ export type MessageMedia =
| Poll
| Invoice
| null
// todo: successful_payment, connected_website
/** @internal */
export function _messageMediaFromTl(
this: Message,
client: TelegramClient,
peers: PeersIndex | null,
m: tl.TypeMessageMedia
): MessageMedia {
switch (m._) {
case 'messageMediaPhoto':
if (!(m.photo?._ === 'photo')) return null
return new Photo(this.client, m.photo)
return new Photo(client, m.photo)
case 'messageMediaDice':
return new Dice(m)
case 'messageMediaContact':
return new Contact(m)
case 'messageMediaDocument':
if (!(m.document?._ === 'document')) return null
return parseDocument(this.client, m.document) as MessageMedia
return parseDocument(client, m.document) as MessageMedia
case 'messageMediaGeo':
if (!(m.geo._ === 'geoPoint')) return null
return new Location(this.client, m.geo)
return new Location(client, m.geo)
case 'messageMediaGeoLive':
if (!(m.geo._ === 'geoPoint')) return null
return new LiveLocation(this.client, m)
return new LiveLocation(client, m)
case 'messageMediaGame':
return new Game(this.client, m.game)
return new Game(client, m.game)
case 'messageMediaWebPage':
if (!(m.webpage._ === 'webPage')) return null
return new WebPage(this.client, m.webpage)
return new WebPage(client, m.webpage)
case 'messageMediaVenue':
return new Venue(this.client, m)
return new Venue(client, m)
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':
return new Invoice(this.client, m)
return new Invoice(client, m)
default:
return null
}

View file

@ -431,7 +431,7 @@ export class Message {
) {
this._media = null
} else {
this._media = _messageMediaFromTl.call(this, this.raw.media)
this._media = _messageMediaFromTl(this.client, this._peers, this.raw.media)
}
}