From ea299cacca0a53424b0fa9ac7403e70aa99fac2e Mon Sep 17 00:00:00 2001 From: teidesu <86301490+teidesu@users.noreply.github.com> Date: Mon, 29 Aug 2022 16:22:57 +0300 Subject: [PATCH] docs: improve documentation --- packages/client/src/client.ts | 2 +- .../src/methods/chats/get-chat-members.ts | 2 +- packages/client/src/types/bots/keyboards.ts | 4 +- packages/client/src/utils/file-utils.ts | 21 ++++++ packages/client/src/utils/inline-utils.ts | 10 +++ packages/client/src/utils/misc-utils.ts | 4 ++ packages/client/src/utils/voice-utils.ts | 12 ++++ packages/core/src/network/authorization.ts | 2 +- .../core/src/network/session-connection.ts | 3 + .../core/src/network/transports/obfuscated.ts | 2 +- packages/core/src/utils/bigint-utils.ts | 25 +++++++ packages/core/src/utils/buffer-utils.ts | 49 +++++++++++++ packages/core/src/utils/condition-variable.ts | 3 + .../core/src/utils/controllable-promise.ts | 17 +++++ packages/core/src/utils/crypto/abstract.ts | 24 +------ packages/core/src/utils/crypto/common.ts | 3 + .../core/src/utils/crypto/factorization.ts | 4 ++ .../core/src/utils/crypto/forge-crypto.ts | 12 +++- packages/core/src/utils/crypto/keys.ts | 21 ++++++ packages/core/src/utils/crypto/mtproto.ts | 28 +++++++- packages/core/src/utils/crypto/node-crypto.ts | 12 +++- packages/core/src/utils/crypto/password.ts | 25 ++++++- packages/core/src/utils/default-dcs.ts | 4 ++ packages/core/src/utils/flood-control.ts | 3 + packages/core/src/utils/linked-list.ts | 3 + packages/core/src/utils/long-utils.ts | 11 +++ packages/core/src/utils/lru-string-set.ts | 4 +- packages/core/src/utils/misc-utils.ts | 5 ++ packages/core/src/utils/peer-utils.ts | 14 +++- packages/core/src/utils/sorted-array.ts | 14 ++-- .../dispatcher/src/callback-data-builder.ts | 2 +- packages/http-proxy/index.ts | 9 +++ packages/markdown-parser/src/index.ts | 8 ++- packages/mtproxy/index.ts | 3 + packages/node/index.ts | 10 ++- packages/socks-proxy/index.ts | 11 ++- packages/sqlite/README.md | 2 +- packages/tl-runtime/src/platform/gzip.ts | 12 ++++ packages/tl-runtime/src/reader.ts | 45 ++++++++++++ packages/tl-runtime/src/writer.ts | 68 +++++++++++++++++++ 40 files changed, 459 insertions(+), 54 deletions(-) diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 26c994e6..0382924f 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -1285,7 +1285,7 @@ export interface TelegramClient extends BaseTelegramClient { * - `recent`: get recent members * - `admins`: get only administrators (and creator) * - `contacts`: get only contacts - * - `mention`: get users that can be mentioned ([learn more](https://mt.tei.su/tl/class/channelParticipantsMentions)) + * - `mention`: get users that can be mentioned (see {@link tl.RawChannelParticipantsMentions}) * * Only used for channels and supergroups. Defaults to `recent` */ diff --git a/packages/client/src/methods/chats/get-chat-members.ts b/packages/client/src/methods/chats/get-chat-members.ts index af9cfcf2..e988e055 100644 --- a/packages/client/src/methods/chats/get-chat-members.ts +++ b/packages/client/src/methods/chats/get-chat-members.ts @@ -58,7 +58,7 @@ export async function getChatMembers( * - `recent`: get recent members * - `admins`: get only administrators (and creator) * - `contacts`: get only contacts - * - `mention`: get users that can be mentioned ([learn more](https://mt.tei.su/tl/class/channelParticipantsMentions)) + * - `mention`: get users that can be mentioned (see {@link tl.RawChannelParticipantsMentions}) * * Only used for channels and supergroups. Defaults to `recent` */ diff --git a/packages/client/src/types/bots/keyboards.ts b/packages/client/src/types/bots/keyboards.ts index c5424518..0a542de2 100644 --- a/packages/client/src/types/bots/keyboards.ts +++ b/packages/client/src/types/bots/keyboards.ts @@ -216,7 +216,7 @@ export namespace BotKeyboard { * @param data Callback data (1-64 bytes). String will be converted to `Buffer` * @param requiresPassword * Whether the user should verify their identity by entering 2FA password. - * See more: [tl.RawKeyboardButtonCallback#requiresPassword](../../tl/interfaces/index.tl.rawkeyboardbuttoncallback.html#requirespassword) + * See more: {@link tl.RawKeyboardButtonCallback#requiresPassword} */ export function callback( text: string, @@ -293,7 +293,7 @@ export namespace BotKeyboard { * Used for inline keyboards, not reply! * * @param text Button label - * @param url Authorization URL (see [TL Reference](https://mt.tei.su/tl/class/inputKeyboardButtonUrlAuth)) + * @param url Authorization URL (see {@link tl.RawInputKeyboardButtonUrlAuth}) * @param params */ export function urlAuth( diff --git a/packages/client/src/utils/file-utils.ts b/packages/client/src/utils/file-utils.ts index e161fcd6..eb2dfc31 100644 --- a/packages/client/src/utils/file-utils.ts +++ b/packages/client/src/utils/file-utils.ts @@ -1,9 +1,14 @@ import { MtArgumentError } from '../types' +/** + * Given file size, determine the appropriate chunk size (in KB) + * for upload/download operations. + */ export function determinePartSize(fileSize: number): number { if (fileSize <= 104857600) return 128 // 100 MB if (fileSize <= 786432000) return 256 // 750 MB if (fileSize <= 2097152000) return 512 // 2000 MB + if (fileSize <= 4194304000) return 1024 // 4000 MB throw new MtArgumentError('File is too large') } @@ -50,6 +55,9 @@ const JPEG_HEADER = Buffer.from( ) const JPEG_FOOTER = Buffer.from('ffd9', 'hex') +/** + * Convert stripped JPEG (from `photoStrippedSize`) to full JPEG + */ export function strippedPhotoToJpg(stripped: Buffer): Buffer { if (stripped.length < 3 || stripped[0] !== 1) { return stripped @@ -64,6 +72,10 @@ export function strippedPhotoToJpg(stripped: Buffer): Buffer { const SVG_LOOKUP = 'AACAAAAHAAALMAAAQASTAVAAAZaacaaaahaaalmaaaqastava.az0123456789-,' +/** + * Inflate compressed preview SVG path to full SVG path + * @param encoded + */ export function inflateSvgPath(encoded: Buffer): string { let path = 'M' const len = encoded.length @@ -87,6 +99,10 @@ export function inflateSvgPath(encoded: Buffer): string { return path } +/** + * Convert SVG path to SVG file + * @param path + */ export function svgPathToFile(path: string): Buffer { return Buffer.from( '' + @@ -101,6 +117,11 @@ export function svgPathToFile(path: string): Buffer { const FILENAME_REGEX = /^(?:file:)?(\/?.+[/\\])*(.+\..+)$/ +/** + * Get file name from file path + * + * @param path File path + */ export function extractFileName(path: string): string { const m = path.match(FILENAME_REGEX) if (m) return m[2] diff --git a/packages/client/src/utils/inline-utils.ts b/packages/client/src/utils/inline-utils.ts index 124bd53a..0ac4fc2f 100644 --- a/packages/client/src/utils/inline-utils.ts +++ b/packages/client/src/utils/inline-utils.ts @@ -7,6 +7,11 @@ import { TlBinaryWriter, } from '@mtcute/core' +/** + * Parse TDLib style inline message ID + * + * @param id Inline message ID + */ export function parseInlineMessageId( id: string ): tl.TypeInputBotInlineMessageID { @@ -31,6 +36,11 @@ export function parseInlineMessageId( } } +/** + * Generate TDLib style inline message ID + * + * @param id Inline message ID object + */ export function encodeInlineMessageId( id: tl.TypeInputBotInlineMessageID ): string { diff --git a/packages/client/src/utils/misc-utils.ts b/packages/client/src/utils/misc-utils.ts index 2dae7db4..3c7a7e63 100644 --- a/packages/client/src/utils/misc-utils.ts +++ b/packages/client/src/utils/misc-utils.ts @@ -2,6 +2,10 @@ import { tl } from '@mtcute/tl' import { MaybeDynamic, Message, MtClientError } from '../types' +/** + * Normalize phone number by stripping formatting + * @param phone Phone number + */ export function normalizePhoneNumber(phone: string): string { phone = phone.trim().replace(/[+()\s-]/g, '') if (!phone.match(/^\d+$/)) throw new MtClientError('Invalid phone number') diff --git a/packages/client/src/utils/voice-utils.ts b/packages/client/src/utils/voice-utils.ts index 197c4f50..e26e0118 100644 --- a/packages/client/src/utils/voice-utils.ts +++ b/packages/client/src/utils/voice-utils.ts @@ -1,3 +1,9 @@ +/** + * Decode 5-bit encoded voice message waveform into + * an array of waveform values (0-32). + * + * @param wf Encoded waveform + */ export function decodeWaveform(wf: Buffer): number[] { const bitsCount = wf.length * 8 const valuesCount = ~~(bitsCount / 5) @@ -33,6 +39,12 @@ export function decodeWaveform(wf: Buffer): number[] { return result } +/** + * Encode an array of waveform values into + * 5-bit encoded voice message waveform into + * + * @param wf Waveform values + */ export function encodeWaveform(wf: number[]): Buffer { const bitsCount = wf.length * 5 const bytesCount = ~~(bitsCount + 7) / 8 diff --git a/packages/core/src/network/authorization.ts b/packages/core/src/network/authorization.ts index 911c4352..5cd6f56c 100644 --- a/packages/core/src/network/authorization.ts +++ b/packages/core/src/network/authorization.ts @@ -94,7 +94,7 @@ async function rsaEncrypt( /** * Execute authorization flow on `connection` using `crypto`. * - * Returns tuple: [authKey, serverSalt, timeOffset] + * @returns tuple: [authKey, serverSalt, timeOffset] */ export async function doAuthorization( connection: SessionConnection, diff --git a/packages/core/src/network/session-connection.ts b/packages/core/src/network/session-connection.ts index a35941f4..74e9a25e 100644 --- a/packages/core/src/network/session-connection.ts +++ b/packages/core/src/network/session-connection.ts @@ -113,6 +113,9 @@ function makeNiceStack( let nextConnectionUid = 0 +/** + * A connection to a single DC. + */ export class SessionConnection extends PersistentConnection { readonly params!: SessionConnectionParams diff --git a/packages/core/src/network/transports/obfuscated.ts b/packages/core/src/network/transports/obfuscated.ts index 011bf6c7..659d4855 100644 --- a/packages/core/src/network/transports/obfuscated.ts +++ b/packages/core/src/network/transports/obfuscated.ts @@ -11,7 +11,7 @@ const BAD_HEADERS = [ Buffer.from('eeeeeeee', 'hex'), ] -interface MtProxyInfo { +export interface MtProxyInfo { dcId: number secret: Buffer test: boolean diff --git a/packages/core/src/utils/bigint-utils.ts b/packages/core/src/utils/bigint-utils.ts index 3eb6f56b..eff34e9f 100644 --- a/packages/core/src/utils/bigint-utils.ts +++ b/packages/core/src/utils/bigint-utils.ts @@ -2,6 +2,13 @@ import bigInt, { BigInteger } from 'big-integer' import { randomBytes } from './buffer-utils' +/** + * Convert a big integer to a buffer + * + * @param value Value to convert + * @param length Length of the resulting buffer (by default it's computed automatically) + * @param le Whether to use little-endian encoding + */ export function bigIntToBuffer( value: BigInteger, length = 0, @@ -23,6 +30,14 @@ export function bigIntToBuffer( return buffer } +/** + * Convert a buffer to a big integer + * + * @param buffer Buffer to convert + * @param offset Offset to start reading from + * @param length Length to read + * @param le Whether to use little-endian encoding + */ export function bufferToBigInt( buffer: Buffer, offset = 0, @@ -36,10 +51,20 @@ export function bufferToBigInt( return bigInt.fromArray(arr, 256) } +/** + * Generate a random big integer of the given size (in bytes) + * @param size Size in bytes + */ export function randomBigInt(size: number): BigInteger { return bufferToBigInt(randomBytes(size)) } +/** + * Generate a random big integer in the range [min, max) + * + * @param max Maximum value (exclusive) + * @param min Minimum value (inclusive) + */ export function randomBigIntInRange( max: BigInteger, min = bigInt.one diff --git a/packages/core/src/utils/buffer-utils.ts b/packages/core/src/utils/buffer-utils.ts index 2a360c96..d7940df9 100644 --- a/packages/core/src/utils/buffer-utils.ts +++ b/packages/core/src/utils/buffer-utils.ts @@ -1,7 +1,13 @@ export { _randomBytes as randomBytes } from './platform/crypto' +const b64urlAvailable = Buffer.isEncoding('base64url') + // from https://github.com/feross/typedarray-to-buffer // licensed under MIT +/** + * Convert a typed array to a Buffer. + * @param arr Typed array to convert + */ export function typedArrayToBuffer(arr: NodeJS.TypedArray): Buffer { return ArrayBuffer.isView(arr) ? // To avoid a copy, use the typed array's underlying ArrayBuffer to back @@ -11,6 +17,12 @@ export function typedArrayToBuffer(arr: NodeJS.TypedArray): Buffer { Buffer.from(arr) } +/** + * Check if two buffers are equal + * + * @param a First buffer + * @param b Second buffer + */ export function buffersEqual(a: Buffer, b: Buffer): boolean { if (a.length !== b.length) return false @@ -21,6 +33,12 @@ export function buffersEqual(a: Buffer, b: Buffer): boolean { return true } +/** + * Perform XOR operation on two buffers and return the new buffer + * + * @param data Buffer to XOR + * @param key Key to XOR with + */ export function xorBuffer(data: Buffer, key: Buffer): Buffer { const ret = Buffer.alloc(data.length) for (let i = 0; i < data.length; i++) { @@ -29,25 +47,56 @@ export function xorBuffer(data: Buffer, key: Buffer): Buffer { return ret } +/** + * Perform XOR operation on two buffers in-place + * + * @param data Buffer to XOR + * @param key Key to XOR with + */ export function xorBufferInPlace(data: Buffer, key: Buffer): void { for (let i = 0; i < data.length; i++) { data[i] ^= key[i] } } +/** + * Copy a buffer + * + * @param buf Buffer to copy + * @param start Start offset + * @param end End offset + */ export function cloneBuffer(buf: Buffer, start = 0, end = buf.length): Buffer { const ret = Buffer.alloc(end - start) buf.copy(ret, 0, start, end) return ret } +/** + * Parse url-safe base64 string + * + * @param str String to parse + */ export function parseUrlSafeBase64(str: string): Buffer { + if (b64urlAvailable) { + return Buffer.from(str, 'base64url') + } + str = str.replace(/-/g, '+').replace(/_/g, '/') while (str.length % 4) str += '=' return Buffer.from(str, 'base64') } +/** + * Convert a buffer to url-safe base64 string + * + * @param buf Buffer to convert + */ export function encodeUrlSafeBase64(buf: Buffer): string { + if (b64urlAvailable) { + return buf.toString('base64url') + } + return buf .toString('base64') .replace(/\+/g, '-') diff --git a/packages/core/src/utils/condition-variable.ts b/packages/core/src/utils/condition-variable.ts index 66d7e257..04488031 100644 --- a/packages/core/src/utils/condition-variable.ts +++ b/packages/core/src/utils/condition-variable.ts @@ -1,3 +1,6 @@ +/** + * Class implementing a condition variable like behaviour. + */ export class ConditionVariable { private _notify?: () => void private _timeout?: NodeJS.Timeout diff --git a/packages/core/src/utils/controllable-promise.ts b/packages/core/src/utils/controllable-promise.ts index a56ab430..8d87adca 100644 --- a/packages/core/src/utils/controllable-promise.ts +++ b/packages/core/src/utils/controllable-promise.ts @@ -1,14 +1,26 @@ +/** + * A promise that can be resolved or rejected from outside. + */ export type ControllablePromise = Promise & { resolve(val: T): void reject(err?: unknown): void } +/** + * A promise that can be cancelled. + */ export type CancellablePromise = Promise & { cancel(): void } +/** + * The promise was cancelled + */ export class PromiseCancelledError extends Error {} +/** + * Creates a promise that can be resolved or rejected from outside. + */ export function createControllablePromise(): ControllablePromise { let _resolve: any let _reject: any @@ -21,6 +33,11 @@ export function createControllablePromise(): ControllablePromise { return promise as ControllablePromise } +/** + * Creates a promise that can be cancelled. + * + * @param onCancel Callback to call when cancellation is requested + */ export function createCancellablePromise( onCancel: () => void ): ControllablePromise & CancellablePromise { diff --git a/packages/core/src/utils/crypto/abstract.ts b/packages/core/src/utils/crypto/abstract.ts index 1ad0690f..30291113 100644 --- a/packages/core/src/utils/crypto/abstract.ts +++ b/packages/core/src/utils/crypto/abstract.ts @@ -43,7 +43,7 @@ export interface ICryptoProvider { factorizePQ(pq: Buffer): MaybeAsync<[Buffer, Buffer]> } -export abstract class BaseCryptoProvider implements ICryptoProvider { +export abstract class BaseCryptoProvider { createAesIge(key: Buffer, iv: Buffer): IEncryptionScheme { return new AesModeOfOperationIge(key, iv, this.createAesEcb(key)) } @@ -54,29 +54,7 @@ export abstract class BaseCryptoProvider implements ICryptoProvider { initialize(): void {} - abstract createAesCtr( - key: Buffer, - iv: Buffer, - encrypt: boolean - ): IEncryptionScheme - abstract createAesEcb(key: Buffer): IEncryptionScheme - - abstract pbkdf2( - password: Buffer, - salt: Buffer, - iterations: number, - keylen?: number, // = 64 - algo?: string // sha1 or sha512 (default sha512) - ): MaybeAsync - - abstract sha1(data: Buffer): MaybeAsync - - abstract sha256(data: Buffer): MaybeAsync - - abstract hmacSha256(data: Buffer, key: Buffer): MaybeAsync - - abstract createMd5(): IHashMethod } export type CryptoProviderFactory = () => ICryptoProvider diff --git a/packages/core/src/utils/crypto/common.ts b/packages/core/src/utils/crypto/common.ts index 4cff5838..e963440e 100644 --- a/packages/core/src/utils/crypto/common.ts +++ b/packages/core/src/utils/crypto/common.ts @@ -1,6 +1,9 @@ import { xorBufferInPlace } from '../buffer-utils' import { IEncryptionScheme } from './abstract' +/** + * AES mode of operation IGE implementation in JS + */ export class AesModeOfOperationIge implements IEncryptionScheme { private _key: Buffer private _iv: Buffer diff --git a/packages/core/src/utils/crypto/factorization.ts b/packages/core/src/utils/crypto/factorization.ts index baea1beb..0727754d 100644 --- a/packages/core/src/utils/crypto/factorization.ts +++ b/packages/core/src/utils/crypto/factorization.ts @@ -6,6 +6,10 @@ import { randomBigIntInRange, } from '../bigint-utils' +/** + * Factorize `p*q` to `p` and `q` synchronously using Brent-Pollard rho algorithm + * @param pq + */ export function factorizePQSync(pq: Buffer): [Buffer, Buffer] { const pq_ = bufferToBigInt(pq) diff --git a/packages/core/src/utils/crypto/forge-crypto.ts b/packages/core/src/utils/crypto/forge-crypto.ts index 441b7543..50b2606f 100644 --- a/packages/core/src/utils/crypto/forge-crypto.ts +++ b/packages/core/src/utils/crypto/forge-crypto.ts @@ -1,12 +1,20 @@ import { MaybeAsync } from '../../types' -import { BaseCryptoProvider, IEncryptionScheme, IHashMethod } from './abstract' +import { + BaseCryptoProvider, + ICryptoProvider, + IEncryptionScheme, + IHashMethod, +} from './abstract' let forge: any = null try { forge = require('node-forge') } catch (e) {} -export class ForgeCryptoProvider extends BaseCryptoProvider { +export class ForgeCryptoProvider + extends BaseCryptoProvider + implements ICryptoProvider +{ constructor() { super() if (!forge) diff --git a/packages/core/src/utils/crypto/keys.ts b/packages/core/src/utils/crypto/keys.ts index 52f1d7b5..e3c9a9b7 100644 --- a/packages/core/src/utils/crypto/keys.ts +++ b/packages/core/src/utils/crypto/keys.ts @@ -5,6 +5,14 @@ import keysIndex, { TlPublicKey } from '@mtcute/tl/binary/rsa-keys' import { parseAsn1, parsePemContents } from '../binary/asn1-parser' import { ICryptoProvider } from './abstract' +/** + * Parse PEM-encoded RSA public key information into modulus and exponent + * and compute its fingerprint as defined by MTProto. + * + * @param crypto Crypto provider + * @param key PEM-encoded RSA public key + * @param old Whether this is an "old" key + */ export async function parsePublicKey( crypto: ICryptoProvider, key: string, @@ -32,6 +40,13 @@ export async function parsePublicKey( } } +/** + * Add public key to the global index. + * + * @param crypto Crypto provider + * @param key PEM-encoded RSA public key + * @param old Whether this is an "old" key + */ export async function addPublicKey( crypto: ICryptoProvider, key: string, @@ -41,6 +56,12 @@ export async function addPublicKey( keysIndex[parsed.fingerprint] = parsed } +/** + * Get public key by its fingerprint. + * + * @param fingerprints Fingerprints to match. The first one to match is returned. + * @param allowOld Whether to allow "old" keys + */ export function findKeyByFingerprints( fingerprints: (string | Long)[], allowOld = false diff --git a/packages/core/src/utils/crypto/mtproto.ts b/packages/core/src/utils/crypto/mtproto.ts index f6f2bd0d..732f5c48 100644 --- a/packages/core/src/utils/crypto/mtproto.ts +++ b/packages/core/src/utils/crypto/mtproto.ts @@ -1,6 +1,14 @@ import { IEncryptionScheme, ICryptoProvider } from './abstract' -// returns tuple: [key, iv] +/** + * Generate AES key and IV from nonces as defined by MTProto. + * Used in authorization flow. + * + * @param crypto Crypto provider + * @param serverNonce Server nonce + * @param newNonce New nonce + * @returns Tuple: `[key, iv]` + */ export async function generateKeyAndIvFromNonce( crypto: ICryptoProvider, serverNonce: Buffer, @@ -16,6 +24,15 @@ export async function generateKeyAndIvFromNonce( return [key, iv] } +/** + * Create AES IGE instance for message (given auth key and message key) + * as defined by MTProto v2. + * + * @param crypto Crypto provider + * @param authKey Authorization key + * @param messageKey Message key + * @param client Whether this is a client to server message + */ export async function createAesIgeForMessage( crypto: ICryptoProvider, authKey: Buffer, @@ -44,6 +61,15 @@ export async function createAesIgeForMessage( return crypto.createAesIge(key, iv) } +/** + * Create AES IGE instance for file (given auth key and message key) + * as defined by MTProto v1. + * + * @param crypto Crypto provider + * @param authKey Authorization key + * @param messageKey Message key + * @param client Whether this is a client to server message + */ export async function createAesIgeForMessageOld( crypto: ICryptoProvider, authKey: Buffer, diff --git a/packages/core/src/utils/crypto/node-crypto.ts b/packages/core/src/utils/crypto/node-crypto.ts index cf49122a..1db06e9f 100644 --- a/packages/core/src/utils/crypto/node-crypto.ts +++ b/packages/core/src/utils/crypto/node-crypto.ts @@ -7,9 +7,17 @@ import { } from 'crypto' import { MaybeAsync } from '../../types' -import { BaseCryptoProvider, IEncryptionScheme, IHashMethod } from './abstract' +import { + BaseCryptoProvider, + ICryptoProvider, + IEncryptionScheme, + IHashMethod, +} from './abstract' -export class NodeCryptoProvider extends BaseCryptoProvider { +export class NodeCryptoProvider + extends BaseCryptoProvider + implements ICryptoProvider +{ constructor() { super() } diff --git a/packages/core/src/utils/crypto/password.ts b/packages/core/src/utils/crypto/password.ts index 2e1e4ea3..56387f26 100644 --- a/packages/core/src/utils/crypto/password.ts +++ b/packages/core/src/utils/crypto/password.ts @@ -5,13 +5,22 @@ import { randomBytes, xorBuffer } from '../buffer-utils' import { bigIntToBuffer, bufferToBigInt } from '../bigint-utils' import { ICryptoProvider } from './abstract' +/** + * Compute password hash as defined by MTProto. + * + * See https://core.telegram.org/api/srp#checking-the-password-with-srp + * + * @param crypto Crypto provider + * @param password Password + * @param salt1 Salt 1 + * @param salt2 Salt 2 + */ export async function computePasswordHash( crypto: ICryptoProvider, password: Buffer, salt1: Buffer, salt2: Buffer ): Promise { - // https://core.telegram.org/api/srp#checking-the-password-with-srp const SH = (data: Buffer, salt: Buffer) => crypto.sha256(Buffer.concat([salt, data, salt])) const PH1 = async (pwd: Buffer, salt1: Buffer, salt2: Buffer) => @@ -25,6 +34,13 @@ export async function computePasswordHash( return PH2(password, salt1, salt2) } +/** + * Compute new password SRP hash as defined by MTProto. + * + * @param crypto Crypto provider + * @param algo KDF algorithm + * @param password Password + */ export async function computeNewPasswordHash( crypto: ICryptoProvider, algo: tl.RawPasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, @@ -49,6 +65,13 @@ export async function computeNewPasswordHash( return bigIntToBuffer(g.modPow(x, p), 256) } +/** + * Compute SRP check parameters for 2fa password as defined by MTProto. + * + * @param crypto Crypto provider + * @param request SRP request + * @param password 2fa password + */ export async function computeSrpParams( crypto: ICryptoProvider, request: tl.account.RawPassword, diff --git a/packages/core/src/utils/default-dcs.ts b/packages/core/src/utils/default-dcs.ts index ed7e891b..620035a6 100644 --- a/packages/core/src/utils/default-dcs.ts +++ b/packages/core/src/utils/default-dcs.ts @@ -1,5 +1,6 @@ import { tl } from '@mtcute/tl' +/** @internal */ export const defaultProductionDc: tl.RawDcOption = { _: 'dcOption', ipAddress: '149.154.167.50', @@ -7,6 +8,7 @@ export const defaultProductionDc: tl.RawDcOption = { id: 2, } +/** @internal */ export const defaultProductionIpv6Dc: tl.RawDcOption = { _: 'dcOption', ipAddress: '2001:67c:4e8:f002::a', @@ -15,6 +17,7 @@ export const defaultProductionIpv6Dc: tl.RawDcOption = { id: 2, } +/** @internal */ export const defaultTestDc: tl.RawDcOption = { _: 'dcOption', ipAddress: '149.154.167.40', @@ -22,6 +25,7 @@ export const defaultTestDc: tl.RawDcOption = { id: 2, } +/** @internal */ export const defaultTestIpv6Dc: tl.RawDcOption = { _: 'dcOption', ipAddress: '2001:67c:4e8:f002::e', diff --git a/packages/core/src/utils/flood-control.ts b/packages/core/src/utils/flood-control.ts index 4aa307d2..2e4dd38a 100644 --- a/packages/core/src/utils/flood-control.ts +++ b/packages/core/src/utils/flood-control.ts @@ -6,6 +6,9 @@ interface FloodControlLimit { pos: number } +/** + * Flood limiter, based on TDlib + */ export class FloodControl { private _wakeupAt = 1 private _withoutUpdate = 0 diff --git a/packages/core/src/utils/linked-list.ts b/packages/core/src/utils/linked-list.ts index 322ceb79..4a3fd4a8 100644 --- a/packages/core/src/utils/linked-list.ts +++ b/packages/core/src/utils/linked-list.ts @@ -4,6 +4,9 @@ interface LinkedListItem { p?: LinkedListItem } +/** + * A sorted linked list. + */ export class SortedLinkedList { _first?: LinkedListItem _last?: LinkedListItem diff --git a/packages/core/src/utils/long-utils.ts b/packages/core/src/utils/long-utils.ts index 44346bd3..361e6888 100644 --- a/packages/core/src/utils/long-utils.ts +++ b/packages/core/src/utils/long-utils.ts @@ -1,6 +1,11 @@ import Long from 'long' import { getRandomInt } from './misc-utils' +/** + * Get a random Long + * + * @param unsigned Whether the number should be unsigned + */ export function randomLong(unsigned = false): Long { const lo = getRandomInt(0xffffffff) const hi = getRandomInt(0xffffffff) @@ -8,6 +13,12 @@ export function randomLong(unsigned = false): Long { return new Long(lo, hi, unsigned) } +/** + * Remove a Long from an array + * + * @param arr Array to remove from + * @param val Value to remove + */ export function removeFromLongArray(arr: Long[], val: Long): boolean { for (let i = 0; i < arr.length; i++) { const v = arr[i] diff --git a/packages/core/src/utils/lru-string-set.ts b/packages/core/src/utils/lru-string-set.ts index 30039d5e..ce38feb9 100644 --- a/packages/core/src/utils/lru-string-set.ts +++ b/packages/core/src/utils/lru-string-set.ts @@ -9,11 +9,9 @@ interface OneWayLinkedList { * Simple class implementing LRU-like behaviour for a set, * falling back to objects when `Set` is not available. * - * Used to store recently received message IDs in {@link TelegramConnection} + * Used to store recently received message IDs in {@link SessionConnection} * * Uses one-way linked list internally to keep track of insertion order - * - * @internal */ export class LruSet { private _capacity: number diff --git a/packages/core/src/utils/misc-utils.ts b/packages/core/src/utils/misc-utils.ts index 4abcd87b..aba2e9e4 100644 --- a/packages/core/src/utils/misc-utils.ts +++ b/packages/core/src/utils/misc-utils.ts @@ -1,3 +1,8 @@ +/** + * Sleep for the given number of ms + * + * @param ms Number of ms to sleep + */ export const sleep = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)) diff --git a/packages/core/src/utils/peer-utils.ts b/packages/core/src/utils/peer-utils.ts index 44c50145..f21ef9c5 100644 --- a/packages/core/src/utils/peer-utils.ts +++ b/packages/core/src/utils/peer-utils.ts @@ -17,6 +17,9 @@ const MAX_USER_ID = 1099511627775 // const MAX_CHANNEL_ID = 997852516352 const MIN_MARKED_CHANNEL_ID = -1997852516352 // ZERO_CHANNEL_ID - MAX_CHANNEL_ID +/** + * Add or remove channel marker from ID + */ export function toggleChannelIdMark(id: number): number { return ZERO_CHANNEL_ID - id } @@ -46,9 +49,9 @@ export function getBarePeerId(peer: tl.TypePeer): number { * - ID is negated and `-1e12` is subtracted for channels */ export function getMarkedPeerId(peerId: number, peerType: BasicPeerType): number -export function getMarkedPeerId(peer: tl.TypePeer | tl.TypeInputPeer): number +export function getMarkedPeerId(peer: tl.TypePeer | tl.TypeInputPeer | tl.TypeInputUser | tl.TypeInputChannel): number export function getMarkedPeerId( - peer: tl.TypePeer | tl.TypeInputPeer | number, + peer: tl.TypePeer | tl.TypeInputPeer | tl.TypeInputUser | tl.TypeInputChannel | number, peerType?: BasicPeerType ): number { if (typeof peer === 'number') { @@ -66,12 +69,14 @@ export function getMarkedPeerId( switch (peer._) { case 'peerUser': case 'inputPeerUser': + case 'inputUser': return peer.userId case 'peerChat': case 'inputPeerChat': return -peer.chatId case 'peerChannel': case 'inputPeerChannel': + case 'inputChannel': return ZERO_CHANNEL_ID - peer.channelId } @@ -112,6 +117,11 @@ export function getBasicPeerType(peer: tl.TypePeer | number): BasicPeerType { throw new Error(`Invalid marked peer id: ${peer}`) } +/** + * Extract bare peer ID from marked ID. + * + * @param peerId Marked peer ID + */ export function markedPeerIdToBare(peerId: number): number { const type = getBasicPeerType(peerId) diff --git a/packages/core/src/utils/sorted-array.ts b/packages/core/src/utils/sorted-array.ts index f3d68866..66ce5135 100644 --- a/packages/core/src/utils/sorted-array.ts +++ b/packages/core/src/utils/sorted-array.ts @@ -1,13 +1,15 @@ -type Comparator = (a: T, b: T) => number - -// Comparator is always called like: -// comparator(itemFromSortedArray, yourItem) +// +// +/** + * Array that adds elements in sorted order. + * + * Comparator is always called like: comparator(itemFromSortedArray, yourItem) + */ export class SortedArray { readonly raw: T[] - comparator: Comparator - constructor(array: T[] = [], comparator: Comparator) { + constructor(array: T[] = [], readonly comparator: (a: T, b: T) => number) { this.raw = array.sort(comparator) this.comparator = comparator } diff --git a/packages/dispatcher/src/callback-data-builder.ts b/packages/dispatcher/src/callback-data-builder.ts index 6611120e..e6dcdab5 100644 --- a/packages/dispatcher/src/callback-data-builder.ts +++ b/packages/dispatcher/src/callback-data-builder.ts @@ -8,7 +8,7 @@ import { UpdateFilter } from './filters' * * This can be used to simplify management of different callbacks. * - * [Learn more in the docs](//mt.tei.su/guide/topics/keyboards.html#callback-data-builders) + * [Learn more in the docs](/guide/topics/keyboards.html#callback-data-builders) */ export class CallbackDataBuilder { private readonly _fields: T[] diff --git a/packages/http-proxy/index.ts b/packages/http-proxy/index.ts index bdcd0c1a..7e9ba790 100644 --- a/packages/http-proxy/index.ts +++ b/packages/http-proxy/index.ts @@ -21,6 +21,9 @@ export class HttpProxyConnectionError extends Error { } } +/** + * HTTP(s) proxy settings + */ export interface HttpProxySettings { /** * Host or IP of the proxy (e.g. `proxy.example.com`, `1.2.3.4`) @@ -171,6 +174,12 @@ export abstract class BaseHttpProxyTcpTransport extends BaseTcpTransport { } } +/** + * HTTP(s) TCP transport using an intermediate packet codec. + * + * Should be the one passed as `transport` to {@link TelegramClient} constructor + * (unless you want to use a custom codec). + */ export class HttpProxyTcpTransport extends BaseHttpProxyTcpTransport { _packetCodec = new IntermediatePacketCodec() } diff --git a/packages/markdown-parser/src/index.ts b/packages/markdown-parser/src/index.ts index 3178e152..3accc943 100644 --- a/packages/markdown-parser/src/index.ts +++ b/packages/markdown-parser/src/index.ts @@ -1,6 +1,5 @@ import Long from 'long' -import type { IMessageEntityParser, MessageEntity, tl } from '@mtcute/client' -import { FormattedString } from '@mtcute/client' +import type { IMessageEntityParser, MessageEntity, tl, FormattedString } from '@mtcute/client' const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/ @@ -60,7 +59,6 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser { name = 'markdown' /** - * Escape the text so it can be safely used inside Markdown code. * * @param str String to be escaped */ @@ -399,6 +397,10 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser { startTag = '[' endTag = `](tg://user?id=${entity.userId!})` break + case 'emoji': + startTag = '[' + endTag = `](tg://emoji?id=${entity.emojiId!})` + break default: continue } diff --git a/packages/mtproxy/index.ts b/packages/mtproxy/index.ts index 72735003..78f74f5b 100644 --- a/packages/mtproxy/index.ts +++ b/packages/mtproxy/index.ts @@ -12,6 +12,9 @@ import { import { FakeTlsPacketCodec, generateFakeTlsHeader } from './fake-tls' +/** + * MTProto proxy settings + */ export interface MtProxySettings { /** * Host or IP of the proxy (e.g. `proxy.example.com`, `1.2.3.4`) diff --git a/packages/node/index.ts b/packages/node/index.ts index e7faed06..d29f6b2f 100644 --- a/packages/node/index.ts +++ b/packages/node/index.ts @@ -18,6 +18,9 @@ try { } catch (e) {} export namespace NodeTelegramClient { + /** + * Options for {@link NodeTelegramClient} + */ export interface Options extends Omit { /** @@ -44,10 +47,14 @@ export namespace NodeTelegramClient { } /** - * Tiny wrapper over `TelegramClient` for usage inside Node JS. + * Tiny wrapper over {@link TelegramClient} for usage inside Node JS. * * This automatically sets the parse modes, native * crypto addon and defaults to SQLite session. + * + * Documentation for this class only contains the + * difference between {@link TelegramClient} and {@link NodeTelegramClient}. + * For the complete documentation, please refer to {@link TelegramClient}. */ export class NodeTelegramClient extends TelegramClient { constructor(opts: NodeTelegramClient.Options) { @@ -75,7 +82,6 @@ export class NodeTelegramClient extends TelegramClient { * Associated `readline` interface is closed * after `run()` returns, or with the client. * - * * @param text Text of the question */ input(text: string): Promise { diff --git a/packages/socks-proxy/index.ts b/packages/socks-proxy/index.ts index c7fb9c15..9b52bb2c 100644 --- a/packages/socks-proxy/index.ts +++ b/packages/socks-proxy/index.ts @@ -25,6 +25,9 @@ export class SocksProxyConnectionError extends Error { } } +/** + * Settings for a SOCKS4/5 proxy + */ export interface SocksProxySettings { /** * Host or IP of the proxy (e.g. `proxy.example.com`, `1.2.3.4`) @@ -49,7 +52,7 @@ export interface SocksProxySettings { /** * Version of the SOCKS proxy (4 or 5) * - * Defaults to `5`. + * @default `5` */ version?: 4 | 5 } @@ -475,6 +478,12 @@ export abstract class BaseSocksTcpTransport extends BaseTcpTransport { } } +/** + * Socks TCP transport using an intermediate packet codec. + * + * Should be the one passed as `transport` to {@link TelegramClient} constructor + * (unless you want to use a custom codec). + */ export class SocksTcpTransport extends BaseSocksTcpTransport { _packetCodec = new IntermediatePacketCodec() } diff --git a/packages/sqlite/README.md b/packages/sqlite/README.md index a731928e..d7b93447 100644 --- a/packages/sqlite/README.md +++ b/packages/sqlite/README.md @@ -1,6 +1,6 @@ # @mtcute/sqlite -SQLite backed storage for mtcute. +SQLite backed storage for mtcute, based on `better-sqlite3` ## Usage diff --git a/packages/tl-runtime/src/platform/gzip.ts b/packages/tl-runtime/src/platform/gzip.ts index 8a336dc2..1281fbde 100644 --- a/packages/tl-runtime/src/platform/gzip.ts +++ b/packages/tl-runtime/src/platform/gzip.ts @@ -1,9 +1,21 @@ import { deflateSync, gunzipSync } from 'zlib' +/** + * Decompress a buffer with gzip. + * @param buf Buffer to decompress + */ export function gzipInflate(buf: Buffer): Buffer { return gunzipSync(buf) } +/** + * Compress a buffer with gzip. + * + * @param buf Buffer to compress + * @param maxRatio + * Maximum compression ratio. If the resulting buffer is smaller than + * `buf.length * ratio`, `null` is returned. + */ export function gzipDeflate(buf: Buffer, maxRatio?: number): Buffer | null { if (maxRatio) { try { diff --git a/packages/tl-runtime/src/reader.ts b/packages/tl-runtime/src/reader.ts index edde15a0..c3bbc851 100644 --- a/packages/tl-runtime/src/reader.ts +++ b/packages/tl-runtime/src/reader.ts @@ -18,10 +18,25 @@ const TWO_PWR_32_DBL = (1 << 16) * (1 << 16) */ export type TlReaderMap = Record any> +/** + * Reader for TL objects. + */ export class TlBinaryReader { + /** + * Underlying buffer. + */ data: Buffer + + /** + * Position in the buffer. + */ pos = 0 + /** + * @param objectsMap Readers map + * @param data Buffer to read from + * @param start Position to start reading from + */ constructor( readonly objectsMap: TlReaderMap | undefined, data: Buffer, @@ -31,10 +46,23 @@ export class TlBinaryReader { this.pos = start } + /** + * Create a new reader without objects map for manual usage + * + * @param data Buffer to read from + * @param start Position to start reading from + */ static manual(data: Buffer, start = 0): TlBinaryReader { return new TlBinaryReader(undefined, data, start) } + /** + * Deserialize a single object + * + * @param objectsMap Readers map + * @param data Buffer to read from + * @param start Position to start reading from + */ static deserializeObject( objectsMap: TlReaderMap, data: Buffer, @@ -55,6 +83,9 @@ export class TlBinaryReader { return res } + /** + * Get the next {@link uint} without advancing the reader cursor + */ peekUint(): number { // e.g. for checking ctor number return this.data.readUInt32LE(this.pos) @@ -98,6 +129,10 @@ export class TlBinaryReader { ) } + /** + * Read raw bytes of the given length + * @param bytes Length of the buffer to read + */ raw(bytes = -1): Buffer { if (bytes === -1) bytes = this.data.length - this.pos @@ -188,10 +223,20 @@ export class TlBinaryReader { return ret } + /** + * Advance the reader cursor by the given amount of bytes + * + * @param delta Amount of bytes to advance (can be negative) + */ seek(delta: number): void { this.seekTo(this.pos + delta) } + /** + * Seek to the given position + * + * @param pos Position to seek to + */ seekTo(pos: number): void { if (pos >= this.data.length || pos < 0) throw new RangeError('New position is out of range') diff --git a/packages/tl-runtime/src/writer.ts b/packages/tl-runtime/src/writer.ts index 552816b4..453f9afe 100644 --- a/packages/tl-runtime/src/writer.ts +++ b/packages/tl-runtime/src/writer.ts @@ -7,11 +7,26 @@ const TWO_PWR_32_DBL = (1 << 16) * (1 << 16) */ export type TlWriterMap = Record void> +/** + * Counter of the required number of bytes to encode a given object. + * + * Used as a pre-pass before using {@link TlBinaryWriter} + * to avoid unnecessary allocations. + */ export class TlSerializationCounter { count = 0 + /** + * @param objectMap Writers map + */ constructor(readonly objectMap: TlWriterMap) {} + /** + * Count bytes required to serialize the given object. + * + * @param objectMap Writers map + * @param obj Object to count bytes for + */ static countNeededBytes( objectMap: TlWriterMap, obj: { _: string } @@ -21,6 +36,12 @@ export class TlSerializationCounter { return cnt.count } + /** + * Count overhead in bytes for the given number of bytes when + * encoded as `bytes` TL type. + * + * @param size Number of bytes + */ static countBytesOverhead(size: number): number { let res = 0 @@ -103,10 +124,25 @@ export class TlSerializationCounter { } } +/** + * Writer for TL objects. + */ export class TlBinaryWriter { + /** + * Underlying buffer. + */ buffer: Buffer + + /** + * Current position in the buffer. + */ pos: number + /** + * @param objectMap Writers map + * @param buffer Buffer to write to + * @param start Position to start writing at + */ constructor( readonly objectMap: TlWriterMap | undefined, buffer: Buffer, @@ -116,18 +152,43 @@ export class TlBinaryWriter { this.pos = start } + /** + * Create a new writer with the given size. + * + * @param objectMap Writers map + * @param size Size of the writer's buffer + */ static alloc(objectMap: TlWriterMap, size: number): TlBinaryWriter { return new TlBinaryWriter(objectMap, Buffer.allocUnsafe(size)) } + /** + * Create a new writer without objects map for manual usage + * + * @param buffer Buffer to write to + * @param start Position to start writing at + */ static manual(buffer: Buffer, start = 0): TlBinaryWriter { return new TlBinaryWriter(undefined, buffer, start) } + /** + * Create a new writer without objects map for manual usage + * with a given size + * + * @param size Size of the writer's buffer + */ static manualAlloc(size: number): TlBinaryWriter { return new TlBinaryWriter(undefined, Buffer.allocUnsafe(size)) } + /** + * Serialize a single object + * + * @param objectMap Writers map + * @param obj Object to serialize + * @param knownSize In case the size is known, pass it here + */ static serializeObject( objectMap: TlWriterMap, obj: { _: string }, @@ -193,6 +254,10 @@ export class TlBinaryWriter { this.pos += 4 } + /** + * Write raw bytes to the buffer + * @param val Buffer to write + */ raw(val: Buffer): void { val.copy(this.buffer, this.pos) this.pos += val.length @@ -249,6 +314,9 @@ export class TlBinaryWriter { val.forEach((it) => fn.call(this, it, bare)) } + /** + * Get the resulting buffer + */ result(): Buffer { return this.buffer.slice(0, this.pos) }