chore: improved eslint config
closes MTQ-54
This commit is contained in:
parent
e7171e32c7
commit
81ce550604
97 changed files with 778 additions and 541 deletions
14
.eslintrc.js
14
.eslintrc.js
|
@ -176,7 +176,7 @@ module.exports = {
|
||||||
files: ['**/*.ts', '**/*.tsx'],
|
files: ['**/*.ts', '**/*.tsx'],
|
||||||
env: { browser: true, es6: true, node: true },
|
env: { browser: true, es6: true, node: true },
|
||||||
extends: [
|
extends: [
|
||||||
'plugin:@typescript-eslint/recommended',
|
'plugin:@typescript-eslint/strict-type-checked',
|
||||||
'plugin:import/typescript',
|
'plugin:import/typescript',
|
||||||
],
|
],
|
||||||
globals: { Atomics: 'readonly', SharedArrayBuffer: 'readonly' },
|
globals: { Atomics: 'readonly', SharedArrayBuffer: 'readonly' },
|
||||||
|
@ -213,6 +213,18 @@ module.exports = {
|
||||||
],
|
],
|
||||||
'@typescript-eslint/no-non-null-assertion': 'off', // todo MTQ-36
|
'@typescript-eslint/no-non-null-assertion': 'off', // todo MTQ-36
|
||||||
'@typescript-eslint/no-empty-function': 'off',
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
'@typescript-eslint/no-confusing-void-expression': 'off',
|
||||||
|
'@typescript-eslint/no-unnecessary-condition': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
'@typescript-eslint/restrict-template-expressions': [
|
||||||
|
'error',
|
||||||
|
{ allowNever: true },
|
||||||
|
],
|
||||||
|
'@typescript-eslint/no-unsafe-enum-comparison': 'off',
|
||||||
|
'@typescript-eslint/no-invalid-void-type': 'off',
|
||||||
|
'@typescript-eslint/unbound-method': 'off',
|
||||||
|
'@typescript-eslint/no-dynamic-delete': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
'import/resolver': {
|
'import/resolver': {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */
|
/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging, @typescript-eslint/unified-signatures */
|
||||||
/* THIS FILE WAS AUTO-GENERATED */
|
/* THIS FILE WAS AUTO-GENERATED */
|
||||||
import { Readable } from 'stream'
|
import { Readable } from 'stream'
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,8 @@ export async function logOut(this: TelegramClient): Promise<true> {
|
||||||
|
|
||||||
this._userId = null
|
this._userId = null
|
||||||
this._isBot = false
|
this._isBot = false
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// some implicit magic in favor of performance
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
|
||||||
this._pts = this._seq = this._date = undefined as any
|
this._pts = this._seq = this._date = undefined as any
|
||||||
this._selfUsername = null
|
this._selfUsername = null
|
||||||
this._selfChanged = true
|
this._selfChanged = true
|
||||||
|
|
|
@ -97,7 +97,11 @@ export async function answerInlineQuery(
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!params) params = {}
|
if (!params) params = {}
|
||||||
|
|
||||||
const [gallery, tlResults] = await BotInline._convertToTl(this, results, params!.parseMode)
|
const [gallery, tlResults] = await BotInline._convertToTl(
|
||||||
|
this,
|
||||||
|
results,
|
||||||
|
params.parseMode,
|
||||||
|
)
|
||||||
|
|
||||||
await this.call({
|
await this.call({
|
||||||
_: 'messages.setInlineBotResults',
|
_: 'messages.setInlineBotResults',
|
||||||
|
|
|
@ -53,7 +53,7 @@ export async function getInlineGameHighScores(
|
||||||
messageId: string | tl.TypeInputBotInlineMessageID,
|
messageId: string | tl.TypeInputBotInlineMessageID,
|
||||||
userId?: InputPeerLike,
|
userId?: InputPeerLike,
|
||||||
): Promise<GameHighScore[]> {
|
): Promise<GameHighScore[]> {
|
||||||
const id = await normalizeInlineId(messageId)
|
const id = normalizeInlineId(messageId)
|
||||||
|
|
||||||
let user: tl.TypeInputUser
|
let user: tl.TypeInputUser
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,7 @@ export async function setInlineGameScore(
|
||||||
|
|
||||||
const user = normalizeToInputUser(await this.resolvePeer(userId), userId)
|
const user = normalizeToInputUser(await this.resolvePeer(userId), userId)
|
||||||
|
|
||||||
const id = await normalizeInlineId(messageId)
|
const id = normalizeInlineId(messageId)
|
||||||
|
|
||||||
await this.call(
|
await this.call(
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,10 +45,7 @@ export async function addChatMembers(
|
||||||
const updates = await this.call({
|
const updates = await this.call({
|
||||||
_: 'channels.inviteToChannel',
|
_: 'channels.inviteToChannel',
|
||||||
channel: normalizeToInputChannel(chat),
|
channel: normalizeToInputChannel(chat),
|
||||||
users: await this.resolvePeerMany(
|
users: await this.resolvePeerMany(users, normalizeToInputUser),
|
||||||
users as InputPeerLike[],
|
|
||||||
normalizeToInputUser,
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
this._handleUpdate(updates)
|
this._handleUpdate(updates)
|
||||||
} else throw new MtInvalidPeerTypeError(chatId, 'chat or channel')
|
} else throw new MtInvalidPeerTypeError(chatId, 'chat or channel')
|
||||||
|
|
|
@ -24,10 +24,7 @@ export async function createGroup(
|
||||||
): Promise<Chat> {
|
): Promise<Chat> {
|
||||||
if (!Array.isArray(users)) users = [users]
|
if (!Array.isArray(users)) users = [users]
|
||||||
|
|
||||||
const peers = await this.resolvePeerMany(
|
const peers = await this.resolvePeerMany(users, normalizeToInputUser)
|
||||||
users as InputPeerLike[],
|
|
||||||
normalizeToInputUser,
|
|
||||||
)
|
|
||||||
|
|
||||||
const res = await this.call({
|
const res = await this.call({
|
||||||
_: 'messages.createChat',
|
_: 'messages.createChat',
|
||||||
|
|
|
@ -28,7 +28,7 @@ export function _pushConversationMessage(
|
||||||
const chatId = getMarkedPeerId(msg.raw.peerId)
|
const chatId = getMarkedPeerId(msg.raw.peerId)
|
||||||
const msgId = msg.raw.id
|
const msgId = msg.raw.id
|
||||||
|
|
||||||
this._pendingConversations[chatId]?.forEach((conv) => {
|
this._pendingConversations[chatId].forEach((conv) => {
|
||||||
conv['_lastMessage'] = msgId
|
conv['_lastMessage'] = msgId
|
||||||
if (incoming) conv['_lastReceivedMessage'] = msgId
|
if (incoming) conv['_lastReceivedMessage'] = msgId
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,11 +5,10 @@ import {
|
||||||
MtUnsupportedError,
|
MtUnsupportedError,
|
||||||
} from '../../types'
|
} from '../../types'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
let fs: typeof import('fs') | null = null
|
||||||
let fs: any = null
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fs = require('fs')
|
fs = require('fs') as typeof import('fs')
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,7 +38,7 @@ export function downloadToFile(
|
||||||
const buf = params.location.location
|
const buf = params.location.location
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.writeFile(filename, buf, (err?: Error) => {
|
fs!.writeFile(filename, buf, (err) => {
|
||||||
if (err) reject(err)
|
if (err) reject(err)
|
||||||
else resolve()
|
else resolve()
|
||||||
})
|
})
|
||||||
|
|
|
@ -47,19 +47,20 @@ export async function* downloadAsIterable(
|
||||||
const input = params.location
|
const input = params.location
|
||||||
let location: tl.TypeInputFileLocation | tl.TypeInputWebFileLocation
|
let location: tl.TypeInputFileLocation | tl.TypeInputWebFileLocation
|
||||||
if (input instanceof FileLocation) {
|
if (input instanceof FileLocation) {
|
||||||
if (typeof input.location === 'function') {
|
let locationInner = input.location
|
||||||
(input as tl.Mutable<FileLocation>).location = input.location()
|
|
||||||
|
if (typeof locationInner === 'function') {
|
||||||
|
locationInner = locationInner()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Buffer.isBuffer(input.location)) {
|
if (Buffer.isBuffer(locationInner)) {
|
||||||
yield input.location
|
yield locationInner
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!dcId) dcId = input.dcId
|
if (!dcId) dcId = input.dcId
|
||||||
if (!fileSize) fileSize = input.fileSize
|
if (!fileSize) fileSize = input.fileSize
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
location = locationInner
|
||||||
location = input.location as any
|
|
||||||
} else if (typeof input === 'string') {
|
} else if (typeof input === 'string') {
|
||||||
const parsed = parseFileId(input)
|
const parsed = parseFileId(input)
|
||||||
|
|
||||||
|
@ -129,7 +130,7 @@ export async function* downloadAsIterable(
|
||||||
result = await this.call(
|
result = await this.call(
|
||||||
{
|
{
|
||||||
_: isWeb ? 'upload.getWebFile' : 'upload.getFile',
|
_: isWeb ? 'upload.getWebFile' : 'upload.getFile',
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line
|
||||||
location: location as any,
|
location: location as any,
|
||||||
offset: chunkSize * chunk,
|
offset: chunkSize * chunk,
|
||||||
limit: chunkSize,
|
limit: chunkSize,
|
||||||
|
@ -182,7 +183,7 @@ export async function* downloadAsIterable(
|
||||||
}
|
}
|
||||||
|
|
||||||
let error: unknown = undefined
|
let error: unknown = undefined
|
||||||
Promise.all(
|
void Promise.all(
|
||||||
Array.from(
|
Array.from(
|
||||||
{ length: Math.min(poolSize * REQUESTS_PER_CONNECTION, numChunks) },
|
{ length: Math.min(poolSize * REQUESTS_PER_CONNECTION, numChunks) },
|
||||||
downloadChunk,
|
downloadChunk,
|
||||||
|
@ -202,6 +203,7 @@ export async function* downloadAsIterable(
|
||||||
while (position < limitBytes) {
|
while (position < limitBytes) {
|
||||||
await nextChunkCv.wait()
|
await nextChunkCv.wait()
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||||
if (error) throw error
|
if (error) throw error
|
||||||
|
|
||||||
while (nextChunkIdx in buffer) {
|
while (nextChunkIdx in buffer) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ export function downloadAsStream(
|
||||||
async read() {},
|
async read() {},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
for await (const chunk of this.downloadAsIterable(params)) {
|
for await (const chunk of this.downloadAsIterable(params)) {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import { fromBuffer as fileTypeFromBuffer } from 'file-type'
|
import { fromBuffer as fileTypeFromBuffer } from 'file-type'
|
||||||
import type { ReadStream } from 'fs'
|
import type { ReadStream } from 'fs'
|
||||||
import { Readable } from 'stream'
|
import { Readable } from 'stream'
|
||||||
|
@ -15,12 +14,12 @@ import {
|
||||||
readBytesFromStream,
|
readBytesFromStream,
|
||||||
} from '../../utils/stream-utils'
|
} from '../../utils/stream-utils'
|
||||||
|
|
||||||
let fs: any = null
|
let fs: typeof import('fs') | null = null
|
||||||
let path: any = null
|
let path: typeof import('path') | null = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fs = require('fs')
|
fs = require('fs') as typeof import('fs')
|
||||||
path = require('path')
|
path = require('path') as typeof import('path')
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
const OVERRIDE_MIME: Record<string, string> = {
|
const OVERRIDE_MIME: Record<string, string> = {
|
||||||
|
@ -132,15 +131,12 @@ export async function uploadFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fs && file instanceof fs.ReadStream) {
|
if (fs && file instanceof fs.ReadStream) {
|
||||||
fileName = path.basename((file as ReadStream).path.toString())
|
fileName = path!.basename(file.path.toString())
|
||||||
fileSize = await new Promise((res, rej) => {
|
fileSize = await new Promise((res, rej) => {
|
||||||
fs.stat(
|
fs!.stat((file as ReadStream).path.toString(), (err, stat) => {
|
||||||
(file as ReadStream).path.toString(),
|
if (err) rej(err)
|
||||||
(err?: any, stat?: any) => {
|
res(stat.size)
|
||||||
if (err) rej(err)
|
})
|
||||||
res(stat.size)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
// fs.ReadStream is a subclass of Readable, no conversion needed
|
// fs.ReadStream is a subclass of Readable, no conversion needed
|
||||||
}
|
}
|
||||||
|
@ -171,7 +167,7 @@ export async function uploadFile(
|
||||||
|
|
||||||
if (idx > -1) {
|
if (idx > -1) {
|
||||||
const raw = disposition.slice(idx + 9).split(';')[0]
|
const raw = disposition.slice(idx + 9).split(';')[0]
|
||||||
fileName = JSON.parse(raw)
|
fileName = JSON.parse(raw) as string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ export async function uploadMedia(
|
||||||
assertTypeIs('uploadMedia', res, 'messageMediaDocument')
|
assertTypeIs('uploadMedia', res, 'messageMediaDocument')
|
||||||
assertTypeIs('uploadMedia', res.document!, 'document')
|
assertTypeIs('uploadMedia', res.document!, 'document')
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line
|
||||||
return parseDocument(this, res.document) as any
|
return parseDocument(this, res.document) as any
|
||||||
case 'inputMediaStory':
|
case 'inputMediaStory':
|
||||||
throw new MtArgumentError("This media (story) can't be uploaded")
|
throw new MtArgumentError("This media (story) can't be uploaded")
|
||||||
|
|
|
@ -76,7 +76,7 @@ export async function editInlineMessage(
|
||||||
let entities: tl.TypeMessageEntity[] | undefined
|
let entities: tl.TypeMessageEntity[] | undefined
|
||||||
let media: tl.TypeInputMedia | undefined = undefined
|
let media: tl.TypeInputMedia | undefined = undefined
|
||||||
|
|
||||||
const id = await normalizeInlineId(messageId)
|
const id = normalizeInlineId(messageId)
|
||||||
|
|
||||||
if (params.media) {
|
if (params.media) {
|
||||||
media = await this._normalizeInputMedia(params.media, params, true)
|
media = await this._normalizeInputMedia(params.media, params, true)
|
||||||
|
|
|
@ -114,6 +114,6 @@ export async function editMessage(
|
||||||
media,
|
media,
|
||||||
})
|
})
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line
|
||||||
return this._findMessageInUpdate(res, true) as any
|
return this._findMessageInUpdate(res, true) as any
|
||||||
}
|
}
|
||||||
|
|
|
@ -291,13 +291,18 @@ export async function forwardMessages(
|
||||||
},
|
},
|
||||||
): Promise<MaybeArray<Message>> {
|
): Promise<MaybeArray<Message>> {
|
||||||
if (!params) params = {}
|
if (!params) params = {}
|
||||||
const isSingle = !Array.isArray(messages)
|
|
||||||
if (isSingle) messages = [messages as number]
|
let isSingle = false
|
||||||
|
|
||||||
|
if (!Array.isArray(messages)) {
|
||||||
|
isSingle = true
|
||||||
|
messages = [messages]
|
||||||
|
}
|
||||||
|
|
||||||
// sending more than 100 will not result in a server-sent
|
// sending more than 100 will not result in a server-sent
|
||||||
// error, instead only first 100 IDs will be forwarded,
|
// error, instead only first 100 IDs will be forwarded,
|
||||||
// which is definitely not the best outcome.
|
// which is definitely not the best outcome.
|
||||||
if ((messages as number[]).length > 100) {
|
if (messages.length > 100) {
|
||||||
throw new MtArgumentError(
|
throw new MtArgumentError(
|
||||||
'You can forward no more than 100 messages at once',
|
'You can forward no more than 100 messages at once',
|
||||||
)
|
)
|
||||||
|
@ -338,12 +343,10 @@ export async function forwardMessages(
|
||||||
_: 'messages.forwardMessages',
|
_: 'messages.forwardMessages',
|
||||||
toPeer,
|
toPeer,
|
||||||
fromPeer: await this.resolvePeer(fromChatId),
|
fromPeer: await this.resolvePeer(fromChatId),
|
||||||
id: messages as number[],
|
id: messages,
|
||||||
silent: params.silent,
|
silent: params.silent,
|
||||||
scheduleDate: normalizeDate(params.schedule),
|
scheduleDate: normalizeDate(params.schedule),
|
||||||
randomId: [...Array((messages as number[]).length)].map(() =>
|
randomId: Array.from({ length: messages.length }, () => randomLong()),
|
||||||
randomLong(),
|
|
||||||
),
|
|
||||||
dropAuthor: params.noAuthor,
|
dropAuthor: params.noAuthor,
|
||||||
dropMediaCaptions: params.noCaption,
|
dropMediaCaptions: params.noCaption,
|
||||||
noforwards: params.forbidForwards,
|
noforwards: params.forbidForwards,
|
||||||
|
|
|
@ -56,7 +56,7 @@ export async function addStickerToSet(
|
||||||
maskCoords: sticker.maskPosition ?
|
maskCoords: sticker.maskPosition ?
|
||||||
{
|
{
|
||||||
_: 'maskCoords',
|
_: 'maskCoords',
|
||||||
n: MASK_POS[sticker.maskPosition.point as keyof typeof MASK_POS],
|
n: MASK_POS[sticker.maskPosition.point],
|
||||||
x: sticker.maskPosition.x,
|
x: sticker.maskPosition.x,
|
||||||
y: sticker.maskPosition.y,
|
y: sticker.maskPosition.y,
|
||||||
zoom: sticker.maskPosition.scale,
|
zoom: sticker.maskPosition.scale,
|
||||||
|
|
|
@ -106,7 +106,10 @@ export async function createStickerSet(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const owner = normalizeToInputUser(await this.resolvePeer(params.owner), params.owner)
|
const owner = normalizeToInputUser(
|
||||||
|
await this.resolvePeer(params.owner),
|
||||||
|
params.owner,
|
||||||
|
)
|
||||||
|
|
||||||
const inputStickers: tl.TypeInputStickerSetItem[] = []
|
const inputStickers: tl.TypeInputStickerSetItem[] = []
|
||||||
|
|
||||||
|
@ -124,7 +127,7 @@ export async function createStickerSet(
|
||||||
maskCoords: sticker.maskPosition ?
|
maskCoords: sticker.maskPosition ?
|
||||||
{
|
{
|
||||||
_: 'maskCoords',
|
_: 'maskCoords',
|
||||||
n: MASK_POS[sticker.maskPosition.point as keyof typeof MASK_POS],
|
n: MASK_POS[sticker.maskPosition.point],
|
||||||
x: sticker.maskPosition.x,
|
x: sticker.maskPosition.x,
|
||||||
y: sticker.maskPosition.y,
|
y: sticker.maskPosition.y,
|
||||||
zoom: sticker.maskPosition.scale,
|
zoom: sticker.maskPosition.scale,
|
||||||
|
|
|
@ -33,6 +33,6 @@ export async function getCustomEmojis(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return doc as Sticker
|
return doc
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -940,7 +940,7 @@ function _fetchChannelDifferenceLater(
|
||||||
fallbackPts?: number,
|
fallbackPts?: number,
|
||||||
force = false,
|
force = false,
|
||||||
): void {
|
): void {
|
||||||
if (!requestedDiff[channelId]) {
|
if (!(channelId in requestedDiff)) {
|
||||||
requestedDiff[channelId] = _fetchChannelDifference
|
requestedDiff[channelId] = _fetchChannelDifference
|
||||||
.call(this, channelId, fallbackPts, force)
|
.call(this, channelId, fallbackPts, force)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
@ -1074,7 +1074,7 @@ function _fetchDifferenceLater(
|
||||||
this: TelegramClient,
|
this: TelegramClient,
|
||||||
requestedDiff: Record<number, Promise<void>>,
|
requestedDiff: Record<number, Promise<void>>,
|
||||||
): void {
|
): void {
|
||||||
if (!requestedDiff[0]) {
|
if (!(0 in requestedDiff)) {
|
||||||
requestedDiff[0] = _fetchDifference
|
requestedDiff[0] = _fetchDifference
|
||||||
.call(this, requestedDiff)
|
.call(this, requestedDiff)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
|
|
@ -8,7 +8,11 @@ import {
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
import { InputPeerLike, MtNotFoundError, MtTypeAssertionError } from '../../types'
|
import {
|
||||||
|
InputPeerLike,
|
||||||
|
MtNotFoundError,
|
||||||
|
MtTypeAssertionError,
|
||||||
|
} from '../../types'
|
||||||
import { normalizeToInputPeer } from '../../utils/peer-utils'
|
import { normalizeToInputPeer } from '../../utils/peer-utils'
|
||||||
import { assertTypeIs } from '../../utils/type-assertion'
|
import { assertTypeIs } from '../../utils/type-assertion'
|
||||||
|
|
||||||
|
@ -109,7 +113,12 @@ export async function resolvePeer(
|
||||||
const found = res.chats.find((it) => it.id === id)
|
const found = res.chats.find((it) => it.id === id)
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
if (!(found._ === 'channel' || found._ === 'channelForbidden')) {
|
if (
|
||||||
|
!(
|
||||||
|
found._ === 'channel' ||
|
||||||
|
found._ === 'channelForbidden'
|
||||||
|
)
|
||||||
|
) {
|
||||||
// chats can't have usernames
|
// chats can't have usernames
|
||||||
// furthermore, our id is a channel id, so it must be a channel
|
// furthermore, our id is a channel id, so it must be a channel
|
||||||
// this should never happen, unless Telegram goes crazy
|
// this should never happen, unless Telegram goes crazy
|
||||||
|
@ -205,7 +214,7 @@ export async function resolvePeer(
|
||||||
// break
|
// break
|
||||||
}
|
}
|
||||||
case 'channel': {
|
case 'channel': {
|
||||||
const id = toggleChannelIdMark(peerId as number)
|
const id = toggleChannelIdMark(peerId)
|
||||||
|
|
||||||
const res = await this.call({
|
const res = await this.call({
|
||||||
_: 'channels.getChannels',
|
_: 'channels.getChannels',
|
||||||
|
|
|
@ -251,6 +251,7 @@ export class Conversation {
|
||||||
|
|
||||||
this.stop()
|
this.stop()
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||||
if (err) throw err
|
if (err) throw err
|
||||||
|
|
||||||
return res!
|
return res!
|
||||||
|
@ -501,7 +502,7 @@ export class Conversation {
|
||||||
const it = this._queuedNewMessage.peekFront()!
|
const it = this._queuedNewMessage.peekFront()!
|
||||||
|
|
||||||
// order does matter for new messages
|
// order does matter for new messages
|
||||||
this._lock.acquire().then(async () => {
|
void this._lock.acquire().then(async () => {
|
||||||
try {
|
try {
|
||||||
if (!it.check || (await it.check(msg))) {
|
if (!it.check || (await it.check(msg))) {
|
||||||
if (it.timeout) clearTimeout(it.timeout)
|
if (it.timeout) clearTimeout(it.timeout)
|
||||||
|
|
|
@ -140,7 +140,7 @@ export class Sticker extends RawDocument {
|
||||||
*/
|
*/
|
||||||
get customEmojiFree(): boolean {
|
get customEmojiFree(): boolean {
|
||||||
return this.attr._ === 'documentAttributeCustomEmoji' ?
|
return this.attr._ === 'documentAttributeCustomEmoji' ?
|
||||||
this.attr?.free ?? false :
|
this.attr.free ?? false :
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -329,7 +329,7 @@ export class Message {
|
||||||
get replyToMessageId(): number | null {
|
get replyToMessageId(): number | null {
|
||||||
if (this.raw.replyTo?._ !== 'messageReplyHeader') return null
|
if (this.raw.replyTo?._ !== 'messageReplyHeader') return null
|
||||||
|
|
||||||
return this.raw.replyTo?.replyToMsgId ?? null
|
return this.raw.replyTo.replyToMsgId ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -339,7 +339,7 @@ export class Message {
|
||||||
get replyToThreadId(): number | null {
|
get replyToThreadId(): number | null {
|
||||||
if (this.raw.replyTo?._ !== 'messageReplyHeader') return null
|
if (this.raw.replyTo?._ !== 'messageReplyHeader') return null
|
||||||
|
|
||||||
return this.raw.replyTo?.replyToTopId ?? null
|
return this.raw.replyTo.replyToTopId ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -239,7 +239,7 @@ export class StickerSet {
|
||||||
*/
|
*/
|
||||||
getStickersByEmoji(emoji: string): StickerInfo[] {
|
getStickersByEmoji(emoji: string): StickerInfo[] {
|
||||||
return this.stickers.filter(
|
return this.stickers.filter(
|
||||||
(it) => it.alt === emoji || it.emoji.indexOf(emoji) !== -1,
|
(it) => it.alt === emoji || it.emoji.includes(emoji),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,8 +271,8 @@ export class StickerSet {
|
||||||
private _getInputDocument(idx: number): tl.TypeInputDocument {
|
private _getInputDocument(idx: number): tl.TypeInputDocument {
|
||||||
if (!this.full) throw new MtEmptyError()
|
if (!this.full) throw new MtEmptyError()
|
||||||
|
|
||||||
if (idx < 0) idx = this.full!.documents.length + idx
|
if (idx < 0) idx = this.full.documents.length + idx
|
||||||
const doc = this.full!.documents[idx] as tl.RawDocument
|
const doc = this.full.documents[idx] as tl.RawDocument
|
||||||
|
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
throw new RangeError(`Sticker set does not have sticker ${idx}`)
|
throw new RangeError(`Sticker set does not have sticker ${idx}`)
|
||||||
|
|
|
@ -33,6 +33,7 @@ export class TakeoutSession {
|
||||||
message: MustEqual<T, tl.RpcMethod>,
|
message: MustEqual<T, tl.RpcMethod>,
|
||||||
params?: RpcCallOptions,
|
params?: RpcCallOptions,
|
||||||
): Promise<tl.RpcCallReturn[T['_']]> {
|
): Promise<tl.RpcCallReturn[T['_']]> {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
return this.client.call(
|
return this.client.call(
|
||||||
{
|
{
|
||||||
_: 'invokeWithTakeout',
|
_: 'invokeWithTakeout',
|
||||||
|
@ -76,6 +77,7 @@ export class TakeoutSession {
|
||||||
get(target, prop, receiver) {
|
get(target, prop, receiver) {
|
||||||
if (prop === 'call') return boundCall
|
if (prop === 'call') return boundCall
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
return Reflect.get(target, prop, receiver)
|
return Reflect.get(target, prop, receiver)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -98,6 +98,7 @@ export class ChatPhotoSize extends FileLocation {
|
||||||
{
|
{
|
||||||
_: 'photo',
|
_: 'photo',
|
||||||
id: this.obj.photoId,
|
id: this.obj.photoId,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
source: {
|
source: {
|
||||||
_: 'dialogPhoto',
|
_: 'dialogPhoto',
|
||||||
big: this.big,
|
big: this.big,
|
||||||
|
|
|
@ -120,7 +120,7 @@ export class Chat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._inputPeer!
|
return this._inputPeer
|
||||||
}
|
}
|
||||||
|
|
||||||
private _chatType?: ChatType
|
private _chatType?: ChatType
|
||||||
|
@ -148,7 +148,7 @@ export class Chat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._chatType!
|
return this._chatType
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -594,7 +594,7 @@ export class Chat {
|
||||||
return new FormattedString(
|
return new FormattedString(
|
||||||
this.client.getParseMode(parseMode).unparse(text, [
|
this.client.getParseMode(parseMode).unparse(text, [
|
||||||
{
|
{
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line
|
||||||
raw: undefined as any,
|
raw: undefined as any,
|
||||||
type: 'text_link',
|
type: 'text_link',
|
||||||
offset: 0,
|
offset: 0,
|
||||||
|
@ -602,7 +602,7 @@ export class Chat {
|
||||||
url: `https://t.me/${this.username}`,
|
url: `https://t.me/${this.username}`,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
parseMode!,
|
parseMode,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ const ERROR_MSG =
|
||||||
'Given peer is not available in this index. This is most likely an internal library error.'
|
'Given peer is not available in this index. This is most likely an internal library error.'
|
||||||
|
|
||||||
export class PeersIndex {
|
export class PeersIndex {
|
||||||
readonly users: Record<number, tl.TypeUser> = Object.create(null)
|
readonly users: Record<number, tl.TypeUser> = {}
|
||||||
readonly chats: Record<number, tl.TypeChat> = Object.create(null)
|
readonly chats: Record<number, tl.TypeChat> = {}
|
||||||
|
|
||||||
hasMin = false
|
hasMin = false
|
||||||
|
|
||||||
|
|
|
@ -324,7 +324,7 @@ export class User {
|
||||||
return new FormattedString(
|
return new FormattedString(
|
||||||
this.client.getParseMode(parseMode).unparse(text, [
|
this.client.getParseMode(parseMode).unparse(text, [
|
||||||
{
|
{
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line
|
||||||
raw: undefined as any,
|
raw: undefined as any,
|
||||||
type: 'text_mention',
|
type: 'text_mention',
|
||||||
offset: 0,
|
offset: 0,
|
||||||
|
@ -332,7 +332,7 @@ export class User {
|
||||||
userId: this.raw.id,
|
userId: this.raw.id,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
parseMode!,
|
parseMode,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,7 +383,7 @@ export class User {
|
||||||
return new FormattedString(
|
return new FormattedString(
|
||||||
this.client.getParseMode(parseMode).unparse(text, [
|
this.client.getParseMode(parseMode).unparse(text, [
|
||||||
{
|
{
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line
|
||||||
raw: undefined as any,
|
raw: undefined as any,
|
||||||
type: 'text_link',
|
type: 'text_link',
|
||||||
offset: 0,
|
offset: 0,
|
||||||
|
@ -393,7 +393,7 @@ export class User {
|
||||||
}&hash=${this.raw.accessHash.toString(16)}`,
|
}&hash=${this.raw.accessHash.toString(16)}`,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
parseMode!,
|
parseMode,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
// ^^ will be looked into in MTQ-35
|
// ^^ will be looked into in MTQ-35
|
||||||
|
|
||||||
|
import { getMarkedPeerId } from '@mtcute/core'
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
|
@ -37,6 +37,26 @@ export type ChatMemberUpdateType =
|
||||||
| 'new_owner'
|
| 'new_owner'
|
||||||
| 'other'
|
| 'other'
|
||||||
|
|
||||||
|
function extractPeerId(
|
||||||
|
raw?: tl.TypeChatParticipant | tl.TypeChannelParticipant,
|
||||||
|
) {
|
||||||
|
if (!raw) return 0
|
||||||
|
|
||||||
|
if (tl.isAnyChatParticipant(raw)) {
|
||||||
|
return raw.userId
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (raw._) {
|
||||||
|
case 'channelParticipant':
|
||||||
|
case 'channelParticipantSelf':
|
||||||
|
case 'channelParticipantCreator':
|
||||||
|
case 'channelParticipantAdmin':
|
||||||
|
return raw.userId
|
||||||
|
default:
|
||||||
|
return getMarkedPeerId(raw.peer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update representing a change in the status
|
* Update representing a change in the status
|
||||||
* of a chat/channel member.
|
* of a chat/channel member.
|
||||||
|
@ -82,12 +102,8 @@ export class ChatMemberUpdate {
|
||||||
const old = this.raw.prevParticipant
|
const old = this.raw.prevParticipant
|
||||||
const cur = this.raw.newParticipant
|
const cur = this.raw.newParticipant
|
||||||
|
|
||||||
const oldId =
|
const oldId = extractPeerId(old)
|
||||||
(old && ((old as any).userId || (old as any).peer.userId)) ||
|
const curId = extractPeerId(cur)
|
||||||
null
|
|
||||||
const curId =
|
|
||||||
(cur && ((cur as any).userId || (cur as any).peer.userId)) ||
|
|
||||||
null
|
|
||||||
|
|
||||||
const actorId = this.raw.actorId
|
const actorId = this.raw.actorId
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-argument */
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
|
@ -19,13 +19,14 @@ import {
|
||||||
PollVoteUpdate,
|
PollVoteUpdate,
|
||||||
PreCheckoutQuery,
|
PreCheckoutQuery,
|
||||||
UserStatusUpdate,
|
UserStatusUpdate,
|
||||||
UserTypingUpdate } from '../index'
|
UserTypingUpdate,
|
||||||
|
} from '../index'
|
||||||
|
|
||||||
type ParserFunction = (
|
type ParserFunction = (
|
||||||
client: TelegramClient,
|
client: TelegramClient,
|
||||||
upd: tl.TypeUpdate | tl.TypeMessage,
|
upd: tl.TypeUpdate | tl.TypeMessage,
|
||||||
peers: PeersIndex
|
peers: PeersIndex
|
||||||
) => any
|
) => ParsedUpdate['data']
|
||||||
type UpdateParser = [ParsedUpdate['name'], ParserFunction]
|
type UpdateParser = [ParsedUpdate['name'], ParserFunction]
|
||||||
|
|
||||||
const baseMessageParser: ParserFunction = (
|
const baseMessageParser: ParserFunction = (
|
||||||
|
@ -35,7 +36,9 @@ const baseMessageParser: ParserFunction = (
|
||||||
) =>
|
) =>
|
||||||
new Message(
|
new Message(
|
||||||
client,
|
client,
|
||||||
tl.isAnyMessage(upd) ? upd : (upd as any).message,
|
tl.isAnyMessage(upd) ?
|
||||||
|
upd :
|
||||||
|
(upd as { message: tl.TypeMessage }).message,
|
||||||
peers,
|
peers,
|
||||||
upd._ === 'updateNewScheduledMessage',
|
upd._ === 'updateNewScheduledMessage',
|
||||||
)
|
)
|
||||||
|
@ -142,7 +145,7 @@ export function _parseUpdate(
|
||||||
return {
|
return {
|
||||||
name: pair[0],
|
name: pair[0],
|
||||||
data: pair[1](client, update, peers),
|
data: pair[1](client, update, peers),
|
||||||
}
|
} as ParsedUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-argument */
|
||||||
import { MaybeAsync } from '@mtcute/core'
|
import { MaybeAsync } from '@mtcute/core'
|
||||||
|
|
||||||
export type MaybeDynamic<T> = MaybeAsync<T> | (() => MaybeAsync<T>)
|
export type MaybeDynamic<T> = MaybeAsync<T> | (() => MaybeAsync<T>)
|
||||||
|
|
||||||
export type ArrayWithTotal<T> = T[] & { total: number }
|
export type ArrayWithTotal<T> = T[] & { total: number }
|
||||||
|
|
||||||
let util: any | null = null
|
let util: typeof import('util') | null = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
util = require('util')
|
util = require('util') as typeof import('util')
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// get all property names. unlike Object.getOwnPropertyNames,
|
// get all property names. unlike Object.getOwnPropertyNames,
|
||||||
|
@ -21,7 +22,7 @@ function getAllGettersNames(obj: object): string[] {
|
||||||
if (
|
if (
|
||||||
prop !== '__proto__' &&
|
prop !== '__proto__' &&
|
||||||
Object.getOwnPropertyDescriptor(obj, prop)?.get &&
|
Object.getOwnPropertyDescriptor(obj, prop)?.get &&
|
||||||
getters.indexOf(prop) === -1
|
!getters.includes(prop)
|
||||||
) {
|
) {
|
||||||
getters.push(prop)
|
getters.push(prop)
|
||||||
}
|
}
|
||||||
|
@ -53,10 +54,11 @@ export function makeInspectable(
|
||||||
const getters: string[] = props ? props : []
|
const getters: string[] = props ? props : []
|
||||||
|
|
||||||
for (const key of getAllGettersNames(obj.prototype)) {
|
for (const key of getAllGettersNames(obj.prototype)) {
|
||||||
if (!hide || hide.indexOf(key) === -1) getters.push(key)
|
if (!hide || !hide.includes(key)) getters.push(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// dirty hack to set name for inspect result
|
// dirty hack to set name for inspect result
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
||||||
const proto = new Function(`return function ${obj.name}(){}`)().prototype
|
const proto = new Function(`return function ${obj.name}(){}`)().prototype
|
||||||
|
|
||||||
obj.prototype.toJSON = function (nested = false) {
|
obj.prototype.toJSON = function (nested = false) {
|
||||||
|
@ -86,6 +88,7 @@ export function makeInspectable(
|
||||||
Buffer.prototype.toJSON = bufferToJsonOriginal
|
Buffer.prototype.toJSON = bufferToJsonOriginal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
if (util) {
|
if (util) {
|
||||||
|
|
|
@ -21,27 +21,30 @@ class NodeReadable extends Readable {
|
||||||
this._reading = true
|
this._reading = true
|
||||||
|
|
||||||
const doRead = () => {
|
const doRead = () => {
|
||||||
this._reader.read().then((res) => {
|
this._reader
|
||||||
if (this._doneReading) {
|
.read()
|
||||||
|
.then((res) => {
|
||||||
|
if (this._doneReading) {
|
||||||
|
this._reading = false
|
||||||
|
this._reader.releaseLock()
|
||||||
|
this._doneReading()
|
||||||
|
}
|
||||||
|
if (res.done) {
|
||||||
|
this.push(null)
|
||||||
|
this._reading = false
|
||||||
|
this._reader.releaseLock()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.push(res.value)) {
|
||||||
|
doRead()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
this._reading = false
|
this._reading = false
|
||||||
this._reader.releaseLock()
|
this._reader.releaseLock()
|
||||||
this._doneReading()
|
})
|
||||||
}
|
.catch((err) => this.emit('error', err))
|
||||||
if (res.done) {
|
|
||||||
this.push(null)
|
|
||||||
this._reading = false
|
|
||||||
this._reader.releaseLock()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (this.push(res.value)) {
|
|
||||||
doRead()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this._reading = false
|
|
||||||
this._reader.releaseLock()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
doRead()
|
doRead()
|
||||||
}
|
}
|
||||||
|
@ -51,9 +54,11 @@ class NodeReadable extends Readable {
|
||||||
const promise = new Promise<void>((resolve) => {
|
const promise = new Promise<void>((resolve) => {
|
||||||
this._doneReading = resolve
|
this._doneReading = resolve
|
||||||
})
|
})
|
||||||
promise.then(() => {
|
promise
|
||||||
this._handleDestroy(err, callback)
|
.then(() => {
|
||||||
})
|
this._handleDestroy(err, callback)
|
||||||
|
})
|
||||||
|
.catch((err) => this.emit('error', err))
|
||||||
} else {
|
} else {
|
||||||
this._handleDestroy(err, callback)
|
this._handleDestroy(err, callback)
|
||||||
}
|
}
|
||||||
|
@ -63,8 +68,10 @@ class NodeReadable extends Readable {
|
||||||
err: Error | null,
|
err: Error | null,
|
||||||
callback: (error?: Error | null) => void,
|
callback: (error?: Error | null) => void,
|
||||||
) {
|
) {
|
||||||
this._webStream.cancel()
|
this._webStream
|
||||||
super._destroy(err, callback)
|
.cancel()
|
||||||
|
.then(() => super._destroy(err, callback))
|
||||||
|
.catch((err: Error) => callback(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,12 +97,12 @@ export async function readBytesFromStream(
|
||||||
): Promise<Buffer | null> {
|
): Promise<Buffer | null> {
|
||||||
if (stream.readableEnded) return null
|
if (stream.readableEnded) return null
|
||||||
|
|
||||||
let res = stream.read(size)
|
let res = stream.read(size) as Buffer
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
stream.on('readable', function handler() {
|
stream.on('readable', function handler() {
|
||||||
res = stream.read(size)
|
res = stream.read(size) as Buffer
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
stream.off('readable', handler)
|
stream.off('readable', handler)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src",
|
||||||
|
"./tests"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,17 +18,18 @@
|
||||||
"./storage/json-file.js": false
|
"./storage/json-file.js": false
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "18.16.0",
|
|
||||||
"@types/events": "3.0.0",
|
|
||||||
"@mtcute/tl": "workspace:^160.0.0",
|
"@mtcute/tl": "workspace:^160.0.0",
|
||||||
"@mtcute/tl-runtime": "workspace:^1.0.0",
|
"@mtcute/tl-runtime": "workspace:^1.0.0",
|
||||||
|
"@types/events": "3.0.0",
|
||||||
|
"@types/node": "18.16.0",
|
||||||
"big-integer": "1.6.51",
|
"big-integer": "1.6.51",
|
||||||
"long": "5.2.3",
|
"events": "3.2.0",
|
||||||
"events": "3.2.0"
|
"long": "5.2.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/dispatcher": "workspace:^1.0.0",
|
"@mtcute/dispatcher": "workspace:^1.0.0",
|
||||||
"@types/ws": "8.5.4",
|
"@types/ws": "8.5.4",
|
||||||
|
"exit-hook": "^4.0.0",
|
||||||
"ws": "8.13.0"
|
"ws": "8.13.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -388,7 +388,7 @@ export class BaseTelegramClient extends EventEmitter {
|
||||||
promise.resolve()
|
promise.resolve()
|
||||||
this._connected = true
|
this._connected = true
|
||||||
})
|
})
|
||||||
.catch((err) => this._emitError(err))
|
.catch((err: Error) => this._emitError(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -401,7 +401,7 @@ export class BaseTelegramClient extends EventEmitter {
|
||||||
* Close all connections and finalize the client.
|
* Close all connections and finalize the client.
|
||||||
*/
|
*/
|
||||||
async close(): Promise<void> {
|
async close(): Promise<void> {
|
||||||
await this._onClose()
|
this._onClose()
|
||||||
|
|
||||||
this._config.destroy()
|
this._config.destroy()
|
||||||
this.network.destroy()
|
this.network.destroy()
|
||||||
|
@ -436,6 +436,7 @@ export class BaseTelegramClient extends EventEmitter {
|
||||||
const res = await this.network.call(message, params, stack)
|
const res = await this.network.call(message, params, stack)
|
||||||
await this._cachePeersFrom(res)
|
await this._cachePeersFrom(res)
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,7 +488,7 @@ export class BaseTelegramClient extends EventEmitter {
|
||||||
let hadMin = false
|
let hadMin = false
|
||||||
let count = 0
|
let count = 0
|
||||||
|
|
||||||
for (const peer of getAllPeersFrom(obj as any)) {
|
for (const peer of getAllPeersFrom(obj as tl.TlObject)) {
|
||||||
if ((peer as any).min) {
|
if ((peer as any).min) {
|
||||||
// absolutely incredible min peer handling, courtesy of levlam.
|
// absolutely incredible min peer handling, courtesy of levlam.
|
||||||
// see this thread: https://t.me/tdlibchat/15084
|
// see this thread: https://t.me/tdlibchat/15084
|
||||||
|
|
|
@ -178,6 +178,7 @@ async function rsaPad(
|
||||||
const decryptedDataBigint = bufferToBigInt(decryptedData)
|
const decryptedDataBigint = bufferToBigInt(decryptedData)
|
||||||
|
|
||||||
if (decryptedDataBigint.geq(keyModulus)) {
|
if (decryptedDataBigint.geq(keyModulus)) {
|
||||||
|
console.log('retrying because decrypted data is too big')
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,6 +225,7 @@ export async function doAuthorization(
|
||||||
const session = connection['_session']
|
const session = connection['_session']
|
||||||
const readerMap = session._readerMap
|
const readerMap = session._readerMap
|
||||||
const writerMap = session._writerMap
|
const writerMap = session._writerMap
|
||||||
|
const log = connection.log.create('auth')
|
||||||
|
|
||||||
function sendPlainMessage(message: mtp.TlObject): Promise<void> {
|
function sendPlainMessage(message: mtp.TlObject): Promise<void> {
|
||||||
const length = TlSerializationCounter.countNeededBytes(
|
const length = TlSerializationCounter.countNeededBytes(
|
||||||
|
@ -234,6 +236,7 @@ export async function doAuthorization(
|
||||||
|
|
||||||
const messageId = session.getMessageId()
|
const messageId = session.getMessageId()
|
||||||
|
|
||||||
|
log.verbose('[PLAIN] >>> %j', message)
|
||||||
writer.long(Long.ZERO)
|
writer.long(Long.ZERO)
|
||||||
writer.long(messageId)
|
writer.long(messageId)
|
||||||
writer.uint(length)
|
writer.uint(length)
|
||||||
|
@ -243,14 +246,17 @@ export async function doAuthorization(
|
||||||
}
|
}
|
||||||
|
|
||||||
async function readNext(): Promise<mtp.TlObject> {
|
async function readNext(): Promise<mtp.TlObject> {
|
||||||
return TlBinaryReader.deserializeObject(
|
const res = TlBinaryReader.deserializeObject<mtp.TlObject>(
|
||||||
readerMap,
|
readerMap,
|
||||||
await connection.waitForUnencryptedMessage(),
|
await connection.waitForUnencryptedMessage(),
|
||||||
20, // skip mtproto header
|
20, // skip mtproto header
|
||||||
)
|
)
|
||||||
|
|
||||||
|
log.verbose('[PLAIN] <<< %j', res)
|
||||||
|
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
const log = connection.log.create('auth')
|
|
||||||
if (expiresIn) log.prefix = '[PFS] '
|
if (expiresIn) log.prefix = '[PFS] '
|
||||||
|
|
||||||
const nonce = randomBytes(16)
|
const nonce = randomBytes(16)
|
||||||
|
@ -508,8 +514,7 @@ export async function doAuthorization(
|
||||||
if (!buffersEqual(expectedHash.slice(4, 20), dhGen.newNonceHash2)) {
|
if (!buffersEqual(expectedHash.slice(4, 20), dhGen.newNonceHash2)) {
|
||||||
throw Error('Step 4: invalid retry nonce hash from server')
|
throw Error('Step 4: invalid retry nonce hash from server')
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
retryId = Long.fromBytesLE(authKeyAuxHash as unknown as number[])
|
||||||
retryId = Long.fromBytesLE(authKeyAuxHash as any)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ export class ConfigManager {
|
||||||
|
|
||||||
if (this._updateTimeout) clearTimeout(this._updateTimeout)
|
if (this._updateTimeout) clearTimeout(this._updateTimeout)
|
||||||
this._updateTimeout = setTimeout(
|
this._updateTimeout = setTimeout(
|
||||||
() => this.update(),
|
() => void this.update().catch(() => {}),
|
||||||
(config.expires - Date.now() / 1000) * 1000,
|
(config.expires - Date.now() / 1000) * 1000,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -250,7 +250,7 @@ export class DcConnectionManager {
|
||||||
private _setupMulti(kind: ConnectionKind): void {
|
private _setupMulti(kind: ConnectionKind): void {
|
||||||
const connection = this[kind]
|
const connection = this[kind]
|
||||||
|
|
||||||
connection.on('key-change', (idx, key) => {
|
connection.on('key-change', (idx, key: Buffer | null) => {
|
||||||
if (kind !== 'main') {
|
if (kind !== 'main') {
|
||||||
// main connection is responsible for authorization,
|
// main connection is responsible for authorization,
|
||||||
// and keys are then sent to other connections
|
// and keys are then sent to other connections
|
||||||
|
@ -266,51 +266,58 @@ export class DcConnectionManager {
|
||||||
this.dcId,
|
this.dcId,
|
||||||
idx,
|
idx,
|
||||||
)
|
)
|
||||||
this.manager._storage.setAuthKeyFor(this.dcId, key)
|
|
||||||
|
|
||||||
// send key to other connections
|
// send key to other connections
|
||||||
Promise.all([
|
Promise.all([
|
||||||
|
this.manager._storage.setAuthKeyFor(this.dcId, key),
|
||||||
this.upload.setAuthKey(key),
|
this.upload.setAuthKey(key),
|
||||||
this.download.setAuthKey(key),
|
this.download.setAuthKey(key),
|
||||||
this.downloadSmall.setAuthKey(key),
|
this.downloadSmall.setAuthKey(key),
|
||||||
]).then(() => {
|
])
|
||||||
this.upload.notifyKeyChange()
|
.then(() => {
|
||||||
this.download.notifyKeyChange()
|
this.upload.notifyKeyChange()
|
||||||
this.downloadSmall.notifyKeyChange()
|
this.download.notifyKeyChange()
|
||||||
})
|
this.downloadSmall.notifyKeyChange()
|
||||||
|
})
|
||||||
|
.catch((e: Error) => this.manager.params._emitError(e))
|
||||||
})
|
})
|
||||||
connection.on('tmp-key-change', (idx, key, expires) => {
|
connection.on(
|
||||||
if (kind !== 'main') {
|
'tmp-key-change',
|
||||||
this.manager._log.warn(
|
(idx: number, key: Buffer | null, expires: number) => {
|
||||||
'got tmp-key-change from non-main connection, ignoring',
|
if (kind !== 'main') {
|
||||||
|
this.manager._log.warn(
|
||||||
|
'got tmp-key-change from non-main connection, ignoring',
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.manager._log.debug(
|
||||||
|
'temp key change for dc %d from connection %d',
|
||||||
|
this.dcId,
|
||||||
|
idx,
|
||||||
)
|
)
|
||||||
|
|
||||||
return
|
// send key to other connections
|
||||||
}
|
Promise.all([
|
||||||
|
this.manager._storage.setTempAuthKeyFor(
|
||||||
this.manager._log.debug(
|
this.dcId,
|
||||||
'temp key change for dc %d from connection %d',
|
idx,
|
||||||
this.dcId,
|
key,
|
||||||
idx,
|
expires * 1000,
|
||||||
)
|
),
|
||||||
this.manager._storage.setTempAuthKeyFor(
|
this.upload.setAuthKey(key, true),
|
||||||
this.dcId,
|
this.download.setAuthKey(key, true),
|
||||||
idx,
|
this.downloadSmall.setAuthKey(key, true),
|
||||||
key,
|
])
|
||||||
expires * 1000,
|
.then(() => {
|
||||||
)
|
this.upload.notifyKeyChange()
|
||||||
|
this.download.notifyKeyChange()
|
||||||
// send key to other connections
|
this.downloadSmall.notifyKeyChange()
|
||||||
Promise.all([
|
})
|
||||||
this.upload.setAuthKey(key, true),
|
.catch((e: Error) => this.manager.params._emitError(e))
|
||||||
this.download.setAuthKey(key, true),
|
},
|
||||||
this.downloadSmall.setAuthKey(key, true),
|
)
|
||||||
]).then(() => {
|
|
||||||
this.upload.notifyKeyChange()
|
|
||||||
this.download.notifyKeyChange()
|
|
||||||
this.downloadSmall.notifyKeyChange()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
connection.on('auth-begin', () => {
|
connection.on('auth-begin', () => {
|
||||||
// we need to propagate auth-begin to all connections
|
// we need to propagate auth-begin to all connections
|
||||||
|
@ -334,7 +341,7 @@ export class DcConnectionManager {
|
||||||
this.main.requestAuth()
|
this.main.requestAuth()
|
||||||
})
|
})
|
||||||
|
|
||||||
connection.on('error', (err, conn) => {
|
connection.on('error', (err: Error, conn: SessionConnection) => {
|
||||||
this.manager.params._emitError(err, conn)
|
this.manager.params._emitError(err, conn)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -429,19 +436,21 @@ export class NetworkManager {
|
||||||
let deviceModel = 'mtcute on '
|
let deviceModel = 'mtcute on '
|
||||||
let appVersion = 'unknown'
|
let appVersion = 'unknown'
|
||||||
if (typeof process !== 'undefined' && typeof require !== 'undefined') {
|
if (typeof process !== 'undefined' && typeof require !== 'undefined') {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
const os = require('os') as typeof import('os')
|
||||||
const os = require('os')
|
|
||||||
deviceModel += `${os.type()} ${os.arch()} ${os.release()}`
|
deviceModel += `${os.type()} ${os.arch()} ${os.release()}`
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// for production builds
|
// for production builds
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
appVersion = require('../package.json').version
|
appVersion = (require('../package.json') as { version: string })
|
||||||
|
.version
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
// for development builds (additional /src/ in path)
|
// for development builds (additional /src/ in path)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
appVersion = require('../../package.json').version
|
appVersion = (
|
||||||
|
require('../../package.json') as { version: string }
|
||||||
|
).version
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
} else if (typeof navigator !== 'undefined') {
|
} else if (typeof navigator !== 'undefined') {
|
||||||
|
@ -458,7 +467,7 @@ export class NetworkManager {
|
||||||
langCode: 'en',
|
langCode: 'en',
|
||||||
...(params.initConnectionOptions ?? {}),
|
...(params.initConnectionOptions ?? {}),
|
||||||
apiId: params.apiId,
|
apiId: params.apiId,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line
|
||||||
query: null as any,
|
query: null as any,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,7 +493,7 @@ export class NetworkManager {
|
||||||
this._lastUpdateTime = Date.now()
|
this._lastUpdateTime = Date.now()
|
||||||
|
|
||||||
if (this._keepAliveInterval) clearInterval(this._keepAliveInterval)
|
if (this._keepAliveInterval) clearInterval(this._keepAliveInterval)
|
||||||
this._keepAliveInterval = setInterval(async () => {
|
this._keepAliveInterval = setInterval(() => {
|
||||||
if (Date.now() - this._lastUpdateTime > 900_000) {
|
if (Date.now() - this._lastUpdateTime > 900_000) {
|
||||||
// telegram asks to fetch pending updates if there are no updates for 15 minutes.
|
// telegram asks to fetch pending updates if there are no updates for 15 minutes.
|
||||||
// it is up to the user to decide whether to do it or not
|
// it is up to the user to decide whether to do it or not
|
||||||
|
@ -494,27 +503,21 @@ export class NetworkManager {
|
||||||
}
|
}
|
||||||
}, 60_000)
|
}, 60_000)
|
||||||
|
|
||||||
Promise.resolve(this._storage.getSelf()).then((self) => {
|
Promise.resolve(this._storage.getSelf())
|
||||||
if (self?.isBot) {
|
.then((self) => {
|
||||||
// bots may receive tmpSessions, which we should respect
|
if (self?.isBot) {
|
||||||
this.config.update(true).catch((e) => {
|
// bots may receive tmpSessions, which we should respect
|
||||||
this.params._emitError(e)
|
return this.config.update(true)
|
||||||
})
|
}
|
||||||
}
|
})
|
||||||
})
|
.catch((e: Error) => this.params._emitError(e))
|
||||||
})
|
})
|
||||||
dc.main.on('update', (update) => {
|
dc.main.on('update', (update: tl.TypeUpdates) => {
|
||||||
this._lastUpdateTime = Date.now()
|
this._lastUpdateTime = Date.now()
|
||||||
this._updateHandler(update)
|
this._updateHandler(update)
|
||||||
})
|
})
|
||||||
|
|
||||||
dc.loadKeys()
|
return dc.loadKeys().then(() => dc.main.ensureConnected())
|
||||||
.catch((e) => {
|
|
||||||
this.params._emitError(e)
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
dc.main.ensureConnected()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dcCreationPromise: Record<number, Promise<void>> = {}
|
private _dcCreationPromise: Record<number, Promise<void>> = {}
|
||||||
|
@ -574,7 +577,7 @@ export class NetworkManager {
|
||||||
|
|
||||||
const dc = new DcConnectionManager(this, defaultDc.id, defaultDc)
|
const dc = new DcConnectionManager(this, defaultDc.id, defaultDc)
|
||||||
this._dcConnections[defaultDc.id] = dc
|
this._dcConnections[defaultDc.id] = dc
|
||||||
this._switchPrimaryDc(dc)
|
await this._switchPrimaryDc(dc)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _exportAuthTo(manager: DcConnectionManager): Promise<void> {
|
private async _exportAuthTo(manager: DcConnectionManager): Promise<void> {
|
||||||
|
@ -681,9 +684,9 @@ export class NetworkManager {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
this._storage.setDefaultDc(option)
|
await this._storage.setDefaultDc(option)
|
||||||
|
|
||||||
this._switchPrimaryDc(this._dcConnections[newDc])
|
await this._switchPrimaryDc(this._dcConnections[newDc])
|
||||||
}
|
}
|
||||||
|
|
||||||
private _floodWaitedRequests: Record<string, number> = {}
|
private _floodWaitedRequests: Record<string, number> = {}
|
||||||
|
@ -738,10 +741,11 @@ export class NetworkManager {
|
||||||
this._lastUpdateTime = Date.now()
|
this._lastUpdateTime = Date.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
return res
|
return res
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
lastError = e
|
lastError = e as Error
|
||||||
|
|
||||||
if (e.code && !(e.code in CLIENT_ERRORS)) {
|
if (e.code && !(e.code in CLIENT_ERRORS)) {
|
||||||
this._log.warn(
|
this._log.warn(
|
||||||
|
@ -811,7 +815,7 @@ export class NetworkManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw lastError
|
throw lastError!
|
||||||
}
|
}
|
||||||
|
|
||||||
setUpdateHandler(handler: NetworkManager['_updateHandler']): void {
|
setUpdateHandler(handler: NetworkManager['_updateHandler']): void {
|
||||||
|
|
|
@ -115,7 +115,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
this._isPfsBindingPending = false
|
this._isPfsBindingPending = false
|
||||||
this._isPfsBindingPendingInBackground = false
|
this._isPfsBindingPendingInBackground = false
|
||||||
this._session._authKeyTemp.reset()
|
this._session._authKeyTemp.reset()
|
||||||
clearTimeout(this._pfsUpdateTimeout!)
|
clearTimeout(this._pfsUpdateTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
this._resetSession()
|
this._resetSession()
|
||||||
|
@ -296,7 +296,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
}
|
}
|
||||||
this.onConnectionUsable()
|
this.onConnectionUsable()
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err: Error) => {
|
||||||
this._session.authorizationPending = false
|
this._session.authorizationPending = false
|
||||||
this.log.error('Authorization error: %s', err.message)
|
this.log.error('Authorization error: %s', err.message)
|
||||||
this.onError(err)
|
this.onError(err)
|
||||||
|
@ -338,7 +338,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const tempKey = await this._session._authKeyTempSecondary
|
const tempKey = this._session._authKeyTempSecondary
|
||||||
await tempKey.setup(tempAuthKey)
|
await tempKey.setup(tempAuthKey)
|
||||||
|
|
||||||
const msgId = this._session.getMessageId()
|
const msgId = this._session.getMessageId()
|
||||||
|
@ -490,7 +490,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
this._authorizePfs(true)
|
this._authorizePfs(true)
|
||||||
}, (TEMP_AUTH_KEY_EXPIRY - 60) * 1000)
|
}, (TEMP_AUTH_KEY_EXPIRY - 60) * 1000)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err: Error) => {
|
||||||
this.log.error('PFS Authorization error: %s', err.message)
|
this.log.error('PFS Authorization error: %s', err.message)
|
||||||
|
|
||||||
if (this._isPfsBindingPendingInBackground) {
|
if (this._isPfsBindingPendingInBackground) {
|
||||||
|
@ -699,6 +699,8 @@ export class SessionConnection extends PersistentConnection {
|
||||||
this._rescheduleInactivity()
|
this._rescheduleInactivity()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.log.verbose('<<< %j', message)
|
||||||
|
|
||||||
if (this.params.disableUpdates) {
|
if (this.params.disableUpdates) {
|
||||||
this.log.warn(
|
this.log.warn(
|
||||||
'received updates, but updates are disabled',
|
'received updates, but updates are disabled',
|
||||||
|
@ -727,6 +729,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
let resultType
|
let resultType
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// eslint-disable-next-line
|
||||||
resultType = (message.object() as any)._
|
resultType = (message.object() as any)._
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
resultType = message.peekUint()
|
resultType = message.peekUint()
|
||||||
|
@ -745,6 +748,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
let result
|
let result
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
result = message.object() as any
|
result = message.object() as any
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
result = '[failed to parse]'
|
result = '[failed to parse]'
|
||||||
|
@ -1490,6 +1494,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
|
|
||||||
const pending: PendingRpc = {
|
const pending: PendingRpc = {
|
||||||
method,
|
method,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
promise: undefined as any, // because we need the object to make a promise
|
promise: undefined as any, // because we need the object to make a promise
|
||||||
data: content,
|
data: content,
|
||||||
stack,
|
stack,
|
||||||
|
@ -1599,7 +1604,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
try {
|
try {
|
||||||
this._doFlush()
|
this._doFlush()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
this.log.error('flush error: %s', e.stack)
|
this.log.error('flush error: %s', (e as Error).stack)
|
||||||
// should not happen unless there's a bug in the code
|
// should not happen unless there's a bug in the code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1718,7 +1723,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
const idx = this._session.getStateSchedule.index(
|
const idx = this._session.getStateSchedule.index(
|
||||||
{ getState: now } as any,
|
{ getState: now } as PendingRpc,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2035,7 +2040,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
this._session
|
this._session
|
||||||
.encryptMessage(result)
|
.encryptMessage(result)
|
||||||
.then((enc) => this.send(enc))
|
.then((enc) => this.send(enc))
|
||||||
.catch((err) => {
|
.catch((err: Error) => {
|
||||||
this.log.error(
|
this.log.error(
|
||||||
'error while sending pending messages (root msg_id = %l): %s',
|
'error while sending pending messages (root msg_id = %l): %s',
|
||||||
rootMsgId,
|
rootMsgId,
|
||||||
|
|
|
@ -98,8 +98,13 @@ export class ObfuscatedPacketCodec
|
||||||
|
|
||||||
feed(data: Buffer): void {
|
feed(data: Buffer): void {
|
||||||
const dec = this._decryptor!.decrypt(data)
|
const dec = this._decryptor!.decrypt(data)
|
||||||
|
|
||||||
if (Buffer.isBuffer(dec)) this._inner.feed(dec)
|
if (Buffer.isBuffer(dec)) this._inner.feed(dec)
|
||||||
else dec.then((dec) => this._inner.feed(dec))
|
else {
|
||||||
|
dec.then((dec) => this._inner.feed(dec)).catch((err) =>
|
||||||
|
this.emit('error', err),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
|
|
|
@ -96,28 +96,31 @@ export abstract class BaseTcpTransport
|
||||||
this.emit('error', error)
|
this.emit('error', error)
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleConnect(): Promise<void> {
|
handleConnect(): void {
|
||||||
this.log.info('connected')
|
this.log.info('connected')
|
||||||
const initialMessage = await this._packetCodec.tag()
|
|
||||||
|
|
||||||
if (initialMessage.length) {
|
Promise.resolve(this._packetCodec.tag())
|
||||||
this._socket!.write(initialMessage, (err) => {
|
.then((initialMessage) => {
|
||||||
if (err) {
|
if (initialMessage.length) {
|
||||||
this.log.error(
|
this._socket!.write(initialMessage, (err) => {
|
||||||
'failed to write initial message: %s',
|
if (err) {
|
||||||
err.stack,
|
this.log.error(
|
||||||
)
|
'failed to write initial message: %s',
|
||||||
this.emit('error')
|
err.stack,
|
||||||
this.close()
|
)
|
||||||
|
this.emit('error')
|
||||||
|
this.close()
|
||||||
|
} else {
|
||||||
|
this._state = TransportState.Ready
|
||||||
|
this.emit('ready')
|
||||||
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
this._state = TransportState.Ready
|
this._state = TransportState.Ready
|
||||||
this.emit('ready')
|
this.emit('ready')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
.catch((err) => this.emit('error', err))
|
||||||
this._state = TransportState.Ready
|
|
||||||
this.emit('ready')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async send(bytes: Buffer): Promise<void> {
|
async send(bytes: Buffer): Promise<void> {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import EventEmitter from 'events'
|
import EventEmitter from 'events'
|
||||||
import type WebSocket from 'ws'
|
|
||||||
|
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
@ -14,13 +13,13 @@ let ws: {
|
||||||
|
|
||||||
if (typeof window === 'undefined' || typeof window.WebSocket === 'undefined') {
|
if (typeof window === 'undefined' || typeof window.WebSocket === 'undefined') {
|
||||||
try {
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
ws = require('ws')
|
ws = require('ws')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ws = null
|
ws = null
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
ws = window.WebSocket
|
||||||
ws = window.WebSocket as any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const subdomainsMap: Record<string, string> = {
|
const subdomainsMap: Record<string, string> = {
|
||||||
|
@ -93,7 +92,9 @@ export abstract class BaseWebSocketTransport
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(dc: tl.RawDcOption, testMode: boolean): void {
|
connect(dc: tl.RawDcOption, testMode: boolean): void {
|
||||||
if (this._state !== TransportState.Idle) { throw new Error('Transport is not IDLE') }
|
if (this._state !== TransportState.Idle) {
|
||||||
|
throw new Error('Transport is not IDLE')
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.packetCodecInitialized) {
|
if (!this.packetCodecInitialized) {
|
||||||
this._packetCodec.setup?.(this._crypto, this.log)
|
this._packetCodec.setup?.(this._crypto, this.log)
|
||||||
|
@ -117,8 +118,9 @@ export abstract class BaseWebSocketTransport
|
||||||
this._socket.binaryType = 'arraybuffer'
|
this._socket.binaryType = 'arraybuffer'
|
||||||
|
|
||||||
this._socket.addEventListener('message', (evt) =>
|
this._socket.addEventListener('message', (evt) =>
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
this._packetCodec.feed(
|
||||||
this._packetCodec.feed(typedArrayToBuffer(evt.data as any)),
|
typedArrayToBuffer(evt.data as NodeJS.TypedArray),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
this._socket.addEventListener('open', this.handleConnect.bind(this))
|
this._socket.addEventListener('open', this.handleConnect.bind(this))
|
||||||
this._socket.addEventListener('error', this.handleError.bind(this))
|
this._socket.addEventListener('error', this.handleError.bind(this))
|
||||||
|
@ -138,22 +140,32 @@ export abstract class BaseWebSocketTransport
|
||||||
this._packetCodec.reset()
|
this._packetCodec.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleError({ error }: { error: Error }): Promise<void> {
|
handleError(event: Event | { error: Error }): void {
|
||||||
|
const error =
|
||||||
|
'error' in event ?
|
||||||
|
event.error :
|
||||||
|
new Error('unknown WebSocket error')
|
||||||
|
|
||||||
this.log.error('error: %s', error.stack)
|
this.log.error('error: %s', error.stack)
|
||||||
this.emit('error', error)
|
this.emit('error', error)
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleConnect(): Promise<void> {
|
handleConnect(): void {
|
||||||
this.log.info('connected')
|
this.log.info('connected')
|
||||||
const initialMessage = await this._packetCodec.tag()
|
|
||||||
|
|
||||||
this._socket!.send(initialMessage)
|
Promise.resolve(this._packetCodec.tag())
|
||||||
this._state = TransportState.Ready
|
.then((initialMessage) => {
|
||||||
this.emit('ready')
|
this._socket!.send(initialMessage)
|
||||||
|
this._state = TransportState.Ready
|
||||||
|
this.emit('ready')
|
||||||
|
})
|
||||||
|
.catch((err) => this.emit('error', err))
|
||||||
}
|
}
|
||||||
|
|
||||||
async send(bytes: Buffer): Promise<void> {
|
async send(bytes: Buffer): Promise<void> {
|
||||||
if (this._state !== TransportState.Ready) { throw new Error('Transport is not READY') }
|
if (this._state !== TransportState.Ready) {
|
||||||
|
throw new Error('Transport is not READY')
|
||||||
|
}
|
||||||
|
|
||||||
const framed = await this._packetCodec.encode(bytes)
|
const framed = await this._packetCodec.encode(bytes)
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
import type * as exitHookNs from 'exit-hook'
|
||||||
|
import type * as fsNs from 'fs'
|
||||||
|
|
||||||
import { JsonMemoryStorage } from './json'
|
import { JsonMemoryStorage } from './json'
|
||||||
|
|
||||||
let fs: any = null
|
type fs = typeof fsNs
|
||||||
|
let fs: fs | null = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fs = require('fs')
|
fs = require('fs') as fs
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
let exitHook: any = null
|
type exitHook = typeof exitHookNs
|
||||||
|
let exitHook: exitHook | null = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exitHook = require('exit-hook')
|
exitHook = require('exit-hook') as exitHook
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
export class JsonFileStorage extends JsonMemoryStorage {
|
export class JsonFileStorage extends JsonMemoryStorage {
|
||||||
|
@ -47,7 +51,9 @@ export class JsonFileStorage extends JsonMemoryStorage {
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
if (!fs || !fs.readFile) { throw new Error('Node fs module is not available!') }
|
if (!fs || !fs.readFile) {
|
||||||
|
throw new Error('Node fs module is not available!')
|
||||||
|
}
|
||||||
|
|
||||||
this._filename = filename
|
this._filename = filename
|
||||||
this._safe = params?.safe ?? true
|
this._safe = params?.safe ?? true
|
||||||
|
@ -60,7 +66,9 @@ export class JsonFileStorage extends JsonMemoryStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._cleanup) {
|
if (this._cleanup) {
|
||||||
this._unsubscribe = exitHook(this._onProcessExit.bind(this))
|
this._unsubscribe = exitHook!.default(
|
||||||
|
this._onProcessExit.bind(this),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,12 +76,8 @@ export class JsonFileStorage extends JsonMemoryStorage {
|
||||||
try {
|
try {
|
||||||
this._loadJson(
|
this._loadJson(
|
||||||
await new Promise((res, rej) =>
|
await new Promise((res, rej) =>
|
||||||
fs.readFile(
|
fs!.readFile(this._filename, 'utf-8', (err, data) =>
|
||||||
this._filename,
|
err ? rej(err) : res(data),
|
||||||
'utf-8',
|
|
||||||
(err?: Error, data?: string) =>
|
|
||||||
|
|
||||||
err ? rej(err) : res(data!),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -82,16 +86,16 @@ export class JsonFileStorage extends JsonMemoryStorage {
|
||||||
|
|
||||||
save(): Promise<void> {
|
save(): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.writeFile(
|
fs!.writeFile(
|
||||||
this._safe ? this._filename + '.tmp' : this._filename,
|
this._safe ? this._filename + '.tmp' : this._filename,
|
||||||
this._saveJson(),
|
this._saveJson(),
|
||||||
(err?: Error) => {
|
(err) => {
|
||||||
if (err) reject(err)
|
if (err) reject(err)
|
||||||
else if (this._safe) {
|
else if (this._safe) {
|
||||||
fs.rename(
|
fs!.rename(
|
||||||
this._filename + '.tmp',
|
this._filename + '.tmp',
|
||||||
this._filename,
|
this._filename,
|
||||||
(err?: any) => {
|
(err) => {
|
||||||
if (err && err.code !== 'ENOENT') reject(err)
|
if (err && err.code !== 'ENOENT') reject(err)
|
||||||
else resolve()
|
else resolve()
|
||||||
},
|
},
|
||||||
|
@ -106,12 +110,12 @@ export class JsonFileStorage extends JsonMemoryStorage {
|
||||||
// on exit handler must be synchronous, thus we use sync methods here
|
// on exit handler must be synchronous, thus we use sync methods here
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(this._filename, this._saveJson())
|
fs!.writeFileSync(this._filename, this._saveJson())
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
if (this._safe) {
|
if (this._safe) {
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync(this._filename + '.tmp')
|
fs!.unlinkSync(this._filename + '.tmp')
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
import { longFromFastString, longToFastString } from '../utils'
|
import { longFromFastString, longToFastString } from '../utils'
|
||||||
import { MemoryStorage } from './memory'
|
import { MemorySessionState, MemoryStorage } from './memory'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class that provides json serialization functions
|
* Helper class that provides json serialization functions
|
||||||
|
@ -12,7 +15,7 @@ export class JsonMemoryStorage extends MemoryStorage {
|
||||||
if (key === 'authKeys') {
|
if (key === 'authKeys') {
|
||||||
const ret: Record<string, Buffer> = {}
|
const ret: Record<string, Buffer> = {}
|
||||||
|
|
||||||
value.split('|').forEach((pair: string) => {
|
;(value as string).split('|').forEach((pair: string) => {
|
||||||
const [dcId, b64] = pair.split(',')
|
const [dcId, b64] = pair.split(',')
|
||||||
ret[dcId] = Buffer.from(b64, 'base64')
|
ret[dcId] = Buffer.from(b64, 'base64')
|
||||||
})
|
})
|
||||||
|
@ -21,27 +24,26 @@ export class JsonMemoryStorage extends MemoryStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'accessHash') {
|
if (key === 'accessHash') {
|
||||||
return longFromFastString(value)
|
return longFromFastString(value as string)
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
return value
|
||||||
}),
|
}) as MemorySessionState,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _saveJson(): string {
|
protected _saveJson(): string {
|
||||||
return JSON.stringify(this._state, (key, value) => {
|
return JSON.stringify(this._state, (key, value) => {
|
||||||
if (key === 'authKeys') {
|
if (key === 'authKeys') {
|
||||||
return Object.entries(value)
|
const value_ = value as Record<string, Buffer | null>
|
||||||
.filter((it) => it[1] !== null)
|
|
||||||
.map(
|
return Object.entries(value_)
|
||||||
([dcId, key]) =>
|
.filter((it): it is [string, Buffer] => it[1] !== null)
|
||||||
dcId + ',' + (key as Buffer).toString('base64'),
|
.map(([dcId, key]) => dcId + ',' + key.toString('base64'))
|
||||||
)
|
|
||||||
.join('|')
|
.join('|')
|
||||||
}
|
}
|
||||||
if (key === 'accessHash') {
|
if (key === 'accessHash') {
|
||||||
return longToFastString(value)
|
return longToFastString(value as tl.Long)
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
|
@ -15,7 +15,7 @@ export class LocalstorageStorage extends JsonMemoryStorage {
|
||||||
|
|
||||||
load(): void {
|
load(): void {
|
||||||
try {
|
try {
|
||||||
this._loadJson(localStorage[this._key])
|
this._loadJson(localStorage[this._key] as string)
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,7 +210,9 @@ export class MemoryStorage implements ITelegramStorage, IStateStorage {
|
||||||
if (tempIndex !== undefined) {
|
if (tempIndex !== undefined) {
|
||||||
const k = `${dcId}:${tempIndex}`
|
const k = `${dcId}:${tempIndex}`
|
||||||
|
|
||||||
if (Date.now() > (this._state.authKeysTempExpiry[k] ?? 0)) { return null }
|
if (Date.now() > (this._state.authKeysTempExpiry[k] ?? 0)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return this._state.authKeysTemp[k]
|
return this._state.authKeysTemp[k]
|
||||||
}
|
}
|
||||||
|
@ -248,7 +250,9 @@ export class MemoryStorage implements ITelegramStorage, IStateStorage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (peer.username) { this._state.usernameIndex[peer.username.toLowerCase()] = peer.id }
|
if (peer.username) {
|
||||||
|
this._state.usernameIndex[peer.username.toLowerCase()] = peer.id
|
||||||
|
}
|
||||||
if (peer.phone) this._state.phoneIndex[peer.phone] = peer.id
|
if (peer.phone) this._state.phoneIndex[peer.phone] = peer.id
|
||||||
this._state.entities[peer.id] = peer
|
this._state.entities[peer.id] = peer
|
||||||
}
|
}
|
||||||
|
@ -357,7 +361,7 @@ export class MemoryStorage implements ITelegramStorage, IStateStorage {
|
||||||
|
|
||||||
// IStateStorage implementation
|
// IStateStorage implementation
|
||||||
|
|
||||||
getState(key: string): unknown | null {
|
getState(key: string): unknown {
|
||||||
const val = this._state.fsm[key]
|
const val = this._state.fsm[key]
|
||||||
if (!val) return null
|
if (!val) return null
|
||||||
|
|
||||||
|
|
|
@ -35,9 +35,10 @@ export class AsyncLock {
|
||||||
|
|
||||||
return this.acquire()
|
return this.acquire()
|
||||||
.then(() => func())
|
.then(() => func())
|
||||||
.catch((e) => (err = e))
|
.catch((e) => void (err = e))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.release()
|
this.release()
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||||
if (err) throw err
|
if (err) throw err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import type * as forgeNs from 'node-forge'
|
||||||
|
|
||||||
import { MaybeAsync } from '../../types'
|
import { MaybeAsync } from '../../types'
|
||||||
import {
|
import {
|
||||||
BaseCryptoProvider,
|
BaseCryptoProvider,
|
||||||
|
@ -5,11 +7,11 @@ import {
|
||||||
IEncryptionScheme,
|
IEncryptionScheme,
|
||||||
} from './abstract'
|
} from './abstract'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
type forge = typeof forgeNs
|
||||||
let forge: any = null
|
let forge: forge | null = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
forge = require('node-forge')
|
forge = require('node-forge') as forge
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
export class ForgeCryptoProvider
|
export class ForgeCryptoProvider
|
||||||
|
@ -26,14 +28,14 @@ export class ForgeCryptoProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
createAesCtr(key: Buffer, iv: Buffer, encrypt: boolean): IEncryptionScheme {
|
createAesCtr(key: Buffer, iv: Buffer, encrypt: boolean): IEncryptionScheme {
|
||||||
const cipher = forge.cipher[
|
const cipher = forge!.cipher[
|
||||||
encrypt ? 'createCipher' : 'createDecipher'
|
encrypt ? 'createCipher' : 'createDecipher'
|
||||||
]('AES-CTR', key.toString('binary'))
|
]('AES-CTR', key.toString('binary'))
|
||||||
cipher.start({ iv: iv.toString('binary') })
|
cipher.start({ iv: iv.toString('binary') })
|
||||||
|
|
||||||
const update = (data: Buffer): Buffer => {
|
const update = (data: Buffer): Buffer => {
|
||||||
cipher.output.data = ''
|
cipher.output.data = ''
|
||||||
cipher.update(forge.util.createBuffer(data.toString('binary')))
|
cipher.update(forge!.util.createBuffer(data.toString('binary')))
|
||||||
|
|
||||||
return Buffer.from(cipher.output.data, 'binary')
|
return Buffer.from(cipher.output.data, 'binary')
|
||||||
}
|
}
|
||||||
|
@ -49,19 +51,24 @@ export class ForgeCryptoProvider
|
||||||
|
|
||||||
return {
|
return {
|
||||||
encrypt(data: Buffer) {
|
encrypt(data: Buffer) {
|
||||||
const cipher = forge.cipher.createCipher('AES-ECB', keyBuffer)
|
const cipher = forge!.cipher.createCipher('AES-ECB', keyBuffer)
|
||||||
cipher.start({})
|
cipher.start({})
|
||||||
|
// @ts-expect-error wrong types
|
||||||
cipher.mode.pad = cipher.mode.unpad = false
|
cipher.mode.pad = cipher.mode.unpad = false
|
||||||
cipher.update(forge.util.createBuffer(data.toString('binary')))
|
cipher.update(forge!.util.createBuffer(data.toString('binary')))
|
||||||
cipher.finish()
|
cipher.finish()
|
||||||
|
|
||||||
return Buffer.from(cipher.output.data, 'binary')
|
return Buffer.from(cipher.output.data, 'binary')
|
||||||
},
|
},
|
||||||
decrypt(data: Buffer) {
|
decrypt(data: Buffer) {
|
||||||
const cipher = forge.cipher.createDecipher('AES-ECB', keyBuffer)
|
const cipher = forge!.cipher.createDecipher(
|
||||||
|
'AES-ECB',
|
||||||
|
keyBuffer,
|
||||||
|
)
|
||||||
cipher.start({})
|
cipher.start({})
|
||||||
|
// @ts-expect-error wrong types
|
||||||
cipher.mode.pad = cipher.mode.unpad = false
|
cipher.mode.pad = cipher.mode.unpad = false
|
||||||
cipher.update(forge.util.createBuffer(data.toString('binary')))
|
cipher.update(forge!.util.createBuffer(data.toString('binary')))
|
||||||
cipher.finish()
|
cipher.finish()
|
||||||
|
|
||||||
return Buffer.from(cipher.output.data, 'binary')
|
return Buffer.from(cipher.output.data, 'binary')
|
||||||
|
@ -77,12 +84,13 @@ export class ForgeCryptoProvider
|
||||||
algo = 'sha512',
|
algo = 'sha512',
|
||||||
): MaybeAsync<Buffer> {
|
): MaybeAsync<Buffer> {
|
||||||
return new Promise((resolve, reject) =>
|
return new Promise((resolve, reject) =>
|
||||||
forge.pkcs5.pbkdf2(
|
forge!.pkcs5.pbkdf2(
|
||||||
password.toString('binary'),
|
password.toString('binary'),
|
||||||
salt.toString('binary'),
|
salt.toString('binary'),
|
||||||
iterations,
|
iterations,
|
||||||
keylen,
|
keylen,
|
||||||
forge.md[algo].create(),
|
// eslint-disable-next-line
|
||||||
|
(forge!.md as any)[algo].create(),
|
||||||
(err: Error | null, buf: string) =>
|
(err: Error | null, buf: string) =>
|
||||||
err !== null ?
|
err !== null ?
|
||||||
reject(err) :
|
reject(err) :
|
||||||
|
@ -93,7 +101,7 @@ export class ForgeCryptoProvider
|
||||||
|
|
||||||
sha1(data: Buffer): MaybeAsync<Buffer> {
|
sha1(data: Buffer): MaybeAsync<Buffer> {
|
||||||
return Buffer.from(
|
return Buffer.from(
|
||||||
forge.md.sha1.create().update(data.toString('binary')).digest()
|
forge!.md.sha1.create().update(data.toString('binary')).digest()
|
||||||
.data,
|
.data,
|
||||||
'binary',
|
'binary',
|
||||||
)
|
)
|
||||||
|
@ -101,14 +109,14 @@ export class ForgeCryptoProvider
|
||||||
|
|
||||||
sha256(data: Buffer): MaybeAsync<Buffer> {
|
sha256(data: Buffer): MaybeAsync<Buffer> {
|
||||||
return Buffer.from(
|
return Buffer.from(
|
||||||
forge.md.sha256.create().update(data.toString('binary')).digest()
|
forge!.md.sha256.create().update(data.toString('binary')).digest()
|
||||||
.data,
|
.data,
|
||||||
'binary',
|
'binary',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
hmacSha256(data: Buffer, key: Buffer): MaybeAsync<Buffer> {
|
hmacSha256(data: Buffer, key: Buffer): MaybeAsync<Buffer> {
|
||||||
const hmac = forge.hmac.create()
|
const hmac = forge!.hmac.create()
|
||||||
hmac.start('sha256', key.toString('binary'))
|
hmac.start('sha256', key.toString('binary'))
|
||||||
hmac.update(data.toString('binary'))
|
hmac.update(data.toString('binary'))
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,6 @@ import {
|
||||||
export class NodeCryptoProvider
|
export class NodeCryptoProvider
|
||||||
extends BaseCryptoProvider
|
extends BaseCryptoProvider
|
||||||
implements ICryptoProvider {
|
implements ICryptoProvider {
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
createAesCtr(key: Buffer, iv: Buffer, encrypt: boolean): IEncryptionScheme {
|
createAesCtr(key: Buffer, iv: Buffer, encrypt: boolean): IEncryptionScheme {
|
||||||
const cipher = (encrypt ? createCipheriv : createDecipheriv)(
|
const cipher = (encrypt ? createCipheriv : createDecipheriv)(
|
||||||
`aes-${key.length * 8}-ctr`,
|
`aes-${key.length * 8}-ctr`,
|
||||||
|
|
|
@ -81,7 +81,7 @@ export async function computeSrpParams(
|
||||||
// nice naming thx durov
|
// nice naming thx durov
|
||||||
if (
|
if (
|
||||||
!request.currentAlgo ||
|
!request.currentAlgo ||
|
||||||
request.currentAlgo?._ !==
|
request.currentAlgo._ !==
|
||||||
'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow'
|
'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow'
|
||||||
) {
|
) {
|
||||||
throw new Error(`Unknown algo ${request.currentAlgo?._}`)
|
throw new Error(`Unknown algo ${request.currentAlgo?._}`)
|
||||||
|
|
|
@ -51,7 +51,7 @@ export class Deque<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._elements = new Array(capacity)
|
this._elements = new Array<T | undefined>(capacity)
|
||||||
this._capacity = capacity
|
this._capacity = capacity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ export class Deque<T> {
|
||||||
const newCapacity = n << 1
|
const newCapacity = n << 1
|
||||||
if (newCapacity < 0) throw new Error('Deque is too big')
|
if (newCapacity < 0) throw new Error('Deque is too big')
|
||||||
|
|
||||||
const arr = new Array(newCapacity)
|
const arr = new Array<T | undefined>(newCapacity)
|
||||||
|
|
||||||
// copy items to the new array
|
// copy items to the new array
|
||||||
// copy head till the end of arr
|
// copy head till the end of arr
|
||||||
|
@ -251,7 +251,7 @@ export class Deque<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
clear(): void {
|
clear(): void {
|
||||||
this._elements = new Array(this._capacity)
|
this._elements = new Array<T | undefined>(this._capacity)
|
||||||
this._head = this._tail = 0
|
this._head = this._tail = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ if (typeof process !== 'undefined') {
|
||||||
defaultLogLevel = envLogLevel
|
defaultLogLevel = envLogLevel
|
||||||
}
|
}
|
||||||
} else if (typeof localStorage !== 'undefined') {
|
} else if (typeof localStorage !== 'undefined') {
|
||||||
const localLogLevel = parseInt(localStorage.MTCUTE_LOG_LEVEL)
|
const localLogLevel = parseInt(localStorage.MTCUTE_LOG_LEVEL as string)
|
||||||
|
|
||||||
if (!isNaN(localLogLevel)) {
|
if (!isNaN(localLogLevel)) {
|
||||||
defaultLogLevel = localLogLevel
|
defaultLogLevel = localLogLevel
|
||||||
|
@ -62,10 +62,10 @@ export class Logger {
|
||||||
|
|
||||||
// custom formatters
|
// custom formatters
|
||||||
if (
|
if (
|
||||||
fmt.indexOf('%h') > -1 ||
|
fmt.includes('%h') ||
|
||||||
fmt.indexOf('%b') > -1 ||
|
fmt.includes('%b') ||
|
||||||
fmt.indexOf('%j') > -1 ||
|
fmt.includes('%j') ||
|
||||||
fmt.indexOf('%l') > -1
|
fmt.includes('%l')
|
||||||
) {
|
) {
|
||||||
let idx = 0
|
let idx = 0
|
||||||
fmt = fmt.replace(FORMATTER_RE, (m) => {
|
fmt = fmt.replace(FORMATTER_RE, (m) => {
|
||||||
|
@ -89,7 +89,9 @@ export class Logger {
|
||||||
v.type === 'Buffer' &&
|
v.type === 'Buffer' &&
|
||||||
Array.isArray(v.data)
|
Array.isArray(v.data)
|
||||||
) {
|
) {
|
||||||
let str = Buffer.from(v.data).toString('base64')
|
let str = Buffer.from(
|
||||||
|
v.data as number[],
|
||||||
|
).toString('base64')
|
||||||
|
|
||||||
if (str.length > 300) {
|
if (str.length > 300) {
|
||||||
str = str.slice(0, 300) + '...'
|
str = str.slice(0, 300) + '...'
|
||||||
|
@ -98,6 +100,7 @@ export class Logger {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
return v
|
return v
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -152,7 +155,7 @@ export class LogManager extends Logger {
|
||||||
|
|
||||||
constructor(tag = 'base') {
|
constructor(tag = 'base') {
|
||||||
// workaround because we cant pass this to super
|
// workaround because we cant pass this to super
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-argument
|
||||||
super(null as any, tag)
|
super(null as any, tag)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
;(this as any).mgr = this
|
;(this as any).mgr = this
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-return */
|
||||||
// ^^ because of performance reasons
|
// ^^ because of performance reasons
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
|
|
||||||
|
@ -84,7 +85,9 @@ export function longFromFastString(val: string, unsigned = false): Long {
|
||||||
const low = parseInt(parts[0])
|
const low = parseInt(parts[0])
|
||||||
const high = parseInt(parts[1])
|
const high = parseInt(parts[1])
|
||||||
|
|
||||||
if (isNaN(low) || isNaN(high)) { throw new Error(`Invalid long fast string: ${val}`) }
|
if (isNaN(low) || isNaN(high)) {
|
||||||
|
throw new Error(`Invalid long fast string: ${val}`)
|
||||||
|
}
|
||||||
|
|
||||||
return new Long(low, high, unsigned)
|
return new Long(low, high, unsigned)
|
||||||
}
|
}
|
||||||
|
@ -166,19 +169,19 @@ export class LongMap<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setForObj(key: Long, value: V): void {
|
private _setForObj(key: Long, value: V): void {
|
||||||
this._obj![longToFastString(key)] = value
|
this._obj[longToFastString(key)] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
private _hasForObj(key: Long): boolean {
|
private _hasForObj(key: Long): boolean {
|
||||||
return longToFastString(key) in this._obj!
|
return longToFastString(key) in this._obj
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getForObj(key: Long): V | undefined {
|
private _getForObj(key: Long): V | undefined {
|
||||||
return this._obj![longToFastString(key)]
|
return this._obj[longToFastString(key)]
|
||||||
}
|
}
|
||||||
|
|
||||||
private _deleteForObj(key: Long): void {
|
private _deleteForObj(key: Long): void {
|
||||||
delete this._obj![longToFastString(key)]
|
delete this._obj[longToFastString(key)]
|
||||||
}
|
}
|
||||||
|
|
||||||
private *_keysForObj(unsigned?: boolean): IterableIterator<Long> {
|
private *_keysForObj(unsigned?: boolean): IterableIterator<Long> {
|
||||||
|
@ -188,7 +191,7 @@ export class LongMap<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private *_valuesForObj(): IterableIterator<V> {
|
private *_valuesForObj(): IterableIterator<V> {
|
||||||
yield* Object.values(this._obj!) as any
|
yield* Object.values(this._obj) as any
|
||||||
}
|
}
|
||||||
|
|
||||||
private _clearForObj(): void {
|
private _clearForObj(): void {
|
||||||
|
@ -254,22 +257,22 @@ export class LongSet {
|
||||||
|
|
||||||
private _addForObj(val: Long) {
|
private _addForObj(val: Long) {
|
||||||
const k = longToFastString(val)
|
const k = longToFastString(val)
|
||||||
if (k in this._obj!) return
|
if (k in this._obj) return
|
||||||
|
|
||||||
this._obj![k] = true
|
this._obj[k] = true
|
||||||
this._objSize! += 1
|
this._objSize! += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
private _deleteForObj(val: Long) {
|
private _deleteForObj(val: Long) {
|
||||||
const k = longToFastString(val)
|
const k = longToFastString(val)
|
||||||
if (!(k in this._obj!)) return
|
if (!(k in this._obj)) return
|
||||||
|
|
||||||
delete this._obj![k]
|
delete this._obj[k]
|
||||||
this._objSize! -= 1
|
this._objSize! -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
private _hasForObj(val: Long) {
|
private _hasForObj(val: Long) {
|
||||||
return longToFastString(val) in this._obj!
|
return longToFastString(val) in this._obj
|
||||||
}
|
}
|
||||||
|
|
||||||
private _clearForObj() {
|
private _clearForObj() {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-return */
|
||||||
// ^^ because of performance reasons
|
// ^^ because of performance reasons
|
||||||
import { LongMap } from './long-utils'
|
import { LongMap } from './long-utils'
|
||||||
|
|
||||||
|
@ -130,7 +131,7 @@ export class LruMap<K extends string | number, V> {
|
||||||
if (oldest) {
|
if (oldest) {
|
||||||
if (oldest.p) {
|
if (oldest.p) {
|
||||||
this._last = oldest.p
|
this._last = oldest.p
|
||||||
this._last!.n = undefined
|
this._last.n = undefined
|
||||||
} else {
|
} else {
|
||||||
// exhausted
|
// exhausted
|
||||||
this._last = undefined
|
this._last = undefined
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||||
// ^^ because of performance reasons
|
// ^^ because of performance reasons
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ describe('IntermediatePacketCodec', () => {
|
||||||
|
|
||||||
it('should correctly parse immediate framing', (done) => {
|
it('should correctly parse immediate framing', (done) => {
|
||||||
const codec = new IntermediatePacketCodec()
|
const codec = new IntermediatePacketCodec()
|
||||||
codec.on('packet', (data) => {
|
codec.on('packet', (data: Buffer) => {
|
||||||
expect([...data]).eql([5, 1, 2, 3, 4])
|
expect([...data]).eql([5, 1, 2, 3, 4])
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
|
@ -21,7 +21,7 @@ describe('IntermediatePacketCodec', () => {
|
||||||
|
|
||||||
it('should correctly parse incomplete framing', (done) => {
|
it('should correctly parse incomplete framing', (done) => {
|
||||||
const codec = new IntermediatePacketCodec()
|
const codec = new IntermediatePacketCodec()
|
||||||
codec.on('packet', (data) => {
|
codec.on('packet', (data: Buffer) => {
|
||||||
expect([...data]).eql([5, 1, 2, 3, 4])
|
expect([...data]).eql([5, 1, 2, 3, 4])
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
|
@ -34,7 +34,7 @@ describe('IntermediatePacketCodec', () => {
|
||||||
|
|
||||||
let number = 0
|
let number = 0
|
||||||
|
|
||||||
codec.on('packet', (data) => {
|
codec.on('packet', (data: Buffer) => {
|
||||||
if (number === 0) {
|
if (number === 0) {
|
||||||
expect([...data]).eql([5, 1, 2, 3, 4])
|
expect([...data]).eql([5, 1, 2, 3, 4])
|
||||||
number = 1
|
number = 1
|
||||||
|
@ -51,7 +51,7 @@ describe('IntermediatePacketCodec', () => {
|
||||||
it('should correctly parse transport errors', (done) => {
|
it('should correctly parse transport errors', (done) => {
|
||||||
const codec = new IntermediatePacketCodec()
|
const codec = new IntermediatePacketCodec()
|
||||||
|
|
||||||
codec.on('error', (err) => {
|
codec.on('error', (err: TransportError) => {
|
||||||
expect(err).to.have.instanceOf(TransportError)
|
expect(err).to.have.instanceOf(TransportError)
|
||||||
expect(err.code).eq(404)
|
expect(err.code).eq(404)
|
||||||
done()
|
done()
|
||||||
|
@ -63,7 +63,7 @@ describe('IntermediatePacketCodec', () => {
|
||||||
it('should reset when called reset()', (done) => {
|
it('should reset when called reset()', (done) => {
|
||||||
const codec = new IntermediatePacketCodec()
|
const codec = new IntermediatePacketCodec()
|
||||||
|
|
||||||
codec.on('packet', (data) => {
|
codec.on('packet', (data: Buffer) => {
|
||||||
expect([...data]).eql([1, 2, 3, 4, 5])
|
expect([...data]).eql([1, 2, 3, 4, 5])
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src",
|
||||||
|
"./tests"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ export class CallbackDataBuilder<T extends string> {
|
||||||
.map((f) => {
|
.map((f) => {
|
||||||
const val = obj[f]
|
const val = obj[f]
|
||||||
|
|
||||||
if (val.indexOf(this.sep) > -1) {
|
if (val.includes(this.sep)) {
|
||||||
throw new MtArgumentError(
|
throw new MtArgumentError(
|
||||||
`Value for ${f} ${val} contains separator ${this.sep} and cannot be used.`,
|
`Value for ${f} ${val} contains separator ${this.sep} and cannot be used.`,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* eslint-disable max-depth */
|
/* eslint-disable */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types */
|
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types */
|
||||||
// ^^ will be looked into in MTQ-29
|
// ^^ will be looked into in MTQ-29
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
constructor(
|
constructor(
|
||||||
client?: TelegramClient | IStateStorage | StateKeyDelegate,
|
client?: TelegramClient | IStateStorage | StateKeyDelegate,
|
||||||
storage?: IStateStorage | StateKeyDelegate,
|
storage?: IStateStorage | StateKeyDelegate,
|
||||||
key?: StateKeyDelegate,
|
key?: StateKeyDelegate
|
||||||
) {
|
) {
|
||||||
this.dispatchRawUpdate = this.dispatchRawUpdate.bind(this)
|
this.dispatchRawUpdate = this.dispatchRawUpdate.bind(this)
|
||||||
this.dispatchUpdate = this.dispatchUpdate.bind(this)
|
this.dispatchUpdate = this.dispatchUpdate.bind(this)
|
||||||
|
@ -196,15 +196,14 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
*/
|
*/
|
||||||
dispatchRawUpdate(
|
dispatchRawUpdate(
|
||||||
update: tl.TypeUpdate | tl.TypeMessage,
|
update: tl.TypeUpdate | tl.TypeMessage,
|
||||||
peers: PeersIndex,
|
peers: PeersIndex
|
||||||
): void {
|
): void {
|
||||||
if (!this._client) return
|
if (!this._client) return
|
||||||
|
|
||||||
// order does not matter in the dispatcher,
|
// order does not matter in the dispatcher,
|
||||||
// so we can handle each update in its own task
|
// so we can handle each update in its own task
|
||||||
this.dispatchRawUpdateNow(update, peers).catch((err) =>
|
this.dispatchRawUpdateNow(update, peers).catch((err) =>
|
||||||
// eslint-disable-next-line dot-notation
|
this._client!['_emitError'](err)
|
||||||
this._client!['_emitError'](err),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +221,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
*/
|
*/
|
||||||
async dispatchRawUpdateNow(
|
async dispatchRawUpdateNow(
|
||||||
update: tl.TypeUpdate | tl.TypeMessage,
|
update: tl.TypeUpdate | tl.TypeMessage,
|
||||||
peers: PeersIndex,
|
peers: PeersIndex
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if (!this._client) return false
|
if (!this._client) return false
|
||||||
|
|
||||||
|
@ -282,8 +281,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
// order does not matter in the dispatcher,
|
// order does not matter in the dispatcher,
|
||||||
// so we can handle each update in its own task
|
// so we can handle each update in its own task
|
||||||
this.dispatchUpdateNow(update).catch((err) =>
|
this.dispatchUpdateNow(update).catch((err) =>
|
||||||
// eslint-disable-next-line dot-notation
|
this._client!['_emitError'](err)
|
||||||
this._client!['_emitError'](err),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,7 +305,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
// this is getting a bit crazy lol
|
// this is getting a bit crazy lol
|
||||||
parsedState?: UpdateState<State, SceneName> | null,
|
parsedState?: UpdateState<State, SceneName> | null,
|
||||||
parsedScene?: string | null,
|
parsedScene?: string | null,
|
||||||
forceScene?: true,
|
forceScene?: true
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if (!this._client) return false
|
if (!this._client) return false
|
||||||
|
|
||||||
|
@ -349,7 +347,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
update,
|
update,
|
||||||
parsedState,
|
parsedState,
|
||||||
parsedScene,
|
parsedScene,
|
||||||
true,
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -369,17 +367,16 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
if (
|
if (
|
||||||
!this._customStateKeyDelegate ||
|
!this._customStateKeyDelegate ||
|
||||||
(customKey = await this._customStateKeyDelegate(
|
(customKey = await this._customStateKeyDelegate(
|
||||||
update.data,
|
update.data
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
parsedState = new UpdateState(
|
parsedState = new UpdateState(
|
||||||
|
this._storage,
|
||||||
this._storage!,
|
|
||||||
key,
|
key,
|
||||||
this._scene ?? null,
|
this._scene ?? null,
|
||||||
this._sceneScoped,
|
this._sceneScoped,
|
||||||
this._customStorage,
|
this._customStorage,
|
||||||
customKey,
|
customKey
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -421,12 +418,12 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
!h.check ||
|
!h.check ||
|
||||||
(await h.check(
|
(await h.check(
|
||||||
update.data as any,
|
update.data as any,
|
||||||
parsedState as never,
|
parsedState as never
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
result = await h.callback(
|
result = await h.callback(
|
||||||
update.data as any,
|
update.data as any,
|
||||||
parsedState as never,
|
parsedState as never
|
||||||
)
|
)
|
||||||
handled = true
|
handled = true
|
||||||
} else continue
|
} else continue
|
||||||
|
@ -443,16 +440,15 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
case 'scene': {
|
case 'scene': {
|
||||||
if (!parsedState) {
|
if (!parsedState) {
|
||||||
throw new MtArgumentError(
|
throw new MtArgumentError(
|
||||||
'Cannot use ToScene without state',
|
'Cannot use ToScene without state'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line dot-notation
|
|
||||||
const scene = parsedState['_scene']
|
const scene = parsedState['_scene']
|
||||||
|
|
||||||
if (!scene) {
|
if (!scene) {
|
||||||
throw new MtArgumentError(
|
throw new MtArgumentError(
|
||||||
'Cannot use ToScene without entering a scene',
|
'Cannot use ToScene without entering a scene'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +458,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
update,
|
update,
|
||||||
undefined,
|
undefined,
|
||||||
scene,
|
scene,
|
||||||
true,
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -474,7 +470,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
const handled = await this._errorHandler(
|
const handled = await this._errorHandler(
|
||||||
e,
|
e,
|
||||||
update,
|
update,
|
||||||
parsedState as never,
|
parsedState as never
|
||||||
)
|
)
|
||||||
if (!handled) throw e
|
if (!handled) throw e
|
||||||
} else {
|
} else {
|
||||||
|
@ -527,7 +523,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
*/
|
*/
|
||||||
removeUpdateHandler(
|
removeUpdateHandler(
|
||||||
handler: UpdateHandler | UpdateHandler['name'] | 'all',
|
handler: UpdateHandler | UpdateHandler['name'] | 'all',
|
||||||
group = 0,
|
group = 0
|
||||||
): void {
|
): void {
|
||||||
if (group !== -1 && !(group in this._groups)) {
|
if (group !== -1 && !(group in this._groups)) {
|
||||||
return
|
return
|
||||||
|
@ -581,7 +577,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
update: ParsedUpdate & T,
|
update: ParsedUpdate & T,
|
||||||
state?: UpdateState<State, SceneName>
|
state?: UpdateState<State, SceneName>
|
||||||
) => MaybeAsync<boolean>)
|
) => MaybeAsync<boolean>)
|
||||||
| null,
|
| null
|
||||||
): void {
|
): void {
|
||||||
if (handler) this._errorHandler = handler
|
if (handler) this._errorHandler = handler
|
||||||
else this._errorHandler = undefined
|
else this._errorHandler = undefined
|
||||||
|
@ -605,7 +601,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
update: ParsedUpdate & T,
|
update: ParsedUpdate & T,
|
||||||
state?: UpdateState<State, SceneName>
|
state?: UpdateState<State, SceneName>
|
||||||
) => MaybeAsync<PropagationAction | void>)
|
) => MaybeAsync<PropagationAction | void>)
|
||||||
| null,
|
| null
|
||||||
): void {
|
): void {
|
||||||
if (handler) this._preUpdateHandler = handler
|
if (handler) this._preUpdateHandler = handler
|
||||||
else this._preUpdateHandler = undefined
|
else this._preUpdateHandler = undefined
|
||||||
|
@ -630,7 +626,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
update: ParsedUpdate & T,
|
update: ParsedUpdate & T,
|
||||||
state?: UpdateState<State, SceneName>
|
state?: UpdateState<State, SceneName>
|
||||||
) => MaybeAsync<void>)
|
) => MaybeAsync<void>)
|
||||||
| null,
|
| null
|
||||||
): void {
|
): void {
|
||||||
if (handler) this._postUpdateHandler = handler
|
if (handler) this._postUpdateHandler = handler
|
||||||
else this._postUpdateHandler = undefined
|
else this._postUpdateHandler = undefined
|
||||||
|
@ -643,9 +639,11 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
propagateErrorToParent(
|
propagateErrorToParent(
|
||||||
err: Error,
|
err: Error,
|
||||||
update: ParsedUpdate,
|
update: ParsedUpdate,
|
||||||
state?: UpdateState<State, SceneName>,
|
state?: UpdateState<State, SceneName>
|
||||||
): MaybeAsync<boolean> {
|
): MaybeAsync<boolean> {
|
||||||
if (!this.parent) { throw new MtArgumentError('This dispatcher is not a child') }
|
if (!this.parent) {
|
||||||
|
throw new MtArgumentError('This dispatcher is not a child')
|
||||||
|
}
|
||||||
|
|
||||||
if (this.parent._errorHandler) {
|
if (this.parent._errorHandler) {
|
||||||
return this.parent._errorHandler(err, update, state)
|
return this.parent._errorHandler(err, update, state)
|
||||||
|
@ -667,9 +665,9 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
if (child._client) {
|
if (child._client) {
|
||||||
throw new MtArgumentError(
|
throw new MtArgumentError(
|
||||||
'Provided dispatcher is ' +
|
'Provided dispatcher is ' +
|
||||||
(child._parent ?
|
(child._parent
|
||||||
'already a child. Use parent.removeChild() before calling addChild()' :
|
? 'already a child. Use parent.removeChild() before calling addChild()'
|
||||||
'already bound to a client. Use unbind() before calling addChild()'),
|
: 'already bound to a client. Use unbind() before calling addChild()')
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -695,7 +693,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param child Other dispatcher
|
* @param child Other dispatcher
|
||||||
*/
|
*/
|
||||||
addChild(child: Dispatcher<State, SceneName>): void {
|
addChild(child: Dispatcher<State, SceneName>): void {
|
||||||
if (this._children.indexOf(child) > -1) return
|
if (this._children.includes(child)) return
|
||||||
|
|
||||||
this._prepareChild(child)
|
this._prepareChild(child)
|
||||||
this._children.push(child)
|
this._children.push(child)
|
||||||
|
@ -740,13 +738,13 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
addScene(
|
addScene(
|
||||||
uid: SceneName,
|
uid: SceneName,
|
||||||
scene: Dispatcher<any, SceneName>,
|
scene: Dispatcher<any, SceneName>,
|
||||||
scoped = true,
|
scoped = true
|
||||||
): void {
|
): void {
|
||||||
if (!this._scenes) this._scenes = {}
|
if (!this._scenes) this._scenes = {}
|
||||||
|
|
||||||
if (uid in this._scenes) {
|
if (uid in this._scenes) {
|
||||||
throw new MtArgumentError(
|
throw new MtArgumentError(
|
||||||
`Scene with UID ${uid} is already registered!`,
|
`Scene with UID ${uid} is already registered!`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -756,7 +754,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
|
|
||||||
if (scene._scene) {
|
if (scene._scene) {
|
||||||
throw new MtArgumentError(
|
throw new MtArgumentError(
|
||||||
`This dispatcher is already registered as scene ${scene._scene}`,
|
`This dispatcher is already registered as scene ${scene._scene}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -807,7 +805,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
extend(other: Dispatcher<State, SceneName>): void {
|
extend(other: Dispatcher<State, SceneName>): void {
|
||||||
if (other._customStorage || other._customStateKeyDelegate) {
|
if (other._customStorage || other._customStateKeyDelegate) {
|
||||||
throw new MtArgumentError(
|
throw new MtArgumentError(
|
||||||
'Provided dispatcher has custom storage and cannot be extended from.',
|
'Provided dispatcher has custom storage and cannot be extended from.'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -849,7 +847,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
this.addScene(
|
this.addScene(
|
||||||
key as any,
|
key as any,
|
||||||
myScenes[key] as any,
|
myScenes[key] as any,
|
||||||
myScenes[key]._sceneScoped as any,
|
myScenes[key]._sceneScoped as any
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -903,7 +901,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
key as any,
|
key as any,
|
||||||
scene as any,
|
scene as any,
|
||||||
|
|
||||||
this._scenes![key]._sceneScoped as any,
|
this._scenes![key]._sceneScoped as any
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -937,22 +935,21 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
object: Parameters<StateKeyDelegate>[0]
|
object: Parameters<StateKeyDelegate>[0]
|
||||||
): Promise<UpdateState<S, SceneName>>
|
): Promise<UpdateState<S, SceneName>>
|
||||||
getState<S = State>(
|
getState<S = State>(
|
||||||
object: string | Parameters<StateKeyDelegate>[0],
|
object: string | Parameters<StateKeyDelegate>[0]
|
||||||
): MaybeAsync<UpdateState<S, SceneName>> {
|
): MaybeAsync<UpdateState<S, SceneName>> {
|
||||||
if (!this._storage) {
|
if (!this._storage) {
|
||||||
throw new MtArgumentError(
|
throw new MtArgumentError(
|
||||||
'Cannot use getUpdateState() filter without state storage',
|
'Cannot use getUpdateState() filter without state storage'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof object === 'string') {
|
if (typeof object === 'string') {
|
||||||
return new UpdateState(
|
return new UpdateState(
|
||||||
|
this._storage,
|
||||||
this._storage!,
|
|
||||||
object,
|
object,
|
||||||
this._scene ?? null,
|
this._scene ?? null,
|
||||||
this._sceneScoped,
|
this._sceneScoped,
|
||||||
this._customStorage,
|
this._customStorage
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -963,12 +960,11 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
|
|
||||||
if (!this._customStateKeyDelegate) {
|
if (!this._customStateKeyDelegate) {
|
||||||
return new UpdateState(
|
return new UpdateState(
|
||||||
|
|
||||||
this._storage!,
|
this._storage!,
|
||||||
key,
|
key,
|
||||||
this._scene ?? null,
|
this._scene ?? null,
|
||||||
this._sceneScoped,
|
this._sceneScoped,
|
||||||
this._customStorage,
|
this._customStorage
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -976,20 +972,19 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
(customKey) => {
|
(customKey) => {
|
||||||
if (!customKey) {
|
if (!customKey) {
|
||||||
throw new MtArgumentError(
|
throw new MtArgumentError(
|
||||||
'Cannot derive custom key from given object',
|
'Cannot derive custom key from given object'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return new UpdateState(
|
return new UpdateState(
|
||||||
|
|
||||||
this._storage!,
|
this._storage!,
|
||||||
key,
|
key,
|
||||||
this._scene ?? null,
|
this._scene ?? null,
|
||||||
this._sceneScoped,
|
this._sceneScoped,
|
||||||
this._customStorage,
|
this._customStorage,
|
||||||
customKey,
|
customKey
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1001,7 +996,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* ignoring local custom storage, key delegate and scene scope.
|
* ignoring local custom storage, key delegate and scene scope.
|
||||||
*/
|
*/
|
||||||
getGlobalState<T>(
|
getGlobalState<T>(
|
||||||
object: Parameters<StateKeyDelegate>[0],
|
object: Parameters<StateKeyDelegate>[0]
|
||||||
): Promise<UpdateState<T, SceneName>> {
|
): Promise<UpdateState<T, SceneName>> {
|
||||||
if (!this._parent) {
|
if (!this._parent) {
|
||||||
throw new MtArgumentError('This dispatcher does not have a parent')
|
throw new MtArgumentError('This dispatcher does not have a parent')
|
||||||
|
@ -1013,11 +1008,10 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new UpdateState(
|
return new UpdateState(
|
||||||
|
|
||||||
this._storage!,
|
this._storage!,
|
||||||
key,
|
key,
|
||||||
this._scene ?? null,
|
this._scene ?? null,
|
||||||
false,
|
false
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1028,7 +1022,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
name: UpdateHandler['name'],
|
name: UpdateHandler['name'],
|
||||||
filter: any,
|
filter: any,
|
||||||
handler?: any,
|
handler?: any,
|
||||||
group?: number,
|
group?: number
|
||||||
): void {
|
): void {
|
||||||
if (typeof handler === 'number' || typeof handler === 'undefined') {
|
if (typeof handler === 'number' || typeof handler === 'undefined') {
|
||||||
this.addUpdateHandler(
|
this.addUpdateHandler(
|
||||||
|
@ -1036,7 +1030,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
name,
|
name,
|
||||||
callback: filter,
|
callback: filter,
|
||||||
},
|
},
|
||||||
handler,
|
handler
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
this.addUpdateHandler(
|
this.addUpdateHandler(
|
||||||
|
@ -1045,7 +1039,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
callback: handler,
|
callback: handler,
|
||||||
check: filter,
|
check: filter,
|
||||||
},
|
},
|
||||||
group,
|
group
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,18 +209,18 @@ export namespace filters {
|
||||||
fn2: UpdateFilter<Base, Mod2, State2>,
|
fn2: UpdateFilter<Base, Mod2, State2>,
|
||||||
): UpdateFilter<Base, Mod1 & Mod2, State1 | State2> {
|
): UpdateFilter<Base, Mod1 & Mod2, State1 | State2> {
|
||||||
return (upd, state) => {
|
return (upd, state) => {
|
||||||
const res1 = fn1(upd, state as any)
|
const res1 = fn1(upd, state as UpdateState<State1>)
|
||||||
|
|
||||||
if (typeof res1 === 'boolean') {
|
if (typeof res1 === 'boolean') {
|
||||||
if (!res1) return false
|
if (!res1) return false
|
||||||
|
|
||||||
return fn2(upd, state as any)
|
return fn2(upd, state as UpdateState<State2>)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res1.then((r1) => {
|
return res1.then((r1) => {
|
||||||
if (!r1) return false
|
if (!r1) return false
|
||||||
|
|
||||||
return fn2(upd, state as any)
|
return fn2(upd, state as UpdateState<State2>)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,18 +247,18 @@ export namespace filters {
|
||||||
fn2: UpdateFilter<Base, Mod2, State2>,
|
fn2: UpdateFilter<Base, Mod2, State2>,
|
||||||
): UpdateFilter<Base, Mod1 | Mod2, State1 | State2> {
|
): UpdateFilter<Base, Mod1 | Mod2, State1 | State2> {
|
||||||
return (upd, state) => {
|
return (upd, state) => {
|
||||||
const res1 = fn1(upd, state as any)
|
const res1 = fn1(upd, state as UpdateState<State1>)
|
||||||
|
|
||||||
if (typeof res1 === 'boolean') {
|
if (typeof res1 === 'boolean') {
|
||||||
if (res1) return true
|
if (res1) return true
|
||||||
|
|
||||||
return fn2(upd, state as any)
|
return fn2(upd, state as UpdateState<State2>)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res1.then((r1) => {
|
return res1.then((r1) => {
|
||||||
if (r1) return true
|
if (r1) return true
|
||||||
|
|
||||||
return fn2(upd, state as any)
|
return fn2(upd, state as UpdateState<State2>)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -947,7 +947,7 @@ export namespace filters {
|
||||||
const m = txt.match(regex)
|
const m = txt.match(regex)
|
||||||
|
|
||||||
if (m) {
|
if (m) {
|
||||||
(obj as any).match = m
|
(obj as typeof obj & { match: RegExpMatchArray }).match = m
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1002,14 +1002,14 @@ export namespace filters {
|
||||||
return (obj) => {
|
return (obj) => {
|
||||||
const txt = extractText(obj)
|
const txt = extractText(obj)
|
||||||
|
|
||||||
return txt != null && txt.toLowerCase().indexOf(str) > -1
|
return txt != null && txt.toLowerCase().includes(str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (obj) => {
|
return (obj) => {
|
||||||
const txt = extractText(obj)
|
const txt = extractText(obj)
|
||||||
|
|
||||||
return txt != null && txt.indexOf(str) > -1
|
return txt != null && txt.includes(str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1157,13 +1157,16 @@ export namespace filters {
|
||||||
// we use .replace to iterate over global regex, not to replace the text
|
// we use .replace to iterate over global regex, not to replace the text
|
||||||
withoutPrefix
|
withoutPrefix
|
||||||
.slice(m[0].length)
|
.slice(m[0].length)
|
||||||
.replace(argumentsRe, ($0, $1, $2, $3) => {
|
.replace(
|
||||||
match.push(
|
argumentsRe,
|
||||||
($2 || $3 || '').replace(unescapeRe, '$1'),
|
($0, $1, $2: string, $3: string) => {
|
||||||
)
|
match.push(
|
||||||
|
($2 || $3 || '').replace(unescapeRe, '$1'),
|
||||||
|
)
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
})
|
},
|
||||||
|
)
|
||||||
;(msg as Message & { command: string[] }).command = match
|
;(msg as Message & { command: string[] }).command = match
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -18,7 +18,7 @@ export interface IStateStorage {
|
||||||
*
|
*
|
||||||
* @param key Key of the state, as defined by {@link StateKeyDelegate}
|
* @param key Key of the state, as defined by {@link StateKeyDelegate}
|
||||||
*/
|
*/
|
||||||
getState(key: string): MaybeAsync<unknown | null>
|
getState(key: string): MaybeAsync<unknown>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save state to the storage
|
* Save state to the storage
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src",
|
||||||
|
"./tests"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src",
|
||||||
|
"./tests",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,14 @@ export function html(
|
||||||
if (typeof it === 'boolean' || !it) return
|
if (typeof it === 'boolean' || !it) return
|
||||||
|
|
||||||
if (typeof it === 'string') {
|
if (typeof it === 'string') {
|
||||||
it = HtmlMessageEntityParser.escape(it, Boolean(str.match(/=['"]$/)))
|
it = HtmlMessageEntityParser.escape(
|
||||||
|
it,
|
||||||
|
Boolean(str.match(/=['"]$/)),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
if (it.mode && it.mode !== 'html') { throw new Error(`Incompatible parse mode: ${it.mode}`) }
|
if (it.mode && it.mode !== 'html') {
|
||||||
|
throw new Error(`Incompatible parse mode: ${it.mode}`)
|
||||||
|
}
|
||||||
it = it.value
|
it = it.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +97,7 @@ export class HtmlMessageEntityParser implements IMessageEntityParser {
|
||||||
function processPendingText(tagEnd = false) {
|
function processPendingText(tagEnd = false) {
|
||||||
if (!pendingText.length) return
|
if (!pendingText.length) return
|
||||||
|
|
||||||
if (!stacks.pre?.length) {
|
if (!stacks.pre.length) {
|
||||||
pendingText = pendingText.replace(/[^\S\u00A0]+/gs, ' ')
|
pendingText = pendingText.replace(/[^\S\u00A0]+/gs, ' ')
|
||||||
|
|
||||||
if (tagEnd) pendingText = pendingText.trimEnd()
|
if (tagEnd) pendingText = pendingText.trimEnd()
|
||||||
|
@ -119,7 +124,7 @@ export class HtmlMessageEntityParser implements IMessageEntityParser {
|
||||||
processPendingText()
|
processPendingText()
|
||||||
|
|
||||||
// ignore tags inside pre (except pre)
|
// ignore tags inside pre (except pre)
|
||||||
if (name !== 'pre' && stacks.pre?.length) return
|
if (name !== 'pre' && stacks.pre.length) return
|
||||||
|
|
||||||
let entity: tl.TypeMessageEntity
|
let entity: tl.TypeMessageEntity
|
||||||
|
|
||||||
|
@ -262,9 +267,9 @@ export class HtmlMessageEntityParser implements IMessageEntityParser {
|
||||||
name = name.toLowerCase()
|
name = name.toLowerCase()
|
||||||
|
|
||||||
// ignore tags inside pre (except pre)
|
// ignore tags inside pre (except pre)
|
||||||
if (name !== 'pre' && stacks.pre?.length) return
|
if (name !== 'pre' && stacks.pre.length) return
|
||||||
|
|
||||||
const entity = stacks[name]?.pop()
|
const entity = stacks[name].pop()
|
||||||
|
|
||||||
if (!entity) return // unmatched close tag
|
if (!entity) return // unmatched close tag
|
||||||
|
|
||||||
|
@ -338,7 +343,9 @@ export class HtmlMessageEntityParser implements IMessageEntityParser {
|
||||||
relativeOffset = lastOffset
|
relativeOffset = lastOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length <= 0 || relativeOffset >= end || relativeOffset < 0) { continue }
|
if (length <= 0 || relativeOffset >= end || relativeOffset < 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
let skip = false
|
let skip = false
|
||||||
|
|
||||||
|
|
|
@ -620,7 +620,7 @@ describe('HtmlMessageEntityParser', () => {
|
||||||
|
|
||||||
expect(() => html`${unsafeString}`.value).not.throw(Error)
|
expect(() => html`${unsafeString}`.value).not.throw(Error)
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
expect(() => html`${unsafeString2}`.value).throw(Error)
|
expect(() => html`${unsafeString2}`.value).throw(Error)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src",
|
||||||
|
"./tests",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,9 @@ export abstract class BaseHttpProxyTcpTransport extends BaseTcpTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(dc: tl.RawDcOption): void {
|
connect(dc: tl.RawDcOption): void {
|
||||||
if (this._state !== TransportState.Idle) { throw new Error('Transport is not IDLE') }
|
if (this._state !== TransportState.Idle) {
|
||||||
|
throw new Error('Transport is not IDLE')
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.packetCodecInitialized) {
|
if (!this.packetCodecInitialized) {
|
||||||
this._packetCodec.on('error', (err) => this.emit('error', err))
|
this._packetCodec.on('error', (err) => this.emit('error', err))
|
||||||
|
@ -133,9 +135,10 @@ export abstract class BaseHttpProxyTcpTransport extends BaseTcpTransport {
|
||||||
}
|
}
|
||||||
headers['Proxy-Connection'] = 'Keep-Alive'
|
headers['Proxy-Connection'] = 'Keep-Alive'
|
||||||
|
|
||||||
const packet = `CONNECT ${ip} HTTP/1.1${Object.keys(headers).map(
|
const headersStr = Object.keys(headers)
|
||||||
(k) => `\r\n${k}: ${headers[k]}`,
|
.map((k) => `\r\n${k}: ${headers[k]}`)
|
||||||
)}\r\n\r\n`
|
.join('')
|
||||||
|
const packet = `CONNECT ${ip} HTTP/1.1${headersStr}\r\n\r\n`
|
||||||
|
|
||||||
this._socket!.write(packet)
|
this._socket!.write(packet)
|
||||||
this._socket!.once('data', (msg) => {
|
this._socket!.once('data', (msg) => {
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import type { BotChatJoinRequestUpdate, BotStoppedUpdate, CallbackQuery, ChatMemberUpdate, ChosenInlineResult, InlineQuery, Message, ParsedUpdate, PollVoteUpdate, User } from '@mtcute/client'
|
import type * as clientNs from '@mtcute/client'
|
||||||
|
|
||||||
import { I18nStrings, I18nValue } from './types'
|
import { I18nStrings, I18nValue } from './types'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
let client: typeof clientNs
|
||||||
let client: any
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
client = require('@mtcute/client')
|
client = require('@mtcute/client') as typeof clientNs
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,7 +42,7 @@ export function createI18nStringsIndex(
|
||||||
* @param update Update to extract language from
|
* @param update Update to extract language from
|
||||||
*/
|
*/
|
||||||
export function extractLanguageFromUpdate(
|
export function extractLanguageFromUpdate(
|
||||||
update: ParsedUpdate['data'],
|
update: clientNs.ParsedUpdate['data'],
|
||||||
): string | null | undefined {
|
): string | null | undefined {
|
||||||
if (!client) {
|
if (!client) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -54,23 +53,26 @@ export function extractLanguageFromUpdate(
|
||||||
switch (update.constructor) {
|
switch (update.constructor) {
|
||||||
case client.Message:
|
case client.Message:
|
||||||
// if sender is Chat it will just be undefined
|
// if sender is Chat it will just be undefined
|
||||||
return ((update as Message).sender as User).language
|
return ((update as clientNs.Message).sender as clientNs.User)
|
||||||
|
.language
|
||||||
|
case client.PollVoteUpdate:
|
||||||
|
// if peer is Chat it will just be undefined
|
||||||
|
return ((update as clientNs.PollVoteUpdate).peer as clientNs.User)
|
||||||
|
.language
|
||||||
case client.ChatMemberUpdate:
|
case client.ChatMemberUpdate:
|
||||||
case client.InlineQuery:
|
case client.InlineQuery:
|
||||||
case client.ChosenInlineResult:
|
case client.ChosenInlineResult:
|
||||||
case client.CallbackQuery:
|
case client.CallbackQuery:
|
||||||
case client.PollVoteUpdate:
|
|
||||||
case client.BotStoppedUpdate:
|
case client.BotStoppedUpdate:
|
||||||
case client.BotChatJoinRequestUpdate:
|
case client.BotChatJoinRequestUpdate:
|
||||||
return (
|
return (
|
||||||
update as
|
update as
|
||||||
| ChatMemberUpdate
|
| clientNs.ChatMemberUpdate
|
||||||
| InlineQuery
|
| clientNs.InlineQuery
|
||||||
| ChosenInlineResult
|
| clientNs.ChosenInlineResult
|
||||||
| CallbackQuery
|
| clientNs.CallbackQuery
|
||||||
| PollVoteUpdate
|
| clientNs.BotStoppedUpdate
|
||||||
| BotStoppedUpdate
|
| clientNs.BotChatJoinRequestUpdate
|
||||||
| BotChatJoinRequestUpdate
|
|
||||||
).user.language
|
).user.language
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src",
|
||||||
|
"./tests",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
|
|
||||||
import type { FormattedString, IMessageEntityParser, MessageEntity, tl } from '@mtcute/client'
|
import type {
|
||||||
|
FormattedString,
|
||||||
|
IMessageEntityParser,
|
||||||
|
MessageEntity,
|
||||||
|
tl,
|
||||||
|
} from '@mtcute/client'
|
||||||
|
|
||||||
const MENTION_REGEX =
|
const MENTION_REGEX =
|
||||||
/^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/
|
/^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/
|
||||||
const EMOJI_REGEX =
|
const EMOJI_REGEX = /^tg:\/\/emoji\?id=(-?\d+)/
|
||||||
/^tg:\/\/emoji\?id=(-?\d+)/
|
|
||||||
|
|
||||||
const TAG_BOLD = '**'
|
const TAG_BOLD = '**'
|
||||||
const TAG_ITALIC = '__'
|
const TAG_ITALIC = '__'
|
||||||
|
@ -41,7 +45,9 @@ export function md(
|
||||||
|
|
||||||
if (typeof it === 'string') it = MarkdownMessageEntityParser.escape(it)
|
if (typeof it === 'string') it = MarkdownMessageEntityParser.escape(it)
|
||||||
else {
|
else {
|
||||||
if (it.mode && it.mode !== 'markdown') { throw new Error(`Incompatible parse mode: ${it.mode}`) }
|
if (it.mode && it.mode !== 'markdown') {
|
||||||
|
throw new Error(`Incompatible parse mode: ${it.mode}`)
|
||||||
|
}
|
||||||
it = it.value
|
it = it.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,12 +130,12 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser {
|
||||||
pos += 3
|
pos += 3
|
||||||
continue
|
continue
|
||||||
|
|
||||||
// closed with single or double backtick
|
// closed with single or double backtick
|
||||||
// i.e. not closed actually! this is totally valid md:
|
// i.e. not closed actually! this is totally valid md:
|
||||||
// ```javascript
|
// ```javascript
|
||||||
// const a = ``;
|
// const a = ``;
|
||||||
// ```
|
// ```
|
||||||
// compensate that `pos` change we made earliers
|
// compensate that `pos` change we made earliers
|
||||||
} else if (c === '\n') {
|
} else if (c === '\n') {
|
||||||
pos -= 1
|
pos -= 1
|
||||||
}
|
}
|
||||||
|
@ -165,7 +171,9 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser {
|
||||||
|
|
||||||
pos += 1 // )
|
pos += 1 // )
|
||||||
|
|
||||||
if (pos > text.length) { throw new Error('Malformed LINK entity, expected )') }
|
if (pos > text.length) {
|
||||||
|
throw new Error('Malformed LINK entity, expected )')
|
||||||
|
}
|
||||||
|
|
||||||
if (url.length) {
|
if (url.length) {
|
||||||
ent.length = result.length - ent.offset
|
ent.length = result.length - ent.offset
|
||||||
|
@ -200,9 +208,8 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser {
|
||||||
).userId = userId
|
).userId = userId
|
||||||
}
|
}
|
||||||
} else if ((m = EMOJI_REGEX.exec(url))) {
|
} else if ((m = EMOJI_REGEX.exec(url))) {
|
||||||
(
|
(ent as tl.Mutable<tl.RawMessageEntityCustomEmoji>)._ =
|
||||||
ent as tl.Mutable<tl.RawMessageEntityCustomEmoji>
|
'messageEntityCustomEmoji'
|
||||||
)._ = 'messageEntityCustomEmoji'
|
|
||||||
;(
|
;(
|
||||||
ent as tl.Mutable<tl.RawMessageEntityCustomEmoji>
|
ent as tl.Mutable<tl.RawMessageEntityCustomEmoji>
|
||||||
).documentId = Long.fromString(m[1])
|
).documentId = Long.fromString(m[1])
|
||||||
|
@ -224,10 +231,11 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser {
|
||||||
pos += 1
|
pos += 1
|
||||||
insideLink = true
|
insideLink = true
|
||||||
if (!('link' in stacks)) stacks.link = []
|
if (!('link' in stacks)) stacks.link = []
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
stacks.link.push({
|
stacks.link.push({
|
||||||
offset: result.length,
|
offset: result.length,
|
||||||
length: 0,
|
length: 0,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} as any) // other fields are added after the second part
|
} as any) // other fields are added after the second part
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -308,9 +316,7 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser {
|
||||||
|
|
||||||
if (isBegin) {
|
if (isBegin) {
|
||||||
stacks[type].push({
|
stacks[type].push({
|
||||||
// this is valid, but idk how to make typescript happy
|
_: `messageEntity${type}`,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
_: ('messageEntity' + type) as any,
|
|
||||||
offset: result.length,
|
offset: result.length,
|
||||||
length: 0,
|
length: 0,
|
||||||
})
|
})
|
||||||
|
@ -379,7 +385,8 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser {
|
||||||
end += escapedPos
|
end += escapedPos
|
||||||
}
|
}
|
||||||
|
|
||||||
let startTag; let endTag: string
|
let startTag
|
||||||
|
let endTag: string
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'bold':
|
case 'bold':
|
||||||
|
@ -420,7 +427,7 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser {
|
||||||
break
|
break
|
||||||
case 'emoji':
|
case 'emoji':
|
||||||
startTag = '['
|
startTag = '['
|
||||||
endTag = `](tg://emoji?id=${entity.emojiId})`
|
endTag = `](tg://emoji?id=${entity.emojiId!.toString()})`
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -689,7 +689,7 @@ describe('MarkdownMessageEntityParser', () => {
|
||||||
|
|
||||||
expect(() => md`${unsafeString}`.value).not.throw(Error)
|
expect(() => md`${unsafeString}`.value).not.throw(Error)
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
expect(() => md`${unsafeString2}`.value).throw(Error)
|
expect(() => md`${unsafeString2}`.value).throw(Error)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src"
|
"./src",
|
||||||
|
"./tests",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,8 +112,7 @@ export class MtProxyTcpTransport extends BaseTcpTransport {
|
||||||
this.packetCodecInitialized = false
|
this.packetCodecInitialized = false
|
||||||
this._packetCodec.reset()
|
this._packetCodec.reset()
|
||||||
this._packetCodec.removeAllListeners()
|
this._packetCodec.removeAllListeners()
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
delete (this as Partial<MtProxyTcpTransport>)._packetCodec
|
||||||
delete (this as any)._packetCodec
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._packetCodec) {
|
if (!this._packetCodec) {
|
||||||
|
@ -155,12 +154,16 @@ export class MtProxyTcpTransport extends BaseTcpTransport {
|
||||||
this._socket = connect(
|
this._socket = connect(
|
||||||
this._proxy.port,
|
this._proxy.port,
|
||||||
this._proxy.host,
|
this._proxy.host,
|
||||||
|
// MTQ-55
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
this._handleConnectFakeTls.bind(this),
|
this._handleConnectFakeTls.bind(this),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
this._socket = connect(
|
this._socket = connect(
|
||||||
this._proxy.port,
|
this._proxy.port,
|
||||||
this._proxy.host,
|
this._proxy.host,
|
||||||
|
// MTQ-55
|
||||||
|
|
||||||
this.handleConnect.bind(this),
|
this.handleConnect.bind(this),
|
||||||
)
|
)
|
||||||
this._socket.on('data', (data) => this._packetCodec.feed(data))
|
this._socket.on('data', (data) => this._packetCodec.feed(data))
|
||||||
|
@ -219,18 +222,17 @@ export class MtProxyTcpTransport extends BaseTcpTransport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const packetHandler = async (buf: Buffer): Promise<void> => {
|
const packetHandler = (buf: Buffer): void => {
|
||||||
try {
|
checkHelloResponse(buf)
|
||||||
await checkHelloResponse(buf)
|
.then(() => {
|
||||||
|
this._socket!.off('data', packetHandler)
|
||||||
|
this._socket!.on('data', (data) =>
|
||||||
|
this._packetCodec.feed(data),
|
||||||
|
)
|
||||||
|
|
||||||
this._socket!.on('data', (data) =>
|
return this.handleConnect()
|
||||||
this._packetCodec.feed(data),
|
})
|
||||||
)
|
.catch((err) => this._socket!.emit('error', err))
|
||||||
this._socket!.off('data', packetHandler)
|
|
||||||
this.handleConnect()
|
|
||||||
} catch (e) {
|
|
||||||
this._socket!.emit('error', e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._socket!.write(hello)
|
this._socket!.write(hello)
|
||||||
|
|
|
@ -16,7 +16,7 @@ export { SqliteStorage }
|
||||||
let nativeCrypto: any
|
let nativeCrypto: any
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line
|
||||||
nativeCrypto = require('@mtcute/crypto-node').NodeNativeCryptoProvider
|
nativeCrypto = require('@mtcute/crypto-node').NodeNativeCryptoProvider
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ export interface NodeTelegramClientOptions
|
||||||
export class NodeTelegramClient extends TelegramClient {
|
export class NodeTelegramClient extends TelegramClient {
|
||||||
constructor(opts: NodeTelegramClientOptions) {
|
constructor(opts: NodeTelegramClientOptions) {
|
||||||
super({
|
super({
|
||||||
|
// eslint-disable-next-line
|
||||||
crypto: nativeCrypto ? () => new nativeCrypto() : undefined,
|
crypto: nativeCrypto ? () => new nativeCrypto() : undefined,
|
||||||
...opts,
|
...opts,
|
||||||
storage:
|
storage:
|
||||||
|
@ -68,7 +69,9 @@ export class NodeTelegramClient extends TelegramClient {
|
||||||
this.registerParseMode(new HtmlMessageEntityParser())
|
this.registerParseMode(new HtmlMessageEntityParser())
|
||||||
this.registerParseMode(new MarkdownMessageEntityParser())
|
this.registerParseMode(new MarkdownMessageEntityParser())
|
||||||
|
|
||||||
if (opts.defaultParseMode) { this.setDefaultParseMode(opts.defaultParseMode) }
|
if (opts.defaultParseMode) {
|
||||||
|
this.setDefaultParseMode(opts.defaultParseMode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _rl?: RlInterface
|
private _rl?: RlInterface
|
||||||
|
@ -98,7 +101,9 @@ export class NodeTelegramClient extends TelegramClient {
|
||||||
if (!params.phone) params.phone = () => this.input('Phone > ')
|
if (!params.phone) params.phone = () => this.input('Phone > ')
|
||||||
if (!params.code) params.code = () => this.input('Code > ')
|
if (!params.code) params.code = () => this.input('Code > ')
|
||||||
|
|
||||||
if (!params.password) { params.password = () => this.input('2FA password > ') }
|
if (!params.password) {
|
||||||
|
params.password = () => this.input('2FA password > ')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.start(params).then((user) => {
|
return super.start(params).then((user) => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// ^^ because of this._socket. we know it's not null, almost everywhere, but TS doesn't
|
// ^^ because of this._socket. we know it's not null, almost everywhere, but TS doesn't
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
import { normalize } from 'ip6'
|
import { normalize } from 'ip6'
|
||||||
import { connect } from 'net'
|
import { connect } from 'net'
|
||||||
|
|
||||||
|
@ -115,8 +115,12 @@ function buildSocks5Auth(username: string, password: string) {
|
||||||
const usernameBuf = Buffer.from(username)
|
const usernameBuf = Buffer.from(username)
|
||||||
const passwordBuf = Buffer.from(password)
|
const passwordBuf = Buffer.from(password)
|
||||||
|
|
||||||
if (usernameBuf.length > 255) { throw new Error(`Too long username (${usernameBuf.length} > 255)`) }
|
if (usernameBuf.length > 255) {
|
||||||
if (passwordBuf.length > 255) { throw new Error(`Too long password (${passwordBuf.length} > 255)`) }
|
throw new Error(`Too long username (${usernameBuf.length} > 255)`)
|
||||||
|
}
|
||||||
|
if (passwordBuf.length > 255) {
|
||||||
|
throw new Error(`Too long password (${passwordBuf.length} > 255)`)
|
||||||
|
}
|
||||||
|
|
||||||
const buf = Buffer.alloc(3 + usernameBuf.length + passwordBuf.length)
|
const buf = Buffer.alloc(3 + usernameBuf.length + passwordBuf.length)
|
||||||
buf[0] = 0x01 // VER of auth
|
buf[0] = 0x01 // VER of auth
|
||||||
|
@ -129,7 +133,8 @@ function buildSocks5Auth(username: string, password: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeIpv6(ip: string, buf: Buffer, offset: number): void {
|
function writeIpv6(ip: string, buf: Buffer, offset: number): void {
|
||||||
ip = normalize(ip)
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
|
ip = normalize(ip) as string
|
||||||
const parts = ip.split(':')
|
const parts = ip.split(':')
|
||||||
|
|
||||||
if (parts.length !== 8) {
|
if (parts.length !== 8) {
|
||||||
|
@ -193,9 +198,14 @@ export abstract class BaseSocksTcpTransport extends BaseTcpTransport {
|
||||||
constructor(proxy: SocksProxySettings) {
|
constructor(proxy: SocksProxySettings) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
if (proxy.version != null && proxy.version !== 4 && proxy.version !== 5) {
|
if (
|
||||||
|
proxy.version != null &&
|
||||||
|
proxy.version !== 4 &&
|
||||||
|
proxy.version !== 5
|
||||||
|
) {
|
||||||
throw new SocksProxyConnectionError(
|
throw new SocksProxyConnectionError(
|
||||||
proxy,
|
proxy,
|
||||||
|
|
||||||
`Invalid SOCKS version: ${proxy.version}`,
|
`Invalid SOCKS version: ${proxy.version}`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -204,7 +214,9 @@ export abstract class BaseSocksTcpTransport extends BaseTcpTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(dc: tl.RawDcOption): void {
|
connect(dc: tl.RawDcOption): void {
|
||||||
if (this._state !== TransportState.Idle) { throw new Error('Transport is not IDLE') }
|
if (this._state !== TransportState.Idle) {
|
||||||
|
throw new Error('Transport is not IDLE')
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.packetCodecInitialized) {
|
if (!this.packetCodecInitialized) {
|
||||||
this._packetCodec.on('error', (err) => this.emit('error', err))
|
this._packetCodec.on('error', (err) => this.emit('error', err))
|
||||||
|
|
|
@ -359,7 +359,7 @@ export class SqliteStorage implements ITelegramStorage, IStateStorage {
|
||||||
private _getFromKv<T>(key: string): T | null {
|
private _getFromKv<T>(key: string): T | null {
|
||||||
const row = this._statements.getKv.get(key) as { value: string } | null
|
const row = this._statements.getKv.get(key) as { value: string } | null
|
||||||
|
|
||||||
return row ? JSON.parse(row.value) : null
|
return row ? (JSON.parse(row.value) as T) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setToKv(key: string, value: unknown, now = false): void {
|
private _setToKv(key: string, value: unknown, now = false): void {
|
||||||
|
@ -375,7 +375,7 @@ export class SqliteStorage implements ITelegramStorage, IStateStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _runMany!: (stmts: [sqlite3.Statement, unknown[]][]) => void
|
private _runMany!: (stmts: [sqlite3.Statement, unknown[]][]) => void
|
||||||
private _updateManyPeers!: (updates: unknown[]) => void
|
private _updateManyPeers!: (updates: unknown[][]) => void
|
||||||
|
|
||||||
private _upgradeDatabase(from: number): void {
|
private _upgradeDatabase(from: number): void {
|
||||||
if (from < 2 || from > CURRENT_VERSION) {
|
if (from < 2 || from > CURRENT_VERSION) {
|
||||||
|
@ -461,14 +461,16 @@ export class SqliteStorage implements ITelegramStorage, IStateStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper methods
|
// helper methods
|
||||||
this._runMany = this._db.transaction((stmts) => {
|
this._runMany = this._db.transaction(
|
||||||
stmts.forEach((stmt: [sqlite3.Statement, unknown[]]) => {
|
(stmts: [sqlite3.Statement, unknown[]][]) => {
|
||||||
stmt[0].run(stmt[1])
|
stmts.forEach((stmt) => {
|
||||||
})
|
stmt[0].run(stmt[1])
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
this._updateManyPeers = this._db.transaction((data) => {
|
this._updateManyPeers = this._db.transaction((data: unknown[][]) => {
|
||||||
data.forEach((it: unknown[]) => {
|
data.forEach((it: unknown) => {
|
||||||
this._statements.updateCachedEnt.run(it)
|
this._statements.updateCachedEnt.run(it)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -727,7 +729,7 @@ export class SqliteStorage implements ITelegramStorage, IStateStorage {
|
||||||
|
|
||||||
// IStateStorage implementation
|
// IStateStorage implementation
|
||||||
|
|
||||||
getState(key: string, parse = true): unknown | null {
|
getState(key: string, parse = true): unknown {
|
||||||
let val: FsmItem | undefined = this._fsmCache?.get(key)
|
let val: FsmItem | undefined = this._fsmCache?.get(key)
|
||||||
const cached = val
|
const cached = val
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ export function gzipInflate(buf: Buffer): Buffer {
|
||||||
return typedArrayToBuffer(inflate(buf))
|
return typedArrayToBuffer(inflate(buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ERROR_SIZE_LIMIT_REACHED = 'ERR_SIZE_LIMIT_REACHED'
|
||||||
|
|
||||||
class DeflateLimited extends Deflate {
|
class DeflateLimited extends Deflate {
|
||||||
constructor(readonly limit: number) {
|
constructor(readonly limit: number) {
|
||||||
super()
|
super()
|
||||||
|
@ -21,7 +23,9 @@ class DeflateLimited extends Deflate {
|
||||||
this._size += (chunk as Uint8Array).length
|
this._size += (chunk as Uint8Array).length
|
||||||
|
|
||||||
if (this._size > this.limit) {
|
if (this._size > this.limit) {
|
||||||
throw 'ERR_SIZE'
|
// caught locally
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||||
|
throw ERROR_SIZE_LIMIT_REACHED
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onData(chunk)
|
super.onData(chunk)
|
||||||
|
@ -36,9 +40,9 @@ export function gzipDeflate(buf: Buffer, maxRatio?: number): Buffer | null {
|
||||||
try {
|
try {
|
||||||
deflator.push(buf, true)
|
deflator.push(buf, true)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e === 'ERR_SIZE') return null
|
if (e === ERROR_SIZE_LIMIT_REACHED) return null
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
return typedArrayToBuffer(deflator.result as Uint8Array)
|
return typedArrayToBuffer(deflator.result)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-return */
|
||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { randomBytes } from 'crypto'
|
import { randomBytes } from 'crypto'
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { randomBytes } from 'crypto'
|
import { randomBytes } from 'crypto'
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
|
|
|
@ -5,5 +5,6 @@
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src",
|
"./src",
|
||||||
|
"./tests",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,6 +188,23 @@ export function generateReaderCodeForTlEntries(
|
||||||
ret += generateReaderCodeForTlEntry(entry, params) + '\n'
|
ret += generateReaderCodeForTlEntry(entry, params) + '\n'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const usedInBareVector: Record<string, 1> = {}
|
||||||
|
ret.replace(
|
||||||
|
new RegExp(`(?<=r\\.vector\\(${variableName}\\[)(\\d+)(?=])`, 'g'),
|
||||||
|
(_, id: string) => {
|
||||||
|
usedInBareVector[id] = 1
|
||||||
|
|
||||||
|
return _
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const id of Object.keys(usedInBareVector)) {
|
||||||
|
ret = ret.replace(
|
||||||
|
new RegExp(`(?<=^${id}:function\\()r(?=\\))`, 'gm'),
|
||||||
|
'r=this',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (params.includeMethodResults) {
|
if (params.includeMethodResults) {
|
||||||
ret += '_results:{\n'
|
ret += '_results:{\n'
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,10 @@ export function generateWriterCodeForTlEntry(
|
||||||
if (entry.id === 0) entry.id = computeConstructorIdFromEntry(entry)
|
if (entry.id === 0) entry.id = computeConstructorIdFromEntry(entry)
|
||||||
|
|
||||||
const name = bare ? entry.id : `'${entry.name}'`
|
const name = bare ? entry.id : `'${entry.name}'`
|
||||||
let ret = `${name}:function(w${entry.arguments.length ? ',v' : ''}){`
|
const defaultWriter = bare ? '=this' : ''
|
||||||
|
let ret = `${name}:function(w${defaultWriter}${
|
||||||
|
entry.arguments.length ? ',v' : ''
|
||||||
|
}){`
|
||||||
|
|
||||||
if (!bare) ret += `w.uint(${entry.id});`
|
if (!bare) ret += `w.uint(${entry.id});`
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ import { generateWriterCodeForTlEntries } from './codegen/writer'
|
||||||
import { parseTlToEntries } from './parse'
|
import { parseTlToEntries } from './parse'
|
||||||
|
|
||||||
function evalForResult<T>(js: string): T {
|
function evalForResult<T>(js: string): T {
|
||||||
return new Function(js)()
|
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
||||||
|
return new Function(js)() as T
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,7 +58,7 @@ export function patchRuntimeTlSchema(
|
||||||
},
|
},
|
||||||
// ts is not smart enough
|
// ts is not smart enough
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
writerMap: {
|
writerMap: {
|
||||||
...writers,
|
...writers,
|
||||||
...newWriters,
|
...newWriters,
|
||||||
|
|
|
@ -68,9 +68,9 @@ export function stringifyArgumentType(
|
||||||
if (!modifiers) return type
|
if (!modifiers) return type
|
||||||
let ret = type
|
let ret = type
|
||||||
|
|
||||||
if (modifiers?.isBareUnion) ret = `%${ret}`
|
if (modifiers.isBareUnion) ret = `%${ret}`
|
||||||
if (modifiers?.isVector) ret = `Vector<${ret}>`
|
if (modifiers.isVector) ret = `Vector<${ret}>`
|
||||||
else if (modifiers?.isBareVector) ret = `vector<${ret}>`
|
else if (modifiers.isBareVector) ret = `vector<${ret}>`
|
||||||
if (modifiers.predicate) ret = `${modifiers.predicate}?${ret}`
|
if (modifiers.predicate) ret = `${modifiers.predicate}?${ret}`
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
|
@ -91,8 +91,8 @@ describe('mergeTlSchemas', () => {
|
||||||
).eq(expected.join('\n'))
|
).eq(expected.join('\n'))
|
||||||
}
|
}
|
||||||
|
|
||||||
it('merges different constructors', () => {
|
it('merges different constructors', async () => {
|
||||||
test(
|
await test(
|
||||||
[
|
[
|
||||||
['testClass = Test;'],
|
['testClass = Test;'],
|
||||||
['testClass2 = Test;'],
|
['testClass2 = Test;'],
|
||||||
|
@ -106,8 +106,8 @@ describe('mergeTlSchemas', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('merges true flags in constructors', () => {
|
it('merges true flags in constructors', async () => {
|
||||||
test(
|
await test(
|
||||||
[
|
[
|
||||||
['test foo:flags.0?true = Test;'],
|
['test foo:flags.0?true = Test;'],
|
||||||
['test bar:flags.0?true = Test;'],
|
['test bar:flags.0?true = Test;'],
|
||||||
|
@ -118,8 +118,8 @@ describe('mergeTlSchemas', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('resolves conflict using user-provided option', () => {
|
it('resolves conflict using user-provided option', async () => {
|
||||||
test(
|
await test(
|
||||||
[
|
[
|
||||||
['test foo:int = Test;'],
|
['test foo:int = Test;'],
|
||||||
['test bar:int = Test;'],
|
['test bar:int = Test;'],
|
||||||
|
@ -128,7 +128,7 @@ describe('mergeTlSchemas', () => {
|
||||||
0,
|
0,
|
||||||
'test foo:int = Test;',
|
'test foo:int = Test;',
|
||||||
)
|
)
|
||||||
test(
|
await test(
|
||||||
[
|
[
|
||||||
['test foo:int = Test;'],
|
['test foo:int = Test;'],
|
||||||
['test bar:int = Test;'],
|
['test bar:int = Test;'],
|
||||||
|
@ -137,11 +137,15 @@ describe('mergeTlSchemas', () => {
|
||||||
1,
|
1,
|
||||||
'test foo:int = Test;',
|
'test foo:int = Test;',
|
||||||
)
|
)
|
||||||
test([['test foo:int = Test;'], [], ['test bar:int = Test;']], 1, '')
|
await test(
|
||||||
|
[['test foo:int = Test;'], [], ['test bar:int = Test;']],
|
||||||
|
1,
|
||||||
|
'',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('merges comments', () => {
|
it('merges comments', async () => {
|
||||||
test(
|
await test(
|
||||||
[
|
[
|
||||||
['test foo:flags.0?true = Test;'],
|
['test foo:flags.0?true = Test;'],
|
||||||
['// test ctor', 'test bar:flags.0?true = Test;'],
|
['// test ctor', 'test bar:flags.0?true = Test;'],
|
||||||
|
@ -156,8 +160,8 @@ describe('mergeTlSchemas', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('merges arguments comments', () => {
|
it('merges arguments comments', async () => {
|
||||||
test(
|
await test(
|
||||||
[
|
[
|
||||||
['test foo:flags.0?true = Test;'],
|
['test foo:flags.0?true = Test;'],
|
||||||
['// @bar bar comment', 'test bar:flags.0?true = Test;'],
|
['// @bar bar comment', 'test bar:flags.0?true = Test;'],
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src/**/*.ts"
|
"./src/**/*.ts",
|
||||||
|
"./tests/**/*.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {
|
||||||
DOC_CACHE_FILE,
|
DOC_CACHE_FILE,
|
||||||
} from './constants'
|
} from './constants'
|
||||||
import { applyDescriptionsYamlFile } from './process-descriptions-yaml'
|
import { applyDescriptionsYamlFile } from './process-descriptions-yaml'
|
||||||
import { packTlSchema, unpackTlSchema } from './schema'
|
import { packTlSchema, TlPackedSchema, unpackTlSchema } from './schema'
|
||||||
import { fetchRetry } from './utils'
|
import { fetchRetry } from './utils'
|
||||||
|
|
||||||
export interface CachedDocumentationEntry {
|
export interface CachedDocumentationEntry {
|
||||||
|
@ -38,7 +38,10 @@ export interface CachedDocumentation {
|
||||||
unions: Record<string, string>
|
unions: Record<string, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeLinks(url: string, el: cheerio.Cheerio<cheerio.Element>): void {
|
function normalizeLinks(
|
||||||
|
url: string,
|
||||||
|
el: cheerio.Cheerio<cheerio.Element>,
|
||||||
|
): void {
|
||||||
el.find('a').each((i, _it) => {
|
el.find('a').each((i, _it) => {
|
||||||
const it = cheerio.default(_it)
|
const it = cheerio.default(_it)
|
||||||
let href = it.attr('href')
|
let href = it.attr('href')
|
||||||
|
@ -314,7 +317,7 @@ export async function getCachedDocumentation(): Promise<CachedDocumentation | nu
|
||||||
try {
|
try {
|
||||||
const file = await readFile(DOC_CACHE_FILE, 'utf8')
|
const file = await readFile(DOC_CACHE_FILE, 'utf8')
|
||||||
|
|
||||||
return JSON.parse(file)
|
return JSON.parse(file) as CachedDocumentation
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e && typeof e === 'object' && 'code' in e && e.code === 'ENOENT') {
|
if (e && typeof e === 'object' && 'code' in e && e.code === 'ENOENT') {
|
||||||
return null
|
return null
|
||||||
|
@ -355,7 +358,9 @@ async function main() {
|
||||||
|
|
||||||
if (act === 1) {
|
if (act === 1) {
|
||||||
const [schema, layer] = unpackTlSchema(
|
const [schema, layer] = unpackTlSchema(
|
||||||
JSON.parse(await readFile(API_SCHEMA_JSON_FILE, 'utf8')),
|
JSON.parse(
|
||||||
|
await readFile(API_SCHEMA_JSON_FILE, 'utf8'),
|
||||||
|
) as TlPackedSchema,
|
||||||
)
|
)
|
||||||
cached = await fetchDocumentation(schema, layer)
|
cached = await fetchDocumentation(schema, layer)
|
||||||
}
|
}
|
||||||
|
@ -381,7 +386,9 @@ async function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const [schema, layer] = unpackTlSchema(
|
const [schema, layer] = unpackTlSchema(
|
||||||
JSON.parse(await readFile(API_SCHEMA_JSON_FILE, 'utf8')),
|
JSON.parse(
|
||||||
|
await readFile(API_SCHEMA_JSON_FILE, 'utf8'),
|
||||||
|
) as TlPackedSchema,
|
||||||
)
|
)
|
||||||
|
|
||||||
applyDocumentation(schema, cached)
|
applyDocumentation(schema, cached)
|
||||||
|
|
|
@ -36,7 +36,7 @@ import {
|
||||||
fetchDocumentation,
|
fetchDocumentation,
|
||||||
getCachedDocumentation,
|
getCachedDocumentation,
|
||||||
} from './documentation'
|
} from './documentation'
|
||||||
import { packTlSchema, unpackTlSchema } from './schema'
|
import { packTlSchema, TlPackedSchema, unpackTlSchema } from './schema'
|
||||||
import { fetchRetry } from './utils'
|
import { fetchRetry } from './utils'
|
||||||
|
|
||||||
import { bumpVersion } from '~scripts/version'
|
import { bumpVersion } from '~scripts/version'
|
||||||
|
@ -137,8 +137,10 @@ async function updatePackageVersion(
|
||||||
rl: readline.Interface,
|
rl: readline.Interface,
|
||||||
currentLayer: number,
|
currentLayer: number,
|
||||||
) {
|
) {
|
||||||
const packageJson = JSON.parse(await readFile(PACKAGE_JSON_FILE, 'utf8'))
|
const packageJson = JSON.parse(
|
||||||
const version: string = packageJson.version
|
await readFile(PACKAGE_JSON_FILE, 'utf8'),
|
||||||
|
) as { version: string }
|
||||||
|
const version = packageJson.version
|
||||||
let [major, minor] = version.split('.').map((i) => parseInt(i))
|
let [major, minor] = version.split('.').map((i) => parseInt(i))
|
||||||
|
|
||||||
if (major === currentLayer) {
|
if (major === currentLayer) {
|
||||||
|
@ -168,7 +170,7 @@ async function overrideInt53(schema: TlFullSchema): Promise<void> {
|
||||||
|
|
||||||
const config = JSON.parse(
|
const config = JSON.parse(
|
||||||
await readFile(join(__dirname, '../data/int53-overrides.json'), 'utf8'),
|
await readFile(join(__dirname, '../data/int53-overrides.json'), 'utf8'),
|
||||||
)
|
) as Record<string, Record<string, string[]>>
|
||||||
|
|
||||||
schema.entries.forEach((entry) => {
|
schema.entries.forEach((entry) => {
|
||||||
const overrides: string[] | undefined = config[entry.kind][entry.name]
|
const overrides: string[] | undefined = config[entry.kind][entry.name]
|
||||||
|
@ -274,8 +276,8 @@ async function main() {
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'Conflict detected at %s %s:',
|
'Conflict detected at %s %s:',
|
||||||
nonEmptyOptions[0].entry?.kind,
|
nonEmptyOptions[0].entry.kind,
|
||||||
nonEmptyOptions[0].entry?.name,
|
nonEmptyOptions[0].entry.name,
|
||||||
)
|
)
|
||||||
console.log('0. Remove')
|
console.log('0. Remove')
|
||||||
nonEmptyOptions.forEach((opt, idx) => {
|
nonEmptyOptions.forEach((opt, idx) => {
|
||||||
|
@ -329,7 +331,9 @@ async function main() {
|
||||||
|
|
||||||
console.log('Writing diff to file...')
|
console.log('Writing diff to file...')
|
||||||
const oldSchema = unpackTlSchema(
|
const oldSchema = unpackTlSchema(
|
||||||
JSON.parse(await readFile(API_SCHEMA_JSON_FILE, 'utf8')),
|
JSON.parse(
|
||||||
|
await readFile(API_SCHEMA_JSON_FILE, 'utf8'),
|
||||||
|
) as TlPackedSchema,
|
||||||
)
|
)
|
||||||
await writeFile(
|
await writeFile(
|
||||||
API_SCHEMA_DIFF_JSON_FILE,
|
API_SCHEMA_DIFF_JSON_FILE,
|
||||||
|
|
|
@ -83,6 +83,13 @@ const virtualErrors: TlError[] = [
|
||||||
]
|
]
|
||||||
virtualErrors.forEach((it) => (it.virtual = true))
|
virtualErrors.forEach((it) => (it.virtual = true))
|
||||||
|
|
||||||
|
interface TelegramErrorsSpec {
|
||||||
|
errors: Record<string, Record<string, string[]>>
|
||||||
|
descriptions: Record<string, string>
|
||||||
|
user_only: string[]
|
||||||
|
bot_only: string[]
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchFromTelegram(errors: TlErrors) {
|
async function fetchFromTelegram(errors: TlErrors) {
|
||||||
const page = await fetch(ERRORS_PAGE_TG).then((it) => it.text())
|
const page = await fetch(ERRORS_PAGE_TG).then((it) => it.text())
|
||||||
const jsonUrl = page.match(
|
const jsonUrl = page.match(
|
||||||
|
@ -90,9 +97,9 @@ async function fetchFromTelegram(errors: TlErrors) {
|
||||||
)?.[1]
|
)?.[1]
|
||||||
if (!jsonUrl) throw new Error('Cannot find JSON URL')
|
if (!jsonUrl) throw new Error('Cannot find JSON URL')
|
||||||
|
|
||||||
const json = await fetch(new URL(jsonUrl, ERRORS_PAGE_TG)).then((it) =>
|
const json = (await fetch(new URL(jsonUrl, ERRORS_PAGE_TG)).then((it) =>
|
||||||
it.json(),
|
it.json(),
|
||||||
)
|
)) as TelegramErrorsSpec
|
||||||
|
|
||||||
// since nobody fucking guarantees that .descriptions
|
// since nobody fucking guarantees that .descriptions
|
||||||
// will have description for each described here (or vice versa),
|
// will have description for each described here (or vice versa),
|
||||||
|
@ -120,7 +127,7 @@ async function fetchFromTelegram(errors: TlErrors) {
|
||||||
errors.throws[method] = []
|
errors.throws[method] = []
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors.throws[method].indexOf(name) === -1) {
|
if (!errors.throws[method].includes(name)) {
|
||||||
errors.throws[method].push(name)
|
errors.throws[method].push(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,14 +192,17 @@ async function fetchFromTelethon(errors: TlErrors) {
|
||||||
// names for better code insights
|
// names for better code insights
|
||||||
// we also prefer description from telegram, if it's available and doesn't use placeholders
|
// we also prefer description from telegram, if it's available and doesn't use placeholders
|
||||||
if (description) {
|
if (description) {
|
||||||
const desc = description.replace(/{([a-z0-9_]+)}/gi, (_, name) => {
|
const desc = description.replace(
|
||||||
if (!obj._paramNames) {
|
/{([a-z0-9_]+)}/gi,
|
||||||
obj._paramNames = []
|
(_, name: string) => {
|
||||||
}
|
if (!obj._paramNames) {
|
||||||
obj._paramNames.push(name)
|
obj._paramNames = []
|
||||||
|
}
|
||||||
|
obj._paramNames.push(name)
|
||||||
|
|
||||||
return '%d'
|
return '%d'
|
||||||
})
|
},
|
||||||
|
)
|
||||||
|
|
||||||
if (!obj.description || obj._paramNames?.length) {
|
if (!obj.description || obj._paramNames?.length) {
|
||||||
obj.description = desc
|
obj.description = desc
|
||||||
|
@ -202,13 +212,24 @@ async function fetchFromTelethon(errors: TlErrors) {
|
||||||
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
parser
|
parser
|
||||||
.on('data', ({ name, codes, description }) =>
|
.on(
|
||||||
addError(name, codes, description),
|
'data',
|
||||||
|
({
|
||||||
|
name,
|
||||||
|
codes,
|
||||||
|
description,
|
||||||
|
}: {
|
||||||
|
name: string
|
||||||
|
codes: string
|
||||||
|
description: string
|
||||||
|
}) => addError(name, codes, description),
|
||||||
)
|
)
|
||||||
.on('end', resolve)
|
.on('end', resolve)
|
||||||
.on('error', reject)
|
.on('error', reject)
|
||||||
|
|
||||||
csv.text().then((it) => parser.write(it)).catch(reject)
|
csv.text()
|
||||||
|
.then((it) => parser.write(it))
|
||||||
|
.catch(reject)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +250,7 @@ async function main() {
|
||||||
await fetchFromTelethon(errors)
|
await fetchFromTelethon(errors)
|
||||||
|
|
||||||
virtualErrors.forEach((err) => {
|
virtualErrors.forEach((err) => {
|
||||||
if (errors.errors[err.name]) {
|
if (err.name in errors.errors) {
|
||||||
console.log(`Error ${err.name} already exists and is not virtual`)
|
console.log(`Error ${err.name} already exists and is not virtual`)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -29,13 +29,13 @@ async function main() {
|
||||||
// remove manually parsed types
|
// remove manually parsed types
|
||||||
entries = entries.filter(
|
entries = entries.filter(
|
||||||
(it) =>
|
(it) =>
|
||||||
[
|
![
|
||||||
'mt_msg_container',
|
'mt_msg_container',
|
||||||
'mt_message',
|
'mt_message',
|
||||||
'mt_msg_copy',
|
'mt_msg_copy',
|
||||||
'mt_gzip_packed',
|
'mt_gzip_packed',
|
||||||
'mt_rpc_result',
|
'mt_rpc_result',
|
||||||
].indexOf(it.name) === -1,
|
].includes(it.name),
|
||||||
)
|
)
|
||||||
|
|
||||||
// mtproto is handled internally, for simplicity we make them all classes
|
// mtproto is handled internally, for simplicity we make them all classes
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
generateTypescriptDefinitionsForTlSchema,
|
generateTypescriptDefinitionsForTlSchema,
|
||||||
generateWriterCodeForTlEntries,
|
generateWriterCodeForTlEntries,
|
||||||
parseFullTlSchema,
|
parseFullTlSchema,
|
||||||
|
TlEntry,
|
||||||
TlErrors,
|
TlErrors,
|
||||||
TlFullSchema,
|
TlFullSchema,
|
||||||
} from '@mtcute/tl-utils'
|
} from '@mtcute/tl-utils'
|
||||||
|
@ -16,7 +17,7 @@ import {
|
||||||
ESM_PRELUDE,
|
ESM_PRELUDE,
|
||||||
MTP_SCHEMA_JSON_FILE,
|
MTP_SCHEMA_JSON_FILE,
|
||||||
} from './constants'
|
} from './constants'
|
||||||
import { unpackTlSchema } from './schema'
|
import { TlPackedSchema, unpackTlSchema } from './schema'
|
||||||
|
|
||||||
const OUT_TYPINGS_FILE = join(__dirname, '../index.d.ts')
|
const OUT_TYPINGS_FILE = join(__dirname, '../index.d.ts')
|
||||||
const OUT_TYPINGS_JS_FILE = join(__dirname, '../index.js')
|
const OUT_TYPINGS_JS_FILE = join(__dirname, '../index.js')
|
||||||
|
@ -92,15 +93,17 @@ async function generateWriters(
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const errors: TlErrors = JSON.parse(
|
const errors = JSON.parse(
|
||||||
await readFile(ERRORS_JSON_FILE, 'utf8'),
|
await readFile(ERRORS_JSON_FILE, 'utf8'),
|
||||||
)
|
) as TlErrors
|
||||||
|
|
||||||
const [apiSchema, apiLayer] = unpackTlSchema(
|
const [apiSchema, apiLayer] = unpackTlSchema(
|
||||||
JSON.parse(await readFile(API_SCHEMA_JSON_FILE, 'utf8')),
|
JSON.parse(
|
||||||
|
await readFile(API_SCHEMA_JSON_FILE, 'utf8'),
|
||||||
|
) as TlPackedSchema,
|
||||||
)
|
)
|
||||||
const mtpSchema = parseFullTlSchema(
|
const mtpSchema = parseFullTlSchema(
|
||||||
JSON.parse(await readFile(MTP_SCHEMA_JSON_FILE, 'utf8')),
|
JSON.parse(await readFile(MTP_SCHEMA_JSON_FILE, 'utf8')) as TlEntry[],
|
||||||
)
|
)
|
||||||
|
|
||||||
await generateTypings(apiSchema, apiLayer, mtpSchema, errors)
|
await generateTypings(apiSchema, apiLayer, mtpSchema, errors)
|
||||||
|
|
|
@ -66,7 +66,7 @@ export function applyDescriptionsYamlFile(
|
||||||
prefix: string,
|
prefix: string,
|
||||||
) {
|
) {
|
||||||
for (const name in obj) {
|
for (const name in obj) {
|
||||||
objIndex[prefix + name] = obj[name]
|
objIndex[prefix + name] = obj[name]!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ export function applyDescriptionsYamlFile(
|
||||||
|
|
||||||
// process byObjects
|
// process byObjects
|
||||||
for (const name in byObjects) {
|
for (const name in byObjects) {
|
||||||
const rules = byObjects[name]
|
const rules = byObjects[name]!
|
||||||
const obj = objIndex[name]
|
const obj = objIndex[name]
|
||||||
|
|
||||||
if (!obj) continue
|
if (!obj) continue
|
||||||
|
@ -88,7 +88,7 @@ export function applyDescriptionsYamlFile(
|
||||||
if (rules.arguments) {
|
if (rules.arguments) {
|
||||||
for (const arg in rules.arguments) {
|
for (const arg in rules.arguments) {
|
||||||
const repl = unwrapMaybe(
|
const repl = unwrapMaybe(
|
||||||
rules.arguments[arg],
|
rules.arguments[arg]!,
|
||||||
obj.arguments !== undefined && arg in obj.arguments,
|
obj.arguments !== undefined && arg in obj.arguments,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -102,15 +102,14 @@ export function applyDescriptionsYamlFile(
|
||||||
|
|
||||||
// process byArguments
|
// process byArguments
|
||||||
for (const i in objIndex) {
|
for (const i in objIndex) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
const obj = objIndex[i]!
|
||||||
const obj = objIndex[i] as any
|
|
||||||
|
|
||||||
for (const arg in byArguments) {
|
for (const arg in byArguments) {
|
||||||
if (obj.arguments && !(arg in obj.arguments)) continue
|
if (obj.arguments && !(arg in obj.arguments)) continue
|
||||||
|
|
||||||
const repl = unwrapMaybe(
|
const repl = unwrapMaybe(
|
||||||
byArguments[arg],
|
byArguments[arg]!,
|
||||||
Boolean(obj.arguments) && arg in obj.arguments,
|
Boolean(obj.arguments && arg in obj.arguments),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (repl) {
|
if (repl) {
|
||||||
|
@ -126,7 +125,7 @@ export function applyDescriptionsYamlFile(
|
||||||
|
|
||||||
if (!rule._cached) {
|
if (!rule._cached) {
|
||||||
let flags = rule.flags || ''
|
let flags = rule.flags || ''
|
||||||
if (flags.indexOf('g') === -1) flags += 'g'
|
if (!flags.includes('g')) flags += 'g'
|
||||||
|
|
||||||
rule._cached = new RegExp(rule.regex, flags)
|
rule._cached = new RegExp(rule.regex, flags)
|
||||||
}
|
}
|
||||||
|
@ -135,7 +134,7 @@ export function applyDescriptionsYamlFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const i in objIndex) {
|
for (const i in objIndex) {
|
||||||
const obj = objIndex[i]
|
const obj = objIndex[i]!
|
||||||
|
|
||||||
byRegex.forEach((rule) => {
|
byRegex.forEach((rule) => {
|
||||||
obj.comment = applyRegex(obj.comment, rule)
|
obj.comment = applyRegex(obj.comment, rule)
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
"./binary/reader.d.ts",
|
"./binary/reader.d.ts",
|
||||||
"./binary/writer.d.ts",
|
"./binary/writer.d.ts",
|
||||||
"./binary/rsa-keys.d.ts",
|
"./binary/rsa-keys.d.ts",
|
||||||
"./scripts"
|
"./scripts",
|
||||||
|
"./tests/types.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,6 +157,9 @@ importers:
|
||||||
'@types/ws':
|
'@types/ws':
|
||||||
specifier: 8.5.4
|
specifier: 8.5.4
|
||||||
version: 8.5.4
|
version: 8.5.4
|
||||||
|
exit-hook:
|
||||||
|
specifier: ^4.0.0
|
||||||
|
version: 4.0.0
|
||||||
ws:
|
ws:
|
||||||
specifier: 8.13.0
|
specifier: 8.13.0
|
||||||
version: 8.13.0
|
version: 8.13.0
|
||||||
|
@ -2442,6 +2445,11 @@ packages:
|
||||||
strip-final-newline: 3.0.0
|
strip-final-newline: 3.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/exit-hook@4.0.0:
|
||||||
|
resolution: {integrity: sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/exit-on-epipe@1.0.1:
|
/exit-on-epipe@1.0.1:
|
||||||
resolution: {integrity: sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==}
|
resolution: {integrity: sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==}
|
||||||
engines: {node: '>=0.8'}
|
engines: {node: '>=0.8'}
|
||||||
|
|
Loading…
Reference in a new issue