From b581ab2f2ce02d5e30d5ea1113b4d133c5e2d10f Mon Sep 17 00:00:00 2001 From: Andrii Fedyk Date: Wed, 10 Dec 2025 10:19:26 +0000 Subject: [PATCH 1/6] add ping support --- package-lock.json | 76 ++++++++++++++++++++++---------------------- package.json | 4 +-- src/builder.ts | 1 + src/mtptoto-types.ts | 9 ++++++ src/rpc.ts | 29 ++++++++++++++--- 5 files changed, 75 insertions(+), 44 deletions(-) diff --git a/package-lock.json b/package-lock.json index 97a6181..d333c57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,20 @@ { "name": "mtproto-nodejs-client", - "version": "8.0.0", + "version": "8.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mtproto-nodejs-client", - "version": "8.0.0", + "version": "8.0.1", "license": "GPL-3.0", "dependencies": { "@types/aes-js": "^3.1.4", "@types/debug": "^4.1.12", "@types/lodash.debounce": "^4.0.9", - "@types/node": "^24.10.1", + "@types/node": "^24.10.2", "@types/pako": "^2.0.4", - "@typescript/native-preview": "^7.0.0-dev.20251113.1", + "@typescript/native-preview": "^7.0.0-dev.20251210.1", "aes-js": "3.1.2", "big-integer": "1.6.52", "debug": "4.4.3", @@ -58,9 +58,9 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, "node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "version": "24.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.2.tgz", + "integrity": "sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==", "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -73,27 +73,27 @@ "license": "MIT" }, "node_modules/@typescript/native-preview": { - "version": "7.0.0-dev.20251113.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20251113.1.tgz", - "integrity": "sha512-gxe+j1h5DSnP/rE2+4BGSzfKRg2RdmMtHlPX+oH1iLGOHTWLQ5IVtS7hW2IlGJY2Y1dXXDARh9wRYzePPXyh8g==", + "version": "7.0.0-dev.20251210.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20251210.1.tgz", + "integrity": "sha512-SiSRpp42K80X+qJKAi8qvjo2xMZIgJgROXCJo0MiZ8a6fjQK6IetRfUvwdApUsDltf/VaEc/j8v2Hiqp8SGhLQ==", "license": "Apache-2.0", "bin": { "tsgo": "bin/tsgo.js" }, "optionalDependencies": { - "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20251113.1", - "@typescript/native-preview-darwin-x64": "7.0.0-dev.20251113.1", - "@typescript/native-preview-linux-arm": "7.0.0-dev.20251113.1", - "@typescript/native-preview-linux-arm64": "7.0.0-dev.20251113.1", - "@typescript/native-preview-linux-x64": "7.0.0-dev.20251113.1", - "@typescript/native-preview-win32-arm64": "7.0.0-dev.20251113.1", - "@typescript/native-preview-win32-x64": "7.0.0-dev.20251113.1" + "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20251210.1", + "@typescript/native-preview-darwin-x64": "7.0.0-dev.20251210.1", + "@typescript/native-preview-linux-arm": "7.0.0-dev.20251210.1", + "@typescript/native-preview-linux-arm64": "7.0.0-dev.20251210.1", + "@typescript/native-preview-linux-x64": "7.0.0-dev.20251210.1", + "@typescript/native-preview-win32-arm64": "7.0.0-dev.20251210.1", + "@typescript/native-preview-win32-x64": "7.0.0-dev.20251210.1" } }, "node_modules/@typescript/native-preview-darwin-arm64": { - "version": "7.0.0-dev.20251113.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20251113.1.tgz", - "integrity": "sha512-ArzjtsMWWUSRnqv/C7p+Lv7SJbL9EDGBPDgBvyI+xIcTqyD9WW2sMStjc1jTwWsimmQXzQQ8jZYpf+xSGj5boQ==", + "version": "7.0.0-dev.20251210.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20251210.1.tgz", + "integrity": "sha512-6FLhFMAdaW/JMzTPqaIxVTgCFhMQY2v88qVGLUmcf9coCJx5plxuY0zxzXaLrvQulGnDurIKZMW5tBGmQy4Buw==", "cpu": [ "arm64" ], @@ -104,9 +104,9 @@ ] }, "node_modules/@typescript/native-preview-darwin-x64": { - "version": "7.0.0-dev.20251113.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20251113.1.tgz", - "integrity": "sha512-airwguNehqwQhr6LjDcJ0LXfBv2GaOJkpj6fzxMK9gWlrx/tVwvRFoazUesdOnR1DM6zSE4gy+2UROxGeumOSw==", + "version": "7.0.0-dev.20251210.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20251210.1.tgz", + "integrity": "sha512-JrsZ913/ZyeoFDCVq+k4h0VUJHyqjT6te/IC8aMtLZQRkusQEX+57Anfr/BVXzWBa8+JwIXPGzllilw5dkrHNw==", "cpu": [ "x64" ], @@ -117,9 +117,9 @@ ] }, "node_modules/@typescript/native-preview-linux-arm": { - "version": "7.0.0-dev.20251113.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20251113.1.tgz", - "integrity": "sha512-lTYX9vskfbq3kq2+vpqu2fwJP68iZ4OuWHuGLMzS/AlQ1vzs535su/s6xhL8UuyRP+n10A9z2xHq7UJSOx8u1w==", + "version": "7.0.0-dev.20251210.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20251210.1.tgz", + "integrity": "sha512-v1hyGd8TWHaoRU8BJfGAdW6ML70TLUOB9+0Xeo30m3xHlOdO1zxunXa/FVhqzrChqzOkcty61PGe9tAPhrIh1w==", "cpu": [ "arm" ], @@ -130,9 +130,9 @@ ] }, "node_modules/@typescript/native-preview-linux-arm64": { - "version": "7.0.0-dev.20251113.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20251113.1.tgz", - "integrity": "sha512-EefJd94Hm2507bMQekjoaad32pjGQgldSNjD1UAp6P0EKsn2wZV7H9gxosbZwL3tgMY/HDrQoS1j240/qv528w==", + "version": "7.0.0-dev.20251210.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20251210.1.tgz", + "integrity": "sha512-GViH0JIkpEHCo04VYD/iAKqYjIB2uLXXtTmmLaImzBVd+h1dlKFgi2aOMi51mYnSefnbj7P7PeqIQNLKwi4NSQ==", "cpu": [ "arm64" ], @@ -143,9 +143,9 @@ ] }, "node_modules/@typescript/native-preview-linux-x64": { - "version": "7.0.0-dev.20251113.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20251113.1.tgz", - "integrity": "sha512-2VLSCSHgnb94BLLeb1LGYTMIRgbCWkpJa74U45Fi9W+ErTW5QJR+b/nzKAzeeL8/6vu5cfnW4Yf5pCU4d+IvTg==", + "version": "7.0.0-dev.20251210.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20251210.1.tgz", + "integrity": "sha512-X6p2MrcRcYE9T+ecNHYZHFYew0t+k9Sc2GbyjbNB0+MJ/Lffp3aUH59dp+JASZoyOxugPwN+MCJ/kNzYqiK9vg==", "cpu": [ "x64" ], @@ -156,9 +156,9 @@ ] }, "node_modules/@typescript/native-preview-win32-arm64": { - "version": "7.0.0-dev.20251113.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20251113.1.tgz", - "integrity": "sha512-byymVRxjqyQPBDpTxcfFprfNTN0e8RXE1VRmVrMU4kJeCxC9SRPQAZCIFw5bJKt74iic7Ixk0O+Ehjp+YlH/VQ==", + "version": "7.0.0-dev.20251210.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20251210.1.tgz", + "integrity": "sha512-FewUZSh7d9YnX0j3hr+N+zQ5B54k4jsVpZp8QbuDduMtautxI18bNO+vL5oO86ezqLWbUnZajXV/61MGfpEtMg==", "cpu": [ "arm64" ], @@ -169,9 +169,9 @@ ] }, "node_modules/@typescript/native-preview-win32-x64": { - "version": "7.0.0-dev.20251113.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20251113.1.tgz", - "integrity": "sha512-H6dR8nmGmuKduG8kODnb5bRgsTScjJ7lLpG6cch+3MflNaIymMAiQsku2kWKtMiFp94wHgQIFk88ov2wG91FYw==", + "version": "7.0.0-dev.20251210.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20251210.1.tgz", + "integrity": "sha512-NdqTPzm/Ls3F1amP/e/akCuhY32mjBXT0NMQnYODgT9ltbdkk08b572Bp4GbdfRj9AiJxK70pqoNeZWw4e5fhg==", "cpu": [ "x64" ], diff --git a/package.json b/package.json index b49eb10..8ecee1f 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "license": "GPL-3.0", "dependencies": { - "@typescript/native-preview": "^7.0.0-dev.20251113.1", + "@typescript/native-preview": "^7.0.0-dev.20251210.1", "aes-js": "3.1.2", "big-integer": "1.6.52", "debug": "4.4.3", @@ -30,7 +30,7 @@ "@types/aes-js": "^3.1.4", "@types/debug": "^4.1.12", "@types/lodash.debounce": "^4.0.9", - "@types/node": "^24.10.1", + "@types/node": "^24.10.2", "@types/pako": "^2.0.4" }, "repository": { diff --git a/src/builder.ts b/src/builder.ts index 6b307a5..efe148d 100644 --- a/src/builder.ts +++ b/src/builder.ts @@ -2254,6 +2254,7 @@ export interface BuilderMap { 'users.getSavedMusicByID': (this: any, params: any) => void 'account.getUniqueGiftChatThemes': (this: any, params: any) => void } + export const builderMap: BuilderMap = { 'mt_vector': function(params) { this.int32(481674261); diff --git a/src/mtptoto-types.ts b/src/mtptoto-types.ts index 074fc78..2c27f36 100644 --- a/src/mtptoto-types.ts +++ b/src/mtptoto-types.ts @@ -17003,5 +17003,14 @@ export interface Methods { hash: string; } response: Account$ChatThemes + }, + 'mt_ping': { + params: { + ping_id: number + } + response: { + msg_id: number + ping_id: number + } } } \ No newline at end of file diff --git a/src/rpc.ts b/src/rpc.ts index c1dd8a5..a3bdad7 100644 --- a/src/rpc.ts +++ b/src/rpc.ts @@ -80,6 +80,7 @@ export class RPC { authKeyAuxHash?: any lastMessageId?: any seqNo: number + pingId: number constructor({ api_id, api_hash, initConnectionParams, dc, storage, updates }: { api_id: number @@ -107,6 +108,7 @@ export class RPC { this.newNonce = new Uint8Array() this.serverNonce = new Uint8Array() this.seqNo = 0 + this.pingId = Date.now() this.handleTransportOpen = this.handleTransportOpen.bind(this); this.handleTransportError = this.handleTransportError.bind(this); @@ -212,8 +214,13 @@ export class RPC { this.isAuth = true; this.sendWaitMessages(); - // This request is necessary to ensure that you start interacting with the server. If we have not made any request, the server will not send us updates. - this.ping() + // This request is necessary to ensure that you start interacting with the server. + // If we have not made any request, the server will not send us updates. + await this.getConfig() + + setTimeout(() => { + this.ping() + }, 5000).unref() } else { this.nonce = getRandomBytes(16); @@ -745,7 +752,7 @@ export class RPC { this.sendAcks(); } - call(method: T | string, params?: Methods[T]["params"]): Promise { + call(method: T, params?: Methods[T]["params"]): Promise { if (!this.isReady) { return new Promise((resolve, reject) => { this.messagesWaitAuth.push({ @@ -961,10 +968,24 @@ export class RPC { return this.storage.get(`${this.dc.id}${key}`); } + getConfig() { + debug("try to get server config after opening the connection") + + return this.call("help.getConfig") + .then(config => { + debug("get config after open connection %j", config) + }) + .catch(err => { + debug("fail to get config after open connection err=%s err_data=%j", String(err), JSON.stringify(err)) + }) + } + ping() { this.pongTimer = setTimeout(this.reconnect, this.pongTimeout).unref() + + debugger - this.call("help.getConfig") + this.call("mt_ping", { ping_id: this.pingId++, }) .then(this.pong) .catch(this.pongError) .finally(this.schedulePing) From d40374191cde0dbe969609dab917da243085ae38 Mon Sep 17 00:00:00 2001 From: Andrii Fedyk Date: Wed, 27 May 2026 14:55:03 +0200 Subject: [PATCH 2/6] add spec --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5bc285a..5b1d454 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store test.sh +spec.sh /lib /node_modules From ddbfc8ac4bb197d6ec7345ae1ad6068b28684120 Mon Sep 17 00:00:00 2001 From: Andrii Fedyk Date: Mon, 29 Jun 2026 10:48:17 +0200 Subject: [PATCH 3/6] bump dependencies --- package-lock.json | 132 ++++++++++++++++++++++++++++++++-------------- package.json | 20 ++++--- 2 files changed, 104 insertions(+), 48 deletions(-) diff --git a/package-lock.json b/package-lock.json index d333c57..e35ed6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,6 @@ "@types/lodash.debounce": "^4.0.9", "@types/node": "^24.10.2", "@types/pako": "^2.0.4", - "@typescript/native-preview": "^7.0.0-dev.20251210.1", "aes-js": "3.1.2", "big-integer": "1.6.52", "debug": "4.4.3", @@ -22,6 +21,11 @@ "lodash.debounce": "4.0.8", "pako": "2.1.0" }, + "devDependencies": { + "@types/qrcode-terminal": "^0.12.2", + "@typescript/native-preview": "^7.0.0-dev.20260629.1", + "qrcode-terminal": "^0.12.0" + }, "engines": { "node": ">=20" } @@ -72,114 +76,153 @@ "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", "license": "MIT" }, + "node_modules/@types/qrcode-terminal": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/qrcode-terminal/-/qrcode-terminal-0.12.2.tgz", + "integrity": "sha512-v+RcIEJ+Uhd6ygSQ0u5YYY7ZM+la7GgPbs0V/7l/kFs2uO4S8BcIUEMoP7za4DNIqNnUD5npf0A/7kBhrCKG5Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript/native-preview": { - "version": "7.0.0-dev.20251210.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20251210.1.tgz", - "integrity": "sha512-SiSRpp42K80X+qJKAi8qvjo2xMZIgJgROXCJo0MiZ8a6fjQK6IetRfUvwdApUsDltf/VaEc/j8v2Hiqp8SGhLQ==", + "version": "7.0.0-dev.20260629.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260629.1.tgz", + "integrity": "sha512-KImWFkxGUa/V78bUEAgzeJQT+6wKQXDDYHHrOavof8wnbMCpj86KuPNyBIPQMtoHiz2If8aNTums2m50oE3oqw==", + "dev": true, "license": "Apache-2.0", "bin": { - "tsgo": "bin/tsgo.js" + "tsgo": "bin/tsgo" + }, + "engines": { + "node": ">=16.20.0" }, "optionalDependencies": { - "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20251210.1", - "@typescript/native-preview-darwin-x64": "7.0.0-dev.20251210.1", - "@typescript/native-preview-linux-arm": "7.0.0-dev.20251210.1", - "@typescript/native-preview-linux-arm64": "7.0.0-dev.20251210.1", - "@typescript/native-preview-linux-x64": "7.0.0-dev.20251210.1", - "@typescript/native-preview-win32-arm64": "7.0.0-dev.20251210.1", - "@typescript/native-preview-win32-x64": "7.0.0-dev.20251210.1" + "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260629.1", + "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260629.1", + "@typescript/native-preview-linux-arm": "7.0.0-dev.20260629.1", + "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260629.1", + "@typescript/native-preview-linux-x64": "7.0.0-dev.20260629.1", + "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260629.1", + "@typescript/native-preview-win32-x64": "7.0.0-dev.20260629.1" } }, "node_modules/@typescript/native-preview-darwin-arm64": { - "version": "7.0.0-dev.20251210.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20251210.1.tgz", - "integrity": "sha512-6FLhFMAdaW/JMzTPqaIxVTgCFhMQY2v88qVGLUmcf9coCJx5plxuY0zxzXaLrvQulGnDurIKZMW5tBGmQy4Buw==", + "version": "7.0.0-dev.20260629.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260629.1.tgz", + "integrity": "sha512-wXRExZJweYoTzE4atRR7T5HwKJYkl6/KHxON0eF0iy2fvgLXDlyq4AQqhmV8mMx10PQKc/4sNbfhD4kjWWvm8A==", "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/@typescript/native-preview-darwin-x64": { - "version": "7.0.0-dev.20251210.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20251210.1.tgz", - "integrity": "sha512-JrsZ913/ZyeoFDCVq+k4h0VUJHyqjT6te/IC8aMtLZQRkusQEX+57Anfr/BVXzWBa8+JwIXPGzllilw5dkrHNw==", + "version": "7.0.0-dev.20260629.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260629.1.tgz", + "integrity": "sha512-xkL/tETzUuDxfRkk2rD0/CjjjGLyOLHeE103T52rpgj0VNSXMnn7tTiw+TSdxnS61b8ImOrWgQJujhPFTp0jng==", "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/@typescript/native-preview-linux-arm": { - "version": "7.0.0-dev.20251210.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20251210.1.tgz", - "integrity": "sha512-v1hyGd8TWHaoRU8BJfGAdW6ML70TLUOB9+0Xeo30m3xHlOdO1zxunXa/FVhqzrChqzOkcty61PGe9tAPhrIh1w==", + "version": "7.0.0-dev.20260629.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260629.1.tgz", + "integrity": "sha512-QlTxO+O6yELc0NYZZYIbncZhkhw+K/vBoVg+mKipbEIK5b1E6cwW7ivWIrDp1FfssQ0Wu7zxincPappoci7KNw==", "cpu": [ "arm" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/@typescript/native-preview-linux-arm64": { - "version": "7.0.0-dev.20251210.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20251210.1.tgz", - "integrity": "sha512-GViH0JIkpEHCo04VYD/iAKqYjIB2uLXXtTmmLaImzBVd+h1dlKFgi2aOMi51mYnSefnbj7P7PeqIQNLKwi4NSQ==", + "version": "7.0.0-dev.20260629.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260629.1.tgz", + "integrity": "sha512-y4ey82krq4iLNHML4G1dUObBWY6gNAjVqaUHFDv7+LBu5bfAJy8VClBaLtAJce3siQQeB0/4SkN4XsAa0oZktQ==", "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/@typescript/native-preview-linux-x64": { - "version": "7.0.0-dev.20251210.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20251210.1.tgz", - "integrity": "sha512-X6p2MrcRcYE9T+ecNHYZHFYew0t+k9Sc2GbyjbNB0+MJ/Lffp3aUH59dp+JASZoyOxugPwN+MCJ/kNzYqiK9vg==", + "version": "7.0.0-dev.20260629.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260629.1.tgz", + "integrity": "sha512-ckb4HyugC5beDpOMPOVpEx1H3l2Z2RgsGPxFOLGMU6LLIHQwlCaSdRjSfH8Hq8yffPgKuotTQLdA89cP1IC8xQ==", "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/@typescript/native-preview-win32-arm64": { - "version": "7.0.0-dev.20251210.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20251210.1.tgz", - "integrity": "sha512-FewUZSh7d9YnX0j3hr+N+zQ5B54k4jsVpZp8QbuDduMtautxI18bNO+vL5oO86ezqLWbUnZajXV/61MGfpEtMg==", + "version": "7.0.0-dev.20260629.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260629.1.tgz", + "integrity": "sha512-Ktgepu9p0blqrbiryzYrw11ddnbESXPdeGKURDG/wXESoHQETsMNxKWhqAo587ItNnWjKOBFxoEM22eP9OzKnw==", "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/@typescript/native-preview-win32-x64": { - "version": "7.0.0-dev.20251210.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20251210.1.tgz", - "integrity": "sha512-NdqTPzm/Ls3F1amP/e/akCuhY32mjBXT0NMQnYODgT9ltbdkk08b572Bp4GbdfRj9AiJxK70pqoNeZWw4e5fhg==", + "version": "7.0.0-dev.20260629.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260629.1.tgz", + "integrity": "sha512-b2wmeIpFJs/peej3NG26320ya+iYQz21JzFYDrnvtsEeR/g66rB9uvp4Owo4J1AG/BcmRWC/nuwrBy3Jdb+UDA==", "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": ">=16.20.0" + } }, "node_modules/aes-js": { "version": "3.1.2", @@ -232,6 +275,15 @@ "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" }, + "node_modules/qrcode-terminal": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", + "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", + "dev": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", diff --git a/package.json b/package.json index 8ecee1f..bd1ebec 100644 --- a/package.json +++ b/package.json @@ -20,21 +20,25 @@ }, "license": "GPL-3.0", "dependencies": { - "@typescript/native-preview": "^7.0.0-dev.20251210.1", + "@types/aes-js": "^3.1.4", + "@types/debug": "^4.1.12", + "@types/lodash.debounce": "^4.0.9", + "@types/node": "^24.10.2", + "@types/pako": "^2.0.4", "aes-js": "3.1.2", "big-integer": "1.6.52", "debug": "4.4.3", "leemon": "6.2.0", "lodash.debounce": "4.0.8", - "pako": "2.1.0", - "@types/aes-js": "^3.1.4", - "@types/debug": "^4.1.12", - "@types/lodash.debounce": "^4.0.9", - "@types/node": "^24.10.2", - "@types/pako": "^2.0.4" + "pako": "2.1.0" }, "repository": { "type": "git", "url": "git+https://github.com/fedyk/mtproto-nodejs-client.git" + }, + "devDependencies": { + "@types/qrcode-terminal": "^0.12.2", + "@typescript/native-preview": "^7.0.0-dev.20260629.1", + "qrcode-terminal": "^0.12.0" } -} \ No newline at end of file +} From 6c5de45a0c696d841e1df6e2e6e2b71de6cf9dbc Mon Sep 17 00:00:00 2001 From: Andrii Fedyk Date: Mon, 29 Jun 2026 12:05:21 +0200 Subject: [PATCH 4/6] add QR login example --- README.md | 4 +- src/example-qr.ts | 161 ++++++++++++++++++++++++++++++++++++++++++++++ src/example.ts | 79 ++++++++++++----------- 3 files changed, 205 insertions(+), 39 deletions(-) create mode 100644 src/example-qr.ts diff --git a/README.md b/README.md index 75f807d..40ca0e5 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,9 @@ npm install mtproto-nodejs-client ## Example -Connect to Telegram servers +Check out the full sign-in example in [example.ts](src/example.ts) and the QR code login example in [example-qr.ts](src/example-qr.ts). + +To connect to Telegram servers: ```js const api_id = 12345678; diff --git a/src/example-qr.ts b/src/example-qr.ts new file mode 100644 index 0000000..e477be5 --- /dev/null +++ b/src/example-qr.ts @@ -0,0 +1,161 @@ +import qrcode from "qrcode-terminal" +import { equal, ok } from "node:assert/strict" +import { createInterface } from "node:readline/promises" +import { stdin as input, stdout as output } from 'node:process'; +import { MTProto } from "./mtproto.js"; +import { createTempStorage } from "./temp-storage.js"; +import { getSRPParams } from "./get-srp-params.js"; +import { + $Auth$LoginToken, + $Auth$LoginTokenMigrateTo, + $Auth$LoginTokenSuccess, + $Updates, + $UpdateShort +} from "./mtptoto-types.js"; + +ok(process.env.API_ID, "`process.env.API_ID` is missing") +ok(process.env.API_HASH, "`process.env.API_HASH` is missing") + +/** + * Tun the script call: + * API_ID=12345678 API_HASH=ABCDEF DEBUG=* node lib/example-qr.js + * + * @see https://my.telegram.org/apps + * @see https://core.telegram.org/api/qr-login + */ +const api_id = Number(process.env.API_ID || 0) +const api_hash = String(process.env.API_HASH || "") +const api_test = Boolean(process.env.API_TEST) +const storage = createTempStorage() +const readline = createInterface({ input, output }) +const mtproto = new MTProto({ + api_id, + api_hash, + storage, + test: api_test, + initConnectionParams: { + device_model: "Test Device", + } +}) + +mtproto.updates.on("updateShort", onUpdateShort) +mtproto.updates.on("updates", onUpdates) + +exportLoginToken() + +function onUpdateShort(updateShort: $UpdateShort) { + console.log("updateShort", updateShort) + + if (updateShort._ === "updateShort" && updateShort.update._ === "updateLoginToken") { + return exportLoginToken() + } +} + +function onUpdates(updates: $Updates) { + for (const update of updates.updates) { + if (update._ === "updateLoginToken") { + exportLoginToken() + } + } +} + +function exportLoginToken() { + return mtproto.call("auth.exportLoginToken", { + api_id, + api_hash, + except_ids: [], + }).then(function (token) { + console.log("loginToken", token) + + if (token._ === "auth.loginToken") { + loginToken(token) + } + + if (token._ === "auth.loginTokenMigrateTo") { + return loginTokenMigrateTo(token) + } + + if (token._ === "auth.loginTokenSuccess") { + return loginTokenSuccess(token) + } + }) +} + +function loginToken(token: $Auth$LoginToken) { + const encodedToken = Buffer.from(token.token).toString("base64url") + const url = `tg://login?token=${encodedToken}` + + console.log("url", url) + + qrcode.generate(url) +} + +async function loginTokenMigrateTo(token: $Auth$LoginTokenMigrateTo) { + await mtproto.setDefaultDc(token.dc_id) + + await mtproto.call("auth.importLoginToken", { + token: token.token + }).then(function (loginToken) { + if (loginToken._ === "auth.loginTokenSuccess") { + loginTokenSuccess(loginToken) + } + }).catch(function (err) { + if (isPasswordNeededError(err)) { + return askPassword() + } + + throw err + }) +} + +function loginTokenSuccess(login: $Auth$LoginTokenSuccess) { + if (login.authorization._ === "auth.authorizationSignUpRequired") { + return console.warn("User sign up required") + } + + if (login.authorization.user._ === "userEmpty") { + return console.error("Authorization user is empty") + } + + console.log("success", login.authorization.user) +} + +async function askPassword() { + const accountPassword = await mtproto.call("account.getPassword") + + equal(accountPassword._, "account.password") + equal(accountPassword.current_algo?._, "passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow") + ok(accountPassword.current_algo.g, "`accountPassword.current_algo.g` is required") + ok(accountPassword.current_algo.p, "`accountPassword.current_algo.p` is required") + ok(accountPassword.current_algo.salt1, "`accountPassword.current_algo.salt1` is required") + ok(accountPassword.current_algo.salt2, "`accountPassword.current_algo.salt2` is required") + ok(accountPassword.srp_B, "`password.srp_B` is required") + ok(accountPassword.srp_id, "`password.srp_id` is required") + + const password = await readline.question(`Enter password (hint ${accountPassword.hint}): `) + const srp = getSRPParams({ + g: accountPassword.current_algo.g, + p: accountPassword.current_algo.p, + salt1: accountPassword.current_algo.salt1, + salt2: accountPassword.current_algo.salt2, + gB: accountPassword.srp_B, + password, + }) + + mtproto.call("auth.checkPassword", { + password: { + _: "inputCheckPasswordSRP", + srp_id: accountPassword.srp_id, + A: srp.A, + M1: srp.M1, + } + }).then(function (resp) { + console.log("success", resp) + }) +} + + +function isPasswordNeededError(err: unknown) { + return err instanceof Error + && err.message === "SESSION_PASSWORD_NEEDED" +} diff --git a/src/example.ts b/src/example.ts index 552f4b3..a1bdaeb 100644 --- a/src/example.ts +++ b/src/example.ts @@ -13,6 +13,7 @@ const debug = Debug("example") */ const api_id = Number(process.env.API_ID || 0) const api_hash = String(process.env.API_HASH || "") +const api_test = Boolean(process.env.API_TEST) ok(api_id, "`process.env.API_ID` is missing") ok(api_hash, "`process.env.API_HASH` is missing") @@ -20,7 +21,7 @@ ok(api_hash, "`process.env.API_HASH` is missing") /** * The default phone number for sign in */ -const default_phone_number = String(process.env.DEFAULT_PHONE_NUMBER || "") +const default_phone_number = String(process.env.PHONE_NUMBER || "") const storage = createTempStorage() const readline = createInterface({ input, output }) @@ -28,7 +29,7 @@ const mtproto = new MTProto({ api_id, api_hash, storage, - test: false, + test: api_test, initConnectionParams: { device_model: "Test Device", } @@ -44,6 +45,9 @@ equal(countries._, "help.countriesList") const enter_phone_number = await readline.question(`Enter phone number (default: ${default_phone_number}): `) const phone_number = enter_phone_number || default_phone_number + +debug("phone_number = %s", phone_number) + const sentCode = await mtproto.call("auth.sendCode", { phone_number, api_id, @@ -58,42 +62,41 @@ equal(sentCode._, "auth.sentCode") const phone_code = await readline.question("Enter phone code: ") const phone_code_hash = sentCode.phone_code_hash -const auth = await mtproto.call("auth.signIn", { phone_number, phone_code_hash, phone_code }) - .catch(async function (err) { - if (err.message === "SESSION_PASSWORD_NEEDED") { - const accountPassword = await mtproto.call("account.getPassword") - - equal(accountPassword._, "account.password") - equal(accountPassword.current_algo?._, "passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow") - ok(accountPassword.current_algo.g, "`accountPassword.current_algo.g` is required") - ok(accountPassword.current_algo.p, "`accountPassword.current_algo.p` is required") - ok(accountPassword.current_algo.salt1, "`accountPassword.current_algo.salt1` is required") - ok(accountPassword.current_algo.salt2, "`accountPassword.current_algo.salt2` is required") - ok(accountPassword.srp_B, "`password.srp_B` is required") - ok(accountPassword.srp_id, "`password.srp_id` is required") - - const password = await readline.question(`Enter password (hint ${accountPassword.hint}): `) - const srp = getSRPParams({ - g: accountPassword.current_algo.g, - p: accountPassword.current_algo.p, - salt1: accountPassword.current_algo.salt1, - salt2: accountPassword.current_algo.salt2, - gB: accountPassword.srp_B, - password, - }) - - return mtproto.call("auth.checkPassword", { - password: { - _: "inputCheckPasswordSRP", - srp_id: accountPassword.srp_id, - A: srp.A, - M1: srp.M1, - } - }) - } - - throw err - }) +const auth = await mtproto.call("auth.signIn", { phone_number, phone_code_hash, phone_code }).catch(async function (err) { + if (err.message === "SESSION_PASSWORD_NEEDED") { + const accountPassword = await mtproto.call("account.getPassword") + + equal(accountPassword._, "account.password") + equal(accountPassword.current_algo?._, "passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow") + ok(accountPassword.current_algo.g, "`accountPassword.current_algo.g` is required") + ok(accountPassword.current_algo.p, "`accountPassword.current_algo.p` is required") + ok(accountPassword.current_algo.salt1, "`accountPassword.current_algo.salt1` is required") + ok(accountPassword.current_algo.salt2, "`accountPassword.current_algo.salt2` is required") + ok(accountPassword.srp_B, "`password.srp_B` is required") + ok(accountPassword.srp_id, "`password.srp_id` is required") + + const password = await readline.question(`Enter password (hint ${accountPassword.hint}): `) + const srp = getSRPParams({ + g: accountPassword.current_algo.g, + p: accountPassword.current_algo.p, + salt1: accountPassword.current_algo.salt1, + salt2: accountPassword.current_algo.salt2, + gB: accountPassword.srp_B, + password, + }) + + return mtproto.call("auth.checkPassword", { + password: { + _: "inputCheckPasswordSRP", + srp_id: accountPassword.srp_id, + A: srp.A, + M1: srp.M1, + } + }) + } + + throw err +}) debug("auth %j", auth) From 8bbb3bd237684d7106cd853a7470a31288ec4194 Mon Sep 17 00:00:00 2001 From: Andrii Fedyk Date: Mon, 29 Jun 2026 12:05:38 +0200 Subject: [PATCH 5/6] ignore .sh scripts --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 5b1d454..c24200c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .DS_Store -test.sh -spec.sh +*.sh /lib /node_modules From e7495903e434c8439a7fbd6c856e7812cd929aa3 Mon Sep 17 00:00:00 2001 From: Andrii Fedyk Date: Mon, 29 Jun 2026 12:12:02 +0200 Subject: [PATCH 6/6] fix tsconfig.json --- tsconfig.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 849339f..5053cdd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,8 +3,10 @@ "target": "ES2022", "module": "NodeNext", "strict": true, - "outDir": "lib", - "declaration": true + "rootDir": "./src/", + "outDir": "./lib/", + "declaration": true, + "types": ["node"] }, "include": [ "src/*.ts"