refactor: use type discrimination for media types in Message
this should result in cleaner code without the need to import everything
This commit is contained in:
parent
abbebeddf9
commit
d0e3ebda80
16 changed files with 72 additions and 33 deletions
|
@ -8,6 +8,8 @@ import { tdFileId } from '@mtcute/file-id'
|
|||
* An audio file
|
||||
*/
|
||||
export class Audio extends RawDocument {
|
||||
readonly type = 'audio' as const
|
||||
|
||||
readonly doc: tl.RawDocument
|
||||
readonly attr: tl.RawDocumentAttributeAudio
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ import { tl } from '@mtcute/tl'
|
|||
import { makeInspectable } from '../utils'
|
||||
|
||||
export class Contact {
|
||||
readonly type = 'contact' as const
|
||||
|
||||
readonly obj: tl.RawMessageMediaContact
|
||||
|
||||
constructor(obj: tl.RawMessageMediaContact) {
|
||||
|
|
|
@ -5,6 +5,8 @@ import { makeInspectable } from '../utils'
|
|||
* A dice or another interactive random emoji.
|
||||
*/
|
||||
export class Dice {
|
||||
readonly type = 'dice' as const
|
||||
|
||||
readonly obj: tl.RawMessageMediaDice
|
||||
|
||||
/**
|
||||
|
|
|
@ -169,6 +169,8 @@ export class RawDocument extends FileLocation {
|
|||
* and only used for documents without any special
|
||||
* attributes.
|
||||
*/
|
||||
export class Document extends RawDocument {}
|
||||
export class Document extends RawDocument {
|
||||
readonly type = 'document' as const
|
||||
}
|
||||
|
||||
makeInspectable(Document, ['fileSize', 'dcId'], ['inputMedia', 'inputDocument'])
|
||||
|
|
|
@ -5,6 +5,8 @@ import { TelegramClient } from '../../client'
|
|||
import { makeInspectable } from '../utils'
|
||||
|
||||
export class Game {
|
||||
readonly type = 'game' as const
|
||||
|
||||
readonly game: tl.RawGame
|
||||
readonly client: TelegramClient
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ import { MtCuteArgumentError } from '../errors'
|
|||
* An invoice
|
||||
*/
|
||||
export class Invoice {
|
||||
readonly type = 'invoice' as const
|
||||
|
||||
readonly client: TelegramClient
|
||||
readonly raw: tl.RawMessageMediaInvoice
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { TelegramClient } from '../../client'
|
|||
/**
|
||||
* A point on the map
|
||||
*/
|
||||
export class Location {
|
||||
export class RawLocation {
|
||||
readonly client: TelegramClient
|
||||
readonly geo: tl.RawGeoPoint
|
||||
|
||||
|
@ -105,7 +105,13 @@ export class Location {
|
|||
}
|
||||
}
|
||||
|
||||
export class LiveLocation extends Location {
|
||||
export class Location extends RawLocation {
|
||||
readonly type = 'location' as const
|
||||
}
|
||||
|
||||
export class LiveLocation extends RawLocation {
|
||||
readonly type = 'live_location' as const
|
||||
|
||||
readonly live: tl.RawMessageMediaGeoLive
|
||||
|
||||
constructor(client: TelegramClient, live: tl.RawMessageMediaGeoLive) {
|
||||
|
|
|
@ -9,6 +9,8 @@ import { makeInspectable } from '../utils'
|
|||
* A photo
|
||||
*/
|
||||
export class Photo extends FileLocation {
|
||||
readonly type: 'photo'
|
||||
|
||||
/** Raw TL object */
|
||||
readonly raw: tl.RawPhoto
|
||||
|
||||
|
@ -78,6 +80,7 @@ export class Photo extends FileLocation {
|
|||
this.raw = raw
|
||||
this.width = width
|
||||
this.height = height
|
||||
this.type = 'photo'
|
||||
}
|
||||
|
||||
/** Date this photo was sent */
|
||||
|
|
|
@ -38,6 +38,8 @@ export namespace Poll {
|
|||
}
|
||||
|
||||
export class Poll {
|
||||
readonly type = 'poll' as const
|
||||
|
||||
readonly client: TelegramClient
|
||||
readonly raw: tl.TypePoll
|
||||
readonly results?: tl.TypePollResults
|
||||
|
|
|
@ -40,6 +40,8 @@ const MASK_POS = ['forehead', 'eyes', 'mouth', 'chin'] as const
|
|||
* A sticker
|
||||
*/
|
||||
export class Sticker extends RawDocument {
|
||||
readonly type = 'sticker' as const
|
||||
|
||||
readonly attr: tl.RawDocumentAttributeSticker
|
||||
readonly attrSize?: tl.RawDocumentAttributeImageSize
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ export namespace Venue {
|
|||
}
|
||||
|
||||
export class Venue {
|
||||
readonly type = 'venue' as const
|
||||
|
||||
readonly client: TelegramClient
|
||||
readonly raw: tl.RawMessageMediaVenue
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ import { tdFileId } from '@mtcute/file-id'
|
|||
* **Note:** Legacy GIF animations are also wrapped with this class.
|
||||
*/
|
||||
export class Video extends RawDocument {
|
||||
readonly type = 'video' as const
|
||||
|
||||
readonly attr:
|
||||
| tl.RawDocumentAttributeVideo
|
||||
| tl.RawDocumentAttributeImageSize
|
||||
|
|
|
@ -8,6 +8,8 @@ import { tdFileId } from '@mtcute/file-id'
|
|||
* An voice note.
|
||||
*/
|
||||
export class Voice extends RawDocument {
|
||||
readonly type = 'voice' as const
|
||||
|
||||
readonly doc: tl.RawDocument
|
||||
readonly attr: tl.RawDocumentAttributeAudio
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ import { MtCuteArgumentError } from '../errors'
|
|||
* of my own observations and experiments.
|
||||
*/
|
||||
export class WebPage {
|
||||
readonly type = 'web_page' as const
|
||||
|
||||
readonly client: TelegramClient
|
||||
readonly raw: tl.RawWebPage
|
||||
|
||||
|
@ -71,7 +73,7 @@ export class WebPage {
|
|||
*
|
||||
* `unknown` is returned if no type is returned in the TL object.
|
||||
*/
|
||||
get type(): string {
|
||||
get previewType(): string {
|
||||
return this.raw.type || 'unknown'
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ export function _messageMediaFromTl(
|
|||
return new Contact(m)
|
||||
case 'messageMediaDocument':
|
||||
if (!(m.document?._ === 'document')) return null
|
||||
return parseDocument(this.client, m.document)
|
||||
return parseDocument(this.client, m.document) as MessageMedia
|
||||
case 'messageMediaGeo':
|
||||
if (!(m.geo._ === 'geoPoint')) return null
|
||||
return new Location(this.client, m.geo)
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
Invoice,
|
||||
Game,
|
||||
WebPage,
|
||||
MessageAction
|
||||
MessageAction, RawLocation,
|
||||
} from '@mtcute/client'
|
||||
import { MaybeArray } from '@mtcute/core'
|
||||
import { ChatMemberUpdate } from './updates'
|
||||
|
@ -52,7 +52,7 @@ import { UserTypingUpdate } from './updates/user-typing-update'
|
|||
* Example without type mod:
|
||||
* ```typescript
|
||||
*
|
||||
* const hasPhoto: UpdateFilter<Message> = msg => msg.media instanceof Photo
|
||||
* const hasPhoto: UpdateFilter<Message> = msg => msg.media?.type === 'photo'
|
||||
*
|
||||
* // ..later..
|
||||
* tg.onNewMessage(hasPhoto, async (msg) => {
|
||||
|
@ -68,7 +68,7 @@ import { UserTypingUpdate } from './updates/user-typing-update'
|
|||
* Example with type mod:
|
||||
* ```typescript
|
||||
*
|
||||
* const hasPhoto: UpdateFilter<Message, { media: Photo }> = msg => msg.media instanceof Photo
|
||||
* const hasPhoto: UpdateFilter<Message, { media: Photo }> = msg => msg.media?.type === 'photo'
|
||||
*
|
||||
* // ..later..
|
||||
* tg.onNewMessage(hasPhoto, async (msg) => {
|
||||
|
@ -87,7 +87,7 @@ import { UserTypingUpdate } from './updates/user-typing-update'
|
|||
* > Bad example:
|
||||
* > ```typescript
|
||||
* > // we check for `Photo`, but type contains `Audio`. this will be a problem!
|
||||
* > const hasPhoto: UpdateFilter<Message, { media: Audio }> = msg => msg.media instanceof Photo
|
||||
* > const hasPhoto: UpdateFilter<Message, { media: Audio }> = msg => msg.media?.type === 'photo'
|
||||
* >
|
||||
* > // ..later..
|
||||
* > tg.onNewMessage(hasPhoto, async (msg) => {
|
||||
|
@ -595,19 +595,19 @@ export namespace filters {
|
|||
* Filter messages containing a photo
|
||||
*/
|
||||
export const photo: UpdateFilter<Message, { media: Photo }> = (msg) =>
|
||||
msg.media?.constructor === Photo
|
||||
msg.media?.type === 'photo'
|
||||
|
||||
/**
|
||||
* Filter messages containing a dice
|
||||
*/
|
||||
export const dice: UpdateFilter<Message, { media: Dice }> = (msg) =>
|
||||
msg.media?.constructor === Dice
|
||||
msg.media?.type === 'dice'
|
||||
|
||||
/**
|
||||
* Filter messages containing a contact
|
||||
*/
|
||||
export const contact: UpdateFilter<Message, { media: Contact }> = (msg) =>
|
||||
msg.media?.constructor === Contact
|
||||
msg.media?.type === 'contact'
|
||||
|
||||
/**
|
||||
* Filter messages containing a document
|
||||
|
@ -615,7 +615,7 @@ export namespace filters {
|
|||
* This will also match media like audio, video, voice
|
||||
* that also use Documents
|
||||
*/
|
||||
export const rawDocument: UpdateFilter<Message, { media: RawDocument }> = (
|
||||
export const anyDocument: UpdateFilter<Message, { media: RawDocument }> = (
|
||||
msg
|
||||
) => msg.media instanceof RawDocument
|
||||
|
||||
|
@ -625,33 +625,33 @@ export namespace filters {
|
|||
* This will not match media like audio, video, voice
|
||||
*/
|
||||
export const document: UpdateFilter<Message, { media: Document }> = (msg) =>
|
||||
msg.media?.constructor === Document
|
||||
msg.media?.type === 'document'
|
||||
|
||||
/**
|
||||
* Filter messages containing an audio file
|
||||
*/
|
||||
export const audio: UpdateFilter<Message, { media: Audio }> = (msg) =>
|
||||
msg.media?.constructor === Audio
|
||||
msg.media?.type === 'audio'
|
||||
|
||||
/**
|
||||
* Filter messages containing a voice note
|
||||
*/
|
||||
export const voice: UpdateFilter<Message, { media: Voice }> = (msg) =>
|
||||
msg.media?.constructor === Voice
|
||||
msg.media?.type === 'voice'
|
||||
|
||||
/**
|
||||
* Filter messages containing a sticker
|
||||
*/
|
||||
export const sticker: UpdateFilter<Message, { media: Sticker }> = (msg) =>
|
||||
msg.media?.constructor === Sticker
|
||||
msg.media?.type === 'sticker'
|
||||
|
||||
/**
|
||||
* Filter messages containing a video.
|
||||
*
|
||||
* This includes videos, round messages and animations
|
||||
*/
|
||||
export const rawVideo: UpdateFilter<Message, { media: Video }> = (msg) =>
|
||||
msg.media?.constructor === Video
|
||||
export const anyVideo: UpdateFilter<Message, { media: Video }> = (msg) =>
|
||||
msg.media?.type === 'video'
|
||||
|
||||
/**
|
||||
* Filter messages containing a simple video.
|
||||
|
@ -670,7 +670,7 @@ export namespace filters {
|
|||
>
|
||||
}
|
||||
> = (msg) =>
|
||||
msg.media?.constructor === Video &&
|
||||
msg.media?.type === 'video' &&
|
||||
!msg.media.isAnimation &&
|
||||
!msg.media.isRound
|
||||
|
||||
|
@ -692,7 +692,7 @@ export namespace filters {
|
|||
>
|
||||
}
|
||||
> = (msg) =>
|
||||
msg.media?.constructor === Video &&
|
||||
msg.media?.type === 'video' &&
|
||||
msg.media.isAnimation &&
|
||||
!msg.media.isRound
|
||||
|
||||
|
@ -711,17 +711,23 @@ export namespace filters {
|
|||
>
|
||||
}
|
||||
> = (msg) =>
|
||||
msg.media?.constructor === Video &&
|
||||
msg.media?.type === 'video' &&
|
||||
!msg.media.isAnimation &&
|
||||
msg.media.isRound
|
||||
|
||||
/**
|
||||
* Filter messages containing a location.
|
||||
*
|
||||
* This includes live locations
|
||||
* Filter messages containing any location (live or static).
|
||||
*/
|
||||
export const location: UpdateFilter<Message, { media: Location }> = (msg) =>
|
||||
msg.media instanceof Location
|
||||
export const anyLocation: UpdateFilter<Message, { media: Location }> = (msg) =>
|
||||
msg.media instanceof RawLocation
|
||||
|
||||
/**
|
||||
* Filter messages containing a static (non-live) location.
|
||||
*/
|
||||
export const location: UpdateFilter<
|
||||
Message,
|
||||
{ media: LiveLocation }
|
||||
> = (msg) => msg.media?.type === 'location'
|
||||
|
||||
/**
|
||||
* Filter messages containing a live location.
|
||||
|
@ -729,37 +735,37 @@ export namespace filters {
|
|||
export const liveLocation: UpdateFilter<
|
||||
Message,
|
||||
{ media: LiveLocation }
|
||||
> = (msg) => msg.media?.constructor === LiveLocation
|
||||
> = (msg) => msg.media?.type === 'live_location'
|
||||
|
||||
/**
|
||||
* Filter messages containing a game.
|
||||
*/
|
||||
export const game: UpdateFilter<Message, { media: Game }> = (msg) =>
|
||||
msg.media?.constructor === Game
|
||||
msg.media?.type === 'game'
|
||||
|
||||
/**
|
||||
* Filter messages containing a webpage preview.
|
||||
*/
|
||||
export const webpage: UpdateFilter<Message, { media: WebPage }> = (msg) =>
|
||||
msg.media?.constructor === WebPage
|
||||
msg.media?.type === 'web_page'
|
||||
|
||||
/**
|
||||
* Filter messages containing a venue.
|
||||
*/
|
||||
export const venue: UpdateFilter<Message, { media: Venue }> = (msg) =>
|
||||
msg.media?.constructor === Venue
|
||||
msg.media?.type === 'venue'
|
||||
|
||||
/**
|
||||
* Filter messages containing a poll.
|
||||
*/
|
||||
export const poll: UpdateFilter<Message, { media: Poll }> = (msg) =>
|
||||
msg.media?.constructor === Poll
|
||||
msg.media?.type === 'poll'
|
||||
|
||||
/**
|
||||
* Filter messages containing an invoice.
|
||||
*/
|
||||
export const invoice: UpdateFilter<Message, { media: Invoice }> = (msg) =>
|
||||
msg.media?.constructor === Invoice
|
||||
msg.media?.type === 'invoice'
|
||||
|
||||
/**
|
||||
* Filter objects that match a given regular expression
|
||||
|
|
Loading…
Reference in a new issue