refactor: reworked errors codegen
This commit is contained in:
parent
91894e87cc
commit
4b7d7d2e35
27 changed files with 374 additions and 469 deletions
|
@ -174,7 +174,7 @@ export async function start(
|
||||||
|
|
||||||
return me
|
return me
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!(e instanceof tl.errors.AuthKeyUnregisteredError)) throw e
|
if (!tl.RpcError.is(e, 'AUTH_KEY_UNREGISTERED')) throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params.phone && !params.botToken) {
|
if (!params.phone && !params.botToken) {
|
||||||
|
@ -221,19 +221,21 @@ export async function start(
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const code = await resolveMaybeDynamic(params.code)
|
const code = await resolveMaybeDynamic(params.code)
|
||||||
if (!code) throw new tl.errors.PhoneCodeEmptyError()
|
if (!code) throw new tl.RpcError(400, 'PHONE_CODE_EMPTY')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = await this.signIn(phone, sentCode.phoneCodeHash, code)
|
result = await this.signIn(phone, sentCode.phoneCodeHash, code)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof tl.errors.SessionPasswordNeededError) {
|
if (!tl.RpcError.is(e)) throw e
|
||||||
|
|
||||||
|
if (e.is('SESSION_PASSWORD_NEEDED')) {
|
||||||
has2fa = true
|
has2fa = true
|
||||||
break
|
break
|
||||||
} else if (
|
} else if (
|
||||||
e instanceof tl.errors.PhoneCodeEmptyError ||
|
e.is('PHONE_CODE_EMPTY') ||
|
||||||
e instanceof tl.errors.PhoneCodeExpiredError ||
|
e.is('PHONE_CODE_EXPIRED') ||
|
||||||
e instanceof tl.errors.PhoneCodeHashEmptyError ||
|
e.is('PHONE_CODE_INVALID') ||
|
||||||
e instanceof tl.errors.PhoneCodeInvalidError
|
e.is('PHONE_CODE_HASH_EMPTY')
|
||||||
) {
|
) {
|
||||||
if (typeof params.code !== 'function') {
|
if (typeof params.code !== 'function') {
|
||||||
throw new MtArgumentError('Provided code was invalid')
|
throw new MtArgumentError('Provided code was invalid')
|
||||||
|
@ -270,7 +272,7 @@ export async function start(
|
||||||
throw new MtArgumentError('Provided password was invalid')
|
throw new MtArgumentError('Provided password was invalid')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e instanceof tl.errors.PasswordHashInvalidError) {
|
if (tl.RpcError.is(e, 'PASSWORD_HASH_INVALID')) {
|
||||||
if (params.invalidCodeCallback) {
|
if (params.invalidCodeCallback) {
|
||||||
await params.invalidCodeCallback('password')
|
await params.invalidCodeCallback('password')
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
import { ChatMember, InputPeerLike, MtInvalidPeerTypeError, PeersIndex } from '../../types'
|
import {
|
||||||
|
ChatMember,
|
||||||
|
InputPeerLike,
|
||||||
|
MtInvalidPeerTypeError,
|
||||||
|
PeersIndex,
|
||||||
|
} from '../../types'
|
||||||
import {
|
import {
|
||||||
isInputPeerChannel,
|
isInputPeerChannel,
|
||||||
isInputPeerChat,
|
isInputPeerChat,
|
||||||
|
@ -27,7 +32,9 @@ export async function getChatMember(
|
||||||
const chat = await this.resolvePeer(chatId)
|
const chat = await this.resolvePeer(chatId)
|
||||||
|
|
||||||
if (isInputPeerChat(chat)) {
|
if (isInputPeerChat(chat)) {
|
||||||
if (!isInputPeerUser(user)) { throw new MtInvalidPeerTypeError(userId, 'user') }
|
if (!isInputPeerUser(user)) {
|
||||||
|
throw new MtInvalidPeerTypeError(userId, 'user')
|
||||||
|
}
|
||||||
|
|
||||||
const res = await this.call({
|
const res = await this.call({
|
||||||
_: 'messages.getFullChat',
|
_: 'messages.getFullChat',
|
||||||
|
@ -57,7 +64,7 @@ export async function getChatMember(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new tl.errors.UserNotParticipantError()
|
throw new tl.RpcError(404, 'USER_NOT_PARTICIPANT')
|
||||||
} else if (isInputPeerChannel(chat)) {
|
} else if (isInputPeerChannel(chat)) {
|
||||||
const res = await this.call({
|
const res = await this.call({
|
||||||
_: 'channels.getParticipant',
|
_: 'channels.getParticipant',
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
import { ChatPreview, MtArgumentError, MtNotFoundError } from '../../types'
|
import { ChatPreview, MtArgumentError, MtPeerNotFoundError } from '../../types'
|
||||||
import { INVITE_LINK_REGEX } from '../../utils/peer-utils'
|
import { INVITE_LINK_REGEX } from '../../utils/peer-utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,7 +25,7 @@ export async function getChatPreview(
|
||||||
})
|
})
|
||||||
|
|
||||||
if (res._ !== 'chatInvite') {
|
if (res._ !== 'chatInvite') {
|
||||||
throw new MtNotFoundError('You have already joined this chat!')
|
throw new MtPeerNotFoundError('You have already joined this chat!')
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ChatPreview(this, res, inviteLink)
|
return new ChatPreview(this, res, inviteLink)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
import { Chat, InputPeerLike, MtNotFoundError } from '../../types'
|
import { Chat, InputPeerLike, MtPeerNotFoundError } from '../../types'
|
||||||
import {
|
import {
|
||||||
INVITE_LINK_REGEX,
|
INVITE_LINK_REGEX,
|
||||||
normalizeToInputChannel,
|
normalizeToInputChannel,
|
||||||
|
@ -40,7 +40,10 @@ export async function joinChat(
|
||||||
|
|
||||||
const res = await this.call({
|
const res = await this.call({
|
||||||
_: 'channels.joinChannel',
|
_: 'channels.joinChannel',
|
||||||
channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId),
|
channel: normalizeToInputChannel(
|
||||||
|
await this.resolvePeer(chatId),
|
||||||
|
chatId,
|
||||||
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
assertIsUpdatesGroup('channels.joinChannel', res)
|
assertIsUpdatesGroup('channels.joinChannel', res)
|
||||||
|
|
|
@ -134,7 +134,9 @@ export async function* getDialogs(
|
||||||
return it.id === params!.folder || it.title === params!.folder
|
return it.id === params!.folder || it.title === params!.folder
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!found) { throw new MtArgumentError(`Could not find folder ${params.folder}`) }
|
if (!found) {
|
||||||
|
throw new MtArgumentError(`Could not find folder ${params.folder}`)
|
||||||
|
}
|
||||||
|
|
||||||
filters = found as tl.RawDialogFilter
|
filters = found as tl.RawDialogFilter
|
||||||
} else {
|
} else {
|
||||||
|
@ -160,7 +162,9 @@ export async function* getDialogs(
|
||||||
!filters ||
|
!filters ||
|
||||||
filters._ === 'dialogFilterDefault' ||
|
filters._ === 'dialogFilterDefault' ||
|
||||||
!filters.pinnedPeers.length
|
!filters.pinnedPeers.length
|
||||||
) { return null }
|
) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
const res = await this.call({
|
const res = await this.call({
|
||||||
_: 'messages.getPeerDialogs',
|
_: 'messages.getPeerDialogs',
|
||||||
peers: filters.pinnedPeers.map((peer) => ({
|
peers: filters.pinnedPeers.map((peer) => ({
|
||||||
|
@ -229,8 +233,7 @@ export async function* getDialogs(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterFolder = filters ?
|
const filterFolder = filters ? // if pinned is `only`, this wouldn't be reached
|
||||||
// if pinned is `only`, this wouldn't be reached
|
|
||||||
// if pinned is `exclude`, we want to exclude them
|
// if pinned is `exclude`, we want to exclude them
|
||||||
// if pinned is `include`, we already yielded them, so we also want to exclude them
|
// if pinned is `include`, we already yielded them, so we also want to exclude them
|
||||||
// if pinned is `keep`, we want to keep them
|
// if pinned is `keep`, we want to keep them
|
||||||
|
@ -266,7 +269,7 @@ export async function* getDialogs(
|
||||||
const last = dialogs[dialogs.length - 1]
|
const last = dialogs[dialogs.length - 1]
|
||||||
offsetPeer = last.chat.inputPeer
|
offsetPeer = last.chat.inputPeer
|
||||||
offsetId = last.raw.topMessage
|
offsetId = last.raw.topMessage
|
||||||
offsetDate = normalizeDate(last.lastMessage.date)!
|
offsetDate = normalizeDate(last.lastMessage?.date) ?? 0
|
||||||
|
|
||||||
for (const d of dialogs) {
|
for (const d of dialogs) {
|
||||||
if (filterFolder && !filterFolder(d)) continue
|
if (filterFolder && !filterFolder(d)) continue
|
||||||
|
|
|
@ -137,13 +137,14 @@ export async function* downloadAsIterable(
|
||||||
},
|
},
|
||||||
{ dcId, kind: connectionKind },
|
{ dcId, kind: connectionKind },
|
||||||
)
|
)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
} catch (e: unknown) {
|
||||||
} catch (e: any) {
|
if (!tl.RpcError.is(e)) throw e
|
||||||
if (e.constructor === tl.errors.FileMigrateXError) {
|
|
||||||
dcId = e.new_dc
|
if (e.is('FILE_MIGRATE_%d')) {
|
||||||
|
dcId = e.newDc
|
||||||
|
|
||||||
return downloadChunk(chunk)
|
return downloadChunk(chunk)
|
||||||
} else if (e.constructor === tl.errors.FilerefUpgradeNeededError) {
|
} else if (e.is('FILEREF_UPGRADE_NEEDED')) {
|
||||||
// todo: implement someday
|
// todo: implement someday
|
||||||
// see: https://github.com/LonamiWebs/Telethon/blob/0e8bd8248cc649637b7c392616887c50986427a0/telethon/client/downloads.py#L99
|
// see: https://github.com/LonamiWebs/Telethon/blob/0e8bd8248cc649637b7c392616887c50986427a0/telethon/client/downloads.py#L99
|
||||||
throw new MtUnsupportedError('File ref expired!')
|
throw new MtUnsupportedError('File ref expired!')
|
||||||
|
|
|
@ -117,7 +117,7 @@ export async function editInlineMessage(
|
||||||
|
|
||||||
return
|
return
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof tl.errors.MediaEmptyError) {
|
if (tl.RpcError.is(e, 'MEDIA_EMPTY')) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { getMarkedPeerId } from '@mtcute/core'
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
|
@ -5,6 +6,7 @@ import {
|
||||||
FormattedString,
|
FormattedString,
|
||||||
InputPeerLike,
|
InputPeerLike,
|
||||||
Message,
|
Message,
|
||||||
|
MtMessageNotFoundError,
|
||||||
ReplyMarkup,
|
ReplyMarkup,
|
||||||
} from '../../types'
|
} from '../../types'
|
||||||
|
|
||||||
|
@ -104,7 +106,13 @@ export async function sendCopy(
|
||||||
|
|
||||||
const msg = await this.getMessages(fromPeer, message)
|
const msg = await this.getMessages(fromPeer, message)
|
||||||
|
|
||||||
if (!msg) throw new tl.errors.MessageNotFoundError()
|
if (!msg) {
|
||||||
|
throw new MtMessageNotFoundError(
|
||||||
|
getMarkedPeerId(fromPeer),
|
||||||
|
message,
|
||||||
|
'to copy',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return msg.sendCopy(toChatId, params)
|
return msg.sendCopy(toChatId, params)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { randomLong } from '@mtcute/core'
|
import { getMarkedPeerId, randomLong } from '@mtcute/core'
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
|
@ -8,6 +8,7 @@ import {
|
||||||
InputPeerLike,
|
InputPeerLike,
|
||||||
Message,
|
Message,
|
||||||
MtArgumentError,
|
MtArgumentError,
|
||||||
|
MtMessageNotFoundError,
|
||||||
PeersIndex,
|
PeersIndex,
|
||||||
ReplyMarkup,
|
ReplyMarkup,
|
||||||
} from '../../types'
|
} from '../../types'
|
||||||
|
@ -136,7 +137,13 @@ export async function sendMediaGroup(
|
||||||
|
|
||||||
const msg = await this.getMessages(peer, replyTo)
|
const msg = await this.getMessages(peer, replyTo)
|
||||||
|
|
||||||
if (!msg) throw new tl.errors.MessageNotFoundError()
|
if (!msg) {
|
||||||
|
throw new MtMessageNotFoundError(
|
||||||
|
getMarkedPeerId(peer),
|
||||||
|
replyTo,
|
||||||
|
'to reply to',
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const multiMedia: tl.RawInputSingleMedia[] = []
|
const multiMedia: tl.RawInputSingleMedia[] = []
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { randomLong } from '@mtcute/core'
|
import { getMarkedPeerId, randomLong } from '@mtcute/core'
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
|
@ -9,6 +9,7 @@ import {
|
||||||
InputPeerLike,
|
InputPeerLike,
|
||||||
Message,
|
Message,
|
||||||
MtArgumentError,
|
MtArgumentError,
|
||||||
|
MtMessageNotFoundError,
|
||||||
ReplyMarkup,
|
ReplyMarkup,
|
||||||
} from '../../types'
|
} from '../../types'
|
||||||
import { normalizeDate, normalizeMessageId } from '../../utils/misc-utils'
|
import { normalizeDate, normalizeMessageId } from '../../utils/misc-utils'
|
||||||
|
@ -173,7 +174,13 @@ export async function sendMedia(
|
||||||
|
|
||||||
const msg = await this.getMessages(peer, replyTo)
|
const msg = await this.getMessages(peer, replyTo)
|
||||||
|
|
||||||
if (!msg) throw new tl.errors.MessageNotFoundError()
|
if (!msg) {
|
||||||
|
throw new MtMessageNotFoundError(
|
||||||
|
getMarkedPeerId(peer),
|
||||||
|
replyTo,
|
||||||
|
'to reply to',
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await this.call({
|
const res = await this.call({
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
InputPeerLike,
|
InputPeerLike,
|
||||||
Message,
|
Message,
|
||||||
MtArgumentError,
|
MtArgumentError,
|
||||||
|
MtMessageNotFoundError,
|
||||||
MtTypeAssertionError,
|
MtTypeAssertionError,
|
||||||
PeersIndex,
|
PeersIndex,
|
||||||
ReplyMarkup,
|
ReplyMarkup,
|
||||||
|
@ -145,7 +146,13 @@ export async function sendText(
|
||||||
|
|
||||||
const msg = await this.getMessages(peer, replyTo)
|
const msg = await this.getMessages(peer, replyTo)
|
||||||
|
|
||||||
if (!msg) throw new tl.errors.MessageNotFoundError()
|
if (!msg) {
|
||||||
|
throw new MtMessageNotFoundError(
|
||||||
|
getMarkedPeerId(peer),
|
||||||
|
replyTo,
|
||||||
|
'to reply to',
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await this.call({
|
const res = await this.call({
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { MaybeArray } from '@mtcute/core'
|
import { getMarkedPeerId, MaybeArray } from '@mtcute/core'
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
import {
|
import {
|
||||||
InputPeerLike,
|
InputPeerLike,
|
||||||
MtArgumentError,
|
MtArgumentError,
|
||||||
|
MtMessageNotFoundError,
|
||||||
MtTypeAssertionError,
|
MtTypeAssertionError,
|
||||||
PeersIndex,
|
PeersIndex,
|
||||||
Poll,
|
Poll,
|
||||||
|
@ -40,9 +41,17 @@ export async function sendVote(
|
||||||
if (options.some((it) => typeof it === 'number')) {
|
if (options.some((it) => typeof it === 'number')) {
|
||||||
const msg = await this.getMessages(peer, message)
|
const msg = await this.getMessages(peer, message)
|
||||||
|
|
||||||
if (!msg) throw new tl.errors.MessageNotFoundError()
|
if (!msg) {
|
||||||
|
throw new MtMessageNotFoundError(
|
||||||
|
getMarkedPeerId(peer),
|
||||||
|
message,
|
||||||
|
'to vote in',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (!(msg.media instanceof Poll)) { throw new MtArgumentError('This message does not contain a poll') }
|
if (!(msg.media instanceof Poll)) {
|
||||||
|
throw new MtArgumentError('This message does not contain a poll')
|
||||||
|
}
|
||||||
|
|
||||||
poll = msg.media
|
poll = msg.media
|
||||||
options = options.map((opt) => {
|
options = options.map((opt) => {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { tl } from '@mtcute/tl'
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
import {
|
import {
|
||||||
InputPeerLike,
|
InputPeerLike,
|
||||||
MtNotFoundError,
|
MtPeerNotFoundError,
|
||||||
MtTypeAssertionError,
|
MtTypeAssertionError,
|
||||||
} from '../../types'
|
} from '../../types'
|
||||||
import { normalizeToInputPeer } from '../../utils/peer-utils'
|
import { normalizeToInputPeer } from '../../utils/peer-utils'
|
||||||
|
@ -72,7 +72,7 @@ export async function resolvePeer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new MtNotFoundError(
|
throw new MtPeerNotFoundError(
|
||||||
`Could not find a peer by phone ${peerId}`,
|
`Could not find a peer by phone ${peerId}`,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -97,7 +97,7 @@ export async function resolvePeer(
|
||||||
// no access hash, we can't use it
|
// no access hash, we can't use it
|
||||||
// this may happen when bot resolves a username
|
// this may happen when bot resolves a username
|
||||||
// of a user who hasn't started a conversation with it
|
// of a user who hasn't started a conversation with it
|
||||||
throw new MtNotFoundError(
|
throw new MtPeerNotFoundError(
|
||||||
`Peer (user) with username ${peerId} was found, but it has no access hash`,
|
`Peer (user) with username ${peerId} was found, but it has no access hash`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,7 @@ export async function resolvePeer(
|
||||||
|
|
||||||
if (!found.accessHash) {
|
if (!found.accessHash) {
|
||||||
// shouldn't happen? but just in case
|
// shouldn't happen? but just in case
|
||||||
throw new MtNotFoundError(
|
throw new MtPeerNotFoundError(
|
||||||
`Peer (channel) with username ${peerId} was found, but it has no access hash`,
|
`Peer (channel) with username ${peerId} was found, but it has no access hash`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ export async function resolvePeer(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new MtNotFoundError(
|
throw new MtPeerNotFoundError(
|
||||||
`Could not find a peer by username ${peerId}`,
|
`Could not find a peer by username ${peerId}`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ export async function resolvePeer(
|
||||||
if (found && found._ === 'user') {
|
if (found && found._ === 'user') {
|
||||||
if (!found.accessHash) {
|
if (!found.accessHash) {
|
||||||
// shouldn't happen? but just in case
|
// shouldn't happen? but just in case
|
||||||
throw new MtNotFoundError(
|
throw new MtPeerNotFoundError(
|
||||||
`Peer (user) with username ${peerId} was found, but it has no access hash`,
|
`Peer (user) with username ${peerId} was found, but it has no access hash`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -235,7 +235,7 @@ export async function resolvePeer(
|
||||||
) {
|
) {
|
||||||
if (!found.accessHash) {
|
if (!found.accessHash) {
|
||||||
// shouldn't happen? but just in case
|
// shouldn't happen? but just in case
|
||||||
throw new MtNotFoundError(
|
throw new MtPeerNotFoundError(
|
||||||
`Peer (channel) with username ${peerId} was found, but it has no access hash`,
|
`Peer (channel) with username ${peerId} was found, but it has no access hash`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -251,5 +251,5 @@ export async function resolvePeer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new MtNotFoundError(`Could not find a peer by ID ${peerId}`)
|
throw new MtPeerNotFoundError(`Could not find a peer by ID ${peerId}`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
import { encodeInlineMessageId } from '../../utils/inline-utils'
|
import { encodeInlineMessageId } from '../../utils/inline-utils'
|
||||||
import { MtArgumentError } from '../errors'
|
import { MtArgumentError, MtMessageNotFoundError } from '../errors'
|
||||||
import { Message } from '../messages'
|
import { Message } from '../messages'
|
||||||
import { PeersIndex, User } from '../peers'
|
import { PeersIndex, User } from '../peers'
|
||||||
import { makeInspectable } from '../utils'
|
import { makeInspectable } from '../utils'
|
||||||
|
@ -182,11 +182,15 @@ export class CallbackQuery {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const msg = await this.client.getMessages(
|
const msg = await this.client.getMessages(this.raw.peer, this.raw.msgId)
|
||||||
getMarkedPeerId(this.raw.peer),
|
|
||||||
this.raw.msgId,
|
if (!msg) {
|
||||||
)
|
throw new MtMessageNotFoundError(
|
||||||
if (!msg) throw new tl.errors.MessageNotFoundError()
|
getMarkedPeerId(this.raw.peer),
|
||||||
|
this.raw.msgId,
|
||||||
|
'with button',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { TelegramClient } from '../client'
|
import { TelegramClient } from '../client'
|
||||||
import { MtArgumentError } from './errors'
|
import { MtArgumentError, MtTimeoutError } from './errors'
|
||||||
import { InputMediaLike } from './media'
|
import { InputMediaLike } from './media'
|
||||||
import { Message } from './messages'
|
import { Message } from './messages'
|
||||||
import { FormattedString } from './parser'
|
import { FormattedString } from './parser'
|
||||||
|
@ -101,14 +101,12 @@ export class Conversation {
|
||||||
this._chatId = getMarkedPeerId(this._inputPeer)
|
this._chatId = getMarkedPeerId(this._inputPeer)
|
||||||
|
|
||||||
const dialog = await this.client.getPeerDialogs(this._inputPeer)
|
const dialog = await this.client.getPeerDialogs(this._inputPeer)
|
||||||
|
const lastMessage = dialog.lastMessage
|
||||||
|
|
||||||
try {
|
if (lastMessage) {
|
||||||
this._lastMessage = this._lastReceivedMessage =
|
this._lastMessage = this._lastReceivedMessage = lastMessage.id
|
||||||
dialog.lastMessage.id
|
} else {
|
||||||
} catch (e) {
|
this._lastMessage = this._lastReceivedMessage = 0
|
||||||
if (e instanceof tl.errors.MessageNotFoundError) {
|
|
||||||
this._lastMessage = this._lastReceivedMessage = 0
|
|
||||||
} else throw e
|
|
||||||
}
|
}
|
||||||
this.client.on('new_message', this._onNewMessage)
|
this.client.on('new_message', this._onNewMessage)
|
||||||
this.client.on('edit_message', this._onEditMessage)
|
this.client.on('edit_message', this._onEditMessage)
|
||||||
|
@ -279,7 +277,7 @@ export class Conversation {
|
||||||
if (timeout !== null) {
|
if (timeout !== null) {
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
console.log('timed out')
|
console.log('timed out')
|
||||||
promise.reject(new tl.errors.TimeoutError())
|
promise.reject(new MtTimeoutError(timeout))
|
||||||
this._queuedNewMessage.removeBy((it) => it.promise === promise)
|
this._queuedNewMessage.removeBy((it) => it.promise === promise)
|
||||||
}, timeout)
|
}, timeout)
|
||||||
}
|
}
|
||||||
|
@ -422,12 +420,13 @@ export class Conversation {
|
||||||
const promise = createControllablePromise<Message>()
|
const promise = createControllablePromise<Message>()
|
||||||
|
|
||||||
let timer: NodeJS.Timeout | undefined = undefined
|
let timer: NodeJS.Timeout | undefined = undefined
|
||||||
|
const timeout = params?.timeout
|
||||||
|
|
||||||
if (params?.timeout !== null) {
|
if (timeout) {
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
promise.reject(new tl.errors.TimeoutError())
|
promise.reject(new MtTimeoutError(timeout))
|
||||||
delete this._pendingEditMessage[msgId]
|
delete this._pendingEditMessage[msgId]
|
||||||
}, params?.timeout ?? 15000)
|
}, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
this._pendingEditMessage[msgId] = {
|
this._pendingEditMessage[msgId] = {
|
||||||
|
@ -477,7 +476,7 @@ export class Conversation {
|
||||||
|
|
||||||
if (timeout !== null) {
|
if (timeout !== null) {
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
promise.reject(new tl.errors.TimeoutError())
|
promise.reject(new MtTimeoutError(timeout))
|
||||||
delete this._pendingRead[msgId]
|
delete this._pendingRead[msgId]
|
||||||
}, timeout)
|
}, timeout)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,26 @@ export class MtClientError extends Error {}
|
||||||
export class MtArgumentError extends MtClientError {}
|
export class MtArgumentError extends MtClientError {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Could not find peer by provided information
|
* Could not find a peer by the provided information
|
||||||
*/
|
*/
|
||||||
export class MtNotFoundError extends MtClientError {}
|
export class MtPeerNotFoundError extends MtClientError {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Could not find a message by the provided information
|
||||||
|
*/
|
||||||
|
export class MtMessageNotFoundError extends MtClientError {
|
||||||
|
constructor(
|
||||||
|
readonly peerId: number,
|
||||||
|
readonly messageId: number,
|
||||||
|
readonly context?: string,
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
`Message${
|
||||||
|
context ? ' ' + context : ''
|
||||||
|
} ${messageId} not found in ${peerId}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Either you requested or the server returned something
|
* Either you requested or the server returned something
|
||||||
|
@ -80,3 +97,9 @@ export class MtEmptyError extends MtClientError {
|
||||||
super('Property is not available on an empty object')
|
super('Property is not available on an empty object')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MtTimeoutError extends MtClientError {
|
||||||
|
constructor(readonly timeout?: number) {
|
||||||
|
super(`Request timed out${timeout ? ` after ${timeout}ms` : ''}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { getMarkedPeerId } from '@mtcute/core'
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
|
import { MtMessageNotFoundError } from '../errors'
|
||||||
import { Chat, PeersIndex } from '../peers'
|
import { Chat, PeersIndex } from '../peers'
|
||||||
import { makeInspectable } from '../utils'
|
import { makeInspectable } from '../utils'
|
||||||
import { DraftMessage } from './draft-message'
|
import { DraftMessage } from './draft-message'
|
||||||
|
@ -94,7 +95,9 @@ export class Dialog {
|
||||||
// manual exclusion/inclusion and pins
|
// manual exclusion/inclusion and pins
|
||||||
if (include[chatId]) return true
|
if (include[chatId]) return true
|
||||||
|
|
||||||
if (exclude[chatId] || (excludePinned && pinned[chatId])) { return false }
|
if (exclude[chatId] || (excludePinned && pinned[chatId])) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// exclusions based on status
|
// exclusions based on status
|
||||||
if (folder.excludeRead && !dialog.isUnread) return false
|
if (folder.excludeRead && !dialog.isUnread) return false
|
||||||
|
@ -196,7 +199,7 @@ export class Dialog {
|
||||||
/**
|
/**
|
||||||
* The latest message sent in this chat
|
* The latest message sent in this chat
|
||||||
*/
|
*/
|
||||||
get lastMessage(): Message {
|
get lastMessage(): Message | null {
|
||||||
if (!this._lastMessage) {
|
if (!this._lastMessage) {
|
||||||
const cid = this.chat.id
|
const cid = this.chat.id
|
||||||
|
|
||||||
|
@ -207,7 +210,7 @@ export class Dialog {
|
||||||
this._peers,
|
this._peers,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
throw new tl.errors.MessageNotFoundError()
|
throw new MtMessageNotFoundError(cid, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,13 @@ import { defaultTransportFactory, TransportFactory } from './transports'
|
||||||
export type ConnectionKind = 'main' | 'upload' | 'download' | 'downloadSmall'
|
export type ConnectionKind = 'main' | 'upload' | 'download' | 'downloadSmall'
|
||||||
|
|
||||||
const CLIENT_ERRORS = {
|
const CLIENT_ERRORS = {
|
||||||
'303': 1,
|
[tl.RpcError.BAD_REQUEST]: 1,
|
||||||
'400': 1,
|
[tl.RpcError.UNAUTHORIZED]: 1,
|
||||||
'401': 1,
|
[tl.RpcError.FORBIDDEN]: 1,
|
||||||
'403': 1,
|
[tl.RpcError.NOT_FOUND]: 1,
|
||||||
'404': 1,
|
[tl.RpcError.FLOOD]: 1,
|
||||||
'406': 1,
|
[tl.RpcError.SEE_OTHER]: 1,
|
||||||
'420': 1,
|
[tl.RpcError.NOT_ACCEPTABLE]: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -714,7 +714,12 @@ export class NetworkManager {
|
||||||
await sleep(delta)
|
await sleep(delta)
|
||||||
delete this._floodWaitedRequests[message._]
|
delete this._floodWaitedRequests[message._]
|
||||||
} else {
|
} else {
|
||||||
throw new tl.errors.FloodWaitXError(delta / 1000)
|
const err = tl.RpcError.create(
|
||||||
|
tl.RpcError.FLOOD,
|
||||||
|
'FLOOD_WAIT_%d',
|
||||||
|
)
|
||||||
|
err.seconds = Math.ceil(delta / 1000)
|
||||||
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -747,14 +752,16 @@ export class NetworkManager {
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
lastError = e as Error
|
lastError = e as Error
|
||||||
|
|
||||||
if (e.code && !(e.code in CLIENT_ERRORS)) {
|
if (!tl.RpcError.is(e)) continue
|
||||||
|
|
||||||
|
if (!(e.code in CLIENT_ERRORS)) {
|
||||||
this._log.warn(
|
this._log.warn(
|
||||||
'Telegram is having internal issues: %d %s, retrying',
|
'Telegram is having internal issues: %d %s, retrying',
|
||||||
e.code,
|
e.code,
|
||||||
e.message,
|
e.message,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (e.message === 'WORKER_BUSY_TOO_LONG_RETRY') {
|
if (e.text === 'WORKER_BUSY_TOO_LONG_RETRY') {
|
||||||
// according to tdlib, "it is dangerous to resend query without timeout, so use 1"
|
// according to tdlib, "it is dangerous to resend query without timeout, so use 1"
|
||||||
await sleep(1000)
|
await sleep(1000)
|
||||||
}
|
}
|
||||||
|
@ -762,11 +769,11 @@ export class NetworkManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
e.constructor === tl.errors.FloodWaitXError ||
|
e.is('FLOOD_WAIT_%d') ||
|
||||||
e.constructor === tl.errors.SlowmodeWaitXError ||
|
e.is('SLOWMODE_WAIT_%d') ||
|
||||||
e.constructor === tl.errors.FloodTestPhoneWaitXError
|
e.is('FLOOD_TEST_PHONE_WAIT_%d')
|
||||||
) {
|
) {
|
||||||
if (e.constructor !== tl.errors.SlowmodeWaitXError) {
|
if (e.text !== 'SLOWMODE_WAIT_%d') {
|
||||||
// SLOW_MODE_WAIT is chat-specific, not request-specific
|
// SLOW_MODE_WAIT is chat-specific, not request-specific
|
||||||
this._floodWaitedRequests[message._] =
|
this._floodWaitedRequests[message._] =
|
||||||
Date.now() + e.seconds * 1000
|
Date.now() + e.seconds * 1000
|
||||||
|
@ -775,7 +782,7 @@ export class NetworkManager {
|
||||||
// In test servers, FLOOD_WAIT_0 has been observed, and sleeping for
|
// In test servers, FLOOD_WAIT_0 has been observed, and sleeping for
|
||||||
// such a short amount will cause retries very fast leading to issues
|
// such a short amount will cause retries very fast leading to issues
|
||||||
if (e.seconds === 0) {
|
if (e.seconds === 0) {
|
||||||
(e as tl.Mutable<typeof e>).seconds = 1
|
e.seconds = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.seconds <= floodSleepThreshold) {
|
if (e.seconds <= floodSleepThreshold) {
|
||||||
|
@ -787,21 +794,19 @@ export class NetworkManager {
|
||||||
|
|
||||||
if (manager === this._primaryDc) {
|
if (manager === this._primaryDc) {
|
||||||
if (
|
if (
|
||||||
e.constructor === tl.errors.PhoneMigrateXError ||
|
e.is('PHONE_MIGRATE_%d') ||
|
||||||
e.constructor === tl.errors.UserMigrateXError ||
|
e.is('NETWORK_MIGRATE_%d') ||
|
||||||
e.constructor === tl.errors.NetworkMigrateXError
|
e.is('USER_MIGRATE_%d')
|
||||||
) {
|
) {
|
||||||
this._log.info('Migrate error, new dc = %d', e.new_dc)
|
this._log.info('Migrate error, new dc = %d', e.newDc)
|
||||||
|
|
||||||
await this.changePrimaryDc(e.new_dc)
|
await this.changePrimaryDc(e.newDc)
|
||||||
manager = this._primaryDc!
|
manager = this._primaryDc!
|
||||||
multi = manager[kind]
|
multi = manager[kind]
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (e.is('AUTH_KEY_UNREGISTERED')) {
|
||||||
e.constructor === tl.errors.AuthKeyUnregisteredError
|
|
||||||
) {
|
|
||||||
// we can try re-exporting auth from the primary connection
|
// we can try re-exporting auth from the primary connection
|
||||||
this._log.warn(
|
this._log.warn(
|
||||||
'exported auth key error, trying re-exporting..',
|
'exported auth key error, trying re-exporting..',
|
||||||
|
|
|
@ -50,12 +50,8 @@ export interface SessionConnectionParams extends PersistentConnectionParams {
|
||||||
// destroy_auth_key#d1435160 = DestroyAuthKeyRes;
|
// destroy_auth_key#d1435160 = DestroyAuthKeyRes;
|
||||||
// const DESTROY_AUTH_KEY = Buffer.from('605134d1', 'hex')
|
// const DESTROY_AUTH_KEY = Buffer.from('605134d1', 'hex')
|
||||||
|
|
||||||
function makeNiceStack(
|
function makeNiceStack(error: tl.RpcError, stack: string, method?: string) {
|
||||||
error: tl.errors.RpcError,
|
error.stack = `RpcError (${error.code} ${error.text}): ${
|
||||||
stack: string,
|
|
||||||
method?: string,
|
|
||||||
) {
|
|
||||||
error.stack = `${error.constructor.name} (${error.code} ${error.text}): ${
|
|
||||||
error.message
|
error.message
|
||||||
}\n at ${method}\n${stack.split('\n').slice(2).join('\n')}`
|
}\n at ${method}\n${stack.split('\n').slice(2).join('\n')}`
|
||||||
}
|
}
|
||||||
|
@ -859,7 +855,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
|
|
||||||
if (rpc.cancelled) return
|
if (rpc.cancelled) return
|
||||||
|
|
||||||
const error = tl.errors.createRpcErrorFromTl(res)
|
const error = tl.RpcError.fromTl(res)
|
||||||
|
|
||||||
if (this.params.niceStacks !== false) {
|
if (this.params.niceStacks !== false) {
|
||||||
makeNiceStack(error, rpc.stack!, rpc.method)
|
makeNiceStack(error, rpc.stack!, rpc.method)
|
||||||
|
@ -1544,7 +1540,8 @@ export class SessionConnection extends PersistentConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onTimeout) {
|
if (onTimeout) {
|
||||||
const error = new tl.errors.RpcTimeoutError()
|
// todo: replace with MtTimeoutError
|
||||||
|
const error = new tl.RpcError(-503, 'Timeout')
|
||||||
|
|
||||||
if (this.params.niceStacks !== false) {
|
if (this.params.niceStacks !== false) {
|
||||||
makeNiceStack(error, rpc.stack!, rpc.method)
|
makeNiceStack(error, rpc.stack!, rpc.method)
|
||||||
|
|
|
@ -1,112 +1,94 @@
|
||||||
import { TlError, TlErrors } from '../types'
|
import { TlErrors } from '../types'
|
||||||
import { camelToPascal, jsComment, snakeToCamel } from './utils'
|
import { snakeToCamel } from './utils'
|
||||||
|
|
||||||
/**
|
const TEMPLATE_JS = `
|
||||||
* Transform TL error name to JS error name
|
const _descriptionsMap = {
|
||||||
*
|
{descriptionsMap}
|
||||||
* @param code TL error code
|
|
||||||
* @example 'MSG_ID_INVALID' -> 'MsgIdInvalidError'
|
|
||||||
*/
|
|
||||||
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 {
|
class RpcError extends Error {
|
||||||
constructor(code, text, description) {
|
constructor(code, name) {
|
||||||
super(description);
|
super(_descriptionsMap[name] || 'Unknown RPC error: [' + code + ':' + name + ']');
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.text = text;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static is(err, name) { return err.constructor === RpcError && (!name || err.name === name); }
|
||||||
|
is(name) { return this.name === name; }
|
||||||
}
|
}
|
||||||
|
RpcError.fromTl = function (obj) {
|
||||||
|
const err = new RpcError(obj.errorCode, obj.errorMessage);
|
||||||
|
|
||||||
|
if (err in _descriptionsMap) return err;
|
||||||
|
|
||||||
|
let match;
|
||||||
|
{matchers}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
{statics}
|
||||||
{exports}RpcError = RpcError;
|
{exports}RpcError = RpcError;
|
||||||
`.trimStart()
|
`.trimStart()
|
||||||
|
|
||||||
const RPC_ERROR_CLASS_TS = `
|
const TEMPLATE_TS = `
|
||||||
|
type MtErrorText =
|
||||||
|
{texts}
|
||||||
|
| (string & {}) // to keep hints
|
||||||
|
|
||||||
|
interface MtErrorArgMap {
|
||||||
|
{argMap}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RpcErrorWithArgs<T extends string> =
|
||||||
|
RpcError & { text: T } & (T extends keyof MtErrorArgMap ? (RpcError & MtErrorArgMap[T]) : {});
|
||||||
|
|
||||||
export class RpcError extends Error {
|
export class RpcError extends Error {
|
||||||
|
{statics}
|
||||||
|
|
||||||
readonly code: number;
|
readonly code: number;
|
||||||
readonly text: string;
|
readonly text: MtErrorText;
|
||||||
constructor(code: number, text: string, description?: string);
|
constructor(code: number, text: MtErrorText);
|
||||||
|
|
||||||
|
is<const T extends MtErrorText>(text: T): this is RpcErrorWithArgs<T>;
|
||||||
|
static is<const T extends MtErrorText>(err: unknown): err is RpcError;
|
||||||
|
static is<const T extends MtErrorText>(err: unknown, text: T): err is RpcErrorWithArgs<T>;
|
||||||
|
static create<const T extends MtErrorText>(code: number, text: T): RpcErrorWithArgs<T>;
|
||||||
|
static fromTl(obj: object): RpcError;
|
||||||
}
|
}
|
||||||
`.trimStart()
|
`.trimStart()
|
||||||
|
|
||||||
const BASE_ERROR_JS = `
|
const template = (
|
||||||
class {className} extends RpcError {
|
str: string,
|
||||||
constructor(name, description) {
|
params: Record<string, string | number>,
|
||||||
super({code}, name, description);
|
): string => {
|
||||||
}
|
|
||||||
}
|
|
||||||
{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, string | number>): string => {
|
|
||||||
return str.replace(/{([a-z]+)}/gi, (_, name) => String(params[name] ?? ''))
|
return str.replace(/{([a-z]+)}/gi, (_, name) => String(params[name] ?? ''))
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCode(
|
function parseCode(err: string, placeholders_?: string[]): [string, string[]] {
|
||||||
err: string,
|
|
||||||
placeholders_?: string[],
|
|
||||||
): [string, string[], boolean] {
|
|
||||||
let addPlaceholders = false
|
let addPlaceholders = false
|
||||||
|
|
||||||
if (!placeholders_) {
|
if (!placeholders_) {
|
||||||
placeholders_ = []
|
placeholders_ = []
|
||||||
addPlaceholders = true
|
addPlaceholders = true
|
||||||
|
} else {
|
||||||
|
placeholders_ = placeholders_.map(snakeToCamel)
|
||||||
}
|
}
|
||||||
|
|
||||||
const placeholders = placeholders_
|
const placeholders = placeholders_
|
||||||
|
|
||||||
let wildcard = false
|
err = err.replace(/%[a-z]/g, (placeholder) => {
|
||||||
|
if (placeholder !== '%d') {
|
||||||
|
throw new Error(`Unsupported placeholder: ${placeholder}`)
|
||||||
|
}
|
||||||
|
|
||||||
err = err
|
if (addPlaceholders) {
|
||||||
.replace(/%[a-z]/g, (ph) => {
|
const idx = placeholders.length
|
||||||
if (ph !== '%d') {
|
placeholders.push(`duration${idx === 0 ? '' : idx}`)
|
||||||
throw new Error(`Unsupported placeholder: ${ph}`)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (addPlaceholders) {
|
return placeholder
|
||||||
const idx = placeholders.length
|
})
|
||||||
placeholders.push(`duration${idx === 0 ? '' : idx}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'X'
|
return [err, placeholders]
|
||||||
})
|
|
||||||
.replace(/_\*$/, () => {
|
|
||||||
wildcard = true
|
|
||||||
|
|
||||||
return ''
|
|
||||||
})
|
|
||||||
|
|
||||||
return [err, placeholders, wildcard]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function placeholderType(_name: string): string {
|
function placeholderType(_name: string): string {
|
||||||
|
@ -127,164 +109,55 @@ export function generateCodeForErrors(
|
||||||
errors: TlErrors,
|
errors: TlErrors,
|
||||||
exports = 'exports.',
|
exports = 'exports.',
|
||||||
): [string, string] {
|
): [string, string] {
|
||||||
let ts = RPC_ERROR_CLASS_TS
|
let descriptionsMap = ''
|
||||||
let js = template(RPC_ERROR_CLASS_JS, { exports })
|
let texts = ''
|
||||||
|
let argMap = ''
|
||||||
|
let matchers = ''
|
||||||
|
let staticsJs = ''
|
||||||
|
let staticsTs = ''
|
||||||
|
|
||||||
const baseErrorsClasses: Record<number, string> = {}
|
for (const [name, code] of Object.entries(errors.base)) {
|
||||||
|
staticsJs += `RpcError.${name} = ${code};\n`
|
||||||
for (const it of errors.base) {
|
staticsTs += ` static ${name} = ${code};\n`
|
||||||
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> = {}
|
for (const error of Object.values(errors.errors)) {
|
||||||
const wildcardClasses: [string, string][] = []
|
const [name, placeholders] = parseCode(error.name, error._paramNames)
|
||||||
const withPlaceholders: [string, string][] = []
|
|
||||||
|
|
||||||
function findBaseClass(it: TlError) {
|
if (error.description) {
|
||||||
for (const [prefix, cls] of wildcardClasses) {
|
descriptionsMap += ` '${name}': ${JSON.stringify(
|
||||||
if (it.name.startsWith(prefix)) return cls
|
error.description,
|
||||||
|
)},\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseErrorsClasses[it.code] ?? 'RpcError'
|
texts += ` | '${name}'\n`
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
if (placeholders.length) {
|
||||||
withPlaceholders.push([it.name, className])
|
const placeholderTypes = placeholders.map(placeholderType)
|
||||||
}
|
argMap +=
|
||||||
|
` '${name}': { ` +
|
||||||
|
placeholders
|
||||||
|
.map((it, i) => `${it}: ${placeholderTypes[i]}`)
|
||||||
|
.join(', ') +
|
||||||
|
' },\n'
|
||||||
|
|
||||||
js +=
|
const regex = name.replace('%d', '(\\d+)')
|
||||||
template(ERROR_PRELUDE, {
|
const setters = placeholders.map(
|
||||||
className,
|
(it, i) => `err.${it} = parseInt(match[${i + 1}])`,
|
||||||
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++]} + "`,
|
|
||||||
)
|
)
|
||||||
|
const settersStr =
|
||||||
if (wildcard) {
|
setters.length > 1 ? `{ ${setters.join('; ')} }` : setters[0]
|
||||||
description = description.replace(/"$/, ': " + description')
|
matchers += ` if ((match=obj.errorMessage.match(/^${regex}$/))!=null)${settersStr}\n`
|
||||||
}
|
|
||||||
|
|
||||||
idx = 0
|
|
||||||
comment += it.description.replace(
|
|
||||||
/%[a-z]/g,
|
|
||||||
() => `{@link ${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'
|
return [
|
||||||
|
template(TEMPLATE_TS, { statics: staticsTs, texts, argMap }),
|
||||||
// and now we need to implement it
|
template(TEMPLATE_JS, {
|
||||||
js += 'const _byName = {\n'
|
exports,
|
||||||
|
statics: staticsJs,
|
||||||
for (const [name, cls] of Object.entries(errorClasses)) {
|
descriptionsMap,
|
||||||
js += `'${name.replace(/%[a-z]/gi, 'X')}': ${cls},\n`
|
matchers,
|
||||||
}
|
}),
|
||||||
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,6 @@
|
||||||
import { TlEntry, TlErrors, TlFullSchema, TlTypeModifiers } from '../types'
|
import { TlEntry, TlErrors, TlFullSchema, TlTypeModifiers } from '../types'
|
||||||
import { groupTlEntriesByNamespace, splitNameToNamespace } from '../utils'
|
import { groupTlEntriesByNamespace, splitNameToNamespace } from '../utils'
|
||||||
import { errorCodeToClassName, generateCodeForErrors } from './errors'
|
import { generateCodeForErrors } from './errors'
|
||||||
import { camelToPascal, indent, jsComment, snakeToCamel } from './utils'
|
import { camelToPascal, indent, jsComment, snakeToCamel } from './utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,9 +117,7 @@ export function generateTypescriptDefinitionsForTlEntry(
|
||||||
if (errors.throws[entry.name]) {
|
if (errors.throws[entry.name]) {
|
||||||
comment +=
|
comment +=
|
||||||
'\n\nThis method *may* throw one of these errors: ' +
|
'\n\nThis method *may* throw one of these errors: ' +
|
||||||
errors.throws[entry.name]
|
errors.throws[entry.name].join(', ')
|
||||||
.map((it) => `{$see ${errorCodeToClassName(it)}`)
|
|
||||||
.join(', ')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,15 +248,16 @@ export function generateTypescriptDefinitionsForTlSchema(
|
||||||
)
|
)
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
ts += '\n namespace errors {\n'
|
// ts += '\n namespace errors {\n'
|
||||||
js += 'ns.errors = {};\n(function(ns){\n'
|
// js += 'ns.errors = {};\n(function(ns){\n'
|
||||||
|
|
||||||
const [_ts, _js] = generateCodeForErrors(errors, 'ns.')
|
const [_ts, _js] = generateCodeForErrors(errors, 'ns.')
|
||||||
ts += indent(8, _ts)
|
// ts += indent(8, _ts)
|
||||||
|
ts += _ts
|
||||||
js += _js
|
js += _js
|
||||||
|
|
||||||
ts += '}\n'
|
// ts += '}\n'
|
||||||
js += '})(ns.errors);\n'
|
// js += '})(ns.errors);\n'
|
||||||
}
|
}
|
||||||
|
|
||||||
const namespaces = groupTlEntriesByNamespace(schema.entries)
|
const namespaces = groupTlEntriesByNamespace(schema.entries)
|
||||||
|
|
|
@ -237,11 +237,6 @@ export interface TlError {
|
||||||
*/
|
*/
|
||||||
description?: string
|
description?: string
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether this is a "virtual" error (only thrown by mtcute itself)
|
|
||||||
*/
|
|
||||||
virtual?: true
|
|
||||||
|
|
||||||
// internal fields used by generator
|
// internal fields used by generator
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
|
@ -255,9 +250,9 @@ export interface TlError {
|
||||||
*/
|
*/
|
||||||
export interface TlErrors {
|
export interface TlErrors {
|
||||||
/**
|
/**
|
||||||
* Base errors
|
* Base errors (map of error names to error code, e.g. `BAD_REQUEST: 400`)
|
||||||
*/
|
*/
|
||||||
base: TlError[]
|
base: Record<string, number>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Index of errors by name
|
* Index of errors by name
|
||||||
|
@ -275,6 +270,11 @@ export interface TlErrors {
|
||||||
* Index of the methods only usable by user
|
* Index of the methods only usable by user
|
||||||
*/
|
*/
|
||||||
userOnly: Record<string, 1>
|
userOnly: Record<string, 1>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index of the methods only usable by bots
|
||||||
|
*/
|
||||||
|
botOnly: Record<string, 1>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
"@mtcute/tl-utils": "workspace:^1.0.0",
|
"@mtcute/tl-utils": "workspace:^1.0.0",
|
||||||
"@types/js-yaml": "^4.0.5",
|
"@types/js-yaml": "^4.0.5",
|
||||||
"cheerio": "1.0.0-rc.12",
|
"cheerio": "1.0.0-rc.12",
|
||||||
"csv-parser": "3.0.0",
|
"csv-parse": "^5.5.0",
|
||||||
"js-yaml": "4.1.0"
|
"js-yaml": "4.1.0"
|
||||||
},
|
},
|
||||||
"typedoc": {
|
"typedoc": {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,87 +1,23 @@
|
||||||
import csvParser from 'csv-parser'
|
import { parse } from 'csv-parse/sync'
|
||||||
import { writeFile } from 'fs/promises'
|
import { writeFile } from 'fs/promises'
|
||||||
|
|
||||||
import { TlError, TlErrors } from '@mtcute/tl-utils'
|
import { TlErrors } from '@mtcute/tl-utils'
|
||||||
|
|
||||||
import { ERRORS_JSON_FILE } from './constants'
|
import { ERRORS_JSON_FILE } from './constants'
|
||||||
|
|
||||||
const ERRORS_PAGE_TG = 'https://corefork.telegram.org/api/errors'
|
const ERRORS_PAGE_TG = 'https://corefork.telegram.org/api/errors'
|
||||||
const ERRORS_PAGE_TELETHON =
|
const ERRORS_PAGE_TELETHON =
|
||||||
'https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_generator/data/errors.csv'
|
'https://raw.githubusercontent.com/LonamiWebs/Telethon/v1/telethon_generator/data/errors.csv'
|
||||||
|
const baseErrors = {
|
||||||
const baseErrors: TlError[] = [
|
BAD_REQUEST: 400,
|
||||||
{
|
UNAUTHORIZED: 401,
|
||||||
code: 400,
|
FORBIDDEN: 403,
|
||||||
name: 'BAD_REQUEST',
|
NOT_FOUND: 404,
|
||||||
description:
|
FLOOD: 420,
|
||||||
'The query contains errors. In the event that a request was created using a form ' +
|
SEE_OTHER: 303,
|
||||||
'and contains user generated data, the user should be notified that the data must ' +
|
NOT_ACCEPTABLE: 406,
|
||||||
'be corrected before the query is repeated',
|
INTERNAL: 500,
|
||||||
},
|
} as const
|
||||||
{
|
|
||||||
code: 401,
|
|
||||||
name: 'UNAUTHORIZED',
|
|
||||||
description:
|
|
||||||
'There was an unauthorized attempt to use functionality available only to authorized users.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 403,
|
|
||||||
name: 'FORBIDDEN',
|
|
||||||
description:
|
|
||||||
'Privacy violation. For example, an attempt to write a message ' +
|
|
||||||
'to someone who has blacklisted the current user.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 404,
|
|
||||||
name: 'NOT_FOUND',
|
|
||||||
description:
|
|
||||||
'An attempt to invoke a non-existent object, such as a method.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 420,
|
|
||||||
name: 'FLOOD',
|
|
||||||
description:
|
|
||||||
'The maximum allowed number of attempts to invoke the given method' +
|
|
||||||
'with the given input parameters has been exceeded. For example, in an' +
|
|
||||||
'attempt to request a large number of text messages (SMS) for the same' +
|
|
||||||
'phone number.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 303,
|
|
||||||
name: 'SEE_OTHER',
|
|
||||||
description:
|
|
||||||
'The request must be repeated, but directed to a different data center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 406,
|
|
||||||
name: 'NOT_ACCEPTABLE',
|
|
||||||
description:
|
|
||||||
'Similar to 400 BAD_REQUEST, but the app should not display any error messages to user ' +
|
|
||||||
'in UI as a result of this response. The error message will be delivered via ' +
|
|
||||||
'updateServiceNotification instead.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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.',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const virtualErrors: TlError[] = [
|
|
||||||
{
|
|
||||||
name: 'RPC_TIMEOUT',
|
|
||||||
code: 408,
|
|
||||||
description: 'The set RPC timeout has exceeded',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'MESSAGE_NOT_FOUND',
|
|
||||||
code: 404,
|
|
||||||
description: 'Message was not found',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
virtualErrors.forEach((it) => (it.virtual = true))
|
|
||||||
|
|
||||||
interface TelegramErrorsSpec {
|
interface TelegramErrorsSpec {
|
||||||
errors: Record<string, Record<string, string[]>>
|
errors: Record<string, Record<string, string[]>>
|
||||||
|
@ -147,6 +83,47 @@ async function fetchFromTelegram(errors: TlErrors) {
|
||||||
}
|
}
|
||||||
|
|
||||||
json.user_only.forEach((it: string) => (errors.userOnly[it] = 1))
|
json.user_only.forEach((it: string) => (errors.userOnly[it] = 1))
|
||||||
|
json.bot_only.forEach((it: string) => (errors.botOnly[it] = 1))
|
||||||
|
|
||||||
|
// process _* wildcard errors
|
||||||
|
// 1. add description to errors that are missing it
|
||||||
|
// 2. replace all wildcards in errors.throws with all matching errors
|
||||||
|
// 3. remove all _* such errors from errors.errors
|
||||||
|
|
||||||
|
for (const name of Object.keys(errors.errors)) {
|
||||||
|
if (!name.endsWith('_*')) continue
|
||||||
|
|
||||||
|
const base = name.slice(0, -2)
|
||||||
|
|
||||||
|
const matchingErrors: string[] = []
|
||||||
|
|
||||||
|
for (const inner of Object.keys(errors.errors)) {
|
||||||
|
if (!inner.startsWith(base) || inner === name) continue
|
||||||
|
|
||||||
|
matchingErrors.push(inner)
|
||||||
|
|
||||||
|
if (!errors.errors[inner].description) {
|
||||||
|
errors.errors[inner].description =
|
||||||
|
errors.errors[name].description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchingErrors.length === 0) continue
|
||||||
|
|
||||||
|
for (const method of Object.keys(errors.throws)) {
|
||||||
|
const idx = errors.throws[method].indexOf(name)
|
||||||
|
if (idx === -1) continue
|
||||||
|
|
||||||
|
errors.throws[method].splice(idx, 1, ...matchingErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete errors.errors[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up: remove duplicates in throws
|
||||||
|
for (const method of Object.keys(errors.throws)) {
|
||||||
|
errors.throws[method] = [...new Set(errors.throws[method])]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchFromTelethon(errors: TlErrors) {
|
async function fetchFromTelethon(errors: TlErrors) {
|
||||||
|
@ -156,22 +133,29 @@ async function fetchFromTelethon(errors: TlErrors) {
|
||||||
throw new Error('No body in response')
|
throw new Error('No body in response')
|
||||||
}
|
}
|
||||||
|
|
||||||
const parser = csvParser()
|
const records = parse(await csv.text(), {
|
||||||
|
columns: true,
|
||||||
|
skip_empty_lines: true,
|
||||||
|
}) as {
|
||||||
|
name: string
|
||||||
|
codes: string
|
||||||
|
description: string
|
||||||
|
}[]
|
||||||
|
|
||||||
function addError(name: string, codes: string, description: string): void {
|
for (const { name: name_, codes, description } of records) {
|
||||||
if (!codes) return
|
if (!codes) continue
|
||||||
if (name === 'TIMEOUT') return
|
if (name_ === 'TIMEOUT') continue
|
||||||
|
|
||||||
|
let name = name_
|
||||||
const code = parseInt(codes)
|
const code = parseInt(codes)
|
||||||
|
|
||||||
if (isNaN(code)) {
|
if (isNaN(code)) {
|
||||||
throw new Error(`Invalid code: ${codes} (name: ${name})`)
|
throw new Error(`Invalid code: ${codes} (name: ${name})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// telethon uses numbers for parameters instead of printf-like
|
// telethon uses X for parameters instead of printf-like
|
||||||
// we'll convert it back to printf-like
|
// we'll convert it back to printf-like
|
||||||
// so far, only one param is supported
|
name = name.replace(/_X(_)?/g, '_%d$1')
|
||||||
name = name.replace(/_0(_)?/g, '_%d$1')
|
|
||||||
|
|
||||||
if (!(name in errors.errors)) {
|
if (!(name in errors.errors)) {
|
||||||
errors.errors[name] = {
|
errors.errors[name] = {
|
||||||
|
@ -209,28 +193,6 @@ async function fetchFromTelethon(errors: TlErrors) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
|
||||||
parser
|
|
||||||
.on(
|
|
||||||
'data',
|
|
||||||
({
|
|
||||||
name,
|
|
||||||
codes,
|
|
||||||
description,
|
|
||||||
}: {
|
|
||||||
name: string
|
|
||||||
codes: string
|
|
||||||
description: string
|
|
||||||
}) => addError(name, codes, description),
|
|
||||||
)
|
|
||||||
.on('end', resolve)
|
|
||||||
.on('error', reject)
|
|
||||||
|
|
||||||
csv.text()
|
|
||||||
.then((it) => parser.write(it))
|
|
||||||
.catch(reject)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
@ -239,6 +201,7 @@ async function main() {
|
||||||
errors: {},
|
errors: {},
|
||||||
throws: {},
|
throws: {},
|
||||||
userOnly: {},
|
userOnly: {},
|
||||||
|
botOnly: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Fetching errors from Telegram...')
|
console.log('Fetching errors from Telegram...')
|
||||||
|
@ -249,16 +212,6 @@ async function main() {
|
||||||
console.log('Fetching errors from Telethon...')
|
console.log('Fetching errors from Telethon...')
|
||||||
await fetchFromTelethon(errors)
|
await fetchFromTelethon(errors)
|
||||||
|
|
||||||
virtualErrors.forEach((err) => {
|
|
||||||
if (err.name in errors.errors) {
|
|
||||||
console.log(`Error ${err.name} already exists and is not virtual`)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
errors.errors[err.name] = err
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log('Saving...')
|
console.log('Saving...')
|
||||||
|
|
||||||
await writeFile(ERRORS_JSON_FILE, JSON.stringify(errors))
|
await writeFile(ERRORS_JSON_FILE, JSON.stringify(errors))
|
||||||
|
|
|
@ -41,7 +41,6 @@ async function generateTypings(
|
||||||
mtpSchema,
|
mtpSchema,
|
||||||
0,
|
0,
|
||||||
'mtp',
|
'mtp',
|
||||||
errors,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await writeFile(
|
await writeFile(
|
||||||
|
|
|
@ -322,9 +322,9 @@ importers:
|
||||||
cheerio:
|
cheerio:
|
||||||
specifier: 1.0.0-rc.12
|
specifier: 1.0.0-rc.12
|
||||||
version: 1.0.0-rc.12
|
version: 1.0.0-rc.12
|
||||||
csv-parser:
|
csv-parse:
|
||||||
specifier: 3.0.0
|
specifier: ^5.5.0
|
||||||
version: 3.0.0
|
version: 5.5.0
|
||||||
js-yaml:
|
js-yaml:
|
||||||
specifier: 4.1.0
|
specifier: 4.1.0
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
|
@ -1834,12 +1834,8 @@ packages:
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/csv-parser@3.0.0:
|
/csv-parse@5.5.0:
|
||||||
resolution: {integrity: sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==}
|
resolution: {integrity: sha512-RxruSK3M4XgzcD7Trm2wEN+SJ26ChIb903+IWxNOcB5q4jT2Cs+hFr6QP39J05EohshRFEvyzEBoZ/466S2sbw==}
|
||||||
engines: {node: '>= 10'}
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
minimist: 1.2.6
|
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/dargs@7.0.0:
|
/dargs@7.0.0:
|
||||||
|
|
Loading…
Reference in a new issue