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), data: JSON.stringify(media.providerData),
}, },
startParam: media.startParam, 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 * 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
} }
/** /**

View file

@ -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)

View file

@ -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}"`

View file

@ -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
} }

View file

@ -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)
} }
} }