feat(client): better support for sticker sets, getInstalledStickers and getStickerSet methods

This commit is contained in:
teidesu 2021-04-25 15:39:42 +03:00
parent 68ce10d292
commit 2fc8a90edf
7 changed files with 299 additions and 17 deletions

View file

@ -75,6 +75,8 @@ import {
setDefaultParseMode,
unregisterParseMode,
} from './methods/parse-modes/parse-modes'
import { getInstalledStickers } from './methods/stickers/get-installed-stickers'
import { getStickerSet } from './methods/stickers/get-sticker-set'
import {
_fetchUpdatesState,
_handleUpdate,
@ -105,6 +107,7 @@ import {
PartialExcept,
ReplyMarkup,
SentCode,
StickerSet,
TakeoutSession,
TermsOfService,
UploadFileLike,
@ -1675,6 +1678,24 @@ export interface TelegramClient extends BaseTelegramClient {
* @throws MtCuteError When given parse mode is not registered.
*/
setDefaultParseMode(name: string): void
/**
* Get a list of all installed sticker packs
*
* > **Note**: This method returns *brief* meta information about
* > the packs, that does not include the stickers themselves.
* > Use {@link StickerSet.getFull} or {@link getStickerSet}
* > to get a stickerset that will include the stickers
*
*/
getInstalledStickers(): Promise<StickerSet[]>
/**
* Get a sticker pack and stickers inside of it.
*
* @param id Sticker pack short name, dice emoji, `"emoji"` for animated emojis or input ID
*/
getStickerSet(
id: string | { dice: string } | tl.TypeInputStickerSet
): Promise<StickerSet>
/**
* Base function for update handling. Replace or override this function
* and implement your own update handler, and call this function
@ -1846,6 +1867,8 @@ export class TelegramClient extends BaseTelegramClient {
unregisterParseMode = unregisterParseMode
getParseMode = getParseMode
setDefaultParseMode = setDefaultParseMode
getInstalledStickers = getInstalledStickers
getStickerSet = getStickerSet
protected _fetchUpdatesState = _fetchUpdatesState
protected _loadStorage = _loadStorage
protected _saveStorage = _saveStorage

View file

@ -26,7 +26,8 @@ import {
Message,
ReplyMarkup,
InputMediaLike,
TakeoutSession
TakeoutSession,
StickerSet
} from '../types'
// @copy

View file

@ -0,0 +1,26 @@
import { TelegramClient } from '../../client'
import { StickerSet } from '../../types'
import { assertTypeIs } from '../../utils/type-assertion'
/**
* Get a list of all installed sticker packs
*
* > **Note**: This method returns *brief* meta information about
* > the packs, that does not include the stickers themselves.
* > Use {@link StickerSet.getFull} or {@link getStickerSet}
* > to get a stickerset that will include the stickers
*
* @internal
*/
export async function getInstalledStickers(
this: TelegramClient
): Promise<StickerSet[]> {
const res = await this.call({
_: 'messages.getAllStickers',
hash: 0
})
assertTypeIs('getInstalledStickers', res, 'messages.allStickers')
return res.sets.map((set) => new StickerSet(this, set))
}

View file

@ -0,0 +1,38 @@
import { TelegramClient } from '../../client'
import { tl } from '@mtcute/tl'
import { StickerSet } from '../../types'
/**
* Get a sticker pack and stickers inside of it.
*
* @param id Sticker pack short name, dice emoji, `"emoji"` for animated emojis or input ID
* @internal
*/
export async function getStickerSet(
this: TelegramClient,
id: string | { dice: string } | tl.TypeInputStickerSet
): Promise<StickerSet> {
let input: tl.TypeInputStickerSet
if (typeof id === 'string') {
input = id === 'emoji' ? {
_: 'inputStickerSetAnimatedEmoji'
} : {
_: 'inputStickerSetShortName',
shortName: id
}
} else if ('dice' in id) {
input = {
_: 'inputStickerSetDice',
emoticon: id.dice
}
} else {
input = id
}
const res = await this.call({
_: 'messages.getStickerSet',
stickerset: input
})
return new StickerSet(this, res)
}

View file

@ -2,6 +2,7 @@ import { RawDocument } from './document'
import { TelegramClient } from '../../client'
import { tl } from '@mtcute/tl'
import { makeInspectable } from '../utils'
import { StickerSet } from '../misc'
/**
* A sticker
@ -36,14 +37,15 @@ export class Sticker extends RawDocument {
}
/**
* Emoji associated with this sticker.
* Primary emoji associated with this sticker,
* that is displayed in dialogs list.
*
* If there is none, empty string is returned.
*
* **Note:** This only contains at most one emoji.
* Some stickers have multiple associated emojis,
* but only one is returned here. This is Telegram's
* limitation! Use {@link getAllEmojis}
* limitation! Use {@link getAllEmojis} instead.
*/
get emoji(): string {
return this.attr.alt
@ -72,13 +74,10 @@ export class Sticker extends RawDocument {
*
* Returns `null` if there's no sticker set.
*/
async getStickerSet(): Promise<tl.messages.RawStickerSet | null> {
async getStickerSet(): Promise<StickerSet | null> {
if (!this.hasStickerSet) return null
return this.client.call({
_: 'messages.getStickerSet',
stickerset: this.attr.stickerset,
})
return this.client.getStickerSet(this.attr.stickerset)
}
/**
@ -88,18 +87,11 @@ export class Sticker extends RawDocument {
* with a sticker pack.
*/
async getAllEmojis(): Promise<string> {
let ret = ''
const set = await this.getStickerSet()
if (!set) return ''
set.packs.forEach((pack) => {
if (pack.documents.some((doc) => doc.eq(this.doc.id))) {
ret += pack.emoticon
}
})
return ret
return set.stickers.find((it) => it.sticker.doc.id.eq(this.doc.id))!
.emoji
}
}

View file

@ -1 +1,2 @@
export * from './takeout-session'
export * from './sticker-set'

View file

@ -0,0 +1,201 @@
import { TelegramClient } from '../../client'
import { tl } from '@mtcute/tl'
import { makeInspectable } from '../utils'
import { Sticker } from '../media'
import { MtCuteEmptyError, MtCuteTypeAssertionError } from '../errors'
import { parseDocument } from '../media/document-utils'
export namespace StickerSet {
/**
* Information about one sticker inside the set
*/
export interface StickerInfo {
/**
* Primary alt emoji that is displayed in dialogs list
*/
readonly alt: string
/**
* One or more emojis representing this sticker
*/
readonly emoji: string
/**
* Document with the actual sticker
*/
readonly sticker: Sticker
}
}
/**
* A stickerset (aka sticker pack)
*/
export class StickerSet {
readonly client: TelegramClient
readonly brief: tl.RawStickerSet
readonly full?: tl.messages.RawStickerSet
/**
* Whether this object contains information about stickers inside the set
*/
readonly isFull: boolean
constructor(
client: TelegramClient,
raw: tl.RawStickerSet | tl.messages.RawStickerSet
) {
this.client = client
if (raw._ === 'messages.stickerSet') {
this.full = raw
this.brief = raw.set
} else {
this.brief = raw
}
this.isFull = raw._ === 'messages.stickerSet'
}
/**
* Whether this stickerset was archived (due to too many saved stickers in the current account)
*/
get isArchived(): boolean {
return this.brief.archived!
}
/**
* Whether this stickerset is official
*/
get isOfficial(): boolean {
return this.brief.official!
}
/**
* Whether this stickerset is a set of masks
*/
get isMasks(): boolean {
return this.brief.masks!
}
/**
* Whether this stickerset is animated
*/
get isAnimated(): boolean {
return this.brief.animated!
}
/**
* Date when this stickerset was installed
*/
get installedDate(): Date | null {
return this.brief.installedDate
? new Date(this.brief.installedDate * 1000)
: null
}
/**
* Number of stickers in this stickerset
*/
get count(): number {
return this.brief.count
}
/**
* Input sticker set to be used in other API methods
*/
get inputStickerSet(): tl.TypeInputStickerSet {
return {
_: 'inputStickerSetID',
id: this.brief.id,
accessHash: this.brief.accessHash
}
}
/**
* Title of the stickerset
*/
get title(): string {
return this.brief.title
}
/**
* Short name of stickerset to use in `tg://addstickers?set=short_name`
* or `https://t.me/addstickers/short_name`
*/
get shortName(): string {
return this.brief.shortName
}
private _stickers?: StickerSet.StickerInfo[]
/**
* List of stickers inside this stickerset
*
* @throws MtCuteEmptyError
* In case this object does not contain info about stickers (i.e. {@link isFull} = false)
*/
get stickers(): StickerSet.StickerInfo[] {
if (!this.isFull) throw new MtCuteEmptyError()
if (!this._stickers) {
this._stickers = []
const index: Record<string, tl.Mutable<StickerSet.StickerInfo>> = {}
this.full!.documents.forEach((doc) => {
const sticker = parseDocument(
this.client,
doc as tl.RawDocument
)
if (!(sticker instanceof Sticker)) {
throw new MtCuteTypeAssertionError(
'StickerSet#stickers (full.documents)',
'Sticker',
sticker.mimeType
)
}
const info: tl.Mutable<StickerSet.StickerInfo> = {
alt: sticker.emoji,
emoji: '', // populated later
sticker
}
this._stickers!.push(info)
index[doc.id.toString()] = info
})
this.full!.packs.forEach((pack) => {
pack.documents.forEach((id) => {
const sid = id.toString()
if (sid in index) {
index[sid].emoji += pack.emoticon
}
})
})
}
return this._stickers
}
/**
* Find stickers given their emoji.
*
* @param emoji Emoji to search for
* @throws MtCuteEmptyError
* In case this object does not contain info about stickers (i.e. {@link isFull} = false)
*/
getStickersByEmoji(emoji: string): StickerSet.StickerInfo[] {
return this.stickers.filter(it => it.alt === emoji || it.emoji.indexOf(emoji) != -1)
}
/**
* Get full stickerset object.
*
* If this object is already full, this method will just
* return `this`
*/
async getFull(): Promise<StickerSet> {
if (this.isFull) return this
return this.client.getStickerSet(this.inputStickerSet)
}
}
makeInspectable(StickerSet, ['isFull'])