ソースを参照

Typedoc most of the functions

painor 3 年 前
コミット
3f243268f2
52 ファイル変更15141 行追加14760 行削除
  1. 23 5
      gramjs/Helpers.ts
  2. 1 1
      gramjs/Password.ts
  3. 52 50
      gramjs/Utils.ts
  4. 1 1
      gramjs/Version.ts
  5. 785 123
      gramjs/client/TelegramClient.ts
  6. 39 9
      gramjs/client/auth.ts
  7. 4 3
      gramjs/client/bots.ts
  8. 2 1
      gramjs/client/buttons.ts
  9. 44 23
      gramjs/client/chats.ts
  10. 36 18
      gramjs/client/dialogs.ts
  11. 49 21
      gramjs/client/downloads.ts
  12. 12 10
      gramjs/client/messageParse.ts
  13. 167 54
      gramjs/client/messages.ts
  14. 131 44
      gramjs/client/telegramBaseClient.ts
  15. 17 15
      gramjs/client/updates.ts
  16. 94 36
      gramjs/client/uploads.ts
  17. 42 33
      gramjs/client/users.ts
  18. 1 0
      gramjs/crypto/AuthKey.ts
  19. 1 1
      gramjs/crypto/RSA.ts
  20. 1 1
      gramjs/crypto/crypto.ts
  21. 73 23
      gramjs/events/NewMessage.ts
  22. 21 6
      gramjs/events/Raw.ts
  23. 34 13
      gramjs/events/common.ts
  24. 2 2
      gramjs/extensions/BinaryReader.ts
  25. 1 1
      gramjs/extensions/Logger.ts
  26. 3 3
      gramjs/extensions/html.ts
  27. 1 2
      gramjs/extensions/markdown.ts
  28. 13 13
      gramjs/network/MTProtoSender.ts
  29. 1 1
      gramjs/network/MTProtoState.ts
  30. 3 4
      gramjs/network/connection/Connection.ts
  31. 1 1
      gramjs/sessions/Abstract.ts
  32. 1 1
      gramjs/sessions/StringSession.ts
  33. 2 2
      gramjs/tl/MTProtoRequest.ts
  34. 12852 14105
      gramjs/tl/api.d.ts
  35. 1 1
      gramjs/tl/core/GZIPPacked.ts
  36. 1 0
      gramjs/tl/core/RPCResult.ts
  37. 10 4
      gramjs/tl/custom/button.ts
  38. 5 0
      gramjs/tl/custom/chatGetter.ts
  39. 7 2
      gramjs/tl/custom/dialog.ts
  40. 16 9
      gramjs/tl/custom/draft.ts
  41. 5 0
      gramjs/tl/custom/file.ts
  42. 5 1
      gramjs/tl/custom/forward.ts
  43. 22 16
      gramjs/tl/custom/inlineResult.ts
  44. 13 6
      gramjs/tl/custom/inlineResults.ts
  45. 231 78
      gramjs/tl/custom/message.ts
  46. 5 0
      gramjs/tl/custom/messageButton.ts
  47. 7 2
      gramjs/tl/custom/senderGetter.ts
  48. 5 5
      gramjs/tl/types-generator/template.js
  49. 280 8
      package-lock.json
  50. 2 1
      package.json
  51. 0 1
      tsconfig.json
  52. 16 0
      type_doc.js

+ 23 - 5
gramjs/Helpers.ts

@@ -2,7 +2,6 @@ import { isNode } from "browser-or-node";
 import bigInt from "big-integer";
 import type { EntityLike } from "./define";
 import type { Api } from "./tl";
-import exp from "constants";
 
 export const IS_NODE = isNode;
 const crypto = require(isNode ? "crypto" : "./crypto/crypto");
@@ -53,10 +52,29 @@ export function groupBy(list: any[], keyGetter: Function) {
     return map;
 }
 
+/**
+ * Outputs the object in a better way by hiding all the private methods/attributes.
+ * @param object - the class to use
+ */
+export function betterConsoleLog(object: { [key: string]: any }) {
+    const toPrint: { [key: string]: any } = {};
+    for (const key in object) {
+        console.log("key is",key);
+        console.log("key starts with _?",key.startsWith("_"));
+
+        if (object.hasOwnProperty(key)) {
+            if (!key.startsWith("_")) {
+                toPrint[key] = object[key];
+            }
+        }
+    }
+    return toPrint;
+}
+
 /**
  * Helper to find if a given object is an array (or similar)
  */
-export const isArrayLike = <T>(x: any): x is ArrayLike<T> =>
+export const isArrayLike = <T>(x: any): x is Array<T> =>
     x &&
     typeof x.length === "number" &&
     typeof x !== "function" &&
@@ -183,9 +201,9 @@ export function mod(n: number, m: number) {
 
 /**
  * returns a positive bigInt
- * @param n {BigInt}
- * @param m {BigInt}
- * @returns {BigInt}
+ * @param n {bigInt.BigInteger}
+ * @param m {bigInt.BigInteger}
+ * @returns {bigInt.BigInteger}
  */
 export function bigIntMod(
     n: bigInt.BigInteger,

+ 1 - 1
gramjs/Password.ts

@@ -8,7 +8,7 @@ import {
     sha256,
 } from "./Helpers";
 import bigInt from "big-integer";
-import { isBrowser, isNode } from "browser-or-node";
+import { isNode } from "browser-or-node";
 const crypto = require(isNode ? "crypto" : "./crypto/crypto");
 
 const SIZE_FOR_HASH = 256;

+ 52 - 50
gramjs/Utils.ts

@@ -12,7 +12,7 @@ import { HTMLParser } from "./extensions/html";
 
 const USERNAME_RE = new RegExp(
     "@|(?:https?:\\/\\/)?(?:www\\.)?" +
-        "(?:telegram\\.(?:me|dog)|t\\.me)\\/(@|joinchat\\/)?",
+    "(?:telegram\\.(?:me|dog)|t\\.me)\\/(@|joinchat\\/)?",
     "i"
 );
 
@@ -26,7 +26,7 @@ const TG_JOIN_RE = new RegExp("tg:\\/\\/(join)\\?invite=", "i");
 
 const VALID_USERNAME_RE = new RegExp(
     "^([a-z]((?!__)[\\w\\d]){3,30}[a-z\\d]|gif|vid|" +
-        "pic|bing|wiki|imdb|bold|vote|like|coub)$",
+    "pic|bing|wiki|imdb|bold|vote|like|coub)$",
     "i"
 );
 
@@ -81,7 +81,7 @@ export function getInputPeer(
         } else if (entity.accessHash !== undefined || !checkHash) {
             return new Api.InputPeerUser({
                 userId: entity.id,
-                accessHash: entity.accessHash || bigInt(0),
+                accessHash: entity.accessHash || bigInt(0)
             });
         } else {
             throw new Error("User without accessHash cannot be input");
@@ -98,7 +98,7 @@ export function getInputPeer(
         if (entity.accessHash !== undefined || !checkHash) {
             return new Api.InputPeerChannel({
                 channelId: entity.id,
-                accessHash: entity.accessHash || bigInt(0),
+                accessHash: entity.accessHash || bigInt(0)
             });
         } else {
             throw new TypeError(
@@ -111,20 +111,20 @@ export function getInputPeer(
         // also not optional, we assume that this truly is the case.
         return new Api.InputPeerChannel({
             channelId: entity.id,
-            accessHash: entity.accessHash,
+            accessHash: entity.accessHash
         });
     }
 
     if (entity instanceof Api.InputUser) {
         return new Api.InputPeerUser({
             userId: entity.userId,
-            accessHash: entity.accessHash,
+            accessHash: entity.accessHash
         });
     }
     if (entity instanceof Api.InputChannel) {
         return new Api.InputPeerChannel({
             channelId: entity.channelId,
-            accessHash: entity.accessHash,
+            accessHash: entity.accessHash
         });
     }
     if (entity instanceof Api.UserEmpty) {
@@ -140,7 +140,7 @@ export function getInputPeer(
 
     if (entity instanceof Api.PeerChat) {
         return new Api.InputPeerChat({
-            chatId: entity.chatId,
+            chatId: entity.chatId
         });
     }
 
@@ -177,17 +177,18 @@ export function _getEntityPair(
     } catch (e) {
         try {
             inputEntity = getInputPeerFunction(inputEntity);
-        } catch (e) {}
+        } catch (e) {
+        }
     }
     return [entity, inputEntity];
 }
 
 export function getInnerText(
     text: string,
-    entities: Map<number, Api.TypeMessageEntity>
+    entities: Api.TypeMessageEntity[]
 ) {
     const result: string[] = [];
-    entities.forEach(function (value, key) {
+    entities.forEach(function(value, key) {
         const start = value.offset;
         const end = value.offset + value.length;
         result.push(text.slice(start, end));
@@ -225,14 +226,14 @@ export function getInputChannel(entity: EntityLike) {
     ) {
         return new Api.InputChannel({
             channelId: entity.id,
-            accessHash: entity.accessHash || bigInt.zero,
+            accessHash: entity.accessHash || bigInt.zero
         });
     }
 
     if (entity instanceof Api.InputPeerChannel) {
         return new Api.InputChannel({
             channelId: entity.channelId,
-            accessHash: entity.accessHash,
+            accessHash: entity.accessHash
         });
     }
     _raiseCastFail(entity, "InputChannel");
@@ -268,7 +269,7 @@ export function getInputUser(entity: EntityLike): Api.InputPeerSelf {
         } else {
             return new Api.InputUser({
                 userId: entity.id,
-                accessHash: entity.accessHash || bigInt.zero,
+                accessHash: entity.accessHash || bigInt.zero
             });
         }
     }
@@ -289,7 +290,7 @@ export function getInputUser(entity: EntityLike): Api.InputPeerSelf {
     if (entity instanceof Api.InputPeerUser) {
         return new Api.InputUser({
             userId: entity.userId,
-            accessHash: entity.accessHash,
+            accessHash: entity.accessHash
         });
     }
 
@@ -358,13 +359,13 @@ export function getInputChatPhoto(photo: any): Api.TypeInputChatPhoto {
     } else if (photo.SUBCLASS_OF_ID === 0xe7655f1f) {
         // crc32(b'InputFile'):
         return new Api.InputChatUploadedPhoto({
-            file: photo,
+            file: photo
         });
     }
     photo = getInputPhoto(photo);
     if (photo instanceof Api.InputPhoto) {
         return new Api.InputChatPhoto({
-            id: photo,
+            id: photo
         });
     } else if (photo instanceof Api.InputPhotoEmpty) {
         return new Api.InputChatPhotoEmpty();
@@ -468,7 +469,7 @@ export function getInputPhoto(photo: any): Api.TypePhoto | Api.InputPhotoEmpty {
         return new Api.InputPhoto({
             id: photo.id,
             accessHash: photo.accessHash,
-            fileReference: photo.fileReference,
+            fileReference: photo.fileReference
         });
     }
     if (photo instanceof Api.PhotoEmpty) {
@@ -522,7 +523,7 @@ export function getInputDocument(
         return new Api.InputDocument({
             id: document.id,
             accessHash: document.accessHash,
-            fileReference: document.fileReference,
+            fileReference: document.fileReference
         });
     }
     if (document instanceof Api.DocumentEmpty) {
@@ -581,7 +582,8 @@ export function getExtension(media: any): string {
     try {
         getInputPhoto(media);
         return ".jpg";
-    } catch (e) {}
+    } catch (e) {
+    }
     if (
         media instanceof Api.UserProfilePhoto ||
         media instanceof Api.ChatPhoto
@@ -656,7 +658,7 @@ export function getAttributes(
         voiceNote = false,
         videoNote = false,
         supportsStreaming = false,
-        thumb = null,
+        thumb = null
     }: GetAttributesParams
 ) {
     const name: string =
@@ -668,7 +670,7 @@ export function getAttributes(
     attrObj.set(
         Api.DocumentAttributeFilename,
         new Api.DocumentAttributeFilename({
-            fileName: name.split(/[\\/]/).pop() || "",
+            fileName: name.split(/[\\/]/).pop() || ""
         })
     );
     if (isAudio(file)) {
@@ -680,7 +682,7 @@ export function getAttributes(
                     voice: voiceNote,
                     title: m.has("title") ? m.get("title") : undefined,
                     performer: m.has("author") ? m.get("author") : undefined,
-                    duration: Number.parseInt(m.get("duration") ?? "0"),
+                    duration: Number.parseInt(m.get("duration") ?? "0")
                 })
             );
         }
@@ -694,7 +696,7 @@ export function getAttributes(
                 w: Number.parseInt(m.get("width") ?? "0"),
                 h: Number.parseInt(m.get("height") ?? "0"),
                 duration: Number.parseInt(m.get("duration") ?? "0"),
-                supportsStreaming: supportsStreaming,
+                supportsStreaming: supportsStreaming
             });
         } else {
             if (thumb) {
@@ -706,7 +708,7 @@ export function getAttributes(
                     h: height,
                     w: width,
                     roundMessage: videoNote,
-                    supportsStreaming: supportsStreaming,
+                    supportsStreaming: supportsStreaming
                 });
             } else {
                 doc = new Api.DocumentAttributeVideo({
@@ -714,7 +716,7 @@ export function getAttributes(
                     h: 1,
                     w: 1,
                     roundMessage: videoNote,
-                    supportsStreaming: supportsStreaming,
+                    supportsStreaming: supportsStreaming
                 });
             }
         }
@@ -728,7 +730,7 @@ export function getAttributes(
                 Api.DocumentAttributeAudio,
                 new Api.DocumentAttributeAudio({
                     duration: 0,
-                    voice: true,
+                    voice: true
                 })
             );
         }
@@ -745,7 +747,7 @@ export function getAttributes(
 
     return {
         attrs: Array.from(attrObj.values()) as Api.TypeDocumentAttribute[],
-        mimeType: mimeType,
+        mimeType: mimeType
     };
 }
 
@@ -776,7 +778,7 @@ export function getInputGeo(geo: any): Api.TypeInputGeoPoint {
     _raiseCastFail(geo, "InputGeoPoint");
 }
 
-interface GetInputMediaInterface {
+export interface GetInputMediaInterface {
     isPhoto?: boolean;
     attributes?: Api.TypeDocumentAttribute[];
     forceDocument?: boolean;
@@ -785,6 +787,7 @@ interface GetInputMediaInterface {
     supportsStreaming?: boolean;
 }
 
+
 /**
  *
  Similar to :meth:`get_input_peer`, but for media.
@@ -793,7 +796,7 @@ interface GetInputMediaInterface {
  it will be treated as an :tl:`InputMediaUploadedPhoto`. Else, the rest
  of parameters will indicate how to treat it.
  * @param media
- * @param isPhoto
+ * @param isPhoto - whether it's a photo or not
  * @param attributes
  * @param forceDocument
  * @param voiceNote
@@ -808,7 +811,7 @@ export function getInputMedia(
         forceDocument = false,
         voiceNote = false,
         videoNote = false,
-        supportsStreaming = false,
+        supportsStreaming = false
     }: GetInputMediaInterface = {}
 ): any {
     if (media.SUBCLASS_OF_ID === undefined) {
@@ -833,7 +836,7 @@ export function getInputMedia(
     if (media instanceof Api.MessageMediaPhoto) {
         return new Api.InputMediaPhoto({
             id: getInputPhoto(media.photo),
-            ttlSeconds: media.ttlSeconds,
+            ttlSeconds: media.ttlSeconds
         });
     }
     if (
@@ -846,7 +849,7 @@ export function getInputMedia(
     if (media instanceof Api.MessageMediaDocument) {
         return new Api.InputMediaDocument({
             id: getInputDocument(media.document),
-            ttlSeconds: media.ttlSeconds,
+            ttlSeconds: media.ttlSeconds
         });
     }
     if (media instanceof Api.Document || media instanceof Api.DocumentEmpty) {
@@ -861,13 +864,13 @@ export function getInputMedia(
                 forceDocument: forceDocument,
                 voiceNote: voiceNote,
                 videoNote: videoNote,
-                supportsStreaming: supportsStreaming,
+                supportsStreaming: supportsStreaming
             });
             return new Api.InputMediaUploadedDocument({
                 file: media,
                 mimeType: mimeType,
                 attributes: attrs,
-                forceFile: forceDocument,
+                forceFile: forceDocument
             });
         }
     }
@@ -875,8 +878,8 @@ export function getInputMedia(
         return new Api.InputMediaGame({
             id: new Api.InputGameID({
                 id: media.game.id,
-                accessHash: media.game.accessHash,
-            }),
+                accessHash: media.game.accessHash
+            })
         });
     }
     if (media instanceof Api.MessageMediaContact) {
@@ -884,7 +887,7 @@ export function getInputMedia(
             phoneNumber: media.phoneNumber,
             firstName: media.firstName,
             lastName: media.lastName,
-            vcard: "",
+            vcard: ""
         });
     }
     if (media instanceof Api.MessageMediaGeo) {
@@ -897,12 +900,12 @@ export function getInputMedia(
             address: media.address,
             provider: media.provider,
             venueId: media.venueId,
-            venueType: "",
+            venueType: ""
         });
     }
     if (media instanceof Api.MessageMediaDice) {
         return new Api.InputMediaDice({
-            emoticon: media.emoticon,
+            emoticon: media.emoticon
         });
     }
     if (
@@ -940,19 +943,17 @@ export function getInputMedia(
             poll: media.poll,
             correctAnswers: correctAnswers,
             solution: media.results.solution,
-            solutionEntities: media.results.solutionEntities,
+            solutionEntities: media.results.solutionEntities
         });
     }
     if (media instanceof Api.Poll) {
         return new Api.InputMediaPoll({
-            poll: media,
+            poll: media
         });
     }
     _raiseCastFail(media, "InputMedia");
 }
 
-//# sourceMappingURL=anothatest.js.map
-
 /**
  * Gets the appropriated part size when uploading or downloading files,
  * given an initial file size.
@@ -1028,7 +1029,8 @@ export function getPeer(peer: EntityLike) {
         } else if (peer instanceof Api.InputPeerChannel) {
             return new Api.PeerChannel({ channelId: peer.channelId });
         }
-    } catch (e) {}
+    } catch (e) {
+    }
     _raiseCastFail(peer, "peer");
 }
 
@@ -1175,7 +1177,7 @@ export function getMessageId(message: any): number | undefined {
 }
 
 /**
- * Parses the given phone, or returns `None` if it's invalid.
+ * Parses the given phone, or returns `undefined` if it's invalid.
  * @param phone
  */
 export function parsePhone(phone: string | number) {
@@ -1194,7 +1196,7 @@ export function resolveInviteLink(link: string): [number, number, number] {
  both the stripped, lowercase username and whether it is
  a joinchat/ hash (in which case is not lowercase'd).
 
- Returns ``(None, False)`` if the ``username`` or link is not valid.
+ Returns ``(undefined, false)`` if the ``username`` or link is not valid.
 
  * @param username {string}
  */
@@ -1210,7 +1212,7 @@ export function parseUsername(username: string): {
         if (m[1]) {
             return {
                 username: username,
-                isInvite: true,
+                isInvite: true
             };
         } else {
             username = rtrim(username, "/");
@@ -1219,12 +1221,12 @@ export function parseUsername(username: string): {
     if (username.match(VALID_USERNAME_RE)) {
         return {
             username: username.toLowerCase(),
-            isInvite: false,
+            isInvite: false
         };
     } else {
         return {
             username: undefined,
-            isInvite: false,
+            isInvite: false
         };
     }
 }

+ 1 - 1
gramjs/Version.ts

@@ -1 +1 @@
-export const version = "1.7.7";
+export const version = "1.7.9";

ファイルの差分が大きいため隠しています
+ 785 - 123
gramjs/client/TelegramClient.ts


+ 39 - 9
gramjs/client/auth.ts

@@ -4,33 +4,61 @@ import { sleep } from "../Helpers";
 import { computeCheck as computePasswordSrpCheck } from "../Password";
 import type { TelegramClient } from "./TelegramClient";
 
+/**
+ * For when you want to login as a {@link Api.User}<br/>
+ * this should handle all needed steps for authorization as a user.<br/>
+ * to stop the operation at any point just raise and error with the message `AUTH_USER_CANCEL`.
+ */
 export interface UserAuthParams {
+    /** Either a string or a callback that returns a string for the phone to use to login. */
     phoneNumber: string | (() => Promise<string>);
+    /** callback that should return the login code that telegram sent.<br/>
+     *  has optional bool `isCodeViaApp` param for whether the code was sent through the app (true) or an SMS (false). */
     phoneCode: (isCodeViaApp?: boolean) => Promise<string>;
+    /** optional string or callback that should return the 2FA password if present.<br/>
+     *  the password hint will be sent in the hint param */
     password: (hint?: string) => Promise<string>;
+    /** in case of a new account creation this callback should return a first name and last name `[first,last]`. */
     firstAndLastNames?: () => Promise<[string, string?]>;
+    /** a qrCode token for login through qrCode.<br/>
+     *  this would need a QR code that you should scan with another app to login with. */
     qrCode?: (qrCode: { token: Buffer; expires: number }) => Promise<void>;
+    /** when an error happens during auth this function will be called with the error.<br/>
+     *  if this returns true the auth operation will stop. */
     onError: (err: Error) => Promise<boolean> | void;
+    /** whether to send the code through SMS or not. */
     forceSMS?: boolean;
 }
 
 interface ReturnString {
     (): string;
 }
-
+/**
+ * For when you want as a normal bot created by https://t.me/Botfather.<br/>
+ * Logging in as bot is simple and requires no callbacks
+ */
 export interface BotAuthParams {
+    /**
+     * the bot token to use.
+     */
     botAuthToken: string | ReturnString;
 }
 
+/**
+ * Credential needed for the authentication. you can get theses from https://my.telegram.org/auth<br/>
+ * Note: This is required for both logging in as a bot and a user.<br/>
+ */
 export interface ApiCredentials {
+    /** The app api id. */
     apiId: number;
+    /** the app api hash */
     apiHash: string;
 }
 
 const QR_CODE_TIMEOUT = 30000;
 
 // region public methods
-
+/** @hidden */
 export async function start(
     client: TelegramClient,
     authParams: UserAuthParams | BotAuthParams
@@ -48,9 +76,9 @@ export async function start(
         apiHash: client.apiHash,
     };
 
-    await client.authFlow(apiCredentials, authParams);
+    await _authFlow(client,apiCredentials, authParams);
 }
-
+/** @hidden */
 export async function checkAuthorization(client: TelegramClient) {
     try {
         await client.invoke(new Api.updates.GetState());
@@ -59,7 +87,7 @@ export async function checkAuthorization(client: TelegramClient) {
         return false;
     }
 }
-
+/** @hidden */
 export async function signInUser(
     client: TelegramClient,
     apiCredentials: ApiCredentials,
@@ -206,6 +234,7 @@ export async function signInUser(
     return client.signInUser(apiCredentials, authParams);
 }
 
+/** @hidden */
 export async function signInUserWithQrCode(
     client: TelegramClient,
     apiCredentials: ApiCredentials,
@@ -292,6 +321,7 @@ export async function signInUserWithQrCode(
     return client.signInUser(apiCredentials, authParams);
 }
 
+/** @hidden */
 export async function sendCode(
     client: TelegramClient,
     apiCredentials: ApiCredentials,
@@ -341,6 +371,7 @@ export async function sendCode(
     }
 }
 
+/** @hidden */
 export async function signInWithPassword(
     client: TelegramClient,
     apiCredentials: ApiCredentials,
@@ -377,7 +408,7 @@ export async function signInWithPassword(
 
     return undefined!; // Never reached (TypeScript fix)
 }
-
+/** @hidden */
 export async function signInBot(
     client: TelegramClient,
     apiCredentials: ApiCredentials,
@@ -408,8 +439,8 @@ export async function signInBot(
     )) as Api.auth.Authorization;
     return user;
 }
-
-export async function authFlow(
+/** @hidden */
+export async function _authFlow(
     client: TelegramClient,
     apiCredentials: ApiCredentials,
     authParams: UserAuthParams | BotAuthParams
@@ -419,6 +450,5 @@ export async function authFlow(
             ? await client.signInUser(apiCredentials, authParams)
             : await client.signInBot(apiCredentials, authParams);
 
-    // TODO @logger
     client._log.info("Signed in successfully as " + utils.getDisplayName(me));
 }

+ 4 - 3
gramjs/client/bots.ts

@@ -5,14 +5,15 @@ import GetInlineBotResults = Api.messages.GetInlineBotResults;
 import type { TelegramClient } from "./TelegramClient";
 
 // BotMethods
+/** @hidden */
 export async function inlineQuery(
     client: TelegramClient,
     bot: EntityLike,
     query: string,
-    entity?: Api.InputPeerSelf | null,
+    entity?: Api.InputPeerSelf,
     offset?: string,
     geoPoint?: Api.GeoPoint
-): Promise<InlineResults<Api.messages.BotResults>> {
+): Promise<InlineResults> {
     bot = await client.getInputEntity(bot);
     let peer = new Api.InputPeerSelf();
     if (entity) {
@@ -30,6 +31,6 @@ export async function inlineQuery(
     return new InlineResults(
         client,
         result,
-        (entity = entity ? peer : undefined)
+        (entity ? peer : undefined)
     );
 }

+ 2 - 1
gramjs/client/buttons.ts

@@ -1,10 +1,11 @@
 import { Api } from "../tl";
-import type { ButtonLike, MarkupLike } from "../define";
+import type { ButtonLike} from "../define";
 import { Button } from "../tl/custom/button";
 import { MessageButton } from "../tl/custom/messageButton";
 import { isArrayLike } from "../Helpers";
 
 // ButtonMethods
+/** @hidden */
 export function buildReplyMarkup(
     buttons:
         | Api.TypeReplyMarkup

+ 44 - 23
gramjs/client/chats.ts

@@ -1,10 +1,11 @@
 import type { TelegramClient } from "./TelegramClient";
 import type { EntitiesLike, Entity, EntityLike, ValueOf } from "../define";
-import { sleep, getMinBigInt, TotalList } from "../Helpers";
+import { sleep, getMinBigInt, TotalList, betterConsoleLog } from "../Helpers";
 import { RequestIter } from "../requestIter";
 import { helpers, utils } from "../";
 import { Api } from "../tl";
 import bigInt, { BigInteger } from "big-integer";
+import { inspect } from "util";
 
 const _MAX_PARTICIPANTS_CHUNK_SIZE = 200;
 const _MAX_ADMIN_LOG_CHUNK_SIZE = 100;
@@ -37,25 +38,29 @@ class _ChatAction {
         document: new Api.SendMessageUploadDocumentAction({ progress: 1 }),
         file: new Api.SendMessageUploadDocumentAction({ progress: 1 }), // alias
 
-        cancel: new Api.SendMessageCancelAction(),
+        cancel: new Api.SendMessageCancelAction()
     };
 
     private _client: TelegramClient;
-    private _chat: EntityLike;
-    private _action: ValueOf<typeof _ChatAction._str_mapping>;
-    private _delay: number;
-    private autoCancel: boolean;
+    private readonly _chat: EntityLike;
+    private readonly _action: ValueOf<typeof _ChatAction._str_mapping>;
+    private readonly _delay: number;
+    private readonly autoCancel: boolean;
     private _request?: Api.AnyRequest;
     private _task: null;
     private _running: boolean;
 
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
+
     constructor(
         client: TelegramClient,
         chat: EntityLike,
         action: ValueOf<typeof _ChatAction._str_mapping>,
         params: ChatActionInterface = {
             delay: 4,
-            autoCancel: true,
+            autoCancel: true
         }
     ) {
         this._client = client;
@@ -71,7 +76,7 @@ class _ChatAction {
     async start() {
         this._request = new Api.messages.SetTyping({
             peer: this._chat,
-            action: this._action,
+            action: this._action
         });
         this._running = true;
         this._update();
@@ -83,7 +88,7 @@ class _ChatAction {
             await this._client.invoke(
                 new Api.messages.SetTyping({
                     peer: this._chat,
-                    action: new Api.SendMessageCancelAction(),
+                    action: new Api.SendMessageCancelAction()
                 })
             );
         }
@@ -115,22 +120,26 @@ export class _ParticipantsIter extends RequestIter {
     private filterEntity: ((entity: Entity) => boolean) | undefined;
     private requests?: Api.channels.GetParticipants[];
 
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
+
     async _init({
-        entity,
-        filter,
-        search,
-    }: ParticipantsIterInterface): Promise<boolean | void> {
+                    entity,
+                    filter,
+                    search
+                }: ParticipantsIterInterface): Promise<boolean | void> {
         if (filter && filter.constructor === Function) {
             if (
                 [
                     Api.ChannelParticipantsBanned,
                     Api.ChannelParticipantsKicked,
                     Api.ChannelParticipantsSearch,
-                    Api.ChannelParticipantsContacts,
+                    Api.ChannelParticipantsContacts
                 ].includes(filter)
             ) {
                 filter = new filter({
-                    q: "",
+                    q: ""
                 });
             } else {
                 filter = new filter();
@@ -160,7 +169,7 @@ export class _ParticipantsIter extends RequestIter {
         if (ty == helpers._EntityType.CHANNEL) {
             const channel = await this.client.invoke(
                 new Api.channels.GetFullChannel({
-                    channel: entity,
+                    channel: entity
                 })
             );
             if (!(channel.fullChat instanceof Api.ChatFull)) {
@@ -175,11 +184,11 @@ export class _ParticipantsIter extends RequestIter {
                     filter:
                         filter ||
                         new Api.ChannelParticipantsSearch({
-                            q: search || "",
+                            q: search || ""
                         }),
                     offset: 0,
                     limit: _MAX_PARTICIPANTS_CHUNK_SIZE,
-                    hash: 0,
+                    hash: 0
                 })
             );
         } else if (ty == helpers._EntityType.CHAT) {
@@ -190,7 +199,7 @@ export class _ParticipantsIter extends RequestIter {
             }
             const full = await this.client.invoke(
                 new Api.messages.GetFullChat({
-                    chatId: entity.chatId,
+                    chatId: entity.chatId
                 })
             );
 
@@ -256,7 +265,7 @@ export class _ParticipantsIter extends RequestIter {
             const participants = results[i];
             if (
                 participants instanceof
-                    Api.channels.ChannelParticipantsNotModified ||
+                Api.channels.ChannelParticipantsNotModified ||
                 !participants.users.length
             ) {
                 this.requests.splice(i, 1);
@@ -317,6 +326,10 @@ class _AdminLogIter extends RequestIter {
     private entity?: Api.TypeInputPeer;
     private request?: Api.channels.GetAdminLog;
 
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
+
     async _init(
         entity: EntityLike,
         searchArgs?: _AdminLogSearchInterface,
@@ -329,7 +342,7 @@ class _AdminLogIter extends RequestIter {
             Object.values(filterArgs).find((element) => element === true)
         ) {
             eventsFilter = new Api.ChannelAdminLogEventsFilter({
-                ...filterArgs,
+                ...filterArgs
             });
         }
         this.entity = await this.client.getInputEntity(entity);
@@ -346,7 +359,7 @@ class _AdminLogIter extends RequestIter {
             maxId: searchArgs?.maxId,
             limit: 0,
             eventsFilter: eventsFilter,
-            admins: adminList || undefined,
+            admins: adminList || undefined
         });
     }
 
@@ -378,12 +391,19 @@ class _AdminLogIter extends RequestIter {
     }
 }
 
+/**
+ * Used in iterParticipant and getParticipant. all params are optional.
+ */
 export interface IterParticipantsParams {
+    /** how many members to retrieve. defaults to Number.MAX_SAFE_INTEGER (everyone) */
     limit?: number;
+    /** a query string to filter participants based on their display names and usernames. defaults to "" (everyone) */
     search?: string;
+    /** optional filter to be used. E.g only admins filter or only banned members filter. PS : some filters need more permissions. */
     filter?: Api.TypeChannelParticipantsFilter;
 }
 
+/** @hidden */
 export function iterParticipants(
     client: TelegramClient,
     entity: EntityLike,
@@ -396,11 +416,12 @@ export function iterParticipants(
         {
             entity: entity,
             filter: filter,
-            search: search,
+            search: search
         }
     );
 }
 
+/** @hidden */
 export async function getParticipants(
     client: TelegramClient,
     entity: EntityLike,

+ 36 - 18
gramjs/client/dialogs.ts

@@ -3,8 +3,7 @@ import { RequestIter } from "../requestIter";
 import { TelegramClient, utils } from "../index";
 import { Message } from "../tl/custom/message";
 import { Dialog } from "../tl/custom/dialog";
-import { DateLike, EntityLike, FileLike, MarkupLike } from "../define";
-import { IterMessagesParams } from "./messages";
+import { DateLike, EntityLike} from "../define";
 import { TotalList } from "../Helpers";
 
 const _MAX_CHUNK_SIZE = 100;
@@ -25,12 +24,12 @@ function _dialogMessageKey(peer: Api.TypePeer, messageId: number): string {
         "" +
         [
             peer instanceof Api.PeerChannel ? peer.channelId : undefined,
-            messageId,
+            messageId
         ]
     );
 }
 
-interface DialogsIterInterface {
+export interface DialogsIterInterface {
     offsetDate: number;
     offsetId: number;
     offsetPeer: Api.TypePeer;
@@ -46,13 +45,13 @@ export class _DialogsIter extends RequestIter {
     private ignoreMigrated?: boolean;
 
     async _init({
-        offsetDate,
-        offsetId,
-        offsetPeer,
-        ignorePinned,
-        ignoreMigrated,
-        folder,
-    }: DialogsIterInterface) {
+                    offsetDate,
+                    offsetId,
+                    offsetPeer,
+                    ignorePinned,
+                    ignoreMigrated,
+                    folder
+                }: DialogsIterInterface) {
         this.request = new Api.messages.GetDialogs({
             offsetDate,
             offsetId,
@@ -60,7 +59,7 @@ export class _DialogsIter extends RequestIter {
             limit: 1,
             hash: 0,
             excludePinned: ignorePinned,
-            folderId: folder,
+            folderId: folder
         });
         if (this.limit <= 0) {
             // Special case, get a single dialog and determine count
@@ -113,14 +112,14 @@ export class _DialogsIter extends RequestIter {
             } catch (e) {
                 this.client._log.error(
                     "Got error while trying to finish init message with id " +
-                        m.id
+                    m.id
                 );
                 if (this.client._log.canSend("error")) {
                     console.error(e);
                 }
             }
             messages.set(
-                _dialogMessageKey(message.peerId, message.id),
+                _dialogMessageKey(message.peerId!, message.id),
                 message
             );
         }
@@ -133,7 +132,7 @@ export class _DialogsIter extends RequestIter {
                 _dialogMessageKey(d.peer, d.topMessage)
             );
             if (this.offsetDate != undefined) {
-                const date = message?.date;
+                const date = message?.date!;
                 if (date != undefined || date > this.offsetDate) {
                     continue;
                 }
@@ -175,23 +174,40 @@ export class _DialogsIter extends RequestIter {
         }
         this.request.excludePinned = true;
         this.request.offsetId = lastMessage ? lastMessage.id : 0;
-        this.request.offsetDate = lastMessage ? lastMessage.date : undefined;
+        this.request.offsetDate = lastMessage ? lastMessage.date! : 0;
         this.request.offsetPeer =
             this.buffer[this.buffer.length - 1].inputEntity;
     }
 }
 
+
+/** interface for iterating and getting dialogs. */
 export interface IterDialogsParams {
+    /**  How many dialogs to be retrieved as maximum. Can be set to undefined to retrieve all dialogs.<br/>
+     * Note that this may take whole minutes if you have hundreds of dialogs, as Telegram will tell the library to slow down through a FloodWaitError.*/
     limit?: number;
+    /** The offset date of last message of dialog to be used. */
     offsetDate?: DateLike;
+    /** The message ID to be used as offset. */
     offsetId?: number;
+    /** offset Peer to be used (defaults to Empty = no offset) */
     offsetPeer?: EntityLike;
+    /** Whether pinned dialogs should be ignored or not. When set to true, these won't be yielded at all. */
     ignorePinned?: boolean;
+    /**  Whether Chat that have migratedTo a Supergroup should be included or not.<br/>
+     * By default all the chats in your dialogs are returned, but setting this to True will ignore (i.e. skip) them in the same way official applications do.*/
     ignoreMigrated?: boolean;
+    /** The folder from which the dialogs should be retrieved.<br/>
+     * If left unspecified, all dialogs (including those from folders) will be returned.<br/>
+     * If set to 0, all dialogs that don't belong to any folder will be returned.<br/>
+     * If set to a folder number like 1, only those from said folder will be returned.<br/>
+     * By default Telegram assigns the folder ID 1 to archived chats, so you should use that if you need to fetch the archived dialogs.<br/> */
     folder?: number;
+    /**  Alias for folder. If unspecified, all will be returned, false implies `folder:0` and True implies `folder:1`.*/
     archived?: boolean;
 }
 
+/** @hidden */
 export function iterDialogs(
     client: TelegramClient,
     {
@@ -202,7 +218,7 @@ export function iterDialogs(
         ignorePinned = false,
         ignoreMigrated = false,
         folder = undefined,
-        archived = undefined,
+        archived = undefined
     }: IterDialogsParams
 ): _DialogsIter {
     if (archived != undefined) {
@@ -219,10 +235,12 @@ export function iterDialogs(
             offsetPeer,
             ignorePinned,
             ignoreMigrated,
-            folder,
+            folder
         }
     );
 }
+
+/** @hidden */
 export async function getDialogs(
     client: TelegramClient,
     params: IterDialogsParams

+ 49 - 21
gramjs/client/downloads.ts

@@ -7,27 +7,48 @@ import type { Message } from "../tl/custom/message";
 import { EntityLike } from "../define";
 import { utils } from "../";
 
+/**
+ * progress callback that will be called each time a new chunk is downloaded.
+ */
 export interface progressCallback {
     (
-        progress: number, // Float between 0 and 1.
+        /** float between 0 and 1 */
+        progress: number,
+        /** other args to be passed if needed */
         ...args: any[]
     ): void;
-
+    /** When this value is set to true the download will stop */
     isCanceled?: boolean;
+    /** Does nothing for now. */
     acceptsBuffer?: boolean;
 }
 
+/**
+ * Low level interface for downloading files
+ */
 export interface DownloadFileParams {
+    /** The dcId that the file belongs to. Used to borrow a sender from that DC */
     dcId: number;
+    /** How much to download. The library will download until it reaches this amount.<br/>
+     *  can be useful for downloading by chunks */
     fileSize: number;
+    /** Used to determine how many download tasks should be run in parallel. anything above 16 is unstable. */
     workers?: number;
+    /** How much to download in each chunk. The larger the less requests to be made. (max is 512kb). */
     partSizeKb?: number;
+    /** Where to start downloading. useful for chunk downloading. */
     start?: number;
+    /** Where to stop downloading. useful for chunk downloading. */
     end?: number;
+    /** Progress callback accepting one param. (progress :number) which is a float between 0 and 1 */
     progressCallback?: progressCallback;
 }
 
+/**
+ * contains optional download params for profile photo.
+ */
 export interface DownloadProfilePhotoParams {
+    /** Whether to download the big version or the small one of the photo */
     isBig?: boolean;
 }
 
@@ -45,6 +66,7 @@ const DEFAULT_CHUNK_SIZE = 64; // kb
 const ONE_MB = 1024 * 1024;
 const REQUEST_TIMEOUT = 15000;
 
+/** @hidden */
 export async function downloadFile(
     client: TelegramClient,
     inputLocation: Api.TypeInputFileLocation,
@@ -129,12 +151,12 @@ export async function downloadFile(
                                 location: inputLocation,
                                 offset,
                                 limit,
-                                precise: isPrecise || undefined,
+                                precise: isPrecise || undefined
                             })
                         ),
                         sleep(REQUEST_TIMEOUT).then(() =>
                             Promise.reject(new Error("REQUEST_TIMEOUT"))
-                        ),
+                        )
                     ]);
 
                     if (progressCallback) {
@@ -177,7 +199,8 @@ class Foreman {
     private deferred: Deferred | undefined;
     private activeWorkers = 0;
 
-    constructor(private maxWorkers: number) {}
+    constructor(private maxWorkers: number) {
+    }
 
     requestWorker() {
         this.activeWorkers++;
@@ -207,24 +230,29 @@ function createDeferred(): Deferred {
 
     return {
         promise,
-        resolve: resolve!,
+        resolve: resolve!
     };
 }
 
+/**
+ * All of these are optional and will be calculated automatically if not specified.
+ */
 export interface DownloadMediaInterface {
     sizeType?: string;
     /** where to start downloading **/
     start?: number;
     /** where to stop downloading **/
     end?: number;
+    /** a progress callback that will be called each time a new chunk is downloaded and passes a number between 0 and 1*/
     progressCallback?: progressCallback;
+    /** number of workers to use while downloading. more means faster but anything above 16 may cause issues. */
     workers?: number;
 }
-
+/** @hidden */
 export async function downloadMedia(
     client: TelegramClient,
     messageOrMedia: Api.Message | Api.TypeMessageMedia | Message,
-    args: DownloadMediaInterface
+    downloadParams: DownloadMediaInterface
 ): Promise<Buffer> {
     let date;
     let media;
@@ -243,19 +271,19 @@ export async function downloadMedia(
         }
     }
     if (media instanceof Api.MessageMediaPhoto || media instanceof Api.Photo) {
-        return client._downloadPhoto(media, args);
+        return _downloadPhoto(client,media, downloadParams);
     } else if (
         media instanceof Api.MessageMediaDocument ||
         media instanceof Api.Document
     ) {
-        return client._downloadDocument(media, args);
+        return _downloadDocument(client,media, downloadParams);
     } else if (media instanceof Api.MessageMediaContact) {
-        return client._downloadContact(media, args);
+        return _downloadContact(client,media, downloadParams);
     } else if (
         media instanceof Api.WebDocument ||
         media instanceof Api.WebDocumentNoProxy
     ) {
-        return client._downloadWebDocument(media, args);
+        return _downloadWebDocument(client,media, downloadParams);
     } else {
         return Buffer.alloc(0);
     }
@@ -289,7 +317,7 @@ export async function _downloadDocument(
             (size instanceof Api.PhotoCachedSize ||
                 size instanceof Api.PhotoStrippedSize)
         ) {
-            return client._downloadCachedPhotoSize(size);
+            return _downloadCachedPhotoSize(size);
         }
     }
     return client.downloadFile(
@@ -297,7 +325,7 @@ export async function _downloadDocument(
             id: doc.id,
             accessHash: doc.accessHash,
             fileReference: doc.fileReference,
-            thumbSize: size ? size.type : "",
+            thumbSize: size ? size.type : ""
         }),
         {
             fileSize:
@@ -308,7 +336,7 @@ export async function _downloadDocument(
             start: args.start,
             end: args.end,
             dcId: doc.dcId,
-            workers: args.workers,
+            workers: args.workers
         }
     );
 }
@@ -351,7 +379,6 @@ function pickFileSize(sizes: Api.TypePhotoSize[], sizeType: string) {
 }
 
 export function _downloadCachedPhotoSize(
-    client: TelegramClient,
     size: Api.PhotoCachedSize | Api.PhotoStrippedSize
 ) {
     // No need to download anything, simply write the bytes
@@ -387,23 +414,24 @@ export async function _downloadPhoto(
         size instanceof Api.PhotoCachedSize ||
         size instanceof Api.PhotoStrippedSize
     ) {
-        return client._downloadCachedPhotoSize(size);
+        return _downloadCachedPhotoSize(size);
     }
     return client.downloadFile(
         new Api.InputPhotoFileLocation({
             id: photo.id,
             accessHash: photo.accessHash,
             fileReference: photo.fileReference,
-            thumbSize: size.type,
+            thumbSize: size.type
         }),
         {
             dcId: photo.dcId,
             fileSize: size.size,
-            progressCallback: args.progressCallback,
+            progressCallback: args.progressCallback
         }
     );
 }
 
+/** @hidden */
 export async function downloadProfilePhoto(
     client: TelegramClient,
     entity: EntityLike,
@@ -429,7 +457,7 @@ export async function downloadProfilePhoto(
         loc = new Api.InputPeerPhotoFileLocation({
             peer: utils.getInputPeer(entity),
             photoId: photo.photoId,
-            big: fileParams.isBig,
+            big: fileParams.isBig
         });
     } else {
         return Buffer.alloc(0);
@@ -437,6 +465,6 @@ export async function downloadProfilePhoto(
     return client.downloadFile(loc, {
         dcId,
         fileSize: 2 * 1024 * 1024,
-        workers: 1,
+        workers: 1
     });
 }

+ 12 - 10
gramjs/client/messageParse.ts

@@ -1,6 +1,6 @@
 import { getPeerId, sanitizeParseMode } from "../Utils";
 import { Api } from "../tl";
-import type { EntityLike, ValueOf } from "../define";
+import type { EntityLike} from "../define";
 import type { TelegramClient } from "./TelegramClient";
 import { utils } from "../index";
 import { _EntityType, _entityType, isArrayLike } from "../Helpers";
@@ -20,7 +20,7 @@ export const DEFAULT_DELIMITERS: {
     __: Api.MessageEntityItalic,
     "~~": Api.MessageEntityStrike,
     "`": Api.MessageEntityCode,
-    "```": Api.MessageEntityPre,
+    "```": Api.MessageEntityPre
 };
 
 // export class MessageParseMethods {
@@ -29,7 +29,7 @@ export interface ParseInterface {
     parse: (message: string) => [string, Api.TypeMessageEntity[]];
     unparse: (
         text: string,
-        entities: Api.TypeMessageEntity[] | undefined
+        entities: Api.TypeMessageEntity[]
     ) => string;
 }
 
@@ -43,7 +43,7 @@ export async function _replaceWithMention(
         entities[i] = new Api.InputMessageEntityMentionName({
             offset: entities[i].offset,
             length: entities[i].length,
-            userId: await client.getInputEntity(user),
+            userId: await client.getInputEntity(user)
         });
         return true;
     } catch (e) {
@@ -54,12 +54,15 @@ export async function _replaceWithMention(
 export function _parseMessageText(
     client: TelegramClient,
     message: string,
-    parseMode: any
-) {
+    parseMode: false | string | ParseInterface
+): [string, Api.TypeMessageEntity[]] {
     if (parseMode == false) {
         return [message, []];
     }
     if (parseMode == undefined) {
+        if (client.parseMode == undefined) {
+            return [message, []];
+        }
         parseMode = client.parseMode;
     } else if (typeof parseMode === "string") {
         parseMode = sanitizeParseMode(parseMode);
@@ -133,8 +136,7 @@ export function _getResponseMessage(
         } else if (
             update instanceof Api.UpdateEditChannelMessage &&
             "peer" in request &&
-            getPeerId(request.peer) ==
-                getPeerId((update.message as unknown as Message).peerId)
+            getPeerId(request.peer) == getPeerId((update.message as unknown as Message).peerId!)
         ) {
             if (request.id == update.message.id) {
                 (update.message as unknown as Message)._finishInit(
@@ -161,10 +163,10 @@ export function _getResponseMessage(
                     peerId: utils.getPeerId(request.peer),
                     media: new Api.MessageMediaPoll({
                         poll: update.poll!,
-                        results: update.results,
+                        results: update.results
                     }),
                     message: "",
-                    date: 0,
+                    date: 0
                 });
                 m._finishInit(client, entities, inputChat);
                 return m;

+ 167 - 54
gramjs/client/messages.ts

@@ -6,7 +6,7 @@ import type {
     FileLike,
     MarkupLike,
     MessageIDLike,
-    MessageLike,
+    MessageLike
 } from "../define";
 import { RequestIter } from "../requestIter";
 import {
@@ -14,11 +14,13 @@ import {
     _entityType,
     TotalList,
     isArrayLike,
-    groupBy,
+    groupBy
 } from "../Helpers";
 import { getMessageId, getPeerId } from "../Utils";
 import type { TelegramClient } from "../";
 import { utils } from "../";
+import { _parseMessageText } from "./messageParse";
+import { _getPeer } from "./users";
 
 const _MAX_CHUNK_SIZE = 100;
 
@@ -49,17 +51,17 @@ export class _MessagesIter extends RequestIter {
     lastId?: number;
 
     async _init({
-        entity,
-        offsetId,
-        minId,
-        maxId,
-        fromUser,
-        offsetDate,
-        addOffset,
-        filter,
-        search,
-        replyTo,
-    }: MessageIterParams) {
+                    entity,
+                    offsetId,
+                    minId,
+                    maxId,
+                    fromUser,
+                    offsetDate,
+                    addOffset,
+                    filter,
+                    search,
+                    replyTo
+                }: MessageIterParams) {
         if (entity) {
             this.entity = await this.client.getInputEntity(entity);
         } else {
@@ -116,7 +118,7 @@ export class _MessagesIter extends RequestIter {
                 offsetRate: undefined,
                 offsetPeer: new Api.InputPeerEmpty(),
                 offsetId: offsetId,
-                limit: 1,
+                limit: 1
             });
         } else if (replyTo !== undefined) {
             this.request = new Api.messages.GetReplies({
@@ -128,7 +130,7 @@ export class _MessagesIter extends RequestIter {
                 limit: 0,
                 maxId: 0,
                 minId: 0,
-                hash: 0,
+                hash: 0
             });
         } else if (
             search !== undefined ||
@@ -153,7 +155,7 @@ export class _MessagesIter extends RequestIter {
                 maxId: 0,
                 minId: 0,
                 hash: 0,
-                fromId: fromUser,
+                fromId: fromUser
             });
             if (
                 filter instanceof Api.InputMessagesFilterEmpty &&
@@ -163,7 +165,7 @@ export class _MessagesIter extends RequestIter {
             ) {
                 for await (const m of this.client.iterMessages(this.entity, {
                     limit: 1,
-                    offsetDate: offsetDate,
+                    offsetDate: offsetDate
                 })) {
                     this.request.offsetId = m.id + 1;
                 }
@@ -177,7 +179,7 @@ export class _MessagesIter extends RequestIter {
                 minId: 0,
                 maxId: 0,
                 addOffset: addOffset,
-                hash: 0,
+                hash: 0
             });
         }
         if (this.limit <= 0) {
@@ -247,7 +249,8 @@ export class _MessagesIter extends RequestIter {
             try {
                 // if this fails it shouldn't be a big problem
                 message._finishInit(this.client, entities, this.entity);
-            } catch (e) {}
+            } catch (e) {
+            }
             message._entities = entities;
             this.buffer?.push(message);
         }
@@ -293,7 +296,7 @@ export class _MessagesIter extends RequestIter {
             this.request.maxDate = -1;
         } else {
             if (!(this.request instanceof Api.messages.SearchGlobal)) {
-                this.request.offsetDate = lastMessage.date;
+                this.request.offsetDate = lastMessage.date!;
             }
         }
         if (this.request instanceof Api.messages.SearchGlobal) {
@@ -352,13 +355,13 @@ export class _IDsIter extends RequestIter {
                 r = await this.client.invoke(
                     new Api.channels.GetMessages({
                         channel: this._entity,
-                        id: ids,
+                        id: ids
                     })
                 );
             } catch (e) {
                 if (e.message == "MESSAGE_IDS_EMPTY") {
                     r = new Api.messages.MessagesNotModified({
-                        count: ids.length,
+                        count: ids.length
                     });
                 } else {
                     throw e;
@@ -367,11 +370,11 @@ export class _IDsIter extends RequestIter {
         } else {
             r = await this.client.invoke(
                 new Api.messages.GetMessages({
-                    id: ids,
+                    id: ids
                 })
             );
             if (this._entity) {
-                fromId = await this.client._getPeer(this._entity);
+                fromId = await _getPeer(this.client, this._entity);
             }
         }
         if (r instanceof Api.messages.MessagesNotModified) {
@@ -400,57 +403,152 @@ export class _IDsIter extends RequestIter {
     }
 }
 
+ /**
+ * Interface for iterating over messages. used in both {@link iterMessages} and {@link getMessages}.
+ */
 export interface IterMessagesParams {
+    /** Number of messages to be retrieved.<br/>
+     * Due to limitations with the API retrieving more than 3000 messages will take longer than half a minute. (might even take longer)<br/>
+     * if undefined is passed instead of a number the library will try to retrieve all the messages.*/
     limit?: number;
+    /** Offset date (messages previous to this date will be retrieved). Exclusive. */
     offsetDate?: DateLike;
+    /** Offset message ID (only messages previous to the given ID will be retrieved). Exclusive. */
     offsetId?: number;
+    /** All the messages with a higher (newer) ID or equal to this will be excluded. */
     maxId?: number;
+    /** All the messages with a lower (older) ID or equal to this will be excluded. */
     minId?: number;
+    /** Additional message offset (all of the specified offsets + this offset = older messages). */
     addOffset?: number;
+    /** The string to be used as a search query. */
     search?: string;
+    /** The filter to use when returning messages.<br/>
+     * For instance, InputMessagesFilterPhotos would yield only messages containing photos.
+     */
     filter?: Api.TypeMessagesFilter | Api.TypeMessagesFilter[];
+    /** Only messages from this user will be returned. */
     fromUser?: EntityLike;
+    /** Wait time (in seconds) between different GetHistory requests.<br/>
+     * Use this parameter to avoid hitting the FloodWaitError as needed.<br/>
+     * If left to undefined, it will default to 1 second only if the number of messages is higher than 3000.
+     * If the ids parameter is used, this time will default to 10 seconds only if the amount of IDs is higher than 300.
+     */
     waitTime?: number;
+    /** A single integer ID (or several IDs) for the message that should be returned.<br/>
+     * This parameter takes precedence over the rest (which will be ignored if this is set).<br/>
+     * This can for instance be used to get the message with ID 123 from a channel.<br/>
+     * **Note** that if the message doesn"t exist, undefined will appear in its place.
+     */
     ids?: number | number[] | Api.TypeInputMessage | Api.TypeInputMessage[];
+    /** If set to `true`, the messages will be returned in reverse order (from oldest to newest, instead of the default newest to oldest).<br/>
+     * This also means that the meaning of offsetId and offsetDate parameters is reversed, although they will still be exclusive.<br/>
+     * `minId` becomes equivalent to `offsetId` instead of being `maxId` as well since messages are returned in ascending order.<br/>
+     * You cannot use this if both entity and ids are undefined.
+     */
     reverse?: boolean;
+    /** If set to a message ID, the messages that reply to this ID will be returned.<br/>
+     * This feature is also known as comments in posts of broadcast channels, or viewing threads in groups.<br/>
+     * This feature can only be used in broadcast channels and their linked supergroups. Using it in a chat or private conversation will result in PEER_ID_INVALID error.<br/>
+     * When using this parameter, the filter and search parameters have no effect, since Telegram's API doesn't support searching messages in replies.
+     */
     replyTo?: number;
 }
 
+/**
+ * Interface for sending a message. only message is required
+ */
 export interface SendMessageParams {
+    /**  The message to be sent, or another message object to resend as a copy.<br/>
+     * The maximum length for a message is 35,000 bytes or 4,096 characters.<br/>
+     * Longer messages will not be sliced automatically, and you should slice them manually if the text to send is longer than said length. */
     message: MessageLike;
+    /** Whether to reply to a message or not. If an integer is provided, it should be the ID of the message that it should reply to. */
     replyTo?: number | Api.Message;
+    /** Optional attributes that override the inferred ones, like DocumentAttributeFilename and so on. */
+    attributes?: Api.TypeDocumentAttribute[];
+    /** See the {@link parseMode} property for allowed values. Markdown parsing will be used by default. */
     parseMode?: any;
+    /** A list of message formatting entities. When provided, the parseMode is ignored. */
     formattingEntities?: Api.TypeMessageEntity[];
+    /** Should the link preview be shown? */
     linkPreview?: boolean;
-    file?: FileLike | FileLike[];
+    /** Sends a message with a file attached (e.g. a photo, video, audio or document). The message may be empty. */
+    file?: FileLike;
+    /** Optional JPEG thumbnail (for documents). Telegram will ignore this parameter unless you pass a .jpg file!<br/>
+     * The file must also be small in dimensions and in disk size. Successful thumbnails were files below 20kB and 320x320px.<br/>
+     *  Width/height and dimensions/size ratios may be important.
+     *  For Telegram to accept a thumbnail, you must provide the dimensions of the underlying media through `attributes:` with DocumentAttributesVideo.
+     */
+    thumb?: FileLike;
+    /** Whether to send the given file as a document or not. */
     forceDocument?: false;
+    /** Whether the existing draft should be cleared or not. */
     clearDraft?: false;
+    /** The matrix (list of lists), row list or button to be shown after sending the message.<br/>
+     *  This parameter will only work if you have signed in as a bot. You can also pass your own ReplyMarkup here.<br/>
+     *  <br/>
+     *  All the following limits apply together:
+     *   - There can be 100 buttons at most (any more are ignored).
+     *   - There can be 8 buttons per row at most (more are ignored).
+     *   - The maximum callback data per button is 64 bytes.
+     *   - The maximum data that can be embedded in total is just over 4KB, shared between inline callback data and text.
+     */
     buttons?: MarkupLike;
+    /** Whether the message should notify people in a broadcast channel or not. Defaults to false, which means it will notify them. Set it to True to alter this behaviour. */
     silent?: boolean;
+    /** Whether the sent video supports streaming or not.<br/>
+     *  Note that Telegram only recognizes as streamable some formats like MP4, and others like AVI or MKV will not work.<br/>
+     *  You should convert these to MP4 before sending if you want them to be streamable. Unsupported formats will result in VideoContentTypeError. */
+    supportStreaming?: boolean;
+    /** If set, the message won't send immediately, and instead it will be scheduled to be automatically sent at a later time. */
     schedule?: DateLike;
 }
 
+/** interface used for forwarding messages */
 export interface ForwardMessagesParams {
+    /** The message(s) to forward, or their integer IDs. */
     messages: MessageIDLike[];
+    /** If the given messages are integer IDs and not instances of the Message class, this must be specified in order for the forward to work.<br/> */
     fromPeer: EntityLike;
+    /** Whether the message should notify people with sound or not.<br/>
+     * Defaults to false (send with a notification sound unless the person has the chat muted). Set it to true to alter this behaviour. */
     silent?: boolean;
+    /** If set, the message(s) won't forward immediately, and instead they will be scheduled to be automatically sent at a later time. */
     schedule?: DateLike;
 }
 
+/** Interface for editing messages */
 export interface EditMessageParams {
+    /** The ID of the message (or Message itself) to be edited. If the entity was a Message, then this message will be treated as the new text. */
     message: Api.Message | number;
+    /** The new text of the message. Does nothing if the entity was a Message. */
     text: string;
+    /** See the {@link TelegramClient.parseMode} property for allowed values. Markdown parsing will be used by default. */
     parseMode?: any;
+    /** A list of message formatting entities. When provided, the parseMode is ignored. */
     formattingEntities?: Api.TypeMessageEntity[];
+    /** Should the link preview be shown? */
     linkPreview?: boolean;
+    /** The file object that should replace the existing media in the message. // not supported yet. */
     file?: FileLike | FileLike[];
+    /** thumbnail to be edited. // not supported yet */
     forceDocument?: false;
+    /** The matrix (list of lists), row list or button to be shown after sending the message.<br/>
+     *  This parameter will only work if you have signed in as a bot. You can also pass your own ReplyMarkup here.<br/>
+     *  <br/>
+     *  All the following limits apply together:
+     *   - There can be 100 buttons at most (any more are ignored).
+     *   - There can be 8 buttons per row at most (more are ignored).
+     *   - The maximum callback data per button is 64 bytes.
+     *   - The maximum data that can be embedded in total is just over 4KB, shared between inline callback data and text.
+     */
     buttons?: MarkupLike;
+    /** If set, the message won't be edited immediately, and instead it will be scheduled to be automatically edited at a later time. */
     schedule?: DateLike;
 }
 
-//  MessageMethods {
-
+//  MessageMethods
 export function iterMessages(
     client: TelegramClient,
     entity: EntityLike | undefined,
@@ -467,7 +565,7 @@ export function iterMessages(
         waitTime,
         ids,
         reverse = false,
-        replyTo,
+        replyTo
     }: IterMessagesParams
 ) {
     if (ids) {
@@ -482,11 +580,11 @@ export function iterMessages(
             idsArray.length,
             {
                 reverse: reverse,
-                waitTime: waitTime,
+                waitTime: waitTime
             },
             {
                 entity: entity,
-                ids: idsArray,
+                ids: idsArray
             }
         );
     }
@@ -495,7 +593,7 @@ export function iterMessages(
         limit || 1,
         {
             waitTime: waitTime,
-            reverse: reverse,
+            reverse: reverse
         },
         {
             entity: entity,
@@ -507,11 +605,12 @@ export function iterMessages(
             addOffset: addOffset,
             filter: filter,
             search: search,
-            replyTo: replyTo,
+            replyTo: replyTo
         }
     );
 }
 
+/** @hidden */
 export async function getMessages(
     client: TelegramClient,
     entity: EntityLike | undefined,
@@ -537,27 +636,44 @@ export async function getMessages(
 }
 
 // region Message
-
+/** @hidden */
 export async function sendMessage(
     client: TelegramClient,
     entity: EntityLike,
     {
         message,
         replyTo,
+        attributes,
         parseMode,
         formattingEntities,
         linkPreview = true,
         file,
+        thumb,
         forceDocument,
         clearDraft,
         buttons,
         silent,
-        schedule,
+        supportStreaming,
+        schedule
     }: SendMessageParams
 ) {
     if (file) {
-        throw new Error("Not Supported Yet");
-        //return this.sendFile();
+        return client.sendFile(entity, {
+            file: file,
+            caption: message ? (typeof message == "string" ? message : message.message) : "",
+            forceDocument: forceDocument,
+            clearDraft: clearDraft,
+            replyTo: replyTo,
+            attributes: attributes,
+            thumb: thumb,
+            supportsStreaming: supportStreaming,
+            parseMode: parseMode,
+            formattingEntities: formattingEntities,
+            silent: silent,
+            scheduleDate: schedule,
+            buttons: buttons
+        });
+
     }
     entity = await client.getInputEntity(entity);
     let markup, request;
@@ -596,12 +712,13 @@ export async function sendMessage(
             entities: message.entities,
             clearDraft: clearDraft,
             noWebpage: !(message.media instanceof Api.MessageMediaWebPage),
-            scheduleDate: schedule,
+            scheduleDate: schedule
         });
         message = message.message;
     } else {
         if (formattingEntities == undefined) {
-            [message, formattingEntities] = await client._parseMessageText(
+            [message, formattingEntities] = _parseMessageText(
+                client,
                 message,
                 parseMode
             );
@@ -620,21 +737,21 @@ export async function sendMessage(
             clearDraft: clearDraft,
             silent: silent,
             replyMarkup: client.buildReplyMarkup(buttons),
-            scheduleDate: schedule,
+            scheduleDate: schedule
         });
     }
     const result = await client.invoke(request);
     if (result instanceof Api.UpdateShortSentMessage) {
         const msg = new Message({
             id: result.id,
-            peerId: await client._getPeer(entity),
+            peerId: await _getPeer(client, entity),
             message: message,
             date: result.date,
             out: result.out,
             media: result.media,
             entities: result.entities,
             replyMarkup: request.replyMarkup,
-            ttlPeriod: result.ttlPeriod,
+            ttlPeriod: result.ttlPeriod
         });
         msg._finishInit(client, new Map(), entity);
         return msg;
@@ -642,6 +759,7 @@ export async function sendMessage(
     return client._getResponseMessage(request, result, entity) as Message;
 }
 
+/** @hidden */
 export async function forwardMessages(
     client: TelegramClient,
     entity: EntityLike,
@@ -666,10 +784,8 @@ export async function forwardMessages(
         }
     };
     const sent: Message[] = [];
-    for (let [chatId, chunk] of groupBy(messages, getKey) as Map<
-        number,
-        Message[] | number[]
-    >) {
+    for (let [chatId, chunk] of groupBy(messages, getKey) as Map<number,
+        Message[] | number[]>) {
         let chat;
         let numbers: number[] = [];
         if (typeof chunk[0] == "number") {
@@ -685,7 +801,7 @@ export async function forwardMessages(
             id: numbers,
             toPeer: entity,
             silent: silent,
-            scheduleDate: schedule,
+            scheduleDate: schedule
         });
         const result = await client.invoke(request);
         sent.push(
@@ -695,11 +811,7 @@ export async function forwardMessages(
     return sent;
 }
 
-/**
- * Used to edit a message by changing it's text or media
- * message refers to the message to be edited not what to edit
- * text refers to the new text
- */
+/** @hidden */
 export async function editMessage(
     client: TelegramClient,
     entity: EntityLike,
@@ -712,12 +824,13 @@ export async function editMessage(
         file,
         forceDocument,
         buttons,
-        schedule,
+        schedule
     }: EditMessageParams
 ) {
     entity = await client.getInputEntity(entity);
     if (formattingEntities == undefined) {
-        [text, formattingEntities] = await client._parseMessageText(
+        [text, formattingEntities] = _parseMessageText(
+            client,
             text,
             parseMode
         );
@@ -730,7 +843,7 @@ export async function editMessage(
         entities: formattingEntities,
         //media: no media for now,
         replyMarkup: client.buildReplyMarkup(buttons),
-        scheduleDate: schedule,
+        scheduleDate: schedule
     });
     const result = await client.invoke(request);
     return client._getResponseMessage(request, result, entity) as Message;

+ 131 - 44
gramjs/client/telegramBaseClient.ts

@@ -2,7 +2,7 @@ import { version } from "../";
 import { IS_NODE } from "../Helpers";
 import {
     ConnectionTCPFull,
-    ConnectionTCPObfuscated,
+    ConnectionTCPObfuscated
 } from "../network/connection";
 import { Session } from "../sessions";
 import { Logger } from "../extensions";
@@ -13,92 +13,176 @@ import type { AuthKey } from "../crypto/AuthKey";
 import { EntityCache } from "../entityCache";
 import type { ParseInterface } from "./messageParse";
 import type { EventBuilder } from "../events/common";
+import { MarkdownParser } from "../extensions/markdown";
 
 const DEFAULT_DC_ID = 1;
 const DEFAULT_IPV4_IP = IS_NODE ? "149.154.167.51" : "pluto.web.telegram.org";
 const DEFAULT_IPV6_IP = "2001:67c:4e8:f002::a";
 
+/**
+ * Interface for creating a new client.
+ * All of these have a default value and you should only change those if you know what you are doing.
+ */
 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;
+    /**
+     * Whether to connect to the servers through IPv6 or not. By default this is false.
+     */
     useIPV6?: boolean;
+    /**
+     * The timeout in seconds to be used when connecting. This does nothing for now.
+     */
     timeout?: number;
+    /**
+     * How many times a request should be retried.<br/>
+     * Request are retried when Telegram is having internal issues (due to INTERNAL error or RPC_CALL_FAIL error),<br/>
+     * when there is a errors.FloodWaitError less than floodSleepThreshold, or when there's a migrate error.<br/>
+     * defaults to 5.
+     */
     requestRetries?: number;
+    /**
+     * How many times the reconnection should retry, either on the initial connection or when Telegram disconnects us.<br/>
+     * May be set to a negative or undefined value for infinite retries, but this is not recommended, since the program can get stuck in an infinite loop.<br/>
+     * defaults to 5
+     */
     connectionRetries?: number;
+    /**
+     * How many times we should retry borrowing a sender from another DC when it fails. defaults to 5
+     */
     downloadRetries?: number;
+    /** The delay in milliseconds to sleep between automatic reconnections. defaults to 1000*/
     retryDelay?: number;
+    /**Whether reconnection should be retried connection_retries times automatically if Telegram disconnects us or not. defaults to true */
     autoReconnect?: boolean;
+    /** does nothing for now */
     sequentialUpdates?: boolean;
+    /** The threshold below which the library should automatically sleep on flood wait and slow mode wait errors (inclusive).<br/>
+     *  For instance, if a FloodWaitError for 17s occurs and floodSleepThreshold is 20s, the library will sleep automatically.<br/>
+     *  If the error was for 21s, it would raise FloodWaitError instead. defaults to 60 sec.*/
     floodSleepThreshold?: number;
+    /**
+     * Device model to be sent when creating the initial connection. Defaults to os.type().toString().
+     */
     deviceModel?: string;
+    /**
+     * System version to be sent when creating the initial connection. defaults to os.release().toString() -.
+     */
     systemVersion?: string;
+    /**
+     * App version to be sent when creating the initial connection. Defaults to 1.0.
+     */
     appVersion?: string;
-    langCode?: "en";
-    systemLangCode?: "en";
+    /**
+     * Language code to be sent when creating the initial connection. Defaults to 'en'.
+     */
+    langCode?: string;
+    /**
+     * System lang code to be sent when creating the initial connection. Defaults to 'en'.
+     */
+    systemLangCode?: string;
+    /**
+     * Does nothing for now. don't change.
+     */
     baseLogger?: string | any;
+    /**
+     * Whether to try to connect over Wss (or 443 port) or not.
+     */
     useWSS?: boolean;
 }
 
+const clientParamsDefault = {
+    connection: IS_NODE ? ConnectionTCPFull : ConnectionTCPObfuscated,
+    useIPV6: false,
+    timeout: 10,
+    requestRetries: 5,
+    connectionRetries: Infinity,
+    retryDelay: 1000,
+    downloadRetries: 5,
+    autoReconnect: true,
+    sequentialUpdates: false,
+    floodSleepThreshold: 60,
+    deviceModel: "",
+    systemVersion: "",
+    appVersion: "",
+    langCode: "en",
+    systemLangCode: "en",
+    baseLogger: "gramjs",
+    useWSS: typeof window !== "undefined"
+        ? window.location.protocol == "https:"
+        : false
+};
+
 export class TelegramBaseClient {
+    /** The current gramJS version. */
     __version__ = version;
+    /** @hidden */
     _config?: Api.Config;
+    /** @hidden */
     public _log: Logger;
+
+    /** @hidden */
     public _floodSleepThreshold: number;
     public session: Session;
     public apiHash: string;
     public apiId: number;
+
+    /** @hidden */
     public _requestRetries: number;
+    /** @hidden */
     public _downloadRetries: number;
+    /** @hidden */
     public _connectionRetries: number;
+    /** @hidden */
     public _retryDelay: number;
+    /** @hidden */
     public _timeout: number;
+    /** @hidden */
     public _autoReconnect: boolean;
+    /** @hidden */
     public _connection: any;
+    /** @hidden */
     public _initRequest: Api.InitConnection;
+    /** @hidden */
     public _sender?: any;
+    /** @hidden */
     public _floodWaitedRequests: any;
+    /** @hidden */
     public _borrowedSenderPromises: any;
+    /** @hidden */
     public _bot?: boolean;
+    /** @hidden */
     public _useIPV6: boolean;
+    /** @hidden */
     public _selfInputPeer?: Api.InputPeerUser;
+    /** @hidden */
     public useWSS: boolean;
+
+    /** @hidden */
     public _eventBuilders: [EventBuilder, CallableFunction][];
+    /** @hidden */
     public _entityCache: EntityCache;
+    /** @hidden */
     public _lastRequest?: number;
+    /** @hidden */
     public _parseMode?: ParseInterface;
 
     constructor(
         session: string | Session,
         apiId: number,
         apiHash: string,
-        {
-            connection = IS_NODE ? ConnectionTCPFull : ConnectionTCPObfuscated,
-            useIPV6 = false,
-            timeout = 10,
-            requestRetries = 5,
-            connectionRetries = Infinity,
-            retryDelay = 1000,
-            downloadRetries = 5,
-            autoReconnect = true,
-            sequentialUpdates = false,
-            floodSleepThreshold = 60,
-            deviceModel = "",
-            systemVersion = "",
-            appVersion = "",
-            langCode = "en",
-            systemLangCode = "en",
-            baseLogger = "gramjs",
-            useWSS = typeof window !== "undefined"
-                ? window.location.protocol == "https:"
-                : false,
-        }: TelegramClientParams
+        clientParams: TelegramClientParams
     ) {
+        clientParams = { ...clientParamsDefault, ...clientParams };
         if (!apiId || !apiHash) {
             throw new Error("Your API ID or Hash cannot be empty or undefined");
         }
-        if (typeof baseLogger == "string") {
+        if (typeof clientParams.baseLogger == "string") {
             this._log = new Logger();
         } else {
-            this._log = baseLogger;
+            this._log = clientParams.baseLogger;
         }
         this._log.debug("Running gramJS version " + version);
         if (!(session instanceof Session)) {
@@ -106,30 +190,30 @@ export class TelegramBaseClient {
                 "Only StringSession and StoreSessions are supported currently :( "
             );
         }
-        this._floodSleepThreshold = floodSleepThreshold;
+        this._floodSleepThreshold = clientParams.floodSleepThreshold!;
         this.session = session;
         this.apiId = apiId;
         this.apiHash = apiHash;
-        this._useIPV6 = useIPV6;
-        this._requestRetries = requestRetries;
-        this._downloadRetries = downloadRetries;
-        this._connectionRetries = connectionRetries;
-        this._retryDelay = retryDelay || 0;
-        this._timeout = timeout;
-        this._autoReconnect = autoReconnect;
-        if (!(connection instanceof Function)) {
+        this._useIPV6 = clientParams.useIPV6!;
+        this._requestRetries = clientParams.requestRetries!;
+        this._downloadRetries = clientParams.downloadRetries!;
+        this._connectionRetries = clientParams.connectionRetries!;
+        this._retryDelay = clientParams.retryDelay || 0;
+        this._timeout = clientParams.timeout!;
+        this._autoReconnect = clientParams.autoReconnect!;
+        if (!(clientParams.connection instanceof Function)) {
             throw new Error("Connection should be a class not an instance");
         }
-        this._connection = connection;
+        this._connection = clientParams.connection;
         this._initRequest = new Api.InitConnection({
             apiId: this.apiId,
-            deviceModel: deviceModel || os.type().toString() || "Unknown",
-            systemVersion: systemVersion || os.release().toString() || "1.0",
-            appVersion: appVersion || "1.0",
-            langCode: langCode,
+            deviceModel: clientParams.deviceModel || os.type().toString() || "Unknown",
+            systemVersion: clientParams.systemVersion || os.release().toString() || "1.0",
+            appVersion: clientParams.appVersion || "1.0",
+            langCode: clientParams.langCode,
             langPack: "", // this should be left empty.
-            systemLangCode: systemLangCode,
-            proxy: undefined, // no proxies yet.
+            systemLangCode: clientParams.systemLangCode,
+            proxy: undefined // no proxies yet.
         });
         this._eventBuilders = [];
 
@@ -137,8 +221,11 @@ export class TelegramBaseClient {
         this._borrowedSenderPromises = {};
         this._bot = undefined;
         this._selfInputPeer = undefined;
-        this.useWSS = useWSS;
+        this.useWSS = clientParams.useWSS!;
         this._entityCache = new EntityCache();
+
+        // parse mode
+        this._parseMode = MarkdownParser;
     }
 
     get floodSleepThreshold() {
@@ -187,7 +274,7 @@ export class TelegramBaseClient {
                 (promise: any) => {
                     return promise.then((sender: any) => sender.disconnect());
                 }
-            ),
+            )
         ]);
 
         this._eventBuilders = [];

+ 17 - 15
gramjs/client/updates.ts

@@ -1,4 +1,4 @@
-import type { EventBuilder, EventCommon } from "../events/common";
+import type { EventBuilder } from "../events/common";
 import { Api } from "../tl";
 import { helpers } from "../";
 import type { TelegramClient } from "../";
@@ -7,9 +7,9 @@ import { UpdateConnectionState } from "../network";
 import type { Raw } from "../events";
 import { utils } from "../index";
 
-// export class UpdateMethods {
-export function on(client: TelegramClient, event: any) {
-    return (f: CallableFunction) => {
+// export class UpdateMethods
+export function on(client: TelegramClient, event?: EventBuilder) {
+    return (f: { (event: any): void }) => {
         client.addEventHandler(f, event);
         return f;
     };
@@ -33,7 +33,7 @@ export function removeEventHandler(
     callback: CallableFunction,
     event: EventBuilder
 ) {
-    client._eventBuilders = client._eventBuilders.filter(function (item) {
+    client._eventBuilders = client._eventBuilders.filter(function(item) {
         return item !== [event, callback];
     });
 }
@@ -52,8 +52,8 @@ export function _handleUpdate(
 ): void {
     if (typeof update === "number") {
         if ([-1, 0, 1].includes(update)) {
-            client._dispatchUpdate({
-                update: new UpdateConnectionState(update),
+            _dispatchUpdate(client, {
+                update: new UpdateConnectionState(update)
             });
             return;
         }
@@ -73,12 +73,12 @@ export function _handleUpdate(
             entities.set(utils.getPeerId(x), x);
         }
         for (const u of update.updates) {
-            client._processUpdate(u, update.updates, entities);
+            _processUpdate(client, u, update.updates, entities);
         }
     } else if (update instanceof Api.UpdateShort) {
-        client._processUpdate(update.update, null);
+        _processUpdate(client, update.update, null);
     } else {
-        client._processUpdate(update, null);
+        _processUpdate(client, update, null);
     }
 }
 
@@ -91,10 +91,10 @@ export function _processUpdate(
     update._entities = entities || new Map();
     const args = {
         update: update,
-        others: others,
+        others: others
     };
 
-    client._dispatchUpdate(args);
+    _dispatchUpdate(client, args);
 }
 
 export async function _dispatchUpdate(
@@ -146,10 +146,11 @@ export async function _updateLoop(client: TelegramClient): Promise<void> {
         try {
             client._sender.send(
                 new Api.Ping({
-                    pingId: bigInt(rnd),
+                    pingId: bigInt(rnd)
                 })
             );
-        } catch (e) {}
+        } catch (e) {
+        }
 
         // We need to send some content-related request at least hourly
         // for Telegram to keep delivering updates, otherwise they will
@@ -162,7 +163,8 @@ export async function _updateLoop(client: TelegramClient): Promise<void> {
         ) {
             try {
                 await client.invoke(new Api.updates.GetState());
-            } catch (e) {}
+            } catch (e) {
+            }
         }
     }
 }

+ 94 - 36
gramjs/client/uploads.ts

@@ -2,11 +2,12 @@ import { Api } from "../tl";
 
 import { TelegramClient } from "./TelegramClient";
 import { generateRandomBytes, readBigIntFromBuffer, sleep } from "../Helpers";
-import { getAppropriatedPartSize, getAttributes } from "../Utils";
+import { getAppropriatedPartSize} from "../Utils";
 import { EntityLike, FileLike, MarkupLike, MessageIDLike } from "../define";
 import path from "path";
 import { promises as fs } from "fs";
 import { utils } from "../index";
+import { _parseMessageText } from "./messageParse";
 
 interface OnProgress {
     // Float between 0 and 1.
@@ -15,16 +16,34 @@ interface OnProgress {
     isCanceled?: boolean;
 }
 
+/**
+ * interface for uploading files.
+ */
 export interface UploadFileParams {
+    /** for browsers this should be an instance of File.<br/>
+     * On node you should use {@link CustomFile} class to wrap your file.
+     */
     file: File | CustomFile;
+    /** How many workers to use to upload the file. anything above 16 is unstable. */
     workers: number;
+    /** a progress callback for the upload. */
     onProgress?: OnProgress;
 }
 
+/**
+ * A custom file class that mimics the browser's File class.<br/>
+ * You should use this whenever you want to upload a file.
+ */
 export class CustomFile {
+    /** The name of the file to be uploaded. This is what will be shown in telegram */
     name: string;
+    /** The size of the file. this should be the exact size to not lose any information */
     size: number;
+    /** The full path on the system to where the file is. this will be used to read the file from.<br/>
+     * Can be left empty to use a buffer instead
+     */
     path: string;
+    /** in case of the no path a buffer can instead be passed instead to upload. */
     buffer?: Buffer;
 
     constructor(name: string, size: number, path: string, buffer?: Buffer) {
@@ -39,6 +58,7 @@ const KB_TO_BYTES = 1024;
 const LARGE_FILE_THRESHOLD = 10 * 1024 * 1024;
 const UPLOAD_TIMEOUT = 15 * 1000;
 
+/** @hidden */
 export async function uploadFile(
     client: TelegramClient,
     fileParams: UploadFileParams
@@ -84,16 +104,16 @@ export async function uploadFile(
                     await sender.send(
                         isLarge
                             ? new Api.upload.SaveBigFilePart({
-                                  fileId,
-                                  filePart: j,
-                                  fileTotalParts: partCount,
-                                  bytes,
-                              })
+                                fileId,
+                                filePart: j,
+                                fileTotalParts: partCount,
+                                bytes
+                            })
                             : new Api.upload.SaveFilePart({
-                                  fileId,
-                                  filePart: j,
-                                  bytes,
-                              })
+                                fileId,
+                                filePart: j,
+                                bytes
+                            })
                     );
 
                     if (onProgress) {
@@ -112,7 +132,7 @@ export async function uploadFile(
                 await Promise.all(sendingParts),
                 sleep(UPLOAD_TIMEOUT * workers).then(() =>
                     Promise.reject(new Error("TIMEOUT"))
-                ),
+                )
             ]);
         } catch (err) {
             if (err.message === "TIMEOUT") {
@@ -126,38 +146,75 @@ export async function uploadFile(
     }
     return isLarge
         ? new Api.InputFileBig({
-              id: fileId,
-              parts: partCount,
-              name,
-          })
+            id: fileId,
+            parts: partCount,
+            name
+        })
         : new Api.InputFile({
-              id: fileId,
-              parts: partCount,
-              name,
-              md5Checksum: "", // This is not a "flag", so not sure if we can make it optional.
-          });
+            id: fileId,
+            parts: partCount,
+            name,
+            md5Checksum: "" // This is not a "flag", so not sure if we can make it optional.
+        });
 }
 
+/**
+ * Interface for sending files to a chat.
+ */
 export interface SendFileInterface {
+    /** a file like object.
+     *   - can be a localpath. the file name will be used.
+     *   - can be a Buffer with a ".name" attribute to use as the file name.
+     *   - can be an external direct URL. Telegram will download the file and send it.
+     *   - can be an existing media from another message.
+     *   - can be a handle to a file that was received by using {@link uploadFile}
+     *   - can be {@link Api.TypeInputMedia} instance. For example if you want to send a dice you would use {@link Api.InputMediaDice}
+     */
     file: FileLike;
+    /** Optional caption for the sent media message.*/
     caption?: string;
+    /** If left to false and the file is a path that ends with the extension of an image file or a video file, it will be sent as such. Otherwise always as a document. */
     forceDocument?: boolean;
+    /** The size of the file to be uploaded if it needs to be uploaded, which will be determined automatically if not specified. */
     fileSize?: number;
+    /** Whether the existing draft should be cleared or not. */
     clearDraft?: boolean;
+    /** progress callback that will be called each time a new chunk is downloaded. */
     progressCallback?: OnProgress;
+    /** Same as `replyTo` from {@link sendMessage}. */
     replyTo?: MessageIDLike;
+    /** Optional attributes that override the inferred ones, like {@link Api.DocumentAttributeFilename} and so on.*/
     attributes?: Api.TypeDocumentAttribute[];
+    /** Optional JPEG thumbnail (for documents). Telegram will ignore this parameter unless you pass a .jpg file!<br/>
+     * The file must also be small in dimensions and in disk size. Successful thumbnails were files below 20kB and 320x320px.<br/>
+     *  Width/height and dimensions/size ratios may be important.
+     *  For Telegram to accept a thumbnail, you must provide the dimensions of the underlying media through `attributes:` with DocumentAttributesVideo.
+     */
     thumb?: FileLike;
+    /** If true the audio will be sent as a voice note. */
     voiceNote?: boolean;
+    /** If true the video will be sent as a video note, also known as a round video message.*/
     videoNote?: boolean;
+    /** Whether the sent video supports streaming or not.<br/>
+     *  Note that Telegram only recognizes as streamable some formats like MP4, and others like AVI or MKV will not work.<br/>
+     *  You should convert these to MP4 before sending if you want them to be streamable. Unsupported formats will result in VideoContentTypeError. */
     supportsStreaming?: boolean;
+    /** See the {@link parseMode} property for allowed values. Markdown parsing will be used by default. */
     parseMode?: any;
+    /** A list of message formatting entities. When provided, the parseMode is ignored. */
     formattingEntities?: Api.TypeMessageEntity[];
+    /** Whether the message should notify people in a broadcast channel or not. Defaults to false, which means it will notify them. Set it to True to alter this behaviour. */
     silent?: boolean;
-    background?: boolean;
-    replyMarkup?: Api.TypeReplyMarkup;
+    /**
+     * If set, the file won't send immediately, and instead it will be scheduled to be automatically sent at a later time.
+     */
     scheduleDate?: number;
+    /**
+     * The matrix (list of lists), row list or button to be shown after sending the message.<br/>
+     * This parameter will only work if you have signed in as a bot. You can also pass your own ReplyMarkup here.
+     */
     buttons?: MarkupLike;
+    /** How many workers to use to upload the file. anything above 16 is unstable. */
     workers?: number;
 }
 
@@ -190,7 +247,7 @@ async function _fileToMedia(
         supportsStreaming = false,
         mimeType,
         asImage,
-        workers = 1,
+        workers = 1
     }: FileToMediaInterface
 ): Promise<{
     fileHandle?: any;
@@ -220,15 +277,15 @@ async function _fileToMedia(
                     forceDocument: forceDocument,
                     voiceNote: voiceNote,
                     videoNote: videoNote,
-                    supportsStreaming: supportsStreaming,
+                    supportsStreaming: supportsStreaming
                 }),
-                image: asImage,
+                image: asImage
             };
         } catch (e) {
             return {
                 fileHandle: undefined,
                 media: undefined,
-                image: isImage,
+                image: isImage
             };
         }
     }
@@ -264,7 +321,7 @@ async function _fileToMedia(
         fileHandle = await uploadFile(client, {
             file: createdFile,
             onProgress: progressCallback,
-            workers: workers,
+            workers: workers
         });
     } else if (file.startsWith("https://") || file.startsWith("http://")) {
         if (asImage) {
@@ -282,7 +339,7 @@ async function _fileToMedia(
         );
     } else if (asImage) {
         media = new Api.InputMediaUploadedPhoto({
-            file: fileHandle,
+            file: fileHandle
         });
     } else {
         // @ts-ignore
@@ -293,7 +350,7 @@ async function _fileToMedia(
             voiceNote: voiceNote,
             videoNote: videoNote,
             supportsStreaming: supportsStreaming,
-            thumb: thumb,
+            thumb: thumb
         });
         attributes = res.attrs;
         mimeType = res.mimeType;
@@ -332,7 +389,7 @@ async function _fileToMedia(
             }
             uploadedThumb = await uploadFile(client, {
                 file: uploadedThumb,
-                workers: 1,
+                workers: 1
             });
         }
         media = new Api.InputMediaUploadedDocument({
@@ -340,13 +397,13 @@ async function _fileToMedia(
             mimeType: mimeType,
             attributes: attributes,
             thumb: uploadedThumb,
-            forceFile: forceDocument && !isImage,
+            forceFile: forceDocument && !isImage
         });
     }
     return {
         fileHandle: fileHandle,
         media: media,
-        image: asImage,
+        image: asImage
     };
 }
 
@@ -371,7 +428,7 @@ export async function sendFile(
         silent,
         supportsStreaming = false,
         scheduleDate,
-        workers = 1,
+        workers = 1
     }: SendFileInterface
 ) {
     if (!file) {
@@ -387,7 +444,8 @@ export async function sendFile(
     if (formattingEntities != undefined) {
         msgEntities = formattingEntities;
     } else {
-        [caption, formattingEntities] = await client._parseMessageText(
+        [caption, formattingEntities] = await _parseMessageText(
+            client,
             caption,
             parseMode
         );
@@ -403,7 +461,7 @@ export async function sendFile(
         voiceNote: voiceNote,
         videoNote: videoNote,
         supportsStreaming: supportsStreaming,
-        workers: workers,
+        workers: workers
     });
     if (media == undefined) {
         throw new Error(`Cannot use ${file} as file.`);
@@ -418,7 +476,7 @@ export async function sendFile(
         replyMarkup: markup,
         silent: silent,
         scheduleDate: scheduleDate,
-        clearDraft: clearDraft,
+        clearDraft: clearDraft
     });
     // todo get message
     const result = client.invoke(request);

+ 42 - 33
gramjs/client/users.ts

@@ -129,16 +129,19 @@ export async function isUserAuthorized(client: TelegramClient) {
 
 export async function getEntity(
     client: TelegramClient,
-    entity: any
-): Promise<Entity> {
+    entity: EntityLike | EntityLike[]
+): Promise<Entity | Entity[]> {
     const single = !isArrayLike(entity);
+    let entityArray: EntityLike[] = [];
+    if (isArrayLike<EntityLike>(entity)) {
+        entityArray = entity;
+    } else {
+        entityArray.push(entity);
 
-    if (single) {
-        entity = [entity];
     }
 
     const inputs = [];
-    for (const x of entity) {
+    for (const x of entityArray) {
         if (typeof x === "string") {
             inputs.push(x);
         } else {
@@ -148,12 +151,13 @@ export async function getEntity(
     const lists = new Map<number, any[]>([
         [_EntityType.USER, []],
         [_EntityType.CHAT, []],
-        [_EntityType.CHANNEL, []],
+        [_EntityType.CHANNEL, []]
     ]);
     for (const x of inputs) {
         try {
             lists.get(_entityType(x))!.push(x);
-        } catch (e) {}
+        } catch (e) {
+        }
     }
     let users = lists.get(_EntityType.USER)!;
     let chats = lists.get(_EntityType.CHAT)!;
@@ -162,7 +166,7 @@ export async function getEntity(
     if (users.length) {
         users = await client.invoke(
             new Api.users.GetUsers({
-                id: users,
+                id: users
             })
         );
     }
@@ -194,7 +198,7 @@ export async function getEntity(
     const result = [];
     for (const x of inputs) {
         if (typeof x === "string") {
-            result.push(await client._getEntityFromString(x));
+            result.push(await _getEntityFromString(client, x));
         } else if (!(x instanceof Api.InputPeerSelf)) {
             result.push(idEntity.get(peerUtils(x)));
         } else {
@@ -218,7 +222,8 @@ export async function getInputEntity(
     try {
         return utils.getInputPeer(peer);
         // eslint-disable-next-line no-empty
-    } catch (e) {}
+    } catch (e) {
+    }
     // Next in priority is having a peer (or its ID) cached in-memory
     try {
         // 0x2d45687 == crc32(b'Peer')
@@ -232,7 +237,8 @@ export async function getInputEntity(
             }
         }
         // eslint-disable-next-line no-empty
-    } catch (e) {}
+    } catch (e) {
+    }
     // Then come known strings that take precedence
     if (typeof peer == "string") {
         if (["me", "this", "self"].includes(peer)) {
@@ -246,10 +252,11 @@ export async function getInputEntity(
             return client.session.getInputEntity(peer);
         }
         // eslint-disable-next-line no-empty
-    } catch (e) {}
+    } catch (e) {
+    }
     // Only network left to try
     if (typeof peer === "string") {
-        return utils.getInputPeer(await client._getEntityFromString(peer));
+        return utils.getInputPeer(await _getEntityFromString(client, peer));
     }
     // If we're a bot and the user has messaged us privately users.getUsers
     // will work with accessHash = 0. Similar for channels.getChannels.
@@ -262,9 +269,9 @@ export async function getInputEntity(
                 id: [
                     new Api.InputUser({
                         userId: peer.userId,
-                        accessHash: bigInt.zero,
-                    }),
-                ],
+                        accessHash: bigInt.zero
+                    })
+                ]
             })
         );
         if (users.length && !(users[0] instanceof Api.UserEmpty)) {
@@ -279,7 +286,7 @@ export async function getInputEntity(
         }
     } else if (peer instanceof Api.PeerChat) {
         return new Api.InputPeerChat({
-            chatId: peer.chatId,
+            chatId: peer.chatId
         });
     } else if (peer instanceof Api.PeerChannel) {
         try {
@@ -288,9 +295,9 @@ export async function getInputEntity(
                     id: [
                         new Api.InputChannel({
                             channelId: peer.channelId,
-                            accessHash: bigInt.zero,
-                        }),
-                    ],
+                            accessHash: bigInt.zero
+                        })
+                    ]
                 })
             );
 
@@ -304,8 +311,8 @@ export async function getInputEntity(
     throw new Error(
         `Could not find the input entity for ${peer}.
          Please read https://` +
-            "docs.telethon.dev/en/latest/concepts/entities.html to" +
-            " find out more details."
+        "docs.telethon.dev/en/latest/concepts/entities.html to" +
+        " find out more details."
     );
 }
 
@@ -318,7 +325,7 @@ export async function _getEntityFromString(
         try {
             const result = await client.invoke(
                 new Api.contacts.GetContacts({
-                    hash: 0,
+                    hash: 0
                 })
             );
             if (!(result instanceof Api.contacts.ContactsNotModified)) {
@@ -332,7 +339,7 @@ export async function _getEntityFromString(
             if (e.message === "BOT_METHOD_INVALID") {
                 throw new Error(
                     "Cannot get entity by phone number as a " +
-                        "bot (try using integer IDs, not strings)"
+                    "bot (try using integer IDs, not strings)"
                 );
             }
             throw e;
@@ -344,13 +351,13 @@ export async function _getEntityFromString(
         if (isInvite) {
             const invite = await client.invoke(
                 new Api.messages.CheckChatInvite({
-                    hash: username,
+                    hash: username
                 })
             );
             if (invite instanceof Api.ChatInvite) {
                 throw new Error(
                     "Cannot get entity from a channel (or group) " +
-                        "that you are not part of. Join the group and retry"
+                    "that you are not part of. Join the group and retry"
                 );
             } else if (invite instanceof Api.ChatInviteAlready) {
                 return invite.chat;
@@ -388,7 +395,7 @@ export async function _getEntityFromString(
 export async function getPeerId(
     client: TelegramClient,
     peer: EntityLike,
-    addMark = false
+    addMark = true
 ) {
     if (typeof peer == "number") {
         return utils.getPeerId(peer, addMark);
@@ -414,7 +421,7 @@ export async function _getPeer(client: TelegramClient, peer: EntityLike) {
     return new cls({
         userId: i,
         channelId: i,
-        chatId: i,
+        chatId: i
     });
 }
 
@@ -427,12 +434,13 @@ export async function _getInputDialog(client: TelegramClient, dialog: any) {
         } else if (dialog.SUBCLASS_OF_ID == 0xc91c90b6) {
             //crc32(b'InputPeer')
             return new Api.InputDialogPeer({
-                peer: dialog,
+                peer: dialog
             });
         }
-    } catch (e) {}
+    } catch (e) {
+    }
     return new Api.InputDialogPeer({
-        peer: dialog,
+        peer: dialog
     });
 }
 
@@ -444,9 +452,10 @@ export async function _getInputNotify(client: TelegramClient, notify: any) {
             }
             return notify;
         }
-    } catch (e) {}
+    } catch (e) {
+    }
     return new Api.InputNotifyPeer({
-        peer: await client.getInputEntity(notify),
+        peer: await client.getInputEntity(notify)
     });
 }
 

+ 1 - 0
gramjs/crypto/AuthKey.ts

@@ -6,6 +6,7 @@ import {
 } from "../Helpers";
 import { BinaryReader } from "../extensions";
 import { sleep } from "../Helpers";
+import bigInt from "big-integer";
 
 export class AuthKey {
     private _key?: Buffer;

+ 1 - 1
gramjs/crypto/RSA.ts

@@ -69,7 +69,7 @@ PUBLIC_KEYS.forEach(({ fingerprint, ...keyInfo }) => {
 
  * @param fingerprint the fingerprint of the RSA key.
  * @param data the data to be encrypted.
- * @returns {Buffer|*|undefined} the cipher text, or None if no key matching this fingerprint is found.
+ * @returns {Buffer|*|undefined} the cipher text, or undefined if no key matching this fingerprint is found.
  */
 export async function encrypt(fingerprint: bigInt.BigInteger, data: Buffer) {
     const key = _serverKeys.get(fingerprint.toString());

+ 1 - 1
gramjs/crypto/crypto.ts

@@ -89,7 +89,7 @@ export function randomBytes(count: Buffer) {
 }
 
 export class Hash {
-    private algorithm: string;
+    private readonly algorithm: string;
     private data?: Uint8Array;
 
     constructor(algorithm: string) {

+ 73 - 23
gramjs/events/NewMessage.ts

@@ -1,41 +1,91 @@
-import { _intoIdSet, EventBuilder, EventCommon } from "./common";
+import { _intoIdSet, DefaultEventInterface, EventBuilder, EventCommon } from "./common";
 import type { Entity, EntityLike } from "../define";
-import type { TelegramClient } from "../client/TelegramClient";
+import type { TelegramClient } from "..";
 import { Api } from "../tl";
 import { Message } from "../tl/patched";
 import type { Message as CustomMessage } from "../tl/custom/message";
 
-interface NewMessageInterface {
-    chats?: EntityLike[];
-    func?: CallableFunction;
+export interface NewMessageInterface extends DefaultEventInterface{
+    func?: { (event: NewMessageEvent): boolean };
+    /**
+     * If set to `true`, only **incoming** messages will be handled.
+     Mutually exclusive with ``outgoing`` (can only set one of either).
+     */
     incoming?: boolean;
+    /**
+     * If set to `true`, only **outgoing** messages will be handled.
+     * Mutually exclusive with ``incoming`` (can only set one of either).
+     */
     outgoing?: boolean;
+    /**
+     * Unlike `chats`, this parameter filters the *senders* of the
+     * message. That is, only messages *sent by these users* will be
+     * handled. Use `chats` if you want private messages with this/these
+     * users. `from_users` lets you filter by messages sent by *one or
+     * more* users across the desired chats (doesn't need a list).
+     */
     fromUsers?: EntityLike[];
+    /**
+     * Whether forwarded messages should be handled or not. By default,
+     * both forwarded and normal messages are included. If it's `True`
+     * **only** forwards will be handled. If it's `False` only messages
+     * that are *not* forwards will be handled.
+     */
     forwards?: boolean;
+    /**
+     *  If set, only messages matching this pattern will be handled.
+     */
     pattern?: RegExp;
-    blacklistChats?: boolean;
 }
 
+/**
+ * Occurs whenever a new text message or a message with media arrives.
+ * @example
+ * ```ts
+ * async function eventPrint(event: NewMessageEvent) {
+ * const message = event.message;
+ *
+ *   // Checks if it's a private message (from user or bot)
+ *   if (event.isPrivate){
+ *       // prints sender id
+ *       console.log(message.senderId);
+ *       // read message
+ *       if (message.text == "hello"){
+ *           const sender = await message.getSender();
+ *           console.log("sender is",sender);
+ *           await client.sendMessage(sender,{
+ *               message:`hi your id is ${message.senderId}`
+ *           });
+ *       }
+ *   }
+ * }
+ * // adds an event handler for new messages
+ * client.addEventHandler(eventPrint, new NewMessage({}));
+ * ```
+ */
 export class NewMessage extends EventBuilder {
     chats?: EntityLike[];
-    func?: CallableFunction;
+    func?: { (event: NewMessageEvent): boolean };
     incoming?: boolean;
     outgoing?: boolean;
     fromUsers?: EntityLike[];
     forwards?: boolean;
     pattern?: RegExp;
-    private _noCheck: boolean;
 
-    constructor({
-        chats,
-        func,
-        incoming,
-        outgoing,
-        fromUsers,
-        forwards,
-        pattern,
-        blacklistChats = true,
-    }: NewMessageInterface) {
+    /** @hidden */
+    private readonly _noCheck: boolean;
+
+    constructor(newMessageParams: NewMessageInterface) {
+        let {
+            chats,
+            func,
+            incoming,
+            outgoing,
+            fromUsers,
+            forwards,
+            pattern,
+            blacklistChats = false
+        } = newMessageParams;
         if (incoming && outgoing) {
             incoming = outgoing = undefined;
         } else if (incoming != undefined && outgoing == undefined) {
@@ -58,7 +108,7 @@ export class NewMessage extends EventBuilder {
             outgoing,
             fromUsers,
             forwards,
-            pattern,
+            pattern
         ].every((v) => v == undefined);
     }
 
@@ -99,7 +149,7 @@ export class NewMessage extends EventBuilder {
                     fwdFrom: update.fwdFrom,
                     viaBotId: update.viaBotId,
                     replyTo: update.replyTo,
-                    entities: update.entities,
+                    entities: update.entities
                     // ttlPeriod:update.ttlPeriod
                 }),
                 update
@@ -119,7 +169,7 @@ export class NewMessage extends EventBuilder {
                     fwdFrom: update.fwdFrom,
                     viaBotId: update.viaBotId,
                     replyTo: update.replyTo,
-                    entities: update.entities,
+                    entities: update.entities
                     // ttlPeriod:update.ttlPeriod
                 }),
                 update
@@ -143,7 +193,7 @@ export class NewMessage extends EventBuilder {
             }
         }
         if (this.pattern) {
-            const match = event.message.message.match(this.pattern);
+            const match = event.message.message?.match(this.pattern);
             if (!match) {
                 return;
             }
@@ -165,7 +215,7 @@ export class NewMessageEvent extends EventCommon {
         super({
             msgId: message.id,
             chatPeer: message.peerId,
-            broadcast: message.post,
+            broadcast: message.post
         });
         this.originalUpdate = originalUpdate;
         this.message = message;

+ 21 - 6
gramjs/events/Raw.ts

@@ -1,18 +1,33 @@
 import { EventBuilder, EventCommon } from "./common";
-import type { TelegramClient } from "../client/TelegramClient";
+import type { TelegramClient } from "..";
 import { Api } from "../tl";
 
-interface RawInterface {
+export interface RawInterface {
+    /**
+     * The types that the {@link Api.TypeUpdate} instance must be.
+     * Equivalent to ``if (update instanceof type) return update``.
+     */
     types?: Function[];
     func?: CallableFunction;
 }
 
+/**
+ * The RAW updates that telegram sends. these are {@link Api.TypeUpdate} objects.
+ * The are useful to handle custom events that you need like user typing or games.
+ * @example
+ * ```ts
+ * client.addEventHandler((update) => {
+ *   console.log("Received new Update");
+ *   console.log(update);
+ * });
+ * ```
+ */
 export class Raw extends EventBuilder {
-    private types?: Function[];
+    private readonly types?: Function[];
 
-    constructor({ types = undefined, func = undefined }: RawInterface) {
-        super({ func: func });
-        this.types = types;
+    constructor(params: RawInterface) {
+        super({ func: params.func });
+        this.types = params.types;
     }
 
     async resolve(client: TelegramClient) {

+ 34 - 13
gramjs/events/common.ts

@@ -6,6 +6,7 @@ import type { TelegramClient } from "..";
 import { isArrayLike } from "../Helpers";
 import { utils } from "../";
 
+/** @hidden */
 export async function _intoIdSet(
     client: TelegramClient,
     chats: EntityLike[] | EntityLike | undefined
@@ -25,21 +26,21 @@ export async function _intoIdSet(
                 result.add(
                     utils.getPeerId(
                         new Api.PeerUser({
-                            userId: chat,
+                            userId: chat
                         })
                     )
                 );
                 result.add(
                     utils.getPeerId(
                         new Api.PeerChat({
-                            chatId: chat,
+                            chatId: chat
                         })
                     )
                 );
                 result.add(
                     utils.getPeerId(
                         new Api.PeerChannel({
-                            channelId: chat,
+                            channelId: chat
                         })
                     )
                 );
@@ -60,23 +61,42 @@ export async function _intoIdSet(
     return Array.from(result);
 }
 
-interface DefaultEventInterface {
+export interface DefaultEventInterface {
+    /**
+     * May be one or more entities (username/peer/etc.), preferably IDs.<br/>
+     * By default, only matching chats will be handled.
+     */
     chats?: EntityLike[];
+    /**
+     * Whether to treat the chats as a blacklist instead of as a whitelist (default).<br/>
+     * This means that every chat will be handled *except* those specified in ``chats``<br/>
+     * which will be ignored if ``blacklistChats:true``.
+     */
     blacklistChats?: boolean;
+    /**
+     * A callable function that should accept the event as input
+     * parameter, and return a value indicating whether the event
+     * should be dispatched or not (any truthy value will do, it
+     * does not need to be a `bool`). It works like a custom filter:
+     */
     func?: CallableFunction;
 }
 
+/**
+ * The common event builder, with builtin support to filter per chat.<br/>
+ * All events inherit this.
+ */
 export class EventBuilder {
     chats?: EntityLike[];
-    private blacklistChats: boolean;
+    blacklistChats: boolean;
     resolved: boolean;
     func?: CallableFunction;
 
-    constructor({ chats, blacklistChats = true, func }: DefaultEventInterface) {
-        this.chats = chats;
-        this.blacklistChats = blacklistChats;
+    constructor(eventParams: DefaultEventInterface) {
+        this.chats = eventParams.chats;
+        this.blacklistChats = eventParams.blacklistChats || false;
         this.resolved = false;
-        this.func = func;
+        this.func = eventParams.func;
     }
 
     build(update: Api.TypeUpdate, others = null): any {
@@ -116,16 +136,17 @@ interface EventCommonInterface {
     msgId?: number;
     broadcast?: boolean;
 }
+
 export class EventCommon extends ChatGetter {
     _eventName = "Event";
     _entities: Map<number, Entity>;
     _messageId?: number;
 
     constructor({
-        chatPeer = undefined,
-        msgId = undefined,
-        broadcast = undefined,
-    }: EventCommonInterface) {
+                    chatPeer = undefined,
+                    msgId = undefined,
+                    broadcast = undefined
+                }: EventCommonInterface) {
         super({ chatPeer, broadcast });
         this._entities = new Map();
         this._client = undefined;

+ 2 - 2
gramjs/extensions/BinaryReader.ts

@@ -5,7 +5,7 @@ import { tlobjects } from "../tl/AllTLObjects";
 import { readBigIntFromBuffer } from "../Helpers";
 
 export class BinaryReader {
-    private stream: Buffer;
+    private readonly stream: Buffer;
     private _last?: Buffer;
     private offset: number;
 
@@ -184,7 +184,7 @@ export class BinaryReader {
         let clazz = tlobjects[constructorId];
         if (clazz === undefined) {
             /**
-             * The class was None, but there's still a
+             * The class was undefined, but there's still a
              * chance of it being a manually parsed value like bool!
              */
             const value = constructorId;

+ 1 - 1
gramjs/extensions/Logger.ts

@@ -4,7 +4,7 @@ let _level: string | undefined = undefined;
 
 export class Logger {
     static levels = ["error", "warn", "info", "debug"];
-    private isBrowser: boolean;
+    private readonly isBrowser: boolean;
     private colors: {
         warn: string;
         debug: string;

+ 3 - 3
gramjs/extensions/html.ts

@@ -6,9 +6,9 @@ import { helpers } from "../index";
 class HTMLToTelegramParser implements Handler {
     text: string;
     entities: Api.TypeMessageEntity[];
-    private _buildingEntities: Map<string, Api.TypeMessageEntity>;
-    private _openTags: string[];
-    private _openTagsMeta: (string | undefined)[];
+    private readonly _buildingEntities: Map<string, Api.TypeMessageEntity>;
+    private readonly _openTags: string[];
+    private readonly _openTagsMeta: (string | undefined)[];
 
     constructor() {
         this.text = "";

+ 1 - 2
gramjs/extensions/markdown.ts

@@ -1,6 +1,5 @@
 import { Api } from "../tl";
-import type { ValueOf } from "../define";
-import { DEFAULT_DELIMITERS, messageEntities } from "../client/messageParse";
+import { DEFAULT_DELIMITERS} from "../client/messageParse";
 
 export class MarkdownParser {
     // TODO maybe there is a better way :shrug:

+ 13 - 13
gramjs/network/MTProtoSender.ts

@@ -62,29 +62,29 @@ export class MTProtoSender {
         senderCallback: null,
     };
     private _connection?: Connection;
-    private _log: any;
+    private readonly _log: any;
     private _dcId: number;
-    private _retries: number;
-    private _delay: number;
+    private readonly _retries: number;
+    private readonly _delay: number;
     private _connectTimeout: null;
     private _autoReconnect: boolean;
-    private _authKeyCallback: any;
-    private _updateCallback: any;
-    private _autoReconnectCallback?: any;
-    private _senderCallback: any;
-    private _isMainSender: boolean;
+    private readonly _authKeyCallback: any;
+    private readonly _updateCallback: any;
+    private readonly _autoReconnectCallback?: any;
+    private readonly _senderCallback: any;
+    private readonly _isMainSender: boolean;
     private _userConnected: boolean;
     private _reconnecting: boolean;
     private _disconnected: boolean;
     private _sendLoopHandle: any;
     private _recvLoopHandle: any;
-    private authKey: AuthKey;
-    private _state: MTProtoState;
+    private readonly authKey: AuthKey;
+    private readonly _state: MTProtoState;
     private _sendQueue: MessagePacker;
     private _pendingState: Map<string, RequestState>;
-    private _pendingAck: Set<any>;
-    private _lastAcks: any[];
-    private _handlers: any;
+    private readonly _pendingAck: Set<any>;
+    private readonly _lastAcks: any[];
+    private readonly _handlers: any;
 
     /**
      * @param authKey

+ 1 - 1
gramjs/network/MTProtoState.ts

@@ -10,7 +10,7 @@ import { IGE } from "../crypto/IGE";
 import { InvalidBufferError, SecurityError } from "../errors";
 
 export class MTProtoState {
-    private authKey?: AuthKey;
+    private readonly authKey?: AuthKey;
     private _log: any;
     timeOffset: number;
     salt: bigInt.BigInteger;

+ 3 - 4
gramjs/network/connection/Connection.ts

@@ -1,4 +1,3 @@
-import type { BinaryReader } from "../../extensions";
 import { PromisedNetSockets, PromisedWebSockets } from "../../extensions";
 import { AsyncQueue } from "../../extensions";
 import { IS_NODE } from "../../Helpers";
@@ -17,8 +16,8 @@ import { IS_NODE } from "../../Helpers";
 class Connection {
     // @ts-ignore
     PacketCodecClass: any; //"typeof AbridgedPacketCodec|typeof FullPacketCodec|typeof ObfuscatedConnection as "
-    private _ip: string;
-    private _port: number;
+    private readonly _ip: string;
+    private readonly _port: number;
     private _dcId: number;
     private _log: any;
     private _connected: boolean;
@@ -26,7 +25,7 @@ class Connection {
     private _recvTask?: Promise<void>;
     protected _codec: any;
     protected _obfuscation: any;
-    private _sendArray: AsyncQueue;
+    private readonly _sendArray: AsyncQueue;
     private _recvArray: AsyncQueue;
     protected socket: PromisedNetSockets | PromisedWebSockets;
 

+ 1 - 1
gramjs/sessions/Abstract.ts

@@ -41,7 +41,7 @@ export abstract class Session {
 
     /**
      * Returns an ``AuthKey`` instance associated with the saved
-     * data center, or `None` if a new one should be generated.
+     * data center, or `undefined` if a new one should be generated.
      */
     abstract get authKey(): AuthKey | undefined;
 

+ 1 - 1
gramjs/sessions/StringSession.ts

@@ -5,7 +5,7 @@ import { AuthKey } from "../crypto/AuthKey";
 const CURRENT_VERSION = "1";
 
 export class StringSession extends MemorySession {
-    private _key?: Buffer;
+    private readonly _key?: Buffer;
 
     /**
      * This session file can be easily saved and loaded as a string. According

+ 2 - 2
gramjs/tl/MTProtoRequest.ts

@@ -2,11 +2,11 @@ export class MTProtoRequest {
     private sent: boolean;
     private sequence: number;
     private msgId: number;
-    private dirty: boolean;
+    private readonly dirty: boolean;
     private sendTime: number;
     private confirmReceived: boolean;
     private constructorId: number;
-    private confirmed: boolean;
+    private readonly confirmed: boolean;
     private responded: boolean;
 
     constructor() {

ファイルの差分が大きいため隠しています
+ 12852 - 14105
gramjs/tl/api.d.ts


+ 1 - 1
gramjs/tl/core/GZIPPacked.ts

@@ -49,7 +49,7 @@ export class GZIPPacked {
         if (constructor !== GZIPPacked.CONSTRUCTOR_ID) {
             throw new Error("not equal");
         }
-        return await GZIPPacked.gzip(reader.tgReadBytes());
+        return GZIPPacked.gzip(reader.tgReadBytes());
     }
 
     static async fromReader(reader: BinaryReader) {

+ 1 - 0
gramjs/tl/core/RPCResult.ts

@@ -1,6 +1,7 @@
 import { Api } from "../api";
 import type { BinaryReader } from "../../extensions";
 import { GZIPPacked } from "./";
+import bigInt from "big-integer";
 
 export class RPCResult {
     static CONSTRUCTOR_ID = 0xf35c6d01;

+ 10 - 4
gramjs/tl/custom/button.ts

@@ -1,6 +1,8 @@
 import type { ButtonLike, EntityLike } from "../../define";
 import { Api } from "../api";
 import { utils } from "../../";
+import { inspect } from "util";
+import { betterConsoleLog } from "../../Helpers";
 
 export class Button {
     public button: ButtonLike;
@@ -8,6 +10,10 @@ export class Button {
     public selective: boolean | undefined;
     public singleUse: boolean | undefined;
 
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
+
     constructor(
         button: Api.TypeKeyboardButton,
         resize?: boolean,
@@ -38,7 +44,7 @@ export class Button {
         }
         return new Api.KeyboardButtonCallback({
             text: text,
-            data: data,
+            data: data
         });
     }
 
@@ -46,14 +52,14 @@ export class Button {
         return new Api.KeyboardButtonSwitchInline({
             text,
             query,
-            samePeer,
+            samePeer
         });
     }
 
     static url(text: string, url?: string) {
         return new Api.KeyboardButtonUrl({
             text: text,
-            url: url || text,
+            url: url || text
         });
     }
 
@@ -69,7 +75,7 @@ export class Button {
             url: url || text,
             bot: utils.getInputPeer(bot || new Api.InputUserSelf()),
             requestWriteAccess: writeAccess,
-            fwdText: fwdText,
+            fwdText: fwdText
         });
     }
 

+ 5 - 0
gramjs/tl/custom/chatGetter.ts

@@ -2,6 +2,8 @@ import type { Entity, EntityLike } from "../../define";
 import type { TelegramClient } from "../../client/TelegramClient";
 import { utils } from "../../";
 import { Api } from "../api";
+import { inspect } from "util";
+import { betterConsoleLog } from "../../Helpers";
 
 export interface ChatGetterConstructorParams {
     chatPeer?: EntityLike;
@@ -16,6 +18,9 @@ export class ChatGetter {
     _chat?: Entity;
     _broadcast?: boolean;
     public _client?: TelegramClient;
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
 
     constructor({
         chatPeer,

+ 7 - 2
gramjs/tl/custom/dialog.ts

@@ -1,9 +1,11 @@
-import type { TelegramClient } from "../../client/TelegramClient";
+import type { TelegramClient } from "../..";
 import { Api } from "../api";
 import type { Entity } from "../../define";
 import { getDisplayName, getInputPeer, getPeerId } from "../../Utils";
 import { Draft } from "./draft";
 import { Message } from "./message";
+import { inspect } from "util";
+import { betterConsoleLog } from "../../Helpers";
 
 export class Dialog {
     _client: TelegramClient;
@@ -24,6 +26,9 @@ export class Dialog {
     isUser: boolean;
     isGroup: boolean;
     isChannel: boolean;
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
 
     constructor(
         client: TelegramClient,
@@ -37,7 +42,7 @@ export class Dialog {
         this.folderId = dialog.folderId;
         this.archived = dialog.folderId != undefined;
         this.message = message;
-        this.date = this.message?.date;
+        this.date = this.message!.date!;
 
         this.entity = entities.get(getPeerId(dialog.peer));
         this.inputEntity = getInputPeer(this.entity);

+ 16 - 9
gramjs/tl/custom/draft.ts

@@ -1,13 +1,14 @@
 import type { Entity } from "../../define";
-import type { TelegramClient } from "../../client/TelegramClient";
+import type { TelegramClient } from "../..";
 import { getInputPeer, getPeer } from "../../Utils";
 import { Api } from "../api";
-import { MarkdownParser } from "../../extensions/markdown";
+import { inspect } from "util";
+import { betterConsoleLog } from "../../Helpers";
 
 export class Draft {
     private _client: TelegramClient;
-    private _entity?: Entity;
-    private _peer: ReturnType<typeof getPeer>;
+    private readonly _entity?: Entity;
+    private readonly _peer: ReturnType<typeof getPeer>;
     private _inputEntity: Api.TypeInputPeer | undefined;
     private _text?: string;
     private _rawText?: string;
@@ -15,6 +16,10 @@ export class Draft {
     private linkPreview?: boolean;
     private replyToMsgId?: Api.int;
 
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
+
     constructor(
         client: TelegramClient,
         entity: Entity,
@@ -25,17 +30,18 @@ export class Draft {
         this._entity = entity;
         this._inputEntity = entity ? getInputPeer(entity) : undefined;
         if (!draft || !(draft instanceof Api.DraftMessage)) {
-            draft = new Api.DraftMessage({
+            draft
+                = new Api.DraftMessage({
                 message: "",
-                date: -1,
+                date: -1
             });
         }
         if (!(draft instanceof Api.DraftMessageEmpty)) {
             this.linkPreview = !draft.noWebpage;
-            this._text = client.parseMode.unparse(
+            this._text = client.parseMode ? client.parseMode.unparse(
                 draft.message,
-                draft.entities
-            );
+                draft.entities|| [],
+            ) : draft.message;
             this._rawText = draft.message;
             this.date = draft.date;
             this.replyToMsgId = draft.replyToMsgId;
@@ -52,5 +58,6 @@ export class Draft {
         }
         return this._inputEntity;
     }
+
     // TODO later
 }

+ 5 - 0
gramjs/tl/custom/file.ts

@@ -1,9 +1,14 @@
 import type { FileLike } from "../../define";
 import { Api } from "../api";
 import { _photoSizeByteCount } from "../../Utils";
+import { inspect } from "util";
+import { betterConsoleLog } from "../../Helpers";
 
 export class File {
     private readonly media: FileLike;
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
 
     constructor(media: FileLike) {
         this.media = media;

+ 5 - 1
gramjs/tl/custom/forward.ts

@@ -3,11 +3,15 @@ import { SenderGetter } from "./senderGetter";
 import { Api } from "../api";
 import type { TelegramClient } from "../../client/TelegramClient";
 import type { Entity } from "../../define";
-import { _EntityType, _entityType } from "../../Helpers";
+import { _EntityType, _entityType, betterConsoleLog } from "../../Helpers";
 import { _getEntityPair, getPeerId } from "../../Utils";
+import { inspect } from "util";
 
 export class Forward {
     private originalFwd: Api.MessageFwdHeader;
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
 
     constructor(
         client: TelegramClient,

+ 22 - 16
gramjs/tl/custom/inlineResult.ts

@@ -1,25 +1,31 @@
-import type { TelegramClient } from "../../client/TelegramClient";
+import type { TelegramClient } from "../..";
 import type { EntityLike, MessageIDLike } from "../../define";
 import { Api } from "../api";
 import { utils } from "../../";
+import { inspect } from "util";
+import { betterConsoleLog } from "../../Helpers";
 
 export class InlineResult {
-    ARTICLE = "article";
-    PHOTO = "photo";
-    GIF = "gif";
-    VIDEO = "video";
-    VIDEO_GIF = "mpeg4_gif";
-    AUDIO = "audio";
-    DOCUMENT = "document";
-    LOCATION = "location";
-    VENUE = "venue";
-    CONTACT = "contact";
-    GAME = "game";
-    private _entity: EntityLike | undefined;
-    private _queryId: Api.long | undefined;
-    private result: Api.TypeBotInlineResult;
+    private _ARTICLE = "article";
+    private _PHOTO = "photo";
+    private _GIF = "gif";
+    private _VIDEO = "video";
+    private _VIDEO_GIF = "mpeg4_gif";
+    private _AUDIO = "audio";
+    private _DOCUMENT = "document";
+    private _LOCATION = "location";
+    private _VENUE = "venue";
+    private _CONTACT = "contact";
+    private _GAME = "game";
+    private readonly _entity: EntityLike | undefined;
+    private readonly _queryId: Api.long | undefined;
+    private readonly result: Api.TypeBotInlineResult;
     private _client: TelegramClient;
 
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
+
     constructor(
         client: TelegramClient,
         original: Api.TypeBotInlineResult,
@@ -90,7 +96,7 @@ export class InlineResult {
             silent: silent,
             clearDraft: clearDraft,
             hideVia: hideVia,
-            replyToMsgId: replyId,
+            replyToMsgId: replyId
         });
         return await this._client.invoke(request);
     }

+ 13 - 6
gramjs/tl/custom/inlineResults.ts

@@ -1,32 +1,39 @@
-import type { TelegramClient } from "../../client/TelegramClient";
+import type { TelegramClient } from "../..";
 import type { EntityLike } from "../../define";
 import { Api } from "../api";
+import { InlineResult } from "./inlineResult";
+import { inspect } from "util";
+import { betterConsoleLog } from "../../Helpers";
 
-export class InlineResults<InlineResult> extends Array<InlineResult> {
+export class InlineResults extends Array<InlineResult> {
     private result: Api.messages.TypeBotResults;
     private queryId: Api.long;
-    private cacheTime: Api.int;
-    private _validUntil: number;
+    private readonly cacheTime: Api.int;
+    private readonly _validUntil: number;
     private users: Api.TypeUser[];
     private gallery: boolean;
     private nextOffset: string | undefined;
     private switchPm: Api.TypeInlineBotSwitchPM | undefined;
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
 
     constructor(
         client: TelegramClient,
         original: Api.messages.TypeBotResults,
         entity?: EntityLike
     ) {
-        super();
+        super(...original.results.map((res) => new InlineResult(client, res, original.queryId, entity)));
         this.result = original;
         this.queryId = original.queryId;
         this.cacheTime = original.cacheTime;
         this._validUntil = new Date().getTime() / 1000 + this.cacheTime;
-        this.users = <Api.TypeUser[]>original.users;
+        this.users = original.users;
         this.gallery = Boolean(original.gallery);
         this.nextOffset = original.nextOffset;
         this.switchPm = original.switchPm;
     }
+
     resultsValid() {
         return new Date().getTime() / 1000 < this._validUntil;
     }

+ 231 - 78
gramjs/tl/custom/message.ts

@@ -9,6 +9,9 @@ import type { File } from "./file";
 import { Mixin } from "ts-mixer";
 import { EditMessageParams, SendMessageParams } from "../../client/messages";
 import { DownloadFileParams } from "../../client/downloads";
+import { inspect } from "util";
+import { betterConsoleLog } from "../../Helpers";
+import { _selfId } from "../../client/users";
 
 interface MessageBaseInterface {
     id: any;
@@ -43,88 +46,235 @@ interface MessageBaseInterface {
     _entities?: Map<number, Entity>;
 }
 
+/**
+ * This custom class aggregates both {@link Api.Message} and {@link Api.MessageService} to ease accessing their members.<br/>
+ * <br/>
+ * Remember that this class implements {@link ChatGetter} and {@link SenderGetter}<br/>
+ * which means you have access to all their sender and chat properties and methods.
+ */
 export class Message extends Mixin(SenderGetter, ChatGetter) {
+
+    /**
+     * Whether the message is outgoing (i.e. you sent it from
+     * another session) or incoming (i.e. someone else sent it).
+     * <br/>
+     * Note that messages in your own chat are always incoming,
+     * but this member will be `true` if you send a message
+     * to your own chat. Messages you forward to your chat are
+     * **not** considered outgoing, just like official clients
+     * display them.
+     */
     out?: boolean;
+    /**
+     * Whether you were mentioned in this message or not.
+     * Note that replies to your own messages also count as mentions.
+     */
     mentioned?: boolean;
+    /** Whether you have read the media in this message
+     * or not, e.g. listened to the voice note media.
+     */
     mediaUnread?: boolean;
+    /**
+     * Whether the message should notify people with sound or not.
+     * Previously used in channels, but since 9 August 2019, it can
+     * also be {@link https://telegram.org/blog/silent-messages-slow-mode|used in private chats}
+     */
     silent?: boolean;
-    post: boolean;
-    fromScheduled: any | undefined;
-    legacy: any | undefined;
-    editHide: any | undefined;
+    /**
+     * Whether this message is a post in a broadcast
+     * channel or not.
+     */
+    post?: boolean;
+    /**
+     * Whether this message was originated from a previously-scheduled
+     * message or not.
+     */
+    fromScheduled?: boolean;
+    /**
+     * Whether this is a legacy message or not.
+     */
+    legacy?: boolean;
+    /**
+     * Whether the edited mark of this message is edited
+     * should be hidden (e.g. in GUI clients) or shown.
+     */
+    editHide?: boolean;
+    /**
+     *  Whether this message is currently pinned or not.
+     */
+    pinned?: boolean;
+    /**
+     * The ID of this message. This field is *always* present.
+     * Any other member is optional and may be `undefined`.
+     */
     id: number;
-    fromId?: EntityLike;
-    peerId: any;
-    fwdFrom: Api.TypeMessageFwdHeader;
-    viaBotId: any;
-    replyTo: Api.MessageReplyHeader;
-    date: any | undefined;
-    message: string;
-    media: any;
-    replyMarkup: any | undefined;
-    entities: any | undefined;
+    /**
+     * The peer who sent this message, which is either
+     * {@link Api.PeerUser}, {@link Api.PeerChat} or {@link Api.PeerChannel}.
+     * This value will be `undefined` for anonymous messages.
+     */
+    fromId?: Api.TypePeer;
+    /**
+     * The peer to which this message was sent, which is either
+     * {@link Api.PeerUser}, {@link Api.PeerChat} or {@link Api.PeerChannel}.
+     * This will always be present except for empty messages.
+     */
+    peerId?: Api.TypePeer;
+    /**
+     * The original forward header if this message is a forward.
+     * You should probably use the `forward` property instead.
+     */
+    fwdFrom?: Api.TypeMessageFwdHeader;
+    /**
+     * The ID of the bot used to send this message
+     * through its inline mode (e.g. "via @like").
+     */
+    viaBotId?: number;
+    /**
+     * The original reply header if this message is replying to another.
+     */
+    replyTo?: Api.MessageReplyHeader;
+    /**
+     * The timestamp indicating when this message was sent.
+     * This will always be present except for empty messages.
+     */
+    date?: number;
+    /**
+     * The string text of the message for {@link Api.Message} instances,
+     * which will be `undefined` for other types of messages.
+     */
+    message?: string;
+    /**
+     * The media sent with this message if any (such as photos, videos, documents, gifs, stickers, etc.).
+     *
+     * You may want to access the `photo`, `document` etc. properties instead.
+     *
+     * If the media was not present or it was {@link Api.MessageMediaEmpty},
+     * this member will instead be `undefined` for convenience.
+     */
+    media?: Api.TypeMessageMedia;
+    /**
+     * The reply markup for this message (which was sent either via a bot or by a bot).
+     * You probably want to access `buttons` instead.
+     */
+    replyMarkup?: Api.TypeReplyMarkup;
+    /**
+     * The list of markup entities in this message,
+     * such as bold, italics, code, hyperlinks, etc.
+     */
+    entities?: Api.TypeMessageEntity[];
+    /**
+     *  The number of views this message from a broadcast channel has.
+     *  This is also present in forwards.
+     */
     views?: number;
-    forwards: any | undefined;
-    replies: any | undefined;
-    editDate: any;
-    pinned: any | undefined;
-    postAuthor: any;
+    /**
+     * The number of times this message has been forwarded.
+     */
+    forwards?: number;
+    /**
+     *  The number of times another message has replied to this message.
+     */
+    replies?: number;
+    /**
+     * The date when this message was last edited.
+     */
+    editDate?: number;
+    /**
+     * The display name of the message sender to show in messages sent to broadcast channels.
+     */
+    postAuthor?: string;
+    /**
+     *  If this message belongs to a group of messages (photo albums or video albums),
+     *  all of them will have the same value here.
+     */
     groupedId?: number;
-    restrictionReason: any;
-    action: any | undefined;
+    /**
+     * An optional list of reasons why this message was restricted.
+     * If the list is `undefined`, this message has not been restricted.
+     */
+    restrictionReason?: Api.TypeRestrictionReason[];
+    /**
+     * The message action object of the message for {@link Api.MessageService}
+     * instances, which will be `undefined` for other types of messages.
+     */
+    action?: any;
+    /**
+     * The Time To Live period configured for this message.
+     * The message should be erased from wherever it's stored (memory, a
+     * local database, etc.) when this threshold is met.
+     */
     ttlPeriod?: number;
-    _actionEntities: any;
-    public _client?: TelegramClient;
+    /** @hidden */
+    _actionEntities?: any;
+    /** @hidden */
+    _client?: TelegramClient;
+    /** @hidden */
     _text?: string;
+    /** @hidden */
     _file?: File;
+    /** @hidden */
     _replyMessage?: Message;
-    _buttons: undefined;
-    _buttonsFlat: undefined;
-    _buttonsCount: number;
+    /** @hidden */
+    _buttons?: undefined;
+    /** @hidden */
+    _buttonsFlat?: undefined;
+    /** @hidden */
+    _buttonsCount?: number;
+    /** @hidden */
     _viaBot?: EntityLike;
+    /** @hidden */
     _viaInputBot?: EntityLike;
-    _inputSender: any;
+    /** @hidden */
+    _inputSender?: any;
+    /** @hidden */
     _forward?: Forward;
-    _sender: any;
-    _entities: Map<number, Entity>;
+    /** @hidden */
+    _sender?: any;
+    /** @hidden */
+    _entities?: Map<number, Entity>;
     patternMatch?: RegExpMatchArray;
 
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
+
     constructor({
-        id,
-        peerId = undefined,
-        date = undefined,
-
-        out = undefined,
-        mentioned = undefined,
-        mediaUnread = undefined,
-        silent = undefined,
-        post = undefined,
-        fromId = undefined,
-        replyTo = undefined,
-
-        message = undefined,
-
-        fwdFrom = undefined,
-        viaBotId = undefined,
-        media = undefined,
-        replyMarkup = undefined,
-        entities = undefined,
-        views = undefined,
-        editDate = undefined,
-        postAuthor = undefined,
-        groupedId = undefined,
-        fromScheduled = undefined,
-        legacy = undefined,
-        editHide = undefined,
-        pinned = undefined,
-        restrictionReason = undefined,
-        forwards = undefined,
-        replies = undefined,
-
-        action = undefined,
-        ttlPeriod = undefined,
-        _entities = new Map<number, Entity>(),
-    }: MessageBaseInterface) {
+                    id,
+                    peerId = undefined,
+                    date = undefined,
+
+                    out = undefined,
+                    mentioned = undefined,
+                    mediaUnread = undefined,
+                    silent = undefined,
+                    post = undefined,
+                    fromId = undefined,
+                    replyTo = undefined,
+
+                    message = undefined,
+
+                    fwdFrom = undefined,
+                    viaBotId = undefined,
+                    media = undefined,
+                    replyMarkup = undefined,
+                    entities = undefined,
+                    views = undefined,
+                    editDate = undefined,
+                    postAuthor = undefined,
+                    groupedId = undefined,
+                    fromScheduled = undefined,
+                    legacy = undefined,
+                    editHide = undefined,
+                    pinned = undefined,
+                    restrictionReason = undefined,
+                    forwards = undefined,
+                    replies = undefined,
+
+                    action = undefined,
+                    ttlPeriod = undefined,
+                    _entities = new Map<number, Entity>()
+                }: MessageBaseInterface) {
         if (!id) throw new Error("id is a required attribute for Message");
         let senderId = undefined;
         if (fromId) {
@@ -242,10 +392,10 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
                     entities.get(
                         utils.getPeerId(
                             new Api.PeerChannel({
-                                channelId: this.action.inviterId,
+                                channelId: this.action.inviterId
                             })
                         )
-                    ),
+                    )
                 ];
             } else if (
                 this.action instanceof Api.MessageActionChannelMigrateFrom
@@ -255,7 +405,7 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
                         utils.getPeerId(
                             new Api.PeerChat({ chatId: this.action.chatId })
                         )
-                    ),
+                    )
                 ];
             }
         }
@@ -271,8 +421,8 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
                 this._text = this.message;
             } else {
                 this._text = this._client.parseMode.unparse(
-                    this.message,
-                    this.entities
+                    this.message || "",
+                    this.entities || []
                 );
             }
         }
@@ -290,7 +440,7 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
     }
 
     get rawText() {
-        return this.message;
+        return this.message || "";
     }
 
     /**
@@ -331,7 +481,7 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
         } catch (e) {
             this._client._log.error(
                 "Got error while trying to finish init message with id " +
-                    this.id
+                this.id
             );
             if (this._client._log.canSend("error")) {
                 console.error(e);
@@ -533,7 +683,7 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
     get toId() {
         if (this._client && !this.out && this.isPrivate) {
             return new Api.PeerUser({
-                userId: this._client._selfId!,
+                userId: _selfId(this._client)!
             });
         }
         return this.peerId;
@@ -543,9 +693,11 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
         let ent = this.entities;
         if (!ent || ent.length == 0) return;
 
-        if (cls) ent = ent.filter((v: any) => v instanceof cls);
+        if (cls){
+            ent = ent.filter((v: any) => v instanceof cls);
+        }
 
-        const texts = utils.getInnerText(this.message, ent);
+        const texts = utils.getInnerText(this.message || "", ent);
         const zip = (rows: any[]) =>
             rows[0].map((_: any, c: string | number) =>
                 rows.map((row) => row[c])
@@ -564,7 +716,7 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
                 await this._client.getMessages(
                     this.isChannel ? await this.getInputChat() : undefined,
                     {
-                        ids: new Api.InputMessageReplyTo({ id: this.id }),
+                        ids: new Api.InputMessageReplyTo({ id: this.id })
                     }
                 )
             )[0];
@@ -578,7 +730,7 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
                     await this._client.getMessages(
                         this.isChannel ? this._inputChat : undefined,
                         {
-                            ids: this.replyToMsgId,
+                            ids: this.replyToMsgId
                         }
                     )
                 )[0];
@@ -611,7 +763,7 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
             entity = await this._client.getInputEntity(entity);
             const params = {
                 messages: [this.id],
-                fromPeer: (await this.getInputChat())!,
+                fromPeer: (await this.getInputChat())!
             };
 
             return this._client.forwardMessages(entity, params);
@@ -776,4 +928,5 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
     }
 }
 
-export interface Message extends ChatGetter, SenderGetter {}
+export interface Message extends ChatGetter, SenderGetter {
+}

+ 5 - 0
gramjs/tl/custom/messageButton.ts

@@ -2,6 +2,8 @@ import type { TelegramClient } from "../../client/TelegramClient";
 import type { ButtonLike, EntityLike, MessageIDLike } from "../../define";
 import { Api } from "../api";
 import { Button } from "./button";
+import { inspect } from "util";
+import { betterConsoleLog } from "../../Helpers";
 
 export class MessageButton {
     private readonly _client: TelegramClient;
@@ -9,6 +11,9 @@ export class MessageButton {
     public readonly button: ButtonLike;
     private readonly _bot: EntityLike;
     private readonly _msgId: MessageIDLike;
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
 
     constructor(
         client: TelegramClient,

+ 7 - 2
gramjs/tl/custom/senderGetter.ts

@@ -1,6 +1,8 @@
 import type { Entity } from "../../define";
-import type { TelegramClient } from "../../client/TelegramClient";
+import type { TelegramClient } from "../..";
 import { Api } from "../api";
+import { inspect } from "util";
+import { betterConsoleLog } from "../../Helpers";
 
 interface SenderGetterConstructorInterface {
     senderId?: number;
@@ -13,6 +15,9 @@ export class SenderGetter {
     _sender?: Entity;
     _inputSender?: Api.TypeInputPeer;
     public _client?: TelegramClient;
+    [inspect.custom]() {
+        return betterConsoleLog(this);
+    }
 
     constructor({
         senderId,
@@ -44,7 +49,7 @@ export class SenderGetter {
             (await this.getInputSender())
         ) {
             try {
-                this._sender = await this._client.getEntity(this._inputSender);
+                this._sender = await this._client.getEntity(this._inputSender!);
             } catch (e) {
                 await this._refetchSender();
             }

+ 5 - 5
gramjs/tl/types-generator/template.js

@@ -47,7 +47,7 @@ module.exports = ({ types, constructors, functions }) => {
                 );
 
                 return `
-      export declare class ${upperFirst(name)} extends VirtualClass<{
+      export class ${upperFirst(name)} extends VirtualClass<{
 ${indent}  ${Object.keys(argsConfig)
                     .map((argName) =>
                         `
@@ -75,7 +75,7 @@ ${indent}}`.trim();
                 const argKeys = Object.keys(argsConfig);
 
                 if (!argKeys.length) {
-                    return `export declare class ${upperFirst(
+                    return `export class ${upperFirst(
                         name
                     )} extends Request<void, ${renderResult(result)}> {
     static fromReader(reader: Reader): ${upperFirst(name)};
@@ -88,7 +88,7 @@ ${indent}}`.trim();
                 );
 
                 return `
-      export declare class ${upperFirst(name)} extends Request<Partial<{
+      export class ${upperFirst(name)} extends Request<Partial<{
 ${indent}  ${argKeys
                     .map((argName) =>
                         `
@@ -194,7 +194,7 @@ export namespace Api {
   type long = BigInteger;
   type bytes = Buffer;
 
-  declare class VirtualClass<Args extends AnyLiteral> {
+  class VirtualClass<Args extends AnyLiteral> {
     static CONSTRUCTOR_ID: number;
     static SUBCLASS_OF_ID: number;
     static className: string;
@@ -213,7 +213,7 @@ export namespace Api {
     constructor(args: Args);
   }
 
-  declare class Request<Args, Response> extends VirtualClass<Partial<Args>> {
+  class Request<Args, Response> extends VirtualClass<Partial<Args>> {
     static readResult(reader: Reader): Buffer;
     resolve(client: Client, utils: Utils): Promise<void>;
 

+ 280 - 8
package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "telegram",
-  "version": "1.7.7",
+  "version": "1.7.9",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "telegram",
-      "version": "1.7.7",
+      "version": "1.7.9",
       "license": "MIT",
       "dependencies": {
         "@cryptography/aes": "^0.1.1",
@@ -41,6 +41,7 @@
         "raw-loader": "^4.0.2",
         "ts-loader": "^8.0.16",
         "ts-node": "^9.1.1",
+        "typedoc": "^0.21.0",
         "typescript": "^4.2.4",
         "webpack": "^5.21.2",
         "webpack-cli": "^4.5.0"
@@ -5506,9 +5507,9 @@
       }
     },
     "node_modules/glob": {
-      "version": "7.1.6",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
-      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "version": "7.1.7",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+      "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
       "dev": true,
       "dependencies": {
         "fs.realpath": "^1.0.0",
@@ -5520,6 +5521,9 @@
       },
       "engines": {
         "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/glob-to-regexp": {
@@ -5549,6 +5553,36 @@
       "dev": true,
       "optional": true
     },
+    "node_modules/handlebars": {
+      "version": "4.7.7",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
+      "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
+      "dev": true,
+      "dependencies": {
+        "minimist": "^1.2.5",
+        "neo-async": "^2.6.0",
+        "source-map": "^0.6.1",
+        "wordwrap": "^1.0.0"
+      },
+      "bin": {
+        "handlebars": "bin/handlebars"
+      },
+      "engines": {
+        "node": ">=0.4.7"
+      },
+      "optionalDependencies": {
+        "uglify-js": "^3.1.4"
+      }
+    },
+    "node_modules/handlebars/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/has": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -8396,6 +8430,12 @@
         "node": ">=10"
       }
     },
+    "node_modules/lunr": {
+      "version": "2.3.9",
+      "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
+      "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
+      "dev": true
+    },
     "node_modules/make-dir": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -8453,6 +8493,18 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/marked": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.1.tgz",
+      "integrity": "sha512-5XFS69o9CzDpQDSpUYC+AN2xvq8yl1EGa5SG/GI1hP78/uTeo3PDfiDNmsUyiahpyhToDDJhQk7fNtJsga+KVw==",
+      "dev": true,
+      "bin": {
+        "marked": "bin/marked"
+      },
+      "engines": {
+        "node": ">= 12"
+      }
+    },
     "node_modules/merge-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -8837,6 +8889,30 @@
         "node": ">=6"
       }
     },
+    "node_modules/onigasm": {
+      "version": "2.2.5",
+      "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz",
+      "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==",
+      "dev": true,
+      "dependencies": {
+        "lru-cache": "^5.1.1"
+      }
+    },
+    "node_modules/onigasm/node_modules/lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "node_modules/onigasm/node_modules/yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+      "dev": true
+    },
     "node_modules/optionator": {
       "version": "0.8.3",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
@@ -9090,6 +9166,15 @@
       "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
       "dev": true
     },
+    "node_modules/progress": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/prompts": {
       "version": "2.4.0",
       "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz",
@@ -9645,6 +9730,16 @@
       "dev": true,
       "optional": true
     },
+    "node_modules/shiki": {
+      "version": "0.9.3",
+      "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.3.tgz",
+      "integrity": "sha512-NEjg1mVbAUrzRv2eIcUt3TG7X9svX7l3n3F5/3OdFq+/BxUdmBOeKGiH4icZJBLHy354Shnj6sfBTemea2e7XA==",
+      "dev": true,
+      "dependencies": {
+        "onigasm": "^2.2.5",
+        "vscode-textmate": "^5.2.0"
+      }
+    },
     "node_modules/signal-exit": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
@@ -10589,6 +10684,41 @@
         "is-typedarray": "^1.0.0"
       }
     },
+    "node_modules/typedoc": {
+      "version": "0.21.0",
+      "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.0.tgz",
+      "integrity": "sha512-InmPBVlpOXptIkg/WnsQhbGYhv9cuDh/cRACUSautQ0QwcJPLAK2kHcfP0Pld6z/NiDvHc159fMq2qS+b/ALUw==",
+      "dev": true,
+      "dependencies": {
+        "glob": "^7.1.7",
+        "handlebars": "^4.7.7",
+        "lodash": "^4.17.21",
+        "lunr": "^2.3.9",
+        "marked": "^2.1.1",
+        "minimatch": "^3.0.0",
+        "progress": "^2.0.3",
+        "shiki": "^0.9.3",
+        "typedoc-default-themes": "^0.12.10"
+      },
+      "bin": {
+        "typedoc": "bin/typedoc"
+      },
+      "engines": {
+        "node": ">= 12.20.0"
+      },
+      "peerDependencies": {
+        "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x"
+      }
+    },
+    "node_modules/typedoc-default-themes": {
+      "version": "0.12.10",
+      "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz",
+      "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
     "node_modules/typescript": {
       "version": "4.2.4",
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
@@ -10602,6 +10732,19 @@
         "node": ">=4.2.0"
       }
     },
+    "node_modules/uglify-js": {
+      "version": "3.13.9",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.9.tgz",
+      "integrity": "sha512-wZbyTQ1w6Y7fHdt8sJnHfSIuWeDgk6B5rCb4E/AM6QNNPbOMIZph21PW5dRB3h7Df0GszN+t7RuUH6sWK5bF0g==",
+      "dev": true,
+      "optional": true,
+      "bin": {
+        "uglifyjs": "bin/uglifyjs"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
     "node_modules/unicode-canonical-property-names-ecmascript": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
@@ -10801,6 +10944,12 @@
         "spdx-expression-parse": "^3.0.0"
       }
     },
+    "node_modules/vscode-textmate": {
+      "version": "5.4.0",
+      "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.4.0.tgz",
+      "integrity": "sha512-c0Q4zYZkcLizeYJ3hNyaVUM2AA8KDhNCA3JvXY8CeZSJuBdAy3bAvSbv46RClC4P3dSO9BdwhnKEx2zOo6vP/w==",
+      "dev": true
+    },
     "node_modules/w3c-hr-time": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
@@ -11160,6 +11309,12 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/wordwrap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+      "dev": true
+    },
     "node_modules/wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -16273,9 +16428,9 @@
       "dev": true
     },
     "glob": {
-      "version": "7.1.6",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
-      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "version": "7.1.7",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+      "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
       "dev": true,
       "requires": {
         "fs.realpath": "^1.0.0",
@@ -16310,6 +16465,27 @@
       "dev": true,
       "optional": true
     },
+    "handlebars": {
+      "version": "4.7.7",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
+      "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
+      "dev": true,
+      "requires": {
+        "minimist": "^1.2.5",
+        "neo-async": "^2.6.0",
+        "source-map": "^0.6.1",
+        "uglify-js": "^3.1.4",
+        "wordwrap": "^1.0.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
     "has": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -18578,6 +18754,12 @@
         "yallist": "^4.0.0"
       }
     },
+    "lunr": {
+      "version": "2.3.9",
+      "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
+      "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
+      "dev": true
+    },
     "make-dir": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -18625,6 +18807,12 @@
         "object-visit": "^1.0.0"
       }
     },
+    "marked": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.1.tgz",
+      "integrity": "sha512-5XFS69o9CzDpQDSpUYC+AN2xvq8yl1EGa5SG/GI1hP78/uTeo3PDfiDNmsUyiahpyhToDDJhQk7fNtJsga+KVw==",
+      "dev": true
+    },
     "merge-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -18945,6 +19133,32 @@
         "mimic-fn": "^2.1.0"
       }
     },
+    "onigasm": {
+      "version": "2.2.5",
+      "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz",
+      "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==",
+      "dev": true,
+      "requires": {
+        "lru-cache": "^5.1.1"
+      },
+      "dependencies": {
+        "lru-cache": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+          "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+          "dev": true,
+          "requires": {
+            "yallist": "^3.0.2"
+          }
+        },
+        "yallist": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+          "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+          "dev": true
+        }
+      }
+    },
     "optionator": {
       "version": "0.8.3",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
@@ -19137,6 +19351,12 @@
       "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
       "dev": true
     },
+    "progress": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+      "dev": true
+    },
     "prompts": {
       "version": "2.4.0",
       "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz",
@@ -19605,6 +19825,16 @@
       "dev": true,
       "optional": true
     },
+    "shiki": {
+      "version": "0.9.3",
+      "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.3.tgz",
+      "integrity": "sha512-NEjg1mVbAUrzRv2eIcUt3TG7X9svX7l3n3F5/3OdFq+/BxUdmBOeKGiH4icZJBLHy354Shnj6sfBTemea2e7XA==",
+      "dev": true,
+      "requires": {
+        "onigasm": "^2.2.5",
+        "vscode-textmate": "^5.2.0"
+      }
+    },
     "signal-exit": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
@@ -20368,12 +20598,42 @@
         "is-typedarray": "^1.0.0"
       }
     },
+    "typedoc": {
+      "version": "0.21.0",
+      "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.0.tgz",
+      "integrity": "sha512-InmPBVlpOXptIkg/WnsQhbGYhv9cuDh/cRACUSautQ0QwcJPLAK2kHcfP0Pld6z/NiDvHc159fMq2qS+b/ALUw==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.7",
+        "handlebars": "^4.7.7",
+        "lodash": "^4.17.21",
+        "lunr": "^2.3.9",
+        "marked": "^2.1.1",
+        "minimatch": "^3.0.0",
+        "progress": "^2.0.3",
+        "shiki": "^0.9.3",
+        "typedoc-default-themes": "^0.12.10"
+      }
+    },
+    "typedoc-default-themes": {
+      "version": "0.12.10",
+      "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz",
+      "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==",
+      "dev": true
+    },
     "typescript": {
       "version": "4.2.4",
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
       "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==",
       "dev": true
     },
+    "uglify-js": {
+      "version": "3.13.9",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.9.tgz",
+      "integrity": "sha512-wZbyTQ1w6Y7fHdt8sJnHfSIuWeDgk6B5rCb4E/AM6QNNPbOMIZph21PW5dRB3h7Df0GszN+t7RuUH6sWK5bF0g==",
+      "dev": true,
+      "optional": true
+    },
     "unicode-canonical-property-names-ecmascript": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
@@ -20537,6 +20797,12 @@
         "spdx-expression-parse": "^3.0.0"
       }
     },
+    "vscode-textmate": {
+      "version": "5.4.0",
+      "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.4.0.tgz",
+      "integrity": "sha512-c0Q4zYZkcLizeYJ3hNyaVUM2AA8KDhNCA3JvXY8CeZSJuBdAy3bAvSbv46RClC4P3dSO9BdwhnKEx2zOo6vP/w==",
+      "dev": true
+    },
     "w3c-hr-time": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
@@ -20829,6 +21095,12 @@
       "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
       "dev": true
     },
+    "wordwrap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+      "dev": true
+    },
     "wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",

+ 2 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "telegram",
-  "version": "1.7.7",
+  "version": "1.7.9",
   "description": "NodeJS MTProto API Telegram client library,",
   "main": "index.js",
   "types": "index.d.ts",
@@ -35,6 +35,7 @@
     "raw-loader": "^4.0.2",
     "ts-loader": "^8.0.16",
     "ts-node": "^9.1.1",
+    "typedoc": "^0.21.0",
     "typescript": "^4.2.4",
     "webpack": "^5.21.2",
     "webpack-cli": "^4.5.0"

+ 0 - 1
tsconfig.json

@@ -7,7 +7,6 @@
     "inlineSourceMap": true,
     "downlevelIteration": true,
     "allowJs": true,
-    "skipLibCheck": true,
     "esModuleInterop": true,
     "allowSyntheticDefaultImports": true,
     "strict": true,

+ 16 - 0
type_doc.js

@@ -0,0 +1,16 @@
+module.exports = {
+  exclude: [
+    "**/crypto/**/*",
+    "**/errors/**/*",
+    "**/network/**/*",
+    "**/example/**/*",
+    "**/extensions/**/*",
+    "**/*+(Password|Version|RequestIter|entityCache).ts",
+    "**/*.js"
+  ],
+  sort: [
+    "source-order"
+  ],
+  excludeExternals: true,
+  excludePrivate: true
+};

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません