Browse Source

Add partial support for web test servers

Painor 3 years ago
parent
commit
fcc0f1f7da

+ 1 - 1
gramjs/Version.ts

@@ -1 +1 @@
-export const version = "2.7.22";
+export const version = "2.8.0";

+ 26 - 22
gramjs/client/TelegramClient.ts

@@ -88,9 +88,9 @@ export class TelegramClient extends TelegramBaseClient {
      * await client.start(botToken="123456:abcdfgh123456789);
      * // logging in as a user account
      * await client.start({
-     *   phoneNumber: async () => await input.text('number ?'),
-     *   password: async () => await input.text('password?'),
-     *   phoneCode: async () => await input.text('Code ?'),
+     *   phoneNumber: async () => await input.text("number ?"),
+     *   password: async () => await input.text("password?"),
+     *   phoneCode: async () => await input.text("Code ?"),
      *   onError: (err) => console.log(err),
      * });
      * >Number ? +1234567897
@@ -137,9 +137,9 @@ export class TelegramClient extends TelegramBaseClient {
      *         apiId:132456,
      *         apiHash:"132456",
      *     },{
-     *     phoneNumber: async () => await input.text('number ?'),
-     *     password: async () => await input.text('password?'),
-     *     phoneCode: async () => await input.text('Code ?'),
+     *     phoneNumber: async () => await input.text("number ?"),
+     *     password: async () => await input.text("password?"),
+     *     phoneCode: async () => await input.text("Code ?"),
      *     onError: (err) => console.log(err),
      *     })
      *  }
@@ -485,14 +485,14 @@ export class TelegramClient extends TelegramBaseClient {
      */
     downloadMedia(
         messageOrMedia: Api.Message | Api.TypeMessageMedia,
-        downloadParams: DownloadMediaInterface
+        downloadParams?: DownloadMediaInterface
     ) {
         return downloadMethods.downloadMedia(
             this,
             messageOrMedia,
-            downloadParams.outputFile,
-            downloadParams.thumb,
-            downloadParams.progressCallback
+            downloadParams?.outputFile,
+            downloadParams?.thumb,
+            downloadParams?.progressCallback
         );
     }
 
@@ -1137,8 +1137,8 @@ export class TelegramClient extends TelegramBaseClient {
      * await client.sendFile(chat, {file: file, progressCallback=console.log})
      *
      * // Dices, including dart and other future emoji
-     * await client.sendFile(chat, {file:new Api.InputMediaDice('')})
-     * await client.sendFile(chat, {file:new Api.InputMediaDice('🎯')})
+     * await client.sendFile(chat, {file:new Api.InputMediaDice("")})
+     * await client.sendFile(chat, {file:new Api.InputMediaDice("🎯")})
      *
      * // Contacts
      * await client.sendFile(chat, {file: new Api.InputMediaContact({
@@ -1366,14 +1366,15 @@ export class TelegramClient extends TelegramBaseClient {
         this._sender._reconnecting = false;
         this._sender._disconnected = true;
 
-        const connection = new this._connection(
-            this.session.serverAddress,
-            // We don't want to use the session for this.
-            this.useWSS ? 443 : 80,
-            this.session.dcId,
-            this._log,
-            this._proxy
-        );
+        const connection = new this._connection({
+            ip: this.session.serverAddress,
+            port: this.useWSS ? 443 : 80,
+            dcId: this.session.dcId,
+            loggers: this._log,
+            proxy: this._proxy,
+            socket: this.socket,
+            testServers: this.testServers,
+        });
         const newConnection = await this._sender.connect(connection);
         if (!newConnection) {
             // we're already connected so no need to reset auth key.
@@ -1422,14 +1423,17 @@ export class TelegramClient extends TelegramBaseClient {
      * This will do an API request to fill the cache if it's the first time it's called.
      * @param dcId The DC ID.
      * @param downloadDC whether to use -1 DCs or not
+     * @param web if true this will get the web DCs.
+     * TODO, hardcode IPs.
      * (These only support downloading/uploading and not creating a new AUTH key)
      */
     async getDC(
         dcId: number,
-        downloadDC = false
+        downloadDC = false,
+        web = false
     ): Promise<{ id: number; ipAddress: string; port: number }> {
         this._log.debug(`Getting DC ${dcId}`);
-        if (!isNode) {
+        if (!isNode || web) {
             switch (dcId) {
                 case 1:
                     return {

+ 9 - 2
gramjs/client/messages.ts

@@ -896,8 +896,15 @@ export async function editMessage(
         schedule,
     }: EditMessageParams
 ) {
-    if (typeof message === "number" && typeof text === "undefined" && !file && !schedule) {
-        throw Error("You have to provide either file or text or schedule property.");
+    if (
+        typeof message === "number" &&
+        typeof text === "undefined" &&
+        !file &&
+        !schedule
+    ) {
+        throw Error(
+            "You have to provide either file or text or schedule property."
+        );
     }
     entity = await client.getInputEntity(entity);
     let id: number | undefined;

+ 33 - 9
gramjs/client/telegramBaseClient.ts

@@ -5,7 +5,7 @@ import {
     ConnectionTCPObfuscated,
 } from "../network/connection";
 import { Session, StoreSession } from "../sessions";
-import { Logger } from "../extensions";
+import { Logger, PromisedNetSockets, PromisedWebSockets } from "../extensions";
 import { Api } from "../tl";
 
 import os from "./os";
@@ -19,7 +19,6 @@ import { LAYER } from "../tl/AllTLObjects";
 import {
     ConnectionTCPMTProxyAbridged,
     ProxyInterface,
-    TCPMTProxy,
 } from "../network/connection/TCPMTProxy";
 import { Semaphore } from "async-mutex";
 import { LogLevel } from "../extensions/Logger";
@@ -118,10 +117,19 @@ export interface TelegramClientParams {
      * Whether to check for tampering in messages or not.
      */
     securityChecks?: boolean;
+    /**
+     * Only for web DCs. Whether to use test servers or not.
+     */
+    testServers?: boolean;
+    /**
+     * What type of network connection to use (Normal Socket (for node) or Websockets (for browsers usually) )
+     */
+    networkSocket?: typeof PromisedNetSockets | typeof PromisedWebSockets;
 }
 
 const clientParamsDefault = {
     connection: isNode ? ConnectionTCPFull : ConnectionTCPObfuscated,
+    networkSocket: isNode ? PromisedNetSockets : PromisedWebSockets,
     useIPV6: false,
     timeout: 10,
     requestRetries: 5,
@@ -138,6 +146,7 @@ const clientParamsDefault = {
     systemLangCode: "en",
     _securityChecks: true,
     useWSS: isBrowser ? window.location.protocol == "https:" : false,
+    testServers: false,
 };
 
 export abstract class TelegramBaseClient {
@@ -217,6 +226,10 @@ export abstract class TelegramBaseClient {
     _semaphore: Semaphore;
     /** @hidden */
     _securityChecks: boolean;
+    /** @hidden */
+    public testServers: boolean;
+    /** @hidden */
+    public socket: PromisedNetSockets | PromisedWebSockets;
 
     constructor(
         session: string | Session,
@@ -257,6 +270,15 @@ export abstract class TelegramBaseClient {
         this._semaphore = new Semaphore(
             clientParams.maxConcurrentDownloads || 1
         );
+        this.testServers = clientParams.testServers || false;
+        if (
+            clientParams.networkSocket!.constructor ===
+            PromisedNetSockets.constructor
+        ) {
+            this.socket = new clientParams.networkSocket!(this._proxy);
+        } else {
+            this.socket = new clientParams.networkSocket!();
+        }
         if (!(clientParams.connection instanceof Function)) {
             throw new Error("Connection should be a class not an instance");
         }
@@ -408,13 +430,15 @@ export abstract class TelegramBaseClient {
         while (true) {
             try {
                 await sender.connect(
-                    new this._connection(
-                        dc.ipAddress,
-                        dc.port,
-                        dcId,
-                        this._log,
-                        this._proxy
-                    )
+                    new this._connection({
+                        ip: dc.ipAddress,
+                        port: dc.port,
+                        dcId: dcId,
+                        loggers: this._log,
+                        proxy: this._proxy,
+                        testServers: this.testServers,
+                        socket: this.socket,
+                    })
                 );
 
                 if (this.session.dcId !== dcId && !sender._authenticated) {

+ 1 - 1
gramjs/extensions/Logger.ts

@@ -51,7 +51,7 @@ export class Logger {
             };
         }
         this.messageFormat = "[%t] [%l] - [%m]";
-        this.tzOffset = new Date().getTimezoneOffset() * 60000
+        this.tzOffset = new Date().getTimezoneOffset() * 60000;
     }
 
     /**

+ 7 - 1
gramjs/extensions/MessagePacker.ts

@@ -141,7 +141,13 @@ export class MessagePacker {
     }
     rejectAll() {
         this._pendingStates.forEach((requestState) => {
-            requestState.reject(new Error("Disconnect"));
+            requestState.reject(
+                new Error(
+                    "Disconnect (caused from " +
+                        requestState?.request?.className +
+                        ")"
+                )
+            );
         });
     }
 }

+ 3 - 0
gramjs/extensions/PromisedNetSockets.ts

@@ -173,4 +173,7 @@ export class PromisedNetSockets {
             });
         }
     }
+    toString() {
+        return "PromisedNetSocket";
+    }
 }

+ 9 - 5
gramjs/extensions/PromisedWebSockets.ts

@@ -64,21 +64,21 @@ export class PromisedWebSockets {
         return toReturn;
     }
 
-    getWebSocketLink(ip: string, port: number) {
+    getWebSocketLink(ip: string, port: number, testServers: boolean) {
         if (port === 443) {
-            return `wss://${ip}:${port}/apiws`;
+            return `wss://${ip}:${port}/apiws${testServers ? "_test" : ""}`;
         } else {
-            return `ws://${ip}:${port}/apiws`;
+            return `ws://${ip}:${port}/apiws${testServers ? "_test" : ""}`;
         }
     }
 
-    async connect(port: number, ip: string) {
+    async connect(port: number, ip: string, testServers: boolean = false) {
         this.stream = Buffer.alloc(0);
         this.canRead = new Promise((resolve) => {
             this.resolveRead = resolve;
         });
         this.closed = false;
-        this.website = this.getWebSocketLink(ip, port);
+        this.website = this.getWebSocketLink(ip, port, testServers);
         this.client = new w3cwebsocket(this.website, "binary");
         return new Promise((resolve, reject) => {
             if (this.client) {
@@ -144,4 +144,8 @@ export class PromisedWebSockets {
             };
         }
     }
+
+    toString() {
+        return "PromisedWebSocket";
+    }
 }

+ 12 - 8
gramjs/network/MTProtoSender.ts

@@ -327,7 +327,9 @@ export class MTProtoSender {
      */
     async _connect() {
         this._log.info(
-            "Connecting to {0}...".replace("{0}", this._connection!.toString())
+            "Connecting to {0} using {1}"
+                .replace("{0}", this._connection!.toString())
+                .replace("{1}", this._connection!.socket.toString())
         );
         await this._connection!.connect();
         this._log.debug("Connection success!");
@@ -935,13 +937,15 @@ export class MTProtoSender {
         const constructor = this._connection!
             .constructor as unknown as typeof Connection;
 
-        const newConnection = new constructor(
-            this._connection!._ip,
-            this._connection!._port,
-            this._connection!._dcId,
-            this._connection!._log,
-            this._connection!._proxy
-        );
+        const newConnection = new constructor({
+            ip: this._connection!._ip,
+            port: this._connection!._port,
+            dcId: this._connection!._dcId,
+            loggers: this._connection!._log,
+            proxy: this._connection!._proxy,
+            testServers: this._connection!._testServers,
+            socket: this._connection!.socket,
+        });
         await this.connect(newConnection, true);
 
         this._reconnecting = false;

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

@@ -4,11 +4,20 @@ import {
     PromisedWebSockets,
 } from "../../extensions";
 import { AsyncQueue } from "../../extensions";
-import { isNode } from "../../platform";
 import { AbridgedPacketCodec } from "./TCPAbridged";
 import { FullPacketCodec } from "./TCPFull";
 import { ProxyInterface } from "./TCPMTProxy";
 
+interface ConnectionInterfaceParams {
+    ip: string;
+    port: number;
+    dcId: number;
+    loggers: Logger;
+    proxy?: ProxyInterface;
+    socket: PromisedNetSockets | PromisedWebSockets;
+    testServers: boolean;
+}
+
 /**
  * The `Connection` class is a wrapper around ``asyncio.open_connection``.
  *
@@ -35,14 +44,17 @@ class Connection {
     private readonly _sendArray: AsyncQueue;
     private _recvArray: AsyncQueue;
     socket: PromisedNetSockets | PromisedWebSockets;
-
-    constructor(
-        ip: string,
-        port: number,
-        dcId: number,
-        loggers: Logger,
-        proxy?: ProxyInterface
-    ) {
+    public _testServers: boolean;
+
+    constructor({
+        ip,
+        port,
+        dcId,
+        loggers,
+        proxy,
+        socket,
+        testServers,
+    }: ConnectionInterfaceParams) {
         this._ip = ip;
         this._port = port;
         this._dcId = dcId;
@@ -55,17 +67,14 @@ class Connection {
         this._obfuscation = undefined; // TcpObfuscated and MTProxy
         this._sendArray = new AsyncQueue();
         this._recvArray = new AsyncQueue();
-        this.socket = isNode
-            ? new PromisedNetSockets(this._proxy)
-            : new PromisedWebSockets();
-
-        //this.socket = new PromisedWebSockets()
+        this.socket = socket;
+        this._testServers = testServers;
     }
 
     async _connect() {
         this._log.debug("Connecting");
         this._codec = new this.PacketCodecClass!(this);
-        await this.socket.connect(this._port, this._ip);
+        await this.socket.connect(this._port, this._ip, this._testServers);
         this._log.debug("Finished connecting");
         // await this.socket.connect({host: this._ip, port: this._port});
         await this._initConn();

+ 28 - 8
gramjs/network/connection/TCPMTProxy.ts

@@ -121,19 +121,39 @@ class MTProxyIO {
     }
 }
 
+interface TCPMTProxyInterfaceParams {
+    ip: string;
+    port: number;
+    dcId: number;
+    loggers: Logger;
+    proxy: ProxyInterface;
+    socket: PromisedNetSockets | PromisedWebSockets;
+    testServers: boolean;
+}
+
 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);
+    constructor({
+        ip,
+        port,
+        dcId,
+        loggers,
+        proxy,
+        socket,
+        testServers,
+    }: TCPMTProxyInterfaceParams) {
+        super({
+            ip: proxy.ip,
+            port: proxy.port,
+            dcId: dcId,
+            loggers: loggers,
+            socket: socket,
+            proxy: proxy,
+            testServers: testServers,
+        });
         if (!proxy.MTProxy) {
             throw new Error("This connection only supports MPTProxies");
         }

+ 2 - 2
package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "telegram",
-  "version": "2.7.21",
+  "version": "2.8.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "telegram",
-      "version": "2.7.21",
+      "version": "2.8.0",
       "license": "MIT",
       "dependencies": {
         "@cryptography/aes": "^0.1.1",

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "telegram",
-  "version": "2.7.21",
+  "version": "2.8.0",
   "description": "NodeJS/Browser MTProto API Telegram client library,",
   "main": "index.js",
   "types": "index.d.ts",