feat(core): new stickers features
This commit is contained in:
parent
756b99e12f
commit
00da6736e6
11 changed files with 161 additions and 58 deletions
32
packages/core/src/highlevel/methods/stickers/_utils.ts
Normal file
32
packages/core/src/highlevel/methods/stickers/_utils.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { tl } from '@mtcute/tl'
|
||||
|
||||
import { ITelegramClient } from '../../client.types.js'
|
||||
import { InputStickerSetItem, MASK_POSITION_POINT_TO_TL } from '../../types/index.js'
|
||||
import { _normalizeFileToDocument } from '../files/normalize-file-to-document.js'
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @noemit
|
||||
*/
|
||||
export async function _normalizeInputStickerSetItem(
|
||||
client: ITelegramClient,
|
||||
sticker: InputStickerSetItem,
|
||||
params?: {
|
||||
progressCallback?: (uploaded: number, total: number) => void
|
||||
},
|
||||
): Promise<tl.TypeInputStickerSetItem> {
|
||||
return {
|
||||
_: 'inputStickerSetItem',
|
||||
document: await _normalizeFileToDocument(client, sticker.file, params ?? {}),
|
||||
emoji: sticker.emojis,
|
||||
maskCoords: sticker.maskPosition ?
|
||||
{
|
||||
_: 'maskCoords',
|
||||
n: MASK_POSITION_POINT_TO_TL[sticker.maskPosition.point],
|
||||
x: sticker.maskPosition.x,
|
||||
y: sticker.maskPosition.y,
|
||||
zoom: sticker.maskPosition.scale,
|
||||
} :
|
||||
undefined,
|
||||
}
|
||||
}
|
|
@ -2,17 +2,16 @@ import { ITelegramClient } from '../../client.types.js'
|
|||
import {
|
||||
InputStickerSet,
|
||||
InputStickerSetItem,
|
||||
MASK_POSITION_POINT_TO_TL,
|
||||
normalizeInputStickerSet,
|
||||
StickerSet,
|
||||
} from '../../types/index.js'
|
||||
import { _normalizeFileToDocument } from '../files/normalize-file-to-document.js'
|
||||
import { _normalizeInputStickerSetItem } from './_utils.js'
|
||||
|
||||
/**
|
||||
* Add a sticker to a sticker set.
|
||||
*
|
||||
* Only for bots, and the sticker set must
|
||||
* have been created by this bot.
|
||||
* For bots the sticker set must have been created by this bot.
|
||||
*
|
||||
* @param setId Sticker set short name or TL object with input sticker set
|
||||
* @param sticker Sticker to be added
|
||||
|
@ -36,20 +35,7 @@ export async function addStickerToSet(
|
|||
const res = await client.call({
|
||||
_: 'stickers.addStickerToSet',
|
||||
stickerset: normalizeInputStickerSet(setId),
|
||||
sticker: {
|
||||
_: 'inputStickerSetItem',
|
||||
document: await _normalizeFileToDocument(client, sticker.file, params ?? {}),
|
||||
emoji: sticker.emojis,
|
||||
maskCoords: sticker.maskPosition ?
|
||||
{
|
||||
_: 'maskCoords',
|
||||
n: MASK_POSITION_POINT_TO_TL[sticker.maskPosition.point],
|
||||
x: sticker.maskPosition.x,
|
||||
y: sticker.maskPosition.y,
|
||||
zoom: sticker.maskPosition.scale,
|
||||
} :
|
||||
undefined,
|
||||
},
|
||||
sticker: await _normalizeInputStickerSetItem(client, sticker, params),
|
||||
})
|
||||
|
||||
return new StickerSet(res)
|
||||
|
|
|
@ -5,20 +5,17 @@ import {
|
|||
InputFileLike,
|
||||
InputPeerLike,
|
||||
InputStickerSetItem,
|
||||
MASK_POSITION_POINT_TO_TL,
|
||||
StickerSet,
|
||||
StickerSourceType,
|
||||
StickerType,
|
||||
} from '../../types/index.js'
|
||||
import { _normalizeFileToDocument } from '../files/normalize-file-to-document.js'
|
||||
import { resolveUser } from '../users/resolve-peer.js'
|
||||
import { _normalizeInputStickerSetItem } from './_utils.js'
|
||||
|
||||
/**
|
||||
* Create a new sticker set.
|
||||
*
|
||||
* This is the only sticker-related method that
|
||||
* users can use (they allowed it with the "import stickers" update)
|
||||
*
|
||||
* @param params
|
||||
* @returns Newly created sticker set
|
||||
*/
|
||||
|
@ -106,22 +103,7 @@ export async function createStickerSet(
|
|||
for (const sticker of params.stickers) {
|
||||
const progressCallback = params.progressCallback?.bind(null, i)
|
||||
|
||||
inputStickers.push({
|
||||
_: 'inputStickerSetItem',
|
||||
document: await _normalizeFileToDocument(client, sticker.file, {
|
||||
progressCallback,
|
||||
}),
|
||||
emoji: sticker.emojis,
|
||||
maskCoords: sticker.maskPosition ?
|
||||
{
|
||||
_: 'maskCoords',
|
||||
n: MASK_POSITION_POINT_TO_TL[sticker.maskPosition.point],
|
||||
x: sticker.maskPosition.x,
|
||||
y: sticker.maskPosition.y,
|
||||
zoom: sticker.maskPosition.scale,
|
||||
} :
|
||||
undefined,
|
||||
})
|
||||
inputStickers.push(await _normalizeInputStickerSetItem(client, sticker, { progressCallback }))
|
||||
|
||||
i += 1
|
||||
}
|
||||
|
|
|
@ -8,8 +8,7 @@ import { fileIdToInputDocument } from '../../utils/convert-file-id.js'
|
|||
/**
|
||||
* Delete a sticker from a sticker set
|
||||
*
|
||||
* Only for bots, and the sticker set must
|
||||
* have been created by this bot.
|
||||
* For bots the sticker set must have been created by this bot.
|
||||
*
|
||||
* @param sticker
|
||||
* TDLib and Bot API compatible File ID, or a
|
||||
|
|
|
@ -9,8 +9,7 @@ import { StickerSet } from '../../types/index.js'
|
|||
*
|
||||
* > **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
|
||||
* > Use {@link getStickerSet} to get a stickerset that will include the stickers
|
||||
*/
|
||||
export async function getInstalledStickers(client: ITelegramClient): Promise<StickerSet[]> {
|
||||
const res = await client.call({
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import Long from 'long'
|
||||
|
||||
import { ITelegramClient } from '../../client.types.js'
|
||||
import { StickerSet } from '../../types/misc/sticker-set.js'
|
||||
import { ArrayPaginated } from '../../types/utils.js'
|
||||
import { makeArrayPaginated } from '../../utils/misc-utils.js'
|
||||
|
||||
// @available=user
|
||||
/**
|
||||
* Get the list of sticker sets that were created by the current user
|
||||
*/
|
||||
export async function getMyStickerSets(
|
||||
client: ITelegramClient,
|
||||
params?: {
|
||||
/** Offset for pagination */
|
||||
offset?: Long
|
||||
/** Limit for pagination */
|
||||
limit?: number
|
||||
},
|
||||
): Promise<ArrayPaginated<StickerSet, Long>> {
|
||||
const res = await client.call({
|
||||
_: 'messages.getMyStickers',
|
||||
offsetId: params?.offset ?? Long.ZERO,
|
||||
limit: params?.limit ?? 100,
|
||||
})
|
||||
|
||||
const items = res.sets.map((x) => new StickerSet(x))
|
||||
|
||||
return makeArrayPaginated(items, res.count, items[items.length - 1]?.brief.id)
|
||||
}
|
|
@ -2,9 +2,9 @@ import { ITelegramClient } from '../../client.types.js'
|
|||
import { InputStickerSet, normalizeInputStickerSet, StickerSet } from '../../types/index.js'
|
||||
|
||||
/**
|
||||
* Get a sticker pack and stickers inside of it.
|
||||
* Get a sticker set and stickers inside of it.
|
||||
*
|
||||
* @param setId Sticker pack short name, dice emoji, `"emoji"` for animated emojis or input ID
|
||||
* @param setId Sticker set identifier
|
||||
*/
|
||||
export async function getStickerSet(client: ITelegramClient, setId: InputStickerSet): Promise<StickerSet> {
|
||||
const res = await client.call({
|
||||
|
|
|
@ -9,16 +9,14 @@ import { fileIdToInputDocument } from '../../utils/convert-file-id.js'
|
|||
* Move a sticker in a sticker set
|
||||
* to another position
|
||||
*
|
||||
* Only for bots, and the sticker set must
|
||||
* have been created by this bot.
|
||||
* For bots the sticker set must have been created by this bot.
|
||||
*
|
||||
* @param sticker
|
||||
* TDLib and Bot API compatible File ID, or a
|
||||
* TL object representing a sticker to be removed
|
||||
* @param position New sticker position (starting from 0)
|
||||
* @returns Modfiied sticker set
|
||||
* @returns Modified sticker set
|
||||
*/
|
||||
|
||||
export async function moveStickerInSet(
|
||||
client: ITelegramClient,
|
||||
sticker: string | tdFileId.RawFullRemoteFileLocation | tl.TypeInputDocument,
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import { tdFileId } from '@mtcute/file-id'
|
||||
import { tl } from '@mtcute/tl'
|
||||
|
||||
import { ITelegramClient } from '../../client.types.js'
|
||||
import { InputStickerSetItem, StickerSet } from '../../types/index.js'
|
||||
import { fileIdToInputDocument } from '../../utils/convert-file-id.js'
|
||||
import { _normalizeInputStickerSetItem } from './_utils.js'
|
||||
|
||||
/**
|
||||
* Replace a sticker in a sticker set with another sticker
|
||||
*
|
||||
* For bots the sticker set must have been created by this bot.
|
||||
*
|
||||
* @param sticker
|
||||
* TDLib and Bot API compatible File ID, or a
|
||||
* TL object representing a sticker to be removed
|
||||
* @param newSticker New sticker to replace the old one with
|
||||
* @returns Modfiied sticker set
|
||||
*/
|
||||
export async function replaceStickerInSet(
|
||||
client: ITelegramClient,
|
||||
sticker: string | tdFileId.RawFullRemoteFileLocation | tl.TypeInputDocument,
|
||||
newSticker: InputStickerSetItem,
|
||||
params?: {
|
||||
/**
|
||||
* Upload progress callback
|
||||
*
|
||||
* @param uploaded Number of bytes uploaded
|
||||
* @param total Total file size
|
||||
*/
|
||||
progressCallback?: (uploaded: number, total: number) => void
|
||||
},
|
||||
): Promise<StickerSet> {
|
||||
if (tdFileId.isFileIdLike(sticker)) {
|
||||
sticker = fileIdToInputDocument(sticker)
|
||||
}
|
||||
|
||||
const res = await client.call({
|
||||
_: 'stickers.replaceSticker',
|
||||
sticker,
|
||||
newSticker: await _normalizeInputStickerSetItem(client, newSticker, params),
|
||||
})
|
||||
|
||||
return new StickerSet(res)
|
||||
}
|
|
@ -9,7 +9,7 @@ import { Voice } from './voice.js'
|
|||
export type ParsedDocument = Sticker | Voice | Audio | Video | Document
|
||||
|
||||
/** @internal */
|
||||
export function parseDocument(doc: tl.RawDocument, media?: tl.RawMessageMediaDocument): ParsedDocument {
|
||||
export function parseSticker(doc: tl.RawDocument) {
|
||||
const stickerAttr = doc.attributes.find(
|
||||
(a) => a._ === 'documentAttributeSticker' || a._ === 'documentAttributeCustomEmoji',
|
||||
)
|
||||
|
@ -21,6 +21,12 @@ export function parseDocument(doc: tl.RawDocument, media?: tl.RawMessageMediaDoc
|
|||
|
||||
return new Sticker(doc, stickerAttr as tl.RawDocumentAttributeSticker | tl.RawDocumentAttributeCustomEmoji, sz)
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function parseDocument(doc: tl.RawDocument, media?: tl.RawMessageMediaDocument): ParsedDocument {
|
||||
const sticker = parseSticker(doc)
|
||||
if (sticker) return sticker
|
||||
|
||||
for (const attr of doc.attributes) {
|
||||
switch (attr._) {
|
||||
|
|
|
@ -6,7 +6,7 @@ import { makeInspectable } from '../../utils/index.js'
|
|||
import { memoizeGetters } from '../../utils/memoize.js'
|
||||
import { MtEmptyError } from '../errors.js'
|
||||
import { InputFileLike } from '../files/index.js'
|
||||
import { parseDocument } from '../media/document-utils.js'
|
||||
import { parseSticker } from '../media/document-utils.js'
|
||||
import { MaskPosition, Sticker, StickerSourceType, StickerType, Thumbnail } from '../media/index.js'
|
||||
|
||||
/**
|
||||
|
@ -98,24 +98,38 @@ export interface StickerInfo {
|
|||
readonly sticker: Sticker
|
||||
}
|
||||
|
||||
function parseStickerOrThrow(doc: tl.RawDocument): Sticker {
|
||||
const sticker = parseSticker(doc)
|
||||
|
||||
if (!sticker) {
|
||||
throw new MtTypeAssertionError('full.documents', 'sticker', 'not a sticker')
|
||||
}
|
||||
|
||||
return sticker
|
||||
}
|
||||
|
||||
/**
|
||||
* A sticker set (aka sticker pack)
|
||||
*/
|
||||
export class StickerSet {
|
||||
readonly brief: tl.RawStickerSet
|
||||
readonly full?: tl.messages.RawStickerSet
|
||||
readonly cover?: tl.TypeStickerSetCovered
|
||||
|
||||
/**
|
||||
* Whether this object contains information about stickers inside the set
|
||||
*/
|
||||
readonly isFull: boolean
|
||||
|
||||
constructor(raw: tl.TypeStickerSet | tl.messages.TypeStickerSet) {
|
||||
constructor(raw: tl.TypeStickerSet | tl.messages.TypeStickerSet | tl.TypeStickerSetCovered) {
|
||||
if (raw._ === 'messages.stickerSet') {
|
||||
this.full = raw
|
||||
this.brief = raw.set
|
||||
} else if (raw._ === 'stickerSet') {
|
||||
this.brief = raw
|
||||
} else if (tl.isAnyStickerSetCovered(raw)) {
|
||||
this.cover = raw
|
||||
this.brief = raw.set
|
||||
} else {
|
||||
throw new MtTypeAssertionError('StickerSet', 'messages.stickerSet | stickerSet', raw._)
|
||||
}
|
||||
|
@ -222,17 +236,13 @@ export class StickerSet {
|
|||
* In case this object does not contain info about stickers (i.e. {@link isFull} = false)
|
||||
*/
|
||||
get stickers(): ReadonlyArray<StickerInfo> {
|
||||
if (!this.isFull) throw new MtEmptyError()
|
||||
if (!this.full) throw new MtEmptyError()
|
||||
|
||||
const stickers: StickerInfo[] = []
|
||||
const index = new LongMap<tl.Mutable<StickerInfo>>()
|
||||
|
||||
this.full!.documents.forEach((doc) => {
|
||||
const sticker = parseDocument(doc as tl.RawDocument)
|
||||
|
||||
if (!(sticker instanceof Sticker)) {
|
||||
throw new MtTypeAssertionError('full.documents', 'Sticker', sticker.mimeType)
|
||||
}
|
||||
this.full.documents.forEach((doc) => {
|
||||
const sticker = parseStickerOrThrow(doc as tl.RawDocument)
|
||||
|
||||
const info: tl.Mutable<StickerInfo> = {
|
||||
alt: sticker.emoji,
|
||||
|
@ -243,7 +253,7 @@ export class StickerSet {
|
|||
index.set(doc.id, info)
|
||||
})
|
||||
|
||||
this.full!.packs.forEach((pack) => {
|
||||
this.full.packs.forEach((pack) => {
|
||||
pack.documents.forEach((id) => {
|
||||
const item = index.get(id)
|
||||
|
||||
|
@ -256,6 +266,22 @@ export class StickerSet {
|
|||
return stickers
|
||||
}
|
||||
|
||||
/** Cover stickers of the sticker set. Not the same as {@link thumbnails} */
|
||||
get covers(): ReadonlyArray<Sticker> {
|
||||
if (!this.cover) return []
|
||||
|
||||
switch (this.cover._) {
|
||||
case 'stickerSetCovered':
|
||||
return [parseStickerOrThrow(this.cover.cover as tl.RawDocument)]
|
||||
case 'stickerSetMultiCovered':
|
||||
return this.cover.covers.map((it) => parseStickerOrThrow(it as tl.RawDocument))
|
||||
case 'stickerSetFullCovered':
|
||||
return this.cover.documents.map((it) => parseStickerOrThrow(it as tl.RawDocument))
|
||||
case 'stickerSetNoCovered':
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Available sticker set thumbnails.
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue