build: updated to layer 139
didn't add any new layers' features, only bumped layer
This commit is contained in:
parent
ec736f8590
commit
9493759572
48 changed files with 651 additions and 405 deletions
|
@ -14,7 +14,7 @@
|
|||
"dependencies": {
|
||||
"@types/long": "^4.0.1",
|
||||
"@types/node": "^15.12.1",
|
||||
"@mtcute/tl": "~134.0",
|
||||
"@mtcute/tl": "~139.0",
|
||||
"@mtcute/core": "^1.0.0",
|
||||
"@mtcute/file-id": "^1.0.0",
|
||||
"eager-async-pool": "^1.0.0",
|
||||
|
|
|
@ -5,7 +5,6 @@ export {
|
|||
tl,
|
||||
defaultDcs
|
||||
} from '@mtcute/core'
|
||||
export * from '@mtcute/tl/errors'
|
||||
|
||||
export * from './types'
|
||||
export * from './client'
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { tl } from '@mtcute/tl'
|
||||
|
||||
// @extension
|
||||
interface AuthState {
|
||||
|
|
|
@ -11,15 +11,7 @@ import {
|
|||
resolveMaybeDynamic,
|
||||
normalizePhoneNumber,
|
||||
} from '../../utils/misc-utils'
|
||||
import {
|
||||
AuthKeyUnregisteredError,
|
||||
PasswordHashInvalidError,
|
||||
PhoneCodeEmptyError,
|
||||
PhoneCodeExpiredError,
|
||||
PhoneCodeHashEmptyError,
|
||||
PhoneCodeInvalidError,
|
||||
SessionPasswordNeededError,
|
||||
} from '@mtcute/tl/errors'
|
||||
import { tl } from '@mtcute/tl'
|
||||
|
||||
/**
|
||||
* Start the client in an interactive and declarative manner,
|
||||
|
@ -174,7 +166,7 @@ export async function start(
|
|||
|
||||
return me
|
||||
} catch (e) {
|
||||
if (!(e instanceof AuthKeyUnregisteredError)) throw e
|
||||
if (!(e instanceof tl.errors.AuthKeyUnregisteredError)) throw e
|
||||
}
|
||||
|
||||
if (!params.phone && !params.botToken)
|
||||
|
@ -215,19 +207,19 @@ export async function start(
|
|||
|
||||
for (;;) {
|
||||
const code = await resolveMaybeDynamic(params.code)
|
||||
if (!code) throw new PhoneCodeEmptyError()
|
||||
if (!code) throw new tl.errors.PhoneCodeEmptyError()
|
||||
|
||||
try {
|
||||
result = await this.signIn(phone, sentCode.phoneCodeHash, code)
|
||||
} catch (e) {
|
||||
if (e instanceof SessionPasswordNeededError) {
|
||||
if (e instanceof tl.errors.SessionPasswordNeededError) {
|
||||
has2fa = true
|
||||
break
|
||||
} else if (
|
||||
e instanceof PhoneCodeEmptyError ||
|
||||
e instanceof PhoneCodeExpiredError ||
|
||||
e instanceof PhoneCodeHashEmptyError ||
|
||||
e instanceof PhoneCodeInvalidError
|
||||
e instanceof tl.errors.PhoneCodeEmptyError ||
|
||||
e instanceof tl.errors.PhoneCodeExpiredError ||
|
||||
e instanceof tl.errors.PhoneCodeHashEmptyError ||
|
||||
e instanceof tl.errors.PhoneCodeInvalidError
|
||||
) {
|
||||
if (typeof params.code !== 'function') {
|
||||
throw new MtArgumentError('Provided code was invalid')
|
||||
|
@ -263,7 +255,7 @@ export async function start(
|
|||
throw new MtArgumentError('Provided password was invalid')
|
||||
}
|
||||
|
||||
if (e instanceof PasswordHashInvalidError) {
|
||||
if (e instanceof tl.errors.PasswordHashInvalidError) {
|
||||
if (params.invalidCodeCallback) {
|
||||
await params.invalidCodeCallback('password')
|
||||
} else {
|
||||
|
|
|
@ -2,33 +2,32 @@ import { TelegramClient } from '../../client'
|
|||
import { InputPeerLike, MtInvalidPeerTypeError } from '../../types'
|
||||
import {
|
||||
normalizeToInputChannel,
|
||||
normalizeToInputUser,
|
||||
normalizeToInputPeer,
|
||||
} from '../../utils/peer-utils'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { createDummyUpdate } from '../../utils/updates-utils'
|
||||
|
||||
/**
|
||||
* Delete all messages of a user in a supergroup
|
||||
* Delete all messages of a user (or channel) in a supergroup
|
||||
*
|
||||
* @param chatId Chat ID
|
||||
* @param userId User ID
|
||||
* @param participantId User/channel ID
|
||||
* @internal
|
||||
*/
|
||||
export async function deleteUserHistory(
|
||||
this: TelegramClient,
|
||||
chatId: InputPeerLike,
|
||||
userId: InputPeerLike
|
||||
participantId: InputPeerLike
|
||||
): Promise<void> {
|
||||
const channel = normalizeToInputChannel(await this.resolvePeer(chatId))
|
||||
if (!channel) throw new MtInvalidPeerTypeError(chatId, 'channel')
|
||||
|
||||
const user = normalizeToInputUser(await this.resolvePeer(userId))
|
||||
if (!user) throw new MtInvalidPeerTypeError(userId, 'user')
|
||||
const peer = normalizeToInputPeer(await this.resolvePeer(participantId))
|
||||
|
||||
const res = await this.call({
|
||||
_: 'channels.deleteUserHistory',
|
||||
_: 'channels.deleteParticipantHistory',
|
||||
channel,
|
||||
userId: user,
|
||||
participant: peer,
|
||||
})
|
||||
|
||||
this._handleUpdate(
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
import { assertTypeIs } from '../../utils/type-assertion'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { ChatMember } from '../../types'
|
||||
import { UserNotParticipantError } from '@mtcute/tl/errors'
|
||||
|
||||
/**
|
||||
* Get information about a single chat member
|
||||
|
@ -59,7 +58,7 @@ export async function getChatMember(
|
|||
}
|
||||
}
|
||||
|
||||
throw new UserNotParticipantError()
|
||||
throw new tl.errors.UserNotParticipantError()
|
||||
} else if (isInputPeerChannel(chat)) {
|
||||
const res = await this.call({
|
||||
_: 'channels.getParticipant',
|
||||
|
|
|
@ -44,7 +44,7 @@ export async function getFullChat(
|
|||
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
|
||||
let res: tl.messages.TypeChatFull | tl.TypeUserFull
|
||||
let res: tl.messages.TypeChatFull | tl.users.TypeUserFull
|
||||
if (isInputPeerChannel(peer)) {
|
||||
res = await this.call({
|
||||
_: 'channels.getFullChannel',
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { determinePartSize } from '../../utils/file-utils'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { FileMigrateError, FilerefUpgradeNeededError } from '@mtcute/tl/errors'
|
||||
import {
|
||||
MtArgumentError,
|
||||
MtUnsupportedError,
|
||||
|
@ -102,14 +101,14 @@ export async function* downloadAsIterable(
|
|||
{ connection }
|
||||
)
|
||||
} catch (e) {
|
||||
if (e.constructor === FileMigrateError) {
|
||||
connection = this._downloadConnections[e.newDc]
|
||||
if (e.constructor === tl.errors.FileMigrateXError) {
|
||||
connection = this._downloadConnections[e.new_dc]
|
||||
if (!connection) {
|
||||
connection = await this.createAdditionalConnection(e.newDc)
|
||||
this._downloadConnections[e.newDc] = connection
|
||||
connection = await this.createAdditionalConnection(e.new_dc)
|
||||
this._downloadConnections[e.new_dc] = connection
|
||||
}
|
||||
return requestCurrent()
|
||||
} else if (e.constructor === FilerefUpgradeNeededError) {
|
||||
} else if (e.constructor === tl.errors.FilerefUpgradeNeededError) {
|
||||
// todo: implement someday
|
||||
// see: https://github.com/LonamiWebs/Telethon/blob/0e8bd8248cc649637b7c392616887c50986427a0/telethon/client/downloads.py#L99
|
||||
throw new MtUnsupportedError('File ref expired!')
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { InputPeerLike, Message, FormattedString, ReplyMarkup } from '../../types'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { MessageNotFoundError } from '@mtcute/tl/errors'
|
||||
|
||||
/**
|
||||
* Copy a message (i.e. send the same message,
|
||||
|
@ -99,7 +98,7 @@ export async function sendCopy(
|
|||
|
||||
const msg = await this.getMessages(fromPeer, message)
|
||||
|
||||
if (!msg) throw new MessageNotFoundError()
|
||||
if (!msg) throw new tl.errors.MessageNotFoundError()
|
||||
|
||||
return msg.sendCopy(toChatId, params)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import {
|
|||
} from '../../utils/misc-utils'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { assertIsUpdatesGroup } from '../../utils/updates-utils'
|
||||
import { MessageNotFoundError } from '@mtcute/tl/errors'
|
||||
import { randomLong } from '@mtcute/core'
|
||||
|
||||
/**
|
||||
|
@ -130,7 +129,7 @@ export async function sendMediaGroup(
|
|||
const msg = await this.getMessages(peer, replyTo)
|
||||
|
||||
if (!msg)
|
||||
throw new MessageNotFoundError()
|
||||
throw new tl.errors.MessageNotFoundError()
|
||||
}
|
||||
|
||||
const multiMedia: tl.RawInputSingleMedia[] = []
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
} from '../../types'
|
||||
import { normalizeDate, normalizeMessageId } from '../../utils/misc-utils'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { MessageNotFoundError } from '@mtcute/tl/errors'
|
||||
import { randomLong } from '@mtcute/core'
|
||||
|
||||
/**
|
||||
|
@ -156,7 +155,7 @@ export async function sendMedia(
|
|||
|
||||
const msg = await this.getMessages(peer, replyTo)
|
||||
|
||||
if (!msg) throw new MessageNotFoundError()
|
||||
if (!msg) throw new tl.errors.MessageNotFoundError()
|
||||
}
|
||||
|
||||
const res = await this.call({
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
MtTypeAssertionError,
|
||||
MtArgumentError, FormattedString, PeersIndex,
|
||||
} from '../../types'
|
||||
import { getMarkedPeerId, MessageNotFoundError, randomLong } from '@mtcute/core'
|
||||
import { getMarkedPeerId, randomLong } from '@mtcute/core'
|
||||
import { createDummyUpdate } from '../../utils/updates-utils'
|
||||
|
||||
/**
|
||||
|
@ -131,7 +131,7 @@ export async function sendText(
|
|||
const msg = await this.getMessages(peer, replyTo)
|
||||
|
||||
if (!msg)
|
||||
throw new MessageNotFoundError()
|
||||
throw new tl.errors.MessageNotFoundError()
|
||||
}
|
||||
|
||||
const res = await this.call({
|
||||
|
|
|
@ -6,9 +6,10 @@ import {
|
|||
PeersIndex,
|
||||
Poll,
|
||||
} from '../../types'
|
||||
import { MaybeArray, MessageNotFoundError } from '@mtcute/core'
|
||||
import { MaybeArray } from '@mtcute/core'
|
||||
import { assertTypeIs } from '../../utils/type-assertion'
|
||||
import { assertIsUpdatesGroup } from '../../utils/updates-utils'
|
||||
import { tl } from '@mtcute/tl'
|
||||
|
||||
/**
|
||||
* Send or retract a vote in a poll.
|
||||
|
@ -37,7 +38,7 @@ export async function sendVote(
|
|||
if (options.some((it) => typeof it === 'number')) {
|
||||
const msg = await this.getMessages(peer, message)
|
||||
|
||||
if (!msg) throw new MessageNotFoundError()
|
||||
if (!msg) throw new tl.errors.MessageNotFoundError()
|
||||
|
||||
if (!(msg.media instanceof Poll))
|
||||
throw new MtArgumentError(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { InputStickerSetItem, StickerSet } from '../../types'
|
||||
import { InputStickerSetItem, MtTypeAssertionError, StickerSet } from '../../types'
|
||||
import { tl } from '@mtcute/tl'
|
||||
|
||||
const MASK_POS = {
|
||||
|
|
|
@ -35,6 +35,7 @@ export async function getStickerSet(
|
|||
const res = await this.call({
|
||||
_: 'messages.getStickerSet',
|
||||
stickerset: input,
|
||||
hash: 0
|
||||
})
|
||||
|
||||
return new StickerSet(this, res)
|
||||
|
|
|
@ -9,6 +9,7 @@ const sentCodeMap: Record<
|
|||
'auth.sentCodeTypeCall': 'call',
|
||||
'auth.sentCodeTypeFlashCall': 'flash_call',
|
||||
'auth.sentCodeTypeSms': 'sms',
|
||||
'auth.sentCodeTypeMissedCall': 'missed_call',
|
||||
}
|
||||
|
||||
const nextCodeMap: Record<
|
||||
|
@ -18,6 +19,7 @@ const nextCodeMap: Record<
|
|||
'auth.codeTypeCall': 'call',
|
||||
'auth.codeTypeFlashCall': 'flash_call',
|
||||
'auth.codeTypeSms': 'sms',
|
||||
'auth.codeTypeMissedCall': 'missed_call',
|
||||
}
|
||||
|
||||
export namespace SentCode {
|
||||
|
@ -28,7 +30,7 @@ export namespace SentCode {
|
|||
* - `call`: Code is sent via voice call
|
||||
* - `flash_call`: Code is the last 5 digits of the caller's phone number
|
||||
*/
|
||||
export type DeliveryType = 'app' | 'sms' | 'call' | 'flash_call'
|
||||
export type DeliveryType = 'app' | 'sms' | 'call' | 'flash_call' | 'missed_call'
|
||||
|
||||
/**
|
||||
* Type describing next code delivery type.
|
||||
|
|
|
@ -6,7 +6,6 @@ import { MtArgumentError } from '../errors'
|
|||
import { BasicPeerType, getBasicPeerType, getMarkedPeerId } from '@mtcute/core'
|
||||
import { encodeInlineMessageId } from '../../utils/inline-utils'
|
||||
import { User, PeersIndex } from '../peers'
|
||||
import { MessageNotFoundError } from '@mtcute/core'
|
||||
|
||||
/**
|
||||
* An incoming callback query, originated from a callback button
|
||||
|
@ -189,7 +188,7 @@ export class CallbackQuery {
|
|||
getMarkedPeerId(this.raw.peer),
|
||||
this.raw.msgId
|
||||
)
|
||||
if (!msg) throw new MessageNotFoundError()
|
||||
if (!msg) throw new tl.errors.MessageNotFoundError()
|
||||
|
||||
return msg
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import { HistoryReadUpdate } from './updates'
|
|||
import { FormattedString } from './parser'
|
||||
import { Message } from './messages'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { TimeoutError } from '@mtcute/tl/errors'
|
||||
|
||||
interface QueuedHandler<T> {
|
||||
promise: ControllablePromise<T>
|
||||
|
@ -270,7 +269,7 @@ export class Conversation {
|
|||
let timer: NodeJS.Timeout | undefined = undefined
|
||||
if (timeout !== null) {
|
||||
timer = setTimeout(() => {
|
||||
promise.reject(new TimeoutError())
|
||||
promise.reject(new tl.errors.TimeoutError())
|
||||
this._queuedNewMessage.removeBy((it) => it.promise === promise)
|
||||
}, timeout)
|
||||
}
|
||||
|
@ -412,7 +411,7 @@ export class Conversation {
|
|||
let timer: NodeJS.Timeout | undefined = undefined
|
||||
if (params?.timeout !== null) {
|
||||
timer = setTimeout(() => {
|
||||
promise.reject(new TimeoutError())
|
||||
promise.reject(new tl.errors.TimeoutError())
|
||||
delete this._pendingEditMessage[msgId]
|
||||
}, params?.timeout ?? 15000)
|
||||
}
|
||||
|
@ -458,7 +457,7 @@ export class Conversation {
|
|||
let timer: NodeJS.Timeout | undefined = undefined
|
||||
if (timeout !== null) {
|
||||
timer = setTimeout(() => {
|
||||
promise.reject(new TimeoutError())
|
||||
promise.reject(new tl.errors.TimeoutError())
|
||||
delete this._pendingRead[msgId]
|
||||
}, timeout)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Chat, PeersIndex } from '../peers'
|
|||
import { Message } from './message'
|
||||
import { DraftMessage } from './draft-message'
|
||||
import { makeInspectable } from '../utils'
|
||||
import { getMarkedPeerId, MessageNotFoundError } from '@mtcute/core'
|
||||
import { getMarkedPeerId } from '@mtcute/core'
|
||||
|
||||
/**
|
||||
* A dialog.
|
||||
|
@ -184,7 +184,7 @@ export class Dialog {
|
|||
this._peers
|
||||
)
|
||||
} else {
|
||||
throw new MessageNotFoundError()
|
||||
throw new tl.errors.MessageNotFoundError()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,13 +42,19 @@ export class StickerSet {
|
|||
|
||||
constructor(
|
||||
readonly client: TelegramClient,
|
||||
raw: tl.RawStickerSet | tl.messages.RawStickerSet
|
||||
raw: tl.TypeStickerSet | tl.messages.TypeStickerSet
|
||||
) {
|
||||
if (raw._ === 'messages.stickerSet') {
|
||||
this.full = raw
|
||||
this.brief = raw.set
|
||||
} else {
|
||||
} else if (raw._ === 'stickerSet') {
|
||||
this.brief = raw
|
||||
} else {
|
||||
throw new MtTypeAssertionError(
|
||||
'StickerSet',
|
||||
'messages.stickerSet | stickerSet',
|
||||
raw._
|
||||
)
|
||||
}
|
||||
|
||||
this.isFull = raw._ === 'messages.stickerSet'
|
||||
|
|
|
@ -494,10 +494,19 @@ export class Chat {
|
|||
/** @internal */
|
||||
static _parseFull(
|
||||
client: TelegramClient,
|
||||
full: tl.messages.RawChatFull | tl.RawUserFull
|
||||
full: tl.messages.RawChatFull | tl.users.TypeUserFull,
|
||||
): Chat {
|
||||
if (full._ === 'userFull') {
|
||||
return new Chat(client, full.user, full)
|
||||
if (full._ === 'users.userFull') {
|
||||
const user = full.users.find((it) => it.id === full.fullUser.id)
|
||||
if (!user || user._ === 'userEmpty') {
|
||||
throw new MtTypeAssertionError(
|
||||
'Chat._parseFull',
|
||||
'user',
|
||||
user?._ ?? 'undefined'
|
||||
)
|
||||
}
|
||||
|
||||
return new Chat(client, user, full.fullUser)
|
||||
} else {
|
||||
const fullChat = full.fullChat
|
||||
let chat: tl.TypeChat | undefined = undefined
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { tl } from '@mtcute/tl'
|
||||
import { MtTypeAssertionError } from '../types'
|
||||
import Long from 'long'
|
||||
|
||||
// dummy updates which are used for methods that return messages.affectedHistory.
|
||||
// that is not an update, but it carries info about pts, and we need to handle it
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"dependencies": {
|
||||
"@types/node": "^15.12.1",
|
||||
"@types/events": "^3.0.0",
|
||||
"@mtcute/tl": "^134.0.0",
|
||||
"@mtcute/tl": "~139.0",
|
||||
"@mtcute/tl-runtime": "^1.0.0",
|
||||
"big-integer": "1.6.48",
|
||||
"long": "^4.0.0",
|
||||
|
|
|
@ -24,17 +24,6 @@ import {
|
|||
defaultTestDc,
|
||||
defaultTestIpv6Dc,
|
||||
} from './utils/default-dcs'
|
||||
import {
|
||||
AuthKeyUnregisteredError,
|
||||
FloodTestPhoneWaitError,
|
||||
FloodWaitError,
|
||||
InternalError,
|
||||
NetworkMigrateError,
|
||||
PhoneMigrateError,
|
||||
RpcError,
|
||||
SlowmodeWaitError,
|
||||
UserMigrateError,
|
||||
} from '@mtcute/tl/errors'
|
||||
import { addPublicKey } from './utils/crypto/keys'
|
||||
import { ITelegramStorage, MemoryStorage } from './storage'
|
||||
import EventEmitter from 'events'
|
||||
|
@ -375,7 +364,7 @@ export class BaseTelegramClient extends EventEmitter {
|
|||
// so we just use getState so the server knows
|
||||
// we still do need updates
|
||||
this.call({ _: 'updates.getState' }).catch((e) => {
|
||||
if (!(e instanceof RpcError)) {
|
||||
if (!(e instanceof tl.errors.RpcError)) {
|
||||
this.primaryConnection.reconnect()
|
||||
}
|
||||
})
|
||||
|
@ -651,7 +640,7 @@ export class BaseTelegramClient extends EventEmitter {
|
|||
await sleep(delta)
|
||||
delete this._floodWaitedRequests[message._]
|
||||
} else {
|
||||
throw new FloodWaitError(delta / 1000)
|
||||
throw new tl.errors.FloodWaitXError(delta / 1000)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -673,7 +662,7 @@ export class BaseTelegramClient extends EventEmitter {
|
|||
} catch (e) {
|
||||
lastError = e
|
||||
|
||||
if (e instanceof InternalError) {
|
||||
if (e instanceof tl.errors.InternalError) {
|
||||
this.log.warn('Telegram is having internal issues: %s', e)
|
||||
if (e.message === 'WORKER_BUSY_TOO_LONG_RETRY') {
|
||||
// according to tdlib, "it is dangerous to resend query without timeout, so use 1"
|
||||
|
@ -683,11 +672,11 @@ export class BaseTelegramClient extends EventEmitter {
|
|||
}
|
||||
|
||||
if (
|
||||
e.constructor === FloodWaitError ||
|
||||
e.constructor === SlowmodeWaitError ||
|
||||
e.constructor === FloodTestPhoneWaitError
|
||||
e.constructor === tl.errors.FloodWaitXError ||
|
||||
e.constructor === tl.errors.SlowmodeWaitXError ||
|
||||
e.constructor === tl.errors.FloodTestPhoneWaitXError
|
||||
) {
|
||||
if (e.constructor !== SlowmodeWaitError) {
|
||||
if (e.constructor !== tl.errors.SlowmodeWaitXError) {
|
||||
// SLOW_MODE_WAIT is chat-specific, not request-specific
|
||||
this._floodWaitedRequests[message._] =
|
||||
Date.now() + e.seconds * 1000
|
||||
|
@ -696,7 +685,7 @@ export class BaseTelegramClient extends EventEmitter {
|
|||
// In test servers, FLOOD_WAIT_0 has been observed, and sleeping for
|
||||
// such a short amount will cause retries very fast leading to issues
|
||||
if (e.seconds === 0) {
|
||||
e.seconds = 1
|
||||
;(e as any).seconds = 1
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -711,16 +700,16 @@ export class BaseTelegramClient extends EventEmitter {
|
|||
|
||||
if (connection.params.dc.id === this._primaryDc.id) {
|
||||
if (
|
||||
e.constructor === PhoneMigrateError ||
|
||||
e.constructor === UserMigrateError ||
|
||||
e.constructor === NetworkMigrateError
|
||||
e.constructor === tl.errors.PhoneMigrateXError ||
|
||||
e.constructor === tl.errors.UserMigrateXError ||
|
||||
e.constructor === tl.errors.NetworkMigrateXError
|
||||
) {
|
||||
this.log.info('Migrate error, new dc = %d', e.newDc)
|
||||
await this.changeDc(e.newDc)
|
||||
this.log.info('Migrate error, new dc = %d', e.new_dc)
|
||||
await this.changeDc(e.new_dc)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if (e.constructor === AuthKeyUnregisteredError) {
|
||||
if (e.constructor === tl.errors.AuthKeyUnregisteredError) {
|
||||
// we can try re-exporting auth from the primary connection
|
||||
this.log.warn('exported auth key error, re-exporting..')
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ export * from './types'
|
|||
export * from './utils'
|
||||
|
||||
export * from '@mtcute/tl'
|
||||
export * from '@mtcute/tl/errors'
|
||||
export * from '@mtcute/tl-runtime'
|
||||
|
||||
export { defaultDcs } from './utils/default-dcs'
|
||||
|
|
|
@ -13,11 +13,6 @@ import {
|
|||
ControllablePromise,
|
||||
createCancellablePromise,
|
||||
} from '../utils/controllable-promise'
|
||||
import {
|
||||
createRpcErrorFromTl,
|
||||
RpcError,
|
||||
RpcTimeoutError,
|
||||
} from '@mtcute/tl/errors'
|
||||
import { gzipDeflate, gzipInflate } from '@mtcute/tl-runtime/src/platform/gzip'
|
||||
import { SortedArray } from '../utils/sorted-array'
|
||||
import { EarlyTimer } from '../utils/early-timer'
|
||||
|
@ -96,7 +91,7 @@ type PendingMessage =
|
|||
// todo
|
||||
const DESTROY_SESSION_ID = Buffer.from('262151e7', 'hex')
|
||||
|
||||
function makeNiceStack(error: RpcError, stack: string, method?: string) {
|
||||
function makeNiceStack(error: tl.errors.RpcError, stack: string, method?: string) {
|
||||
error.stack = `${error.constructor.name} (${error.code} ${error.text}): ${
|
||||
error.message
|
||||
}\n at ${method}\n${stack.split('\n').slice(2).join('\n')}`
|
||||
|
@ -526,7 +521,7 @@ export class SessionConnection extends PersistentConnection {
|
|||
|
||||
if (rpc.cancelled) return
|
||||
|
||||
const error = createRpcErrorFromTl(res)
|
||||
const error = tl.errors.createRpcErrorFromTl(res)
|
||||
if (this.params.niceStacks !== false) {
|
||||
makeNiceStack(error, rpc.stack!, rpc.method)
|
||||
}
|
||||
|
@ -1144,7 +1139,7 @@ export class SessionConnection extends PersistentConnection {
|
|||
}
|
||||
|
||||
if (onTimeout) {
|
||||
const error = new RpcTimeoutError()
|
||||
const error = new tl.errors.RpcTimeoutError()
|
||||
if (this.params.niceStacks !== false) {
|
||||
makeNiceStack(error, rpc.stack!, rpc.method)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { describe, it } from 'mocha'
|
||||
import { expect } from 'chai'
|
||||
import { randomBytes } from '../../src'
|
||||
import { sleep } from '../../src/utils/misc-utils'
|
||||
import { UserMigrateError } from '@mtcute/tl/errors'
|
||||
import { randomBytes, sleep } from '../../src'
|
||||
import { createTestTelegramClient } from '../e2e/utils'
|
||||
import { tl } from '@mtcute/tl'
|
||||
|
||||
require('dotenv-flow').config()
|
||||
|
||||
|
@ -62,7 +61,7 @@ describe('fuzz : session', async function () {
|
|||
await client.waitUntilUsable()
|
||||
|
||||
const conn = await client.createAdditionalConnection(1)
|
||||
await conn.sendForResult({ _: 'help.getConfig' })
|
||||
await conn.sendRpc({ _: 'help.getConfig' })
|
||||
|
||||
await sleep(10000)
|
||||
|
||||
|
@ -116,7 +115,7 @@ describe('fuzz : session', async function () {
|
|||
]
|
||||
}, { connection: conn })
|
||||
} catch (e) {
|
||||
if (e instanceof UserMigrateError) {
|
||||
if (e instanceof tl.errors.UserMigrateXError) {
|
||||
hadError = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mtcute/tl": "^134.0",
|
||||
"@mtcute/tl": "~139.0",
|
||||
"@mtcute/core": "^1.0.0",
|
||||
"@mtcute/client": "^1.0.0",
|
||||
"events": "^3.2.0"
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mtcute/tl": "^134.0.0",
|
||||
"@mtcute/tl": "~139.0",
|
||||
"@mtcute/tl-runtime": "^1.0.0",
|
||||
"@mtcute/core": "^1.0.0",
|
||||
"long": "^4.0.0"
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"docs": "npx typedoc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mtcute/tl": "^134.0.0",
|
||||
"@mtcute/tl": "~139.0",
|
||||
"htmlparser2": "^6.0.1",
|
||||
"long": "^4.0.0"
|
||||
},
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"docs": "npx typedoc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mtcute/tl": "^134.0.0",
|
||||
"@mtcute/tl": "~139.0",
|
||||
"long": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
274
packages/tl-utils/src/codegen/errors.ts
Normal file
274
packages/tl-utils/src/codegen/errors.ts
Normal file
|
@ -0,0 +1,274 @@
|
|||
import { camelToPascal, jsComment, snakeToCamel } from './utils'
|
||||
import { TlError, TlErrors } from '../types'
|
||||
|
||||
export function errorCodeToClassName(code: string): string {
|
||||
let str =
|
||||
camelToPascal(
|
||||
snakeToCamel(
|
||||
code
|
||||
.toLowerCase()
|
||||
.replace(/ /g, '_')
|
||||
)
|
||||
) + 'Error'
|
||||
if (str[0].match(/\d/)) {
|
||||
str = '_' + str
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
const RPC_ERROR_CLASS_JS = `
|
||||
class RpcError extends Error {
|
||||
constructor(code, text, description) {
|
||||
super(description);
|
||||
this.code = code;
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
{exports}RpcError = RpcError;
|
||||
`.trimStart()
|
||||
|
||||
const RPC_ERROR_CLASS_TS = `
|
||||
export class RpcError extends Error {
|
||||
readonly code: number;
|
||||
readonly text: string;
|
||||
constructor(code: number, text: string, description?: string);
|
||||
}
|
||||
`.trimStart()
|
||||
|
||||
const BASE_ERROR_JS = `
|
||||
class {className} extends RpcError {
|
||||
constructor(name, description) {
|
||||
super({code}, name, description);
|
||||
}
|
||||
}
|
||||
{exports}{className} = {className}
|
||||
`
|
||||
const BASE_ERROR_TS = `
|
||||
export class {className} extends RpcError {
|
||||
constructor(name: string, description: string);
|
||||
}
|
||||
`.trimStart()
|
||||
|
||||
const ERROR_PRELUDE = `{ts}class {className} extends {base} {
|
||||
constructor({arguments})`
|
||||
|
||||
const TL_BUILDER_TEMPLATE_JS = `
|
||||
{exports}createRpcErrorFromTl = function (obj) {
|
||||
if (obj.errorMessage in _byName) return new _byName[obj.errorMessage]();
|
||||
|
||||
let match;
|
||||
{inner}
|
||||
|
||||
if (obj.errorCode in _byCode) return new _byCode[obj.errorCode](obj.errorMessage);
|
||||
|
||||
return new RpcError(obj.errorCode, obj.errorMessage);
|
||||
}
|
||||
`.trim()
|
||||
|
||||
const template = (str: string, params: Record<string, any>): string => {
|
||||
return str.replace(/{([a-z]+)}/gi, (_, name) => params[name] ?? '')
|
||||
}
|
||||
|
||||
function parseCode(
|
||||
err: string,
|
||||
placeholders?: string[]
|
||||
): [string, string[], boolean] {
|
||||
let addPlaceholders = false
|
||||
|
||||
if (!placeholders) {
|
||||
placeholders = []
|
||||
addPlaceholders = true
|
||||
}
|
||||
|
||||
let wildcard = false
|
||||
|
||||
err = err
|
||||
.replace(/%[a-z]/g, (ph) => {
|
||||
if (ph !== '%d') {
|
||||
throw new Error(`Unsupported placeholder: ${ph}`)
|
||||
}
|
||||
|
||||
if (addPlaceholders) {
|
||||
const idx = placeholders!.length
|
||||
placeholders!.push(`duration${idx === 0 ? '' : idx}`)
|
||||
}
|
||||
|
||||
return 'X'
|
||||
})
|
||||
.replace(/_\*$/, () => {
|
||||
wildcard = true
|
||||
return ''
|
||||
})
|
||||
|
||||
return [err, placeholders, wildcard]
|
||||
}
|
||||
|
||||
function placeholderType(name: string): string {
|
||||
// if (!name.startsWith('duration')) {
|
||||
// throw new Error('Invalid placeholder name')
|
||||
// }
|
||||
return 'number'
|
||||
}
|
||||
|
||||
export function generateCodeForErrors(
|
||||
errors: TlErrors,
|
||||
exports = 'exports.'
|
||||
): [string, string] {
|
||||
let ts = RPC_ERROR_CLASS_TS
|
||||
let js = template(RPC_ERROR_CLASS_JS, { exports })
|
||||
|
||||
const baseErrorsClasses: Record<number, string> = {}
|
||||
|
||||
for (const it of errors.base) {
|
||||
const className = errorCodeToClassName(it.name)
|
||||
baseErrorsClasses[it.code] = className
|
||||
|
||||
if (it.description) ts += jsComment(it.description) + '\n'
|
||||
ts +=
|
||||
template(BASE_ERROR_TS, {
|
||||
className,
|
||||
}) + '\n'
|
||||
js += template(BASE_ERROR_JS, {
|
||||
className,
|
||||
code: it.code,
|
||||
exports,
|
||||
})
|
||||
}
|
||||
|
||||
const errorClasses: Record<string, string> = {}
|
||||
const wildcardClasses: [string, string][] = []
|
||||
const withPlaceholders: [string, string][] = []
|
||||
|
||||
function findBaseClass(it: TlError) {
|
||||
for (const [prefix, cls] of wildcardClasses) {
|
||||
if (it.name.startsWith(prefix)) return cls
|
||||
}
|
||||
|
||||
return baseErrorsClasses[it.code] ?? 'RpcError'
|
||||
}
|
||||
|
||||
for (const it of Object.values(errors.errors)) {
|
||||
if (it._auto) {
|
||||
// information about the error is incomplete
|
||||
continue
|
||||
}
|
||||
|
||||
const [name, placeholders, wildcard] = parseCode(
|
||||
it.name,
|
||||
it._paramNames
|
||||
)
|
||||
|
||||
const className = errorCodeToClassName(name)
|
||||
const baseClass = findBaseClass(it)
|
||||
|
||||
if (!it.virtual && !wildcard) {
|
||||
errorClasses[it.name] = className
|
||||
}
|
||||
if (wildcard) {
|
||||
wildcardClasses.push([it.name.replace('*', ''), className])
|
||||
}
|
||||
if (placeholders.length) {
|
||||
withPlaceholders.push([it.name, className])
|
||||
}
|
||||
|
||||
js +=
|
||||
template(ERROR_PRELUDE, {
|
||||
className,
|
||||
base: baseClass,
|
||||
arguments: wildcard
|
||||
? 'code, description'
|
||||
: placeholders.join(', '),
|
||||
}) + '{\n'
|
||||
|
||||
let description
|
||||
let comment = ''
|
||||
if (it.description) {
|
||||
let idx = 0
|
||||
description = JSON.stringify(it.description).replace(
|
||||
/%[a-z]/g,
|
||||
() => `" + ${placeholders[idx++]} + "`
|
||||
)
|
||||
|
||||
if (wildcard) {
|
||||
description = description.replace(/"$/, ': " + description')
|
||||
}
|
||||
|
||||
idx = 0
|
||||
comment += it.description.replace(
|
||||
/%[a-z]/g,
|
||||
() => `{@see ${placeholders[idx++]}}`
|
||||
)
|
||||
} else {
|
||||
description = `"Unknown RPC error: [${it.code}:${it.name}]"`
|
||||
}
|
||||
|
||||
if (it.virtual) {
|
||||
if (comment) comment += '\n\n'
|
||||
comment +=
|
||||
'This is a *virtual* error, meaning that it may only occur when using MTCute APIs (not MTProto)'
|
||||
}
|
||||
|
||||
if (wildcard) {
|
||||
if (comment) comment += '\n\n'
|
||||
comment +=
|
||||
'This is an *abstract* error, meaning that only its subclasses may occur when using the API'
|
||||
}
|
||||
|
||||
if (comment) ts += jsComment(comment) + '\n'
|
||||
ts +=
|
||||
template(ERROR_PRELUDE, {
|
||||
ts: 'export ',
|
||||
className,
|
||||
base: baseClass,
|
||||
arguments: placeholders
|
||||
.map((it) => `${it}: ${placeholderType(it)}`)
|
||||
.join(', '),
|
||||
}) + ';'
|
||||
|
||||
if (baseClass === 'RpcError') {
|
||||
js += `super(${it.code}, '${it.name}', ${description});`
|
||||
} else if (wildcard) {
|
||||
js += `super(code, ${description});`
|
||||
} else {
|
||||
js += `super('${it.name}', ${description});`
|
||||
}
|
||||
|
||||
for (const ph of placeholders) {
|
||||
js += `\nthis.${ph} = ${ph};`
|
||||
ts += `\n readonly ${ph}: ${placeholderType(ph)}`
|
||||
}
|
||||
|
||||
js += '\n }\n}\n'
|
||||
js += `${exports}${className} = ${className};\n`
|
||||
|
||||
ts += '\n}\n'
|
||||
}
|
||||
|
||||
ts += 'export function createRpcErrorFromTl (obj: object): RpcError;\n'
|
||||
|
||||
// and now we need to implement it
|
||||
js += 'const _byName = {\n'
|
||||
for (const [name, cls] of Object.entries(errorClasses)) {
|
||||
js += `'${name.replace(/%[a-z]/gi, 'X')}': ${cls},\n`
|
||||
}
|
||||
js += '};\n'
|
||||
|
||||
js += 'const _byCode = {\n'
|
||||
for (const [code, cls] of Object.entries(baseErrorsClasses)) {
|
||||
js += `${code}: ${cls},\n`
|
||||
}
|
||||
js += '};\n'
|
||||
|
||||
// finally, the function itself
|
||||
|
||||
let inner = ''
|
||||
|
||||
for (const [name, cls] of withPlaceholders) {
|
||||
const regex = name.replace('%d', '(\\d+)')
|
||||
inner += `if ((match=obj.errorMessage.match(/^${regex}$/))!=null)return new ${cls}(parseInt(match[1]));\n`
|
||||
}
|
||||
|
||||
js += template(TL_BUILDER_TEMPLATE_JS, { inner, exports })
|
||||
|
||||
return [ts, js]
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import { TlEntry, TlFullSchema } from '../types'
|
||||
import { TlEntry, TlErrors, TlFullSchema } from '../types'
|
||||
import { groupTlEntriesByNamespace, splitNameToNamespace } from '../utils'
|
||||
import { camelToPascal, snakeToCamel } from './utils'
|
||||
import { camelToPascal, indent, jsComment, snakeToCamel } from './utils'
|
||||
import { errorCodeToClassName, generateCodeForErrors } from './errors'
|
||||
|
||||
const PRIMITIVE_TO_TS: Record<string, string> = {
|
||||
int: 'number',
|
||||
|
@ -14,23 +15,7 @@ const PRIMITIVE_TO_TS: Record<string, string> = {
|
|||
Bool: 'boolean',
|
||||
true: 'boolean',
|
||||
null: 'null',
|
||||
any: 'any'
|
||||
}
|
||||
|
||||
function jsComment(s: string): string {
|
||||
return (
|
||||
'/**' +
|
||||
s
|
||||
.replace(/(?![^\n]{1,60}$)([^\n]{1,60})\s/g, '$1\n')
|
||||
.replace(/\n|^/g, '\n * ') +
|
||||
'\n */'
|
||||
)
|
||||
}
|
||||
|
||||
function indent(size: number, s: string): string {
|
||||
let prefix = ''
|
||||
while (size--) prefix += ' '
|
||||
return prefix + s.replace(/\n/g, '\n' + prefix)
|
||||
any: 'any',
|
||||
}
|
||||
|
||||
function fullTypeName(
|
||||
|
@ -68,7 +53,8 @@ function entryFullTypeName(entry: TlEntry): string {
|
|||
|
||||
export function generateTypescriptDefinitionsForTlEntry(
|
||||
entry: TlEntry,
|
||||
baseNamespace = 'tl.'
|
||||
baseNamespace = 'tl.',
|
||||
errors?: TlErrors
|
||||
): string {
|
||||
let ret = ''
|
||||
|
||||
|
@ -83,6 +69,20 @@ export function generateTypescriptDefinitionsForTlEntry(
|
|||
entry.type,
|
||||
baseNamespace
|
||||
)}}`
|
||||
|
||||
if (errors) {
|
||||
if (errors.userOnly[entry.name]) {
|
||||
comment += `\n\nThis method is **not** available for bots`
|
||||
}
|
||||
|
||||
if (errors.throws[entry.name]) {
|
||||
comment +=
|
||||
`\n\nThis method *may* throw one of these errors: ` +
|
||||
errors.throws[entry.name]
|
||||
.map((it) => `{$see ${errorCodeToClassName(it)}`)
|
||||
.join(', ')
|
||||
}
|
||||
}
|
||||
}
|
||||
if (comment) ret += jsComment(comment) + '\n'
|
||||
|
||||
|
@ -172,7 +172,8 @@ ns.LAYER = $LAYER$;
|
|||
export function generateTypescriptDefinitionsForTlSchema(
|
||||
schema: TlFullSchema,
|
||||
layer: number,
|
||||
namespace = 'tl'
|
||||
namespace = 'tl',
|
||||
errors?: TlErrors
|
||||
): [string, string] {
|
||||
let ts = PRELUDE.replace('$NS$', namespace).replace('$LAYER$', layer + '')
|
||||
let js = PRELUDE_JS.replace('$NS$', namespace).replace(
|
||||
|
@ -180,6 +181,18 @@ export function generateTypescriptDefinitionsForTlSchema(
|
|||
layer + ''
|
||||
)
|
||||
|
||||
if (errors) {
|
||||
ts += `\n namespace errors {\n`
|
||||
js += `ns.errors = {};\n(function(ns){\n`
|
||||
|
||||
const [_ts, _js] = generateCodeForErrors(errors, 'ns.')
|
||||
ts += indent(8, _ts)
|
||||
js += _js
|
||||
|
||||
ts += `}\n`
|
||||
js += `})(ns.errors);\n`
|
||||
}
|
||||
|
||||
const namespaces = groupTlEntriesByNamespace(schema.entries)
|
||||
|
||||
for (const ns in namespaces) {
|
||||
|
|
|
@ -6,3 +6,19 @@ export const snakeToCamel = (s: string): string => {
|
|||
|
||||
export const camelToPascal = (s: string): string =>
|
||||
s[0].toUpperCase() + s.substr(1)
|
||||
|
||||
export function jsComment(s: string): string {
|
||||
return (
|
||||
'/**' +
|
||||
s
|
||||
.replace(/(?![^\n]{1,60}$)([^\n]{1,60})\s/g, '$1\n')
|
||||
.replace(/\n|^/g, '\n * ') +
|
||||
'\n */'
|
||||
)
|
||||
}
|
||||
|
||||
export function indent(size: number, s: string): string {
|
||||
let prefix = ''
|
||||
while (size--) prefix += ' '
|
||||
return prefix + s.replace(/\n/g, '\n' + prefix)
|
||||
}
|
||||
|
|
|
@ -42,6 +42,24 @@ export interface TlFullSchema {
|
|||
unions: Record<string, TlUnion>
|
||||
}
|
||||
|
||||
export interface TlError {
|
||||
code: number
|
||||
name: string
|
||||
description?: string
|
||||
virtual?: true
|
||||
|
||||
// internal fields used by generator
|
||||
_auto?: true
|
||||
_paramNames?: string[]
|
||||
}
|
||||
|
||||
export interface TlErrors {
|
||||
base: TlError[]
|
||||
errors: Record<string, TlError>
|
||||
throws: Record<string, string[]>
|
||||
userOnly: Record<string, 1>
|
||||
}
|
||||
|
||||
interface BasicDiff<T, K> {
|
||||
added: T[]
|
||||
removed: T[]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> TL schema and related utils used for MTCute.
|
||||
|
||||
Generated from TL layer **134** (last updated on 20.11.2021).
|
||||
Generated from TL layer **139** (last updated on 23.03.2022).
|
||||
|
||||
## About
|
||||
|
||||
|
@ -34,15 +34,31 @@ use-cases for mutable TL objects, so you can use exported
|
|||
`tl` is exported as a namespace to allow better code insights, and to avoid cluttering global namespace and very long
|
||||
import statements.
|
||||
|
||||
MTProto schema is available in namespace `mtp`, also exported by this package.
|
||||
|
||||
```typescript
|
||||
import { tl } from '@mtcute/tl'
|
||||
const obj: tl.RawInputPeerChat = { _: 'inputPeerChat', chatId: 42 }
|
||||
console.log(tl.isAnyInputPeer(obj)) // true
|
||||
```
|
||||
|
||||
### `@mtcute/tl/raw-schema`
|
||||
|
||||
[Documentation](./modules/raw_schema.html)
|
||||
RPC errors are also exposed in this package in `tl.errors` namespace:
|
||||
|
||||
```typescript
|
||||
import { tl } from '@mtcute/tl'
|
||||
try {
|
||||
await client.call(...)
|
||||
} catch (e) {
|
||||
if (e instanceof tl.errors.ChatInvalidError) {
|
||||
console.log('invalid chat')
|
||||
} else throw e
|
||||
}
|
||||
```
|
||||
|
||||
### `@mtcute/tl/api-schema`
|
||||
|
||||
[Documentation](./modules/api_schema.html)
|
||||
|
||||
JSON file describing all available TL classes, methods and unions. Can be used to write custom code generators
|
||||
> This very file is used to generate binary serialization and TypeScript typings for `@mtcute/tl`.
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,29 +1,24 @@
|
|||
// this file is sourced *before* actual schema.
|
||||
// should be used for types that are not currently added to the schema,
|
||||
// but seem to work
|
||||
// in case of conflict, type from main schema is preferred
|
||||
|
||||
// for internal use
|
||||
---types---
|
||||
|
||||
dummyUpdate pts:int pts_count:int channel_id:int53 = Update;
|
||||
|
||||
// reactions
|
||||
// taken from official docs coz why not lol
|
||||
// ctor ids will be generated by the codegen
|
||||
// *does* work with layer 131 (as of 25.07.21)
|
||||
// taken from OLD official
|
||||
// no longer work on newer layers because they changed this thing entirely
|
||||
// kept only as a historical reference
|
||||
|
||||
---types---
|
||||
|
||||
updateMessageReactions peer:Peer msg_id:int reactions:MessageReactions = Update;
|
||||
messageReactions flags:# min:flags.0?true results:Vector<ReactionCount> = MessageReactions;
|
||||
reactionCount flags:# chosen:flags.0?true reaction:string count:int = ReactionCount;
|
||||
|
||||
messageReactionsList flags:# count:int reactions:Vector<MessageUserReaction> users:Vector<User> next_offset:flags.0?string = MessageReactionsList;
|
||||
messageUserReaction user_id:int reaction:string = MessageUserReaction;
|
||||
|
||||
---functions---
|
||||
|
||||
messages.sendReaction flags:# peer:InputPeer msg_id:int reaction:flags.0?string = Updates;
|
||||
messages.getMessagesReactions peer:InputPeer id:Vector<int> = Updates;
|
||||
messages.getMessageReactionsList flags:# peer:InputPeer id:int reaction:flags.0?string offset:flags.1?string limit:int = MessageReactionsList;
|
||||
// ---types---
|
||||
//
|
||||
// updateMessageReactions peer:Peer msg_id:int reactions:MessageReactions = Update;
|
||||
// messageReactions flags:# min:flags.0?true results:Vector<ReactionCount> = MessageReactions;
|
||||
// reactionCount flags:# chosen:flags.0?true reaction:string count:int = ReactionCount;
|
||||
//
|
||||
// messageReactionsList flags:# count:int reactions:Vector<MessageUserReaction> users:Vector<User> next_offset:flags.0?string = MessageReactionsList;
|
||||
// messageUserReaction user_id:int reaction:string = MessageUserReaction;
|
||||
//
|
||||
// ---functions---
|
||||
//
|
||||
// messages.sendReaction flags:# peer:InputPeer msg_id:int reaction:flags.0?string = Updates;
|
||||
// messages.getMessagesReactions peer:InputPeer id:Vector<int> = Updates;
|
||||
// messages.getMessageReactionsList flags:# peer:InputPeer id:int reaction:flags.0?string offset:flags.1?string limit:int = MessageReactionsList;
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
"inputPrivacyValueDisallowChatParticipants": ["chats"],
|
||||
"inputUser": ["user_id"],
|
||||
"inputUserFromMessage": ["user_id"],
|
||||
"keyboardButtonUserProfile": ["user_id"],
|
||||
"message": ["via_bot_id"],
|
||||
"messageActionChannelMigrateFrom": ["chat_id"],
|
||||
"messageActionChatAddUser": ["users"],
|
||||
|
@ -120,6 +121,7 @@
|
|||
"updateUserTyping": ["user_id"],
|
||||
"user": ["id"],
|
||||
"userEmpty": ["id"],
|
||||
"userFull": ["id"],
|
||||
"webAuthorization": ["bot_id"],
|
||||
"_": "Dummy line teehee~"
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@mtcute/tl",
|
||||
"version": "134.0.0",
|
||||
"version": "139.0.0",
|
||||
"description": "TL schema used for MTCute",
|
||||
"main": "index.js",
|
||||
"author": "Alisa Sireneva <me@tei.su>",
|
||||
|
@ -27,4 +27,4 @@
|
|||
"csv-parser": "^3.0.0",
|
||||
"js-yaml": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -4,8 +4,10 @@ export const DOC_CACHE_FILE = join(__dirname, '.documentation.cache.json')
|
|||
export const DESCRIPTIONS_YAML_FILE = join(__dirname, '../data/descriptions.yaml')
|
||||
export const API_SCHEMA_JSON_FILE = join(__dirname, '../api-schema.json')
|
||||
export const MTP_SCHEMA_JSON_FILE = join(__dirname, '../mtp-schema.json')
|
||||
export const ERRORS_JSON_FILE = join(__dirname, '../raw-errors.json')
|
||||
|
||||
export const CORE_DOMAIN = 'https://corefork.telegram.org'
|
||||
export const CORE_DOMAIN = 'https://core.telegram.org'
|
||||
export const COREFORK_DOMAIN = 'https://core.telegram.org'
|
||||
|
||||
export const TDESKTOP_SCHEMA =
|
||||
'https://raw.githubusercontent.com/telegramdesktop/tdesktop/dev/Telegram/Resources/tl/api.tl'
|
||||
|
|
|
@ -15,10 +15,7 @@ import fetch from 'node-fetch'
|
|||
import readline from 'readline'
|
||||
import { writeTlEntryToString } from '@mtcute/tl-utils/src/stringify'
|
||||
import {
|
||||
CORE_DOMAIN,
|
||||
API_SCHEMA_JSON_FILE,
|
||||
TDESKTOP_SCHEMA,
|
||||
TDLIB_SCHEMA,
|
||||
CORE_DOMAIN, API_SCHEMA_JSON_FILE, TDESKTOP_SCHEMA, TDLIB_SCHEMA, COREFORK_DOMAIN
|
||||
} from './constants'
|
||||
import { fetchRetry } from './utils'
|
||||
import {
|
||||
|
@ -69,8 +66,8 @@ async function fetchTdesktopSchema(): Promise<Schema> {
|
|||
}
|
||||
}
|
||||
|
||||
async function fetchCoreSchema(): Promise<Schema> {
|
||||
const html = await fetchRetry(`${CORE_DOMAIN}/schema`)
|
||||
async function fetchCoreSchema(domain = CORE_DOMAIN, name = 'Core'): Promise<Schema> {
|
||||
const html = await fetchRetry(`${domain}/schema`)
|
||||
const $ = cheerio.load(html)
|
||||
// cheerio doesn't always unescape them
|
||||
const schema = $('.page_scheme code')
|
||||
|
@ -85,7 +82,7 @@ async function fetchCoreSchema(): Promise<Schema> {
|
|||
if (!layer) throw new Error('Layer number not available')
|
||||
|
||||
return {
|
||||
name: 'Core',
|
||||
name,
|
||||
layer: parseInt(layer[1]),
|
||||
content: tlToFullSchema(schema),
|
||||
}
|
||||
|
@ -175,6 +172,7 @@ async function main() {
|
|||
await fetchTdlibSchema(),
|
||||
await fetchTdesktopSchema(),
|
||||
await fetchCoreSchema(),
|
||||
await fetchCoreSchema(COREFORK_DOMAIN, 'Corefork'),
|
||||
{
|
||||
name: 'Custom',
|
||||
layer: 0, // handled manually
|
||||
|
|
|
@ -1,26 +1,16 @@
|
|||
import { join } from 'path'
|
||||
import { TlError, TlErrors } from '@mtcute/tl-utils/src/types'
|
||||
import fetch from 'node-fetch'
|
||||
import csvParser from 'csv-parser'
|
||||
import { camelToPascal, snakeToCamel } from '@mtcute/tl-utils/src/codegen/utils'
|
||||
import { writeFile } from 'fs/promises'
|
||||
import { ERRORS_JSON_FILE } from './constants'
|
||||
|
||||
const OUT_TS_FILE = join(__dirname, '../errors.d.ts')
|
||||
const OUT_JS_FILE = join(__dirname, '../errors.js')
|
||||
|
||||
const TELETHON_ERRORS_CSV =
|
||||
const ERRORS_PAGE_TG = 'https://corefork.telegram.org/api/errors'
|
||||
const ERRORS_PAGE_TELETHON =
|
||||
'https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_generator/data/errors.csv'
|
||||
|
||||
interface ErrorInfo {
|
||||
base?: true
|
||||
virtual?: true
|
||||
codes: string
|
||||
name: string
|
||||
description: string
|
||||
}
|
||||
|
||||
const baseErrors: ErrorInfo[] = [
|
||||
const baseErrors: TlError[] = [
|
||||
{
|
||||
codes: '400',
|
||||
code: 400,
|
||||
name: 'BAD_REQUEST',
|
||||
description:
|
||||
'The query contains errors. In the event that a request was created using a form ' +
|
||||
|
@ -28,26 +18,26 @@ const baseErrors: ErrorInfo[] = [
|
|||
'be corrected before the query is repeated',
|
||||
},
|
||||
{
|
||||
codes: '401',
|
||||
code: 401,
|
||||
name: 'UNAUTHORIZED',
|
||||
description:
|
||||
'There was an unauthorized attempt to use functionality available only to authorized users.',
|
||||
},
|
||||
{
|
||||
codes: '403',
|
||||
code: 403,
|
||||
name: 'FORBIDDEN',
|
||||
description:
|
||||
'Privacy violation. For example, an attempt to write a message ' +
|
||||
'to someone who has blacklisted the current user.',
|
||||
},
|
||||
{
|
||||
codes: '404',
|
||||
code: 404,
|
||||
name: 'NOT_FOUND',
|
||||
description:
|
||||
'An attempt to invoke a non-existent object, such as a method.',
|
||||
},
|
||||
{
|
||||
codes: '420',
|
||||
code: 420,
|
||||
name: 'FLOOD',
|
||||
description:
|
||||
'The maximum allowed number of attempts to invoke the given method' +
|
||||
|
@ -56,13 +46,13 @@ const baseErrors: ErrorInfo[] = [
|
|||
'phone number.',
|
||||
},
|
||||
{
|
||||
codes: '303',
|
||||
code: 303,
|
||||
name: 'SEE_OTHER',
|
||||
description:
|
||||
'The request must be repeated, but directed to a different data center',
|
||||
},
|
||||
{
|
||||
codes: '406',
|
||||
code: 406,
|
||||
name: 'NOT_ACCEPTABLE',
|
||||
description:
|
||||
'Similar to 400 BAD_REQUEST, but the app should not display any error messages to user ' +
|
||||
|
@ -70,239 +60,174 @@ const baseErrors: ErrorInfo[] = [
|
|||
'updateServiceNotification instead.',
|
||||
},
|
||||
{
|
||||
codes: '500',
|
||||
code: 500,
|
||||
name: 'INTERNAL',
|
||||
description:
|
||||
'An internal server error occurred while a request was being processed; ' +
|
||||
'for example, there was a disruption while accessing a database or file storage.',
|
||||
},
|
||||
]
|
||||
baseErrors.forEach((it) => (it.base = true))
|
||||
|
||||
const virtualErrors: ErrorInfo[] = [
|
||||
const virtualErrors: TlError[] = [
|
||||
{
|
||||
name: 'RPC_TIMEOUT',
|
||||
codes: '408',
|
||||
code: 408,
|
||||
description: 'The set RPC timeout has exceeded',
|
||||
},
|
||||
{
|
||||
name: 'MESSAGE_NOT_FOUND',
|
||||
codes: '404',
|
||||
code: 404,
|
||||
description: 'Message was not found',
|
||||
},
|
||||
]
|
||||
virtualErrors.forEach((it) => (it.virtual = true))
|
||||
|
||||
const inheritanceTable: Record<string, string> = {
|
||||
400: 'BadRequestError',
|
||||
401: 'UnauthorizedError',
|
||||
403: 'ForbiddenError',
|
||||
404: 'NotFoundError',
|
||||
420: 'FloodError',
|
||||
303: 'SeeOtherError',
|
||||
406: 'NotAcceptableError',
|
||||
500: 'InternalError',
|
||||
}
|
||||
async function fetchFromTelegram(errors: TlErrors) {
|
||||
const page = await fetch(ERRORS_PAGE_TG).then((it) => it.text())
|
||||
const jsonUrl = page.match(
|
||||
/can be found <a href="([^"]+)">here »<\/a>/i
|
||||
)![1]
|
||||
|
||||
const RPC_ERROR_CLASS_JS = `
|
||||
class RpcError extends Error {
|
||||
constructor(code, text, description) {
|
||||
super(description);
|
||||
this.code = code;
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
exports.RpcError = RpcError;
|
||||
`.trimStart()
|
||||
|
||||
const RPC_ERROR_CLASS_TS = `
|
||||
export declare class RpcError extends Error {
|
||||
code: number;
|
||||
text: string;
|
||||
constructor (code: number, text: string, description?: string);
|
||||
}
|
||||
`.trimStart()
|
||||
|
||||
const BASE_ERROR_TEMPLATE_JS = `
|
||||
class {className} extends RpcError {
|
||||
constructor(name, description) {
|
||||
super({code}, name, description);
|
||||
}
|
||||
}
|
||||
exports.{className} = {className}
|
||||
`
|
||||
const BASE_ERROR_TEMPLATE_TS = `
|
||||
export declare class {className} extends RpcError {
|
||||
constructor (name: string, description: string);
|
||||
}
|
||||
`
|
||||
|
||||
const TL_BUILDER_TEMPLATE_JS = `
|
||||
exports.createRpcErrorFromTl = function (obj) {
|
||||
if (obj.errorMessage in _staticNameErrors) return new _staticNameErrors[obj.errorMessage]();
|
||||
|
||||
let match;
|
||||
{parametrized}
|
||||
|
||||
if (obj.errorCode in _baseCodeErrors) return new _baseCodeErrors[obj.errorCode](obj.errorMessage);
|
||||
|
||||
return new RpcError(obj.errorCode, obj.errorMessage);
|
||||
}
|
||||
`.trim()
|
||||
|
||||
const DESCRIPTION_PARAM_RE = /_X_|_X$|^X_/
|
||||
|
||||
function jsComment(s: string): string {
|
||||
return (
|
||||
'/**' +
|
||||
s
|
||||
.replace(/(?![^\n]{1,60}$)([^\n]{1,60})\s/g, '$1\n')
|
||||
.replace(/\n|^/g, '\n * ') +
|
||||
'\n */'
|
||||
const json = await fetch(new URL(jsonUrl, ERRORS_PAGE_TG)).then((it) =>
|
||||
it.json()
|
||||
)
|
||||
|
||||
// since nobody fucking guarantees that .descriptions
|
||||
// will have description for each described here (or vice versa),
|
||||
// we will process them independently
|
||||
|
||||
for (const code of Object.keys(json.errors)) {
|
||||
for (const name of Object.keys(json.errors[code])) {
|
||||
const thrownBy = json.errors[code][name]
|
||||
|
||||
const _code = parseInt(code)
|
||||
if (isNaN(_code)) {
|
||||
throw new Error(`Invalid code: ${code}`)
|
||||
}
|
||||
|
||||
if (!(name in errors.errors)) {
|
||||
errors.errors[name] = {
|
||||
code: _code,
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
for (const method of thrownBy) {
|
||||
if (!(method in errors.throws)) {
|
||||
errors.throws[method] = []
|
||||
}
|
||||
|
||||
if (errors.throws[method].indexOf(name) === -1) {
|
||||
errors.throws[method].push(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const name of Object.keys(json.descriptions)) {
|
||||
if (!(name in errors.errors)) {
|
||||
errors.errors[name] = {
|
||||
_auto: true,
|
||||
code: 400,
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
errors.errors[name].description = json.descriptions[name]
|
||||
}
|
||||
|
||||
json.user_only.forEach((it: string) => (errors.userOnly[it] = 1))
|
||||
}
|
||||
|
||||
async function fetchTelethonErrors(): Promise<ErrorInfo[]> {
|
||||
const stream = await fetch(TELETHON_ERRORS_CSV).then((i) => i.body!)
|
||||
async function fetchFromTelethon(errors: TlErrors) {
|
||||
const csv = await fetch(ERRORS_PAGE_TELETHON)
|
||||
const parser = csvParser()
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const ret: ErrorInfo[] = []
|
||||
function addError(name: string, codes: string, description: string): void {
|
||||
if (!codes) return
|
||||
if (name === 'TIMEOUT') return
|
||||
|
||||
stream
|
||||
.pipe(csvParser())
|
||||
.on('data', (it) => ret.push(it))
|
||||
.on('end', () => resolve(ret))
|
||||
const code = parseInt(codes)
|
||||
if (isNaN(code)) {
|
||||
throw new Error(`Invalid code: ${codes} (name: ${name})`)
|
||||
}
|
||||
|
||||
// telethon uses numbers for parameters instead of printf-like
|
||||
// we'll convert it back to printf-like
|
||||
// so far, only one param is supported
|
||||
name = name.replace(/_0(_)?/g, '_%d$1')
|
||||
|
||||
if (!(name in errors.errors)) {
|
||||
errors.errors[name] = {
|
||||
code,
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
const obj = errors.errors[name]
|
||||
if (obj._auto) {
|
||||
obj.code = code
|
||||
delete obj._auto
|
||||
}
|
||||
|
||||
// same with descriptions, telethon uses python-like formatting
|
||||
// strings. we'll convert it to printf-like, while also saving parameter
|
||||
// names for better code insights
|
||||
// we also prefer description from telegram, if it's available and doesn't use placeholders
|
||||
if (description) {
|
||||
const desc = description.replace(/{([a-z0-9_]+)}/gi, (_, name) => {
|
||||
if (!obj._paramNames) {
|
||||
obj._paramNames = []
|
||||
}
|
||||
obj._paramNames.push(name)
|
||||
return '%d'
|
||||
})
|
||||
|
||||
if (!obj.description || obj._paramNames?.length) {
|
||||
obj.description = desc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
csv.body
|
||||
.pipe(parser)
|
||||
.on('data', ({ name, codes, description }) =>
|
||||
addError(name, codes, description)
|
||||
)
|
||||
.on('end', resolve)
|
||||
.on('error', reject)
|
||||
})
|
||||
}
|
||||
|
||||
interface ExtendedErrorInfo extends ErrorInfo {
|
||||
className: string
|
||||
code: string
|
||||
inherits: string
|
||||
argument: string | null
|
||||
}
|
||||
|
||||
function getExtendedErrorInfo(err: ErrorInfo): ExtendedErrorInfo {
|
||||
const [, argument] = err.description.match(/{([a-z_]+)}/i) ?? [, null]
|
||||
|
||||
const ret = err as ExtendedErrorInfo
|
||||
|
||||
let className = err.name
|
||||
if (className[0].match(/[0-9]/)) {
|
||||
className = '_' + className
|
||||
}
|
||||
className = className.replace(DESCRIPTION_PARAM_RE, (i) =>
|
||||
i === '_X_' ? '_' : ''
|
||||
)
|
||||
|
||||
ret.className =
|
||||
camelToPascal(snakeToCamel(className.toLowerCase())) + 'Error'
|
||||
ret.code = err.codes.split(' ')[0]
|
||||
ret.inherits = err.base
|
||||
? 'RpcError'
|
||||
: inheritanceTable[ret.code] ?? 'RpcError'
|
||||
ret.argument = argument ? snakeToCamel(argument) : null
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const errors: TlErrors = {
|
||||
base: baseErrors,
|
||||
errors: {},
|
||||
throws: {},
|
||||
userOnly: {},
|
||||
}
|
||||
|
||||
console.log('Fetching errors from Telegram...')
|
||||
await fetchFromTelegram(errors)
|
||||
|
||||
// using some incredible fucking crutches we are also able to parse telethon errors file
|
||||
// and add missing error descriptions
|
||||
console.log('Fetching errors from Telethon...')
|
||||
await fetchFromTelethon(errors)
|
||||
|
||||
const errors = [
|
||||
...baseErrors,
|
||||
...(await fetchTelethonErrors()),
|
||||
...virtualErrors,
|
||||
].map(getExtendedErrorInfo)
|
||||
|
||||
console.log('Generating code...')
|
||||
|
||||
let js = RPC_ERROR_CLASS_JS
|
||||
let ts = RPC_ERROR_CLASS_TS
|
||||
|
||||
for (const err of errors) {
|
||||
// generate error class in js
|
||||
if (err.base) {
|
||||
js += BASE_ERROR_TEMPLATE_JS.replace(
|
||||
/{className}/g,
|
||||
err.className
|
||||
).replace(/{code}/g, err.code)
|
||||
} else {
|
||||
js += `class ${err.className} extends ${err.inherits} {\n`
|
||||
js += ` constructor(${err.argument ?? ''}) {\n`
|
||||
|
||||
const description = JSON.stringify(err.description).replace(
|
||||
/{([a-z_]+)}/gi,
|
||||
(_, $1) => `" + ${snakeToCamel($1)} + "`
|
||||
)
|
||||
|
||||
if (err.inherits === 'RpcError') {
|
||||
js += ` super(${err.code}, '${err.name}', ${description});\n`
|
||||
} else {
|
||||
js += ` super('${err.name}', ${description});\n`
|
||||
}
|
||||
|
||||
if (err.argument) {
|
||||
js += ` this.${err.argument} = ${err.argument};\n`
|
||||
}
|
||||
|
||||
js += ' }\n}\n'
|
||||
js += `exports.${err.className} = ${err.className};\n`
|
||||
virtualErrors.forEach((err) => {
|
||||
if (errors.errors[err.name]) {
|
||||
console.log(`Error ${err.name} already exists and is not virtual`)
|
||||
return
|
||||
}
|
||||
|
||||
// generate error class typings
|
||||
if (err.description) {
|
||||
ts += jsComment(err.description) + '\n'
|
||||
}
|
||||
if (err.base) {
|
||||
ts += BASE_ERROR_TEMPLATE_TS.replace(/{className}/g, err.className)
|
||||
} else {
|
||||
ts += `export declare class ${err.className} extends ${err.inherits} {\n`
|
||||
errors.errors[err.name] = err
|
||||
})
|
||||
|
||||
if (err.argument) {
|
||||
ts += ` ${err.argument}: number;\n`
|
||||
}
|
||||
console.log('Saving...')
|
||||
|
||||
ts += ` constructor (${
|
||||
err.argument ? err.argument + ': number' : ''
|
||||
});\n`
|
||||
ts += '}\n'
|
||||
}
|
||||
}
|
||||
|
||||
ts +=
|
||||
'export declare function createRpcErrorFromTl (obj: object): RpcError;\n'
|
||||
|
||||
js += 'const _staticNameErrors = {\n'
|
||||
for (const err of errors) {
|
||||
if (err.virtual || err.argument) continue
|
||||
js += ` '${err.name}': ${err.className},\n`
|
||||
}
|
||||
js += ` 'Timeout': TimeoutError,\n` // because telegram c:
|
||||
js += '};\n'
|
||||
|
||||
js += 'const _baseCodeErrors = {\n'
|
||||
for (const [code, error] of Object.entries(inheritanceTable)) {
|
||||
js += ` ${code}: ${error},\n`
|
||||
}
|
||||
js += '};\n'
|
||||
|
||||
let builderInner = ''
|
||||
for (const err of errors) {
|
||||
if (err.virtual || !err.argument) continue
|
||||
|
||||
const regex = err.name.replace(DESCRIPTION_PARAM_RE, (s) =>
|
||||
s.replace('X', '(\\d+)')
|
||||
)
|
||||
builderInner +=
|
||||
`if ((match=obj.errorMessage.match(/${regex}/))!=null)` +
|
||||
`return new ${err.className}(parseInt(match[1]));\n`
|
||||
}
|
||||
js += TL_BUILDER_TEMPLATE_JS.replace('{parametrized}', builderInner)
|
||||
|
||||
await writeFile(OUT_JS_FILE, js)
|
||||
await writeFile(OUT_TS_FILE, ts)
|
||||
await writeFile(ERRORS_JSON_FILE, JSON.stringify(errors))
|
||||
}
|
||||
|
||||
main().catch(console.error)
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import { unpackTlSchema } from './schema'
|
||||
import { API_SCHEMA_JSON_FILE, MTP_SCHEMA_JSON_FILE, ESM_PRELUDE } from './constants'
|
||||
import {
|
||||
API_SCHEMA_JSON_FILE,
|
||||
MTP_SCHEMA_JSON_FILE,
|
||||
ESM_PRELUDE,
|
||||
ERRORS_JSON_FILE,
|
||||
} from './constants'
|
||||
import { readFile, writeFile } from 'fs/promises'
|
||||
import { parseFullTlSchema } from '@mtcute/tl-utils/src/schema'
|
||||
import { TlFullSchema } from '@mtcute/tl-utils/src/types'
|
||||
import { TlErrors, TlFullSchema } from '@mtcute/tl-utils/src/types'
|
||||
import { join } from 'path'
|
||||
import { generateTypescriptDefinitionsForTlSchema } from '@mtcute/tl-utils/src/codegen/types'
|
||||
import { generateReaderCodeForTlEntries } from '@mtcute/tl-utils/src/codegen/reader'
|
||||
|
@ -16,17 +21,21 @@ const OUT_WRITERS_FILE = join(__dirname, '../binary/writer.js')
|
|||
async function generateTypings(
|
||||
apiSchema: TlFullSchema,
|
||||
apiLayer: number,
|
||||
mtpSchema: TlFullSchema
|
||||
mtpSchema: TlFullSchema,
|
||||
errors: TlErrors
|
||||
) {
|
||||
console.log('Generating typings...')
|
||||
const [apiTs, apiJs] = generateTypescriptDefinitionsForTlSchema(
|
||||
apiSchema,
|
||||
apiLayer
|
||||
apiLayer,
|
||||
undefined,
|
||||
errors
|
||||
)
|
||||
const [mtpTs, mtpJs] = generateTypescriptDefinitionsForTlSchema(
|
||||
mtpSchema,
|
||||
0,
|
||||
'mtp'
|
||||
'mtp',
|
||||
errors
|
||||
)
|
||||
|
||||
await writeFile(
|
||||
|
@ -67,6 +76,10 @@ async function generateWriters(
|
|||
}
|
||||
|
||||
async function main() {
|
||||
const errors: TlErrors = JSON.parse(
|
||||
await readFile(ERRORS_JSON_FILE, 'utf8')
|
||||
)
|
||||
|
||||
const [apiSchema, apiLayer] = unpackTlSchema(
|
||||
JSON.parse(await readFile(API_SCHEMA_JSON_FILE, 'utf8'))
|
||||
)
|
||||
|
@ -74,7 +87,7 @@ async function main() {
|
|||
JSON.parse(await readFile(MTP_SCHEMA_JSON_FILE, 'utf8'))
|
||||
)
|
||||
|
||||
await generateTypings(apiSchema, apiLayer, mtpSchema)
|
||||
await generateTypings(apiSchema, apiLayer, mtpSchema, errors)
|
||||
await generateReaders(apiSchema, mtpSchema)
|
||||
await generateWriters(apiSchema, mtpSchema)
|
||||
|
||||
|
|
|
@ -2,22 +2,14 @@
|
|||
// This is a test for TypeScript typings
|
||||
// This file is never executed, only compiled
|
||||
|
||||
import * as rawSchema from '../raw-schema'
|
||||
import readerMap from '../binary/reader'
|
||||
import writerMap from '../binary/writer'
|
||||
import { BadRequestError, RpcError } from '../errors'
|
||||
import { tl } from '../'
|
||||
|
||||
const layer: string = rawSchema.apiLayer
|
||||
Object.entries(rawSchema.api).forEach(([ns, content]) => {
|
||||
content.classes.forEach((cls) => {
|
||||
const name: string = cls.type
|
||||
})
|
||||
})
|
||||
readerMap[0].call(null, null)
|
||||
writerMap['mt_message'].call(null, null, {})
|
||||
|
||||
readerMap[0].call(null)
|
||||
writerMap['mt_message'].call(null, {})
|
||||
|
||||
const error: RpcError = new BadRequestError(
|
||||
const error: tl.errors.RpcError = new tl.errors.BadRequestError(
|
||||
'BAD_REQUEST',
|
||||
'Client has issued an invalid request'
|
||||
)
|
||||
|
|
|
@ -1121,7 +1121,7 @@
|
|||
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
|
||||
|
||||
"@types/integer@*":
|
||||
version "4.0.0"
|
||||
version "5.4.1
|
||||
resolved "https://registry.yarnpkg.com/@types/integer/-/integer-4.0.0.tgz#3b778715df72d2cf8ba73bad27bd9d830907f944"
|
||||
integrity sha512-2U1i6bIRiqizl6O+ETkp2HhUZIxg7g+burUabh9tzGd0qcszfNaFRaY9bGNlQKgEU7DCsH5qMajRDW5QamWQbw==
|
||||
|
||||
|
|
Loading…
Reference in a new issue