Browse Source

Add public proxy support (no auth).
Add MTProxy support.

painor 3 years ago
parent
commit
bf3ed9d322

+ 1 - 1
gramjs/Version.ts

@@ -1 +1 @@
-export const version = "1.8.15";
+export const version = "1.9.0";

+ 4 - 2
gramjs/client/TelegramClient.ts

@@ -1195,9 +1195,11 @@ export class TelegramClient extends TelegramBaseClient {
 
         const connection = new this._connection(
             this.session.serverAddress,
-            this.session.port,
+            // We don't want to use the session for this.
+            this.useWSS ? 433 : 80,
             this.session.dcId,
-            this._log
+            this._log,
+            this._proxy
         );
         const newConnection = await this._sender.connect(connection);
         if (!newConnection) {

+ 37 - 6
gramjs/client/telegramBaseClient.ts

@@ -1,4 +1,4 @@
-import { TelegramClient, version } from "../";
+import { Connection, TelegramClient, version } from "../";
 import { IS_NODE, sleep } from "../Helpers";
 import {
     ConnectionTCPFull,
@@ -16,6 +16,11 @@ import type { EventBuilder } from "../events/common";
 import { MarkdownParser } from "../extensions/markdown";
 import { MTProtoSender } from "../network";
 import { LAYER } from "../tl/AllTLObjects";
+import {
+    ConnectionTCPMTProxyAbridged,
+    ProxyInterface,
+    TCPMTProxy,
+} from "../network/connection/TCPMTProxy";
 
 const EXPORTED_SENDER_RECONNECT_TIMEOUT = 1000; // 1 sec
 const EXPORTED_SENDER_RELEASE_TIMEOUT = 30000; // 30 sec
@@ -32,7 +37,7 @@ export interface TelegramClientParams {
     /** The connection instance to be used when creating a new connection to the servers. It must be a type.<br/>
      * Defaults to {@link ConnectionTCPFull} on Node and {@link ConnectionTCPObfuscated} on browsers.
      */
-    connection?: any;
+    connection?: typeof Connection;
     /**
      * Whether to connect to the servers through IPv6 or not. By default this is false.
      */
@@ -54,6 +59,10 @@ export interface TelegramClientParams {
      * defaults to 5
      */
     connectionRetries?: number;
+    /**
+     * Experimental proxy to be used for the connection. (only supports MTProxies)
+     */
+    proxy?: ProxyInterface;
     /**
      * How many times we should retry borrowing a sender from another DC when it fails. defaults to 5
      */
@@ -118,7 +127,7 @@ const clientParamsDefault = {
     useWSS:
         typeof window !== "undefined"
             ? window.location.protocol == "https:"
-            : false,
+            : true,
 };
 
 export abstract class TelegramBaseClient {
@@ -148,7 +157,7 @@ export abstract class TelegramBaseClient {
     /** @hidden */
     public _autoReconnect: boolean;
     /** @hidden */
-    public _connection: any;
+    public _connection: typeof Connection;
     /** @hidden */
     public _initRequest: Api.InitConnection;
     /** @hidden */
@@ -187,6 +196,7 @@ export abstract class TelegramBaseClient {
     protected _loopStarted: boolean;
     _reconnecting: boolean;
     _destroyed: boolean;
+    protected _proxy?: ProxyInterface;
 
     constructor(
         session: string | Session,
@@ -223,10 +233,20 @@ export abstract class TelegramBaseClient {
         this._retryDelay = clientParams.retryDelay || 0;
         this._timeout = clientParams.timeout!;
         this._autoReconnect = clientParams.autoReconnect!;
+        this._proxy = clientParams.proxy;
+
         if (!(clientParams.connection instanceof Function)) {
             throw new Error("Connection should be a class not an instance");
         }
         this._connection = clientParams.connection;
+        let initProxy;
+        if (this._proxy?.MTProxy) {
+            this._connection = ConnectionTCPMTProxyAbridged;
+            initProxy = new Api.InputClientProxy({
+                address: this._proxy!.ip,
+                port: this._proxy!.port,
+            });
+        }
         this._initRequest = new Api.InitConnection({
             apiId: this.apiId,
             deviceModel:
@@ -237,7 +257,7 @@ export abstract class TelegramBaseClient {
             langCode: clientParams.langCode,
             langPack: "", // this should be left empty.
             systemLangCode: clientParams.systemLangCode,
-            proxy: undefined, // no proxies yet.
+            proxy: initProxy,
         });
         this._eventBuilders = [];
 
@@ -246,6 +266,11 @@ export abstract class TelegramBaseClient {
         this._bot = undefined;
         this._selfInputPeer = undefined;
         this.useWSS = clientParams.useWSS!;
+        if (this.useWSS && this._proxy) {
+            throw new Error(
+                "Cannot use SSL with proxies. You need to disable the useWSS client param in TelegramClient"
+            );
+        }
         this._entityCache = new EntityCache();
         // These will be set later
         this._config = undefined;
@@ -356,7 +381,13 @@ export abstract class TelegramBaseClient {
         while (true) {
             try {
                 await sender.connect(
-                    new this._connection(dc.ipAddress, dc.port, dcId, this._log)
+                    new this._connection(
+                        dc.ipAddress,
+                        dc.port,
+                        dcId,
+                        this._log,
+                        this._proxy
+                    )
                 );
 
                 if (this.session.dcId !== dcId && !sender._authenticated) {

+ 44 - 4
gramjs/extensions/PromisedNetSockets.ts

@@ -1,5 +1,8 @@
 import { Socket } from "net";
+import { SocksClient } from "socks";
+
 import { Mutex } from "async-mutex";
+import { ProxyInterface } from "../network/connection/TCPMTProxy";
 
 const mutex = new Mutex();
 
@@ -11,11 +14,23 @@ export class PromisedNetSockets {
     private stream: Buffer;
     private canRead?: boolean | Promise<boolean>;
     private resolveRead: ((value?: any) => void) | undefined;
+    private proxy?: ProxyInterface;
 
-    constructor() {
+    constructor(proxy?: ProxyInterface) {
         this.client = undefined;
         this.closed = true;
         this.stream = Buffer.alloc(0);
+        if (!proxy?.MTProxy) {
+            // we only want to use this when it's not an MTProto proxy.
+            if (proxy) {
+                if (!proxy.ip || !proxy.port || !proxy.socksType) {
+                    throw new Error(
+                        `Invalid sockets params. ${proxy.ip}, ${proxy.port}, ${proxy.socksType}`
+                    );
+                }
+            }
+            this.proxy = proxy;
+        }
     }
 
     async readExactly(number: number) {
@@ -69,18 +84,43 @@ export class PromisedNetSockets {
      */
     async connect(port: number, ip: string) {
         this.stream = Buffer.alloc(0);
+        let connected = false;
+        if (this.proxy) {
+            const info = await SocksClient.createConnection({
+                proxy: {
+                    host: this.proxy.ip,
+                    port: this.proxy.port,
+                    type: this.proxy.socksType!, // Proxy version (4 or 5)
+                },
+
+                command: "connect",
+                timeout: (this.proxy.timeout || 5) * 1000,
+                destination: {
+                    host: ip,
+                    port: port,
+                },
+            });
+            this.client = info.socket;
+            connected = true;
+        } else {
+            this.client = new Socket();
+        }
 
-        this.client = new Socket();
         this.canRead = new Promise((resolve) => {
             this.resolveRead = resolve;
         });
         this.closed = false;
         return new Promise((resolve, reject) => {
             if (this.client) {
-                this.client.connect(port, ip, () => {
+                if (connected) {
                     this.receive();
                     resolve(this);
-                });
+                } else {
+                    this.client.connect(port, ip, () => {
+                        this.receive();
+                        resolve(this);
+                    });
+                }
                 this.client.on("error", reject);
                 this.client.on("close", () => {
                     if (this.client && this.client.destroyed) {

+ 2 - 1
gramjs/network/MTProtoSender.ts

@@ -925,7 +925,8 @@ export class MTProtoSender {
             this._connection!._ip,
             this._connection!._port,
             this._connection!._dcId,
-            this._connection!._log
+            this._connection!._log,
+            this._connection!._proxy
         );
         await this.connect(newConnection, true);
 

+ 24 - 9
gramjs/network/connection/Connection.ts

@@ -1,6 +1,13 @@
-import { PromisedNetSockets, PromisedWebSockets } from "../../extensions";
+import {
+    Logger,
+    PromisedNetSockets,
+    PromisedWebSockets,
+} from "../../extensions";
 import { AsyncQueue } from "../../extensions";
 import { IS_NODE } from "../../Helpers";
+import { AbridgedPacketCodec } from "./TCPAbridged";
+import { FullPacketCodec } from "./TCPFull";
+import { ProxyInterface } from "./TCPMTProxy";
 
 /**
  * The `Connection` class is a wrapper around ``asyncio.open_connection``.
@@ -14,12 +21,12 @@ import { IS_NODE } from "../../Helpers";
  * the client is disconnected (includes remote disconnections).
  */
 class Connection {
-    // @ts-ignore
-    PacketCodecClass: any; //"typeof AbridgedPacketCodec|typeof FullPacketCodec|typeof ObfuscatedConnection as "
+    PacketCodecClass?: typeof AbridgedPacketCodec | typeof FullPacketCodec;
     readonly _ip: string;
     readonly _port: number;
     _dcId: number;
-    _log: any;
+    _log: Logger;
+    _proxy?: ProxyInterface;
     private _connected: boolean;
     private _sendTask?: Promise<void>;
     private _recvTask?: Promise<void>;
@@ -27,13 +34,20 @@ class Connection {
     protected _obfuscation: any;
     private readonly _sendArray: AsyncQueue;
     private _recvArray: AsyncQueue;
-    protected socket: PromisedNetSockets | PromisedWebSockets;
-
-    constructor(ip: string, port: number, dcId: number, loggers: any) {
+    socket: PromisedNetSockets | PromisedWebSockets;
+
+    constructor(
+        ip: string,
+        port: number,
+        dcId: number,
+        loggers: Logger,
+        proxy?: ProxyInterface
+    ) {
         this._ip = ip;
         this._port = port;
         this._dcId = dcId;
         this._log = loggers;
+        this._proxy = proxy;
         this._connected = false;
         this._sendTask = undefined;
         this._recvTask = undefined;
@@ -42,7 +56,7 @@ class Connection {
         this._sendArray = new AsyncQueue();
         this._recvArray = new AsyncQueue();
         this.socket = IS_NODE
-            ? new PromisedNetSockets()
+            ? new PromisedNetSockets(this._proxy)
             : new PromisedWebSockets();
 
         //this.socket = new PromisedWebSockets()
@@ -50,7 +64,7 @@ class Connection {
 
     async _connect() {
         this._log.debug("Connecting");
-        this._codec = new this.PacketCodecClass(this);
+        this._codec = new this.PacketCodecClass!(this);
         await this.socket.connect(this._port, this._ip);
         this._log.debug("Finished connecting");
         // await this.socket.connect({host: this._ip, port: this._port});
@@ -155,6 +169,7 @@ class ObfuscatedConnection extends Connection {
 
     async _initConn() {
         this._obfuscation = new this.ObfuscatedIO(this);
+        await this._obfuscation.initHeader();
         this.socket.write(this._obfuscation.header);
     }
 

+ 1 - 1
gramjs/network/connection/TCPAbridged.ts

@@ -8,7 +8,7 @@ export class AbridgedPacketCodec extends PacketCodec {
     static tag = Buffer.from("ef", "hex");
     static obfuscateTag = Buffer.from("efefefef", "hex");
     private tag: Buffer;
-    private obfuscateTag: Buffer;
+    obfuscateTag: Buffer;
 
     constructor(props: any) {
         super(props);

+ 1 - 1
gramjs/network/connection/TCPFull.ts

@@ -3,7 +3,7 @@ import { crc32 } from "../../Helpers";
 import { InvalidChecksumError } from "../../errors";
 import type { PromisedNetSockets, PromisedWebSockets } from "../../extensions";
 
-class FullPacketCodec extends PacketCodec {
+export class FullPacketCodec extends PacketCodec {
     private _sendCounter: number;
 
     constructor(connection: any) {

+ 147 - 0
gramjs/network/connection/TCPMTProxy.ts

@@ -0,0 +1,147 @@
+import { ObfuscatedConnection } from "./Connection";
+import { AbridgedPacketCodec } from "./TCPAbridged";
+import { generateRandomBytes, sha256 } from "../../Helpers";
+import {
+    Logger,
+    PromisedNetSockets,
+    PromisedWebSockets,
+} from "../../extensions";
+import { CTR } from "../../crypto/CTR";
+
+export interface ProxyInterface {
+    socksType?: 4 | 5;
+    ip: string;
+    port: number;
+    secret?: string;
+    MTProxy?: boolean;
+    timeout?: number;
+}
+
+class MTProxyIO {
+    header?: Buffer = undefined;
+    private connection: PromisedNetSockets | PromisedWebSockets;
+    private _encrypt?: CTR;
+    private _decrypt?: CTR;
+    private _packetClass: AbridgedPacketCodec;
+    private _secret: Buffer;
+    private _dcId: number;
+
+    constructor(connection: TCPMTProxy) {
+        this.connection = connection.socket;
+        this._packetClass =
+            connection.PacketCodecClass as unknown as AbridgedPacketCodec;
+
+        this._secret = connection._secret;
+        this._dcId = connection._dcId;
+    }
+
+    async initHeader() {
+        let secret = this._secret;
+        const isDD = secret.length == 17 && secret[0] == 0xdd;
+        secret = isDD ? secret.slice(1) : secret;
+        if (secret.length != 16) {
+            throw new Error(
+                "MTProxy secret must be a hex-string representing 16 bytes"
+            );
+        }
+        const keywords = [
+            Buffer.from("50567247", "hex"),
+            Buffer.from("474554", "hex"),
+            Buffer.from("504f5354", "hex"),
+            Buffer.from("eeeeeeee", "hex"),
+        ];
+        let random;
+
+        // eslint-disable-next-line no-constant-condition
+        while (true) {
+            random = generateRandomBytes(64);
+            if (
+                random[0] !== 0xef &&
+                !random.slice(4, 8).equals(Buffer.alloc(4))
+            ) {
+                let ok = true;
+                for (const key of keywords) {
+                    if (key.equals(random.slice(0, 4))) {
+                        ok = false;
+                        break;
+                    }
+                }
+                if (ok) {
+                    break;
+                }
+            }
+        }
+        random = random.toJSON().data;
+        const randomReversed = Buffer.from(random.slice(8, 56)).reverse();
+        // Encryption has "continuous buffer" enabled
+        const encryptKey = await sha256(
+            Buffer.concat([Buffer.from(random.slice(8, 40)), secret])
+        );
+        const encryptIv = Buffer.from(random.slice(40, 56));
+
+        const decryptKey = await sha256(
+            Buffer.concat([Buffer.from(randomReversed.slice(0, 32)), secret])
+        );
+        const decryptIv = Buffer.from(randomReversed.slice(32, 48));
+
+        const encryptor = new CTR(encryptKey, encryptIv);
+        const decryptor = new CTR(decryptKey, decryptIv);
+        random = Buffer.concat([
+            Buffer.from(random.slice(0, 56)),
+            this._packetClass.obfuscateTag,
+            Buffer.from(random.slice(60)),
+        ]);
+        const dcIdBytes = Buffer.alloc(2);
+        dcIdBytes.writeInt8(this._dcId);
+        random = Buffer.concat([
+            Buffer.from(random.slice(0, 60)),
+            dcIdBytes,
+            Buffer.from(random.slice(62)),
+        ]);
+        random = Buffer.concat([
+            Buffer.from(random.slice(0, 56)),
+            Buffer.from(encryptor.encrypt(random).slice(56, 64)),
+            Buffer.from(random.slice(64)),
+        ]);
+        this.header = random;
+
+        this._encrypt = encryptor;
+        this._decrypt = decryptor;
+    }
+
+    async read(n: number) {
+        const data = await this.connection.readExactly(n);
+        return this._decrypt!.encrypt(data);
+    }
+
+    write(data: Buffer) {
+        this.connection.write(this._encrypt!.encrypt(data));
+    }
+}
+
+export class TCPMTProxy extends ObfuscatedConnection {
+    ObfuscatedIO = MTProxyIO;
+
+    _secret: Buffer;
+
+    constructor(
+        ip: string,
+        port: number,
+        dcId: number,
+        loggers: Logger,
+        proxy: ProxyInterface
+    ) {
+        super(proxy.ip, proxy.port, dcId, loggers);
+        if (!proxy.MTProxy) {
+            throw new Error("This connection only supports MPTProxies");
+        }
+        if (!proxy.secret) {
+            throw new Error("You need to provide the secret for the MTProxy");
+        }
+        this._secret = Buffer.from(proxy.secret, "hex");
+    }
+}
+
+export class ConnectionTCPMTProxyAbridged extends TCPMTProxy {
+    PacketCodecClass = AbridgedPacketCodec;
+}

+ 13 - 14
gramjs/network/connection/TCPObfuscated.ts

@@ -7,19 +7,15 @@ import { PromisedNetSockets, PromisedWebSockets } from "../../extensions";
 class ObfuscatedIO {
     header?: Buffer = undefined;
     private connection: PromisedNetSockets | PromisedWebSockets;
-    private _encrypt: CTR;
-    private _decrypt: CTR;
-
-    constructor(connection: any) {
+    private _encrypt?: CTR;
+    private _decrypt?: CTR;
+    private _packetClass;
+    constructor(connection: ConnectionTCPObfuscated) {
         this.connection = connection.socket;
-        const res = this.initHeader(connection.PacketCodecClass);
-        this.header = res.random;
-
-        this._encrypt = res.encryptor;
-        this._decrypt = res.decryptor;
+        this._packetClass = connection.PacketCodecClass;
     }
 
-    initHeader(packetCodec: any) {
+    async initHeader() {
         // Obfuscated messages secrets cannot start with any of these
         const keywords = [
             Buffer.from("50567247", "hex"),
@@ -61,7 +57,7 @@ class ObfuscatedIO {
 
         random = Buffer.concat([
             Buffer.from(random.slice(0, 56)),
-            packetCodec.obfuscateTag,
+            this._packetClass.obfuscateTag,
             Buffer.from(random.slice(60)),
         ]);
         random = Buffer.concat([
@@ -69,16 +65,19 @@ class ObfuscatedIO {
             Buffer.from(encryptor.encrypt(random).slice(56, 64)),
             Buffer.from(random.slice(64)),
         ]);
-        return { random, encryptor, decryptor };
+        this.header = random;
+
+        this._encrypt = encryptor;
+        this._decrypt = decryptor;
     }
 
     async read(n: number) {
         const data = await this.connection.readExactly(n);
-        return this._decrypt.encrypt(data);
+        return this._decrypt!.encrypt(data);
     }
 
     write(data: Buffer) {
-        this.connection.write(this._encrypt.encrypt(data));
+        this.connection.write(this._encrypt!.encrypt(data));
     }
 }
 

+ 2 - 2
gramjs/sessions/Abstract.ts

@@ -32,12 +32,12 @@ export abstract class Session {
     /**
      * Returns the server address where the library should connect to.
      */
-    abstract get serverAddress(): string | undefined;
+    abstract get serverAddress(): string;
 
     /**
      * Returns the port to which the library should connect to.
      */
-    abstract get port(): number | undefined;
+    abstract get port(): number;
 
     /**
      * Returns an ``AuthKey`` instance associated with the saved

+ 2 - 2
gramjs/sessions/Memory.ts

@@ -40,11 +40,11 @@ export class MemorySession extends Session {
     }
 
     get serverAddress() {
-        return this._serverAddress;
+        return this._serverAddress!;
     }
 
     get port() {
-        return this._port;
+        return this._port!;
     }
 
     get authKey() {

+ 49 - 2
package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "telegram",
-  "version": "1.8.15",
+  "version": "1.9.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "telegram",
-      "version": "1.8.15",
+      "version": "1.9.0",
       "license": "MIT",
       "dependencies": {
         "@cryptography/aes": "^0.1.1",
@@ -21,6 +21,7 @@
         "pako": "^2.0.3",
         "path-browserify": "^1.0.1",
         "process": "^0.11.10",
+        "socks": "^2.6.1",
         "store2": "^2.12.0",
         "ts-custom-error": "^3.2.0",
         "ts-mixer": "^5.4.0",
@@ -5818,6 +5819,11 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/ip": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+      "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
+    },
     "node_modules/is-accessor-descriptor": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@@ -9898,6 +9904,15 @@
         "node": "*"
       }
     },
+    "node_modules/smart-buffer": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+      "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+      "engines": {
+        "node": ">= 6.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
     "node_modules/snapdragon": {
       "version": "0.8.2",
       "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -10044,6 +10059,19 @@
       "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
       "dev": true
     },
+    "node_modules/socks": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz",
+      "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==",
+      "dependencies": {
+        "ip": "^1.1.5",
+        "smart-buffer": "^4.1.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0",
+        "npm": ">= 3.0.0"
+      }
+    },
     "node_modules/source-list-map": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
@@ -16882,6 +16910,11 @@
       "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
       "dev": true
     },
+    "ip": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+      "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
+    },
     "is-accessor-descriptor": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@@ -20173,6 +20206,11 @@
       "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
       "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
     },
+    "smart-buffer": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+      "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="
+    },
     "snapdragon": {
       "version": "0.8.2",
       "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -20295,6 +20333,15 @@
         }
       }
     },
+    "socks": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz",
+      "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==",
+      "requires": {
+        "ip": "^1.1.5",
+        "smart-buffer": "^4.1.0"
+      }
+    },
     "source-list-map": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",

+ 2 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "telegram",
-  "version": "1.8.15",
+  "version": "1.9.0",
   "description": "NodeJS/Browser MTProto API Telegram client library,",
   "main": "index.js",
   "types": "index.d.ts",
@@ -59,6 +59,7 @@
     "pako": "^2.0.3",
     "path-browserify": "^1.0.1",
     "process": "^0.11.10",
+    "socks": "^2.6.1",
     "store2": "^2.12.0",
     "ts-custom-error": "^3.2.0",
     "ts-mixer": "^5.4.0",