Explorar el Código

add buttons to messages
add click method

painor hace 3 años
padre
commit
1176753f30

+ 1 - 1
README.md

@@ -31,7 +31,7 @@ You can find all methods and types at [gram.js.org](https://gram.js.org).
 
 ## Docs
 
-Youu can find the docs at [painor.gitbook.io/gramjs](https://painor.gitbook.io/gramjs) visit [gram.js.org](https://gram.js.org) to see all supported API methods and types.
+Youu can find the docs at [painor.gitbook.io/gramjs](https://painor.gitbook.io/gramjs) visit [gram.js.org](https://gram.js.org) and the beta docs [gram.js.org/beta](https://gram.js.org/beta)  to see all supported API methods and types.
 
 ## Asking questions
 

+ 1 - 1
gramjs/Version.ts

@@ -1 +1 @@
-export const version = "2.4.6";
+export const version = "2.5.0";

+ 1 - 1
gramjs/client/TelegramClient.ts

@@ -1165,7 +1165,7 @@ export class TelegramClient extends TelegramBaseClient {
      * const result = await client.invoke(new Api.account.CheckUsername({
      *      username: 'some string here'
      *   }));
-     * console.log("does this username exist?,result);
+     * console.log("does this username exist?",result);
      *
      * ```
      */

+ 14 - 0
gramjs/client/telegramBaseClient.ts

@@ -200,16 +200,24 @@ export abstract class TelegramBaseClient {
         string,
         [ReturnType<typeof setTimeout>, Api.TypeUpdate[]]
     >();
+    /** @hidden */
     private _exportedSenderPromises = new Map<number, Promise<MTProtoSender>>();
+    /** @hidden */
     private _exportedSenderReleaseTimeouts = new Map<
         number,
         ReturnType<typeof setTimeout>
     >();
+    /** @hidden */
     protected _loopStarted: boolean;
+    /** @hidden */
     _reconnecting: boolean;
+    /** @hidden */
     _destroyed: boolean;
+    /** @hidden */
     protected _proxy?: ProxyInterface;
+    /** @hidden */
     _semaphore: Semaphore;
+    /** @hidden */
     _securityChecks: boolean;
     constructor(
         session: string | Session,
@@ -377,11 +385,13 @@ export abstract class TelegramBaseClient {
         this._eventBuilders = [];
     }
 
+    /** @hidden */
     async _authKeyCallback(authKey: AuthKey, dcId: number) {
         this.session.setAuthKey(authKey, dcId);
         await this.session.save();
     }
 
+    /** @hidden */
     async _cleanupExportedSender(dcId: number) {
         if (this.session.dcId !== dcId) {
             this.session.setAuthKey(undefined, dcId);
@@ -391,6 +401,7 @@ export abstract class TelegramBaseClient {
         await sender?.disconnect();
     }
 
+    /** @hidden */
     async _connectSender(sender: MTProtoSender, dcId: number) {
         // if we don't already have an auth key we want to use normal DCs not -1
         const dc = await this.getDC(dcId, !!sender.authKey.getKey());
@@ -446,6 +457,7 @@ export abstract class TelegramBaseClient {
         }
     }
 
+    /** @hidden */
     async _borrowExportedSender(
         dcId: number,
         shouldReconnect?: boolean,
@@ -496,6 +508,7 @@ export abstract class TelegramBaseClient {
         return sender;
     }
 
+    /** @hidden */
     _createExportedSender(dcId: number) {
         return new MTProtoSender(this.session.getAuthKey(dcId), {
             logger: this._log,
@@ -512,6 +525,7 @@ export abstract class TelegramBaseClient {
         });
     }
 
+    /** @hidden */
     getSender(dcId: number): Promise<MTProtoSender> {
         return dcId
             ? this._borrowExportedSender(dcId)

+ 1 - 1
gramjs/tl/apiTl.js

@@ -1370,4 +1370,4 @@ stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
 stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel = stats.MegagroupStats;
 stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
 stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
-`;
+`;

+ 350 - 116
gramjs/tl/custom/message.ts

@@ -5,7 +5,7 @@ import type { TelegramClient } from "../..";
 import { ChatGetter } from "./chatGetter";
 import * as utils from "../../Utils";
 import { Forward } from "./forward";
-import type { File } from "./file";
+import { File } from "./file";
 import {
     EditMessageParams,
     SendMessageParams,
@@ -17,6 +17,7 @@ import { betterConsoleLog, returnBigInt } from "../../Helpers";
 import { _selfId } from "../../client/users";
 import bigInt, { BigInteger } from "big-integer";
 import { LogLevel } from "../../extensions/Logger";
+import { MessageButton } from "./messageButton";
 
 interface MessageBaseInterface {
     id: any;
@@ -53,6 +54,110 @@ interface MessageBaseInterface {
     _entities?: Map<string, Entity>;
 }
 
+/**
+ * Interface for clicking a message button.
+ * Calls `SendVote` with the specified poll option
+ * or `button.click <MessageButton.click>`
+ * on the specified button.
+ * Does nothing if the message is not a poll or has no buttons.
+ * @example
+ * ```ts
+ *    # Click the first button
+ *    await message.click(0)
+ *
+ *    # Click some row/column
+ *    await message.click(row, column)
+ *
+ *    # Click by text
+ *    await message.click(text='👍')
+ *
+ *    # Click by data
+ *    await message.click(data=b'payload')
+ *
+ *    # Click on a button requesting a phone
+ *    await message.click(0, share_phone=True)
+ * ```
+ */
+
+export interface ButtonClickParam {
+    /** Clicks the i'th button or poll option (starting from the index 0).
+     For multiple-choice polls, a list with the indices should be used.
+     Will ``raise IndexError`` if out of bounds. Example:
+
+     >>> message = ...  # get the message somehow
+     >>> # Clicking the 3rd button
+     >>> # [button1] [button2]
+     >>> # [     button3     ]
+     >>> # [button4] [button5]
+     >>> await message.click(2)  # index
+     . */
+    i?: number | number[];
+    /**
+     * Clicks the button at position (i, j), these being the
+     * indices for the (row, column) respectively. Example:
+
+     >>> # Clicking the 2nd button on the 1st row.
+     >>> # [button1] [button2]
+     >>> # [     button3     ]
+     >>> # [button4] [button5]
+     >>> await message.click(0, 1)  # (row, column)
+
+     This is equivalent to ``message.buttons[0][1].click()``.
+     */
+    j?: number;
+    /**                 Clicks the first button or poll option with the text "text". This may
+     also be a callable, like a ``new RegExp('foo*').test``,
+     and the text will be passed to it.
+
+     If you need to select multiple options in a poll,
+     pass a list of indices to the ``i`` parameter.
+     */
+    text?: string | Function;
+    /** Clicks the first button or poll option for which the callable
+     returns `True`. The callable should accept a single
+     `MessageButton <messagebutton.MessageButton>`
+     or `PollAnswer <PollAnswer>` argument.
+     If you need to select multiple options in a poll,
+     pass a list of indices to the ``i`` parameter.
+     */
+    filter?: Function;
+    /** This argument overrides the rest and will not search any
+     buttons. Instead, it will directly send the request to
+     behave as if it clicked a button with said data. Note
+     that if the message does not have this data, it will
+     ``DATA_INVALID``.
+     */
+    data?: Buffer;
+    /** When clicking on a keyboard button requesting a phone number
+     (`KeyboardButtonRequestPhone`), this argument must be
+     explicitly set to avoid accidentally sharing the number.
+
+     It can be `true` to automatically share the current user's
+     phone, a string to share a specific phone number, or a contact
+     media to specify all details.
+
+     If the button is pressed without this, `new Error("Value Error")` is raised.
+     */
+    sharePhone?: boolean | string | Api.InputMediaContact;
+    /** When clicking on a keyboard button requesting a geo location
+     (`KeyboardButtonRequestGeoLocation`), this argument must
+     be explicitly set to avoid accidentally sharing the location.
+
+     It must be a `list` of `float` as ``(longitude, latitude)``,
+     or a :tl:`InputGeoPoint` instance to avoid accidentally using
+     the wrong roder.
+
+     If the button is pressed without this, `ValueError` is raised.
+     */
+    shareGeo?: [number, number] | Api.InputMediaGeoPoint;
+    /**When clicking certain buttons (such as BotFather's confirmation
+     button to transfer ownership), if your account has 2FA enabled,
+     you need to provide your account's password. Otherwise,
+     `PASSWORD_HASH_INVALID` is raised.
+     */
+    password?: string;
+}
+
 /**
  * This custom class aggregates both {@link Api.Message} and {@link Api.MessageService} to ease accessing their members.<br/>
  * <br/>
@@ -228,9 +333,9 @@ export class CustomMessage extends SenderGetter {
     /** @hidden */
     _replyMessage?: Api.Message;
     /** @hidden */
-    _buttons?: undefined;
+    _buttons?: MessageButton[][];
     /** @hidden */
-    _buttonsFlat?: undefined;
+    _buttonsFlat?: MessageButton[];
     /** @hidden */
     _buttonsCount?: number;
     /** @hidden */
@@ -526,58 +631,72 @@ export class CustomMessage extends SenderGetter {
         this._actionEntities = msg._actionEntities;
     }
 
-    /*
-            get buttons() {
-                if (!this._buttons && this.replyMarkup) {
-                    if (!this.inputChat) {
-                        return undefined
-                    }
-
-                    const bot = this._neededMarkupBot();
-                    if (!bot) {
-                        this._setButtons(this._inputChat, bot)
-                    }
-                }
-                return this._buttons
+    /**
+     * Returns a list of lists of `MessageButton <MessageButton>`, if any.
+     * Otherwise, it returns `undefined`.
+     */
+    get buttons() {
+        if (!this._buttons && this.replyMarkup) {
+            if (!this.inputChat) {
+                return;
             }
-            async getButtons() {
-                if (!this.buttons && this.replyMarkup) {
-                    const chat = await this.getInputChat();
-                    if (!chat) return;
-                    let bot = this._neededMarkupBot();
-                    if (!bot) {
-                        await this._reloadMessage();
-                        bot = this._neededMarkupBot()
-                    }
-                    this._setButtons(chat, bot)
-                }
-                return this._buttons
+            try {
+                const bot = this._neededMarkupBot();
+                this._setButtons(this.inputChat, bot);
+            } catch (e) {
+                return;
             }
-        /
-            get buttonCount() {
-                if (!this._buttonsCount) {
-                    if ((this.replyMarkup instanceof Api.ReplyInlineMarkup) ||
-                        (this.replyMarkup instanceof Api.ReplyKeyboardMarkup)) {
-                        this._buttonsCount = (this.replyMarkup.rows.map((r) => r.buttons.length)).reduce(function (a, b) {
-                            return a + b;
-                        }, 0);
-                    } else {
-                        this._buttonsCount = 0
-                    }
-                }
-                return this._buttonsCount
+        }
+        return this._buttons;
+    }
+
+    /**
+     * Returns `buttons` when that property fails (this is rarely needed).
+     */
+    async getButtons() {
+        if (!this.buttons && this.replyMarkup) {
+            const chat = await this.getInputChat();
+            if (!chat) return;
+            let bot;
+            try {
+                bot = this._neededMarkupBot();
+            } catch (e) {
+                await this._reloadMessage();
+                bot = this._neededMarkupBot();
             }
+            this._setButtons(chat, bot);
+        }
+        return this._buttons;
+    }
 
-            get file() {
-                if (!this._file) {
-                    const media = this.photo || this.document;
-                    if (media) {
-                        this._file = new File(media);
-                    }
-                }
-                return this._file
+    get buttonCount() {
+        if (!this._buttonsCount) {
+            if (
+                this.replyMarkup instanceof Api.ReplyInlineMarkup ||
+                this.replyMarkup instanceof Api.ReplyKeyboardMarkup
+            ) {
+                this._buttonsCount = this.replyMarkup.rows
+                    .map((r) => r.buttons.length)
+                    .reduce(function (a, b) {
+                        return a + b;
+                    }, 0);
+            } else {
+                this._buttonsCount = 0;
+            }
+        }
+        return this._buttonsCount;
+    }
+
+    get file() {
+        if (!this._file) {
+            const media = this.photo || this.document;
+            if (media) {
+                this._file = new File(media);
             }
-    */
+        }
+        return this._file;
+    }
+
     get photo() {
         if (this.media instanceof Api.MessageMediaPhoto) {
             if (this.media.photo instanceof Api.Photo) return this.media.photo;
@@ -863,95 +982,210 @@ export class CustomMessage extends SenderGetter {
         }
     }
 
-    /* TODO doesn't look good enough.
-    async click({ i = undefined, j = undefined, text = undefined, filter = undefined, data = undefined }) {
-        if (!this._client) return;
-
+    async click({
+        i,
+        j,
+        text,
+        filter,
+        data,
+        sharePhone,
+        shareGeo,
+        password,
+    }: ButtonClickParam) {
+        if (!this.client) {
+            return;
+        }
         if (data) {
-            if (!(await this._getInputChat()))
-                return undefined;
-
-            try {
-                return await this._client.invoke(functions.messages.GetBotCallbackAnswerRequest({
-                    peer: this._inputChat,
-                    msgId: this.id,
-                    data: data
-                }));
-            } catch (e) {
-                if (e instanceof errors.BotTimeout)
-                    return undefined;
+            const chat = await this.getInputChat();
+            if (!chat) {
+                return;
             }
+
+            const button = new Api.KeyboardButtonCallback({
+                text: "",
+                data: data,
+            });
+            return await new MessageButton(
+                this.client,
+                button,
+                chat,
+                undefined,
+                this.id
+            ).click({
+                sharePhone: sharePhone,
+                shareGeo: shareGeo,
+                password: password,
+            });
         }
+        if (this.poll) {
+            function findPoll(answers: Api.PollAnswer[]) {
+                if (i != undefined) {
+                    if (Array.isArray(i)) {
+                        const corrects = [];
+                        for (let x = 0; x < i.length; x++) {
+                            corrects.push(answers[x].option);
+                        }
+                        return corrects;
+                    }
+                    return [answers[i].option];
+                }
+                if (text != undefined) {
+                    if (typeof text == "function") {
+                        for (const answer of answers) {
+                            if (text(answer.text)) {
+                                return [answer.option];
+                            }
+                        }
+                    } else {
+                        for (const answer of answers) {
+                            if (answer.text == text) {
+                                return [answer.option];
+                            }
+                        }
+                    }
+                    return;
+                }
+                if (filter != undefined) {
+                    for (const answer of answers) {
+                        if (filter(answer)) {
+                            return [answer.option];
+                        }
+                    }
+                    return;
+                }
+            }
 
-        if ([i, text, filter].filter((x) => !!x) > 1)
-            throw new Error("You can only set either of i, text or filter");
+            const options = findPoll(this.poll.poll.answers) || [];
+            return await this.client.invoke(
+                new Api.messages.SendVote({
+                    peer: this.inputChat,
+                    msgId: this.id,
+                    options: options,
+                })
+            );
+        }
 
-        if (!(await this.getButtons()))
-            return;
+        if (!(await this.getButtons())) {
+            return; // Accessing the property sets this._buttons[_flat]
+        }
 
-        if (text) {
-            if (callable(text)) {
-                for (const button of this._buttonsFlat) {
-                    if (text(button.text)) {
-                        return button.click();
+        function findButton(self: CustomMessage) {
+            if (!self._buttonsFlat || !self._buttons) {
+                return;
+            }
+            if (Array.isArray(i)) {
+                i = i[0];
+            }
+            if (text != undefined) {
+                if (typeof text == "function") {
+                    for (const button of self._buttonsFlat) {
+                        if (text(button.text)) {
+                            return button;
+                        }
+                    }
+                } else {
+                    for (const button of self._buttonsFlat) {
+                        if (button.text == text) {
+                            return button;
+                        }
                     }
                 }
-            } else {
-                for (const button of this._buttonsFlat) {
-                    if (button.text === text) {
-                        return button.click();
+                return;
+            }
+            if (filter != undefined) {
+                for (const button of self._buttonsFlat) {
+                    if (filter(button)) {
+                        return button;
                     }
                 }
+                return;
             }
+            if (i == undefined) {
+                i = 0;
+            }
+            if (j == undefined) {
+                return self._buttonsFlat[i];
+            } else {
+                return self._buttons[i][j];
+            }
+        }
+
+        const button = findButton(this);
+        if (button) {
+            return await button.click({
+                sharePhone: sharePhone,
+                shareGeo: shareGeo,
+                password: password,
+            });
         }
+    }
 
-        if (filter && callable(filter)) {
-            for (const button of this._buttonsFlat) {
-                if (filter(button)) {
-                    return button.click();
+    /**
+     * Helper methods to set the buttons given the input sender and chat.
+     */
+    _setButtons(chat: EntityLike, bot?: EntityLike) {
+        if (
+            this.client &&
+            (this.replyMarkup instanceof Api.ReplyInlineMarkup ||
+                this.replyMarkup instanceof Api.ReplyKeyboardMarkup)
+        ) {
+            this._buttons = [];
+            this._buttonsFlat = [];
+            for (const row of this.replyMarkup.rows) {
+                const tmp = [];
+                for (const button of row.buttons) {
+                    const btn = new MessageButton(
+                        this.client,
+                        button,
+                        chat,
+                        bot,
+                        this.id
+                    );
+                    tmp.push(btn);
+                    this._buttonsFlat.push(btn);
                 }
+                this._buttons.push(tmp);
             }
-            return undefined;
         }
-
-        i = !i ? 0 : i;
-        if (!j) return this._buttonsFlat[i].click();
-        else return this._buttons[i][j].click();
     }
-    /*
-        _setButtons(chat, bot) {
-            // TODO: Implement MessageButton
-            // if (this._client && (this.replyMarkup instanceof types.ReplyInlineMarkup ||
-            //         this.replyMarkup instanceof types.ReplyKeyboardMarkup)) {
-            //     this._buttons = this.replyMarkup.rows.map((row) =>
-            //         row.buttons.map((button) => new Messagebutton(this._client, button, chat, bot, this.id)))
-            // }
-            // this._buttonsFlat = this._buttons.flat()
-        }
 
-        _neededMarkupBot() {
-            if (this._client && !(this.replyMarkup instanceof types.ReplyInlineMarkup ||
-                this.replyMarkup instanceof types.ReplyKeyboardMarkup)) {
-                return undefined;
-            }
+    /**
+     *Returns the input peer of the bot that's needed for the reply markup.
 
-            for (const row of this.replyMarkup.rows) {
-                for (const button of row.buttons) {
-                    if (button instanceof types.KeyboardButtonSwitchInline) {
-                        if (button.samePeer) {
-                            const bot = this._inputSender;
-                            if (!bot) throw new Error("No input sender");
-                            return bot;
-                        } else {
-                            const ent = this._client._entityCache[this.viaBotId];
-                            if (!ent) throw new Error("No input sender");
-                            return ent;
-                        }
+     This is necessary for `KeyboardButtonSwitchInline` since we need
+     to know what bot we want to start. Raises ``Error`` if the bot
+     cannot be found but is needed. Returns `None` if it's not needed.
+     */
+    _neededMarkupBot() {
+        if (!this.client || this.replyMarkup == undefined) {
+            return;
+        }
+        if (
+            !(
+                this.replyMarkup instanceof Api.ReplyInlineMarkup ||
+                this.replyMarkup instanceof Api.ReplyKeyboardMarkup
+            )
+        ) {
+            return;
+        }
+        for (const row of this.replyMarkup.rows) {
+            for (const button of row.buttons) {
+                if (button instanceof Api.KeyboardButtonSwitchInline) {
+                    if (button.samePeer || !this.viaBotId) {
+                        const bot = this._inputSender;
+                        if (!bot) throw new Error("No input sender");
+                        return bot;
+                    } else {
+                        const ent = this.client!._entityCache.get(
+                            this.viaBotId
+                        );
+                        if (!ent) throw new Error("No input sender");
+                        return ent;
                     }
                 }
             }
         }
-    */
+    }
 
     // TODO fix this
 

+ 60 - 17
gramjs/tl/custom/messageButton.ts

@@ -4,13 +4,15 @@ import { Api } from "../api";
 import { Button } from "./button";
 import { inspect } from "util";
 import { betterConsoleLog } from "../../Helpers";
+import { computeCheck } from "../../Password";
 
 export class MessageButton {
     private readonly _client: TelegramClient;
     private readonly _chat: EntityLike;
     public readonly button: ButtonLike;
-    private readonly _bot: EntityLike;
+    private readonly _bot?: EntityLike;
     private readonly _msgId: MessageIDLike;
+
     [inspect.custom]() {
         return betterConsoleLog(this);
     }
@@ -19,7 +21,7 @@ export class MessageButton {
         client: TelegramClient,
         original: ButtonLike,
         chat: EntityLike,
-        bot: EntityLike,
+        bot: EntityLike | undefined,
         msgId: MessageIDLike
     ) {
         this.button = original;
@@ -55,17 +57,62 @@ export class MessageButton {
         }
     }
 
-    async click({ sharePhone = false, shareGeo = [0, 0] }) {
+    /**
+     * Emulates the behaviour of clicking this button.
+
+     If it's a normal `KeyboardButton` with text, a message will be
+     sent, and the sent `Message <Message>` returned.
+
+     If it's an inline `KeyboardButtonCallback` with text and data,
+     it will be "clicked" and the `BotCallbackAnswer` returned.
+
+     If it's an inline `KeyboardButtonSwitchInline` button, the
+     `StartBot` will be invoked and the resulting updates
+     returned.
+
+     If it's a `KeyboardButtonUrl`, the URL of the button will
+     be returned.
+
+     If it's a `KeyboardButtonRequestPhone`, you must indicate that you
+     want to ``sharePhone=True`` in order to share it. Sharing it is not a
+     default because it is a privacy concern and could happen accidentally.
+
+     You may also use ``sharePhone=phone`` to share a specific number, in
+     which case either `str` or `InputMediaContact` should be used.
+
+     If it's a `KeyboardButtonRequestGeoLocation`, you must pass a
+     tuple in ``shareGeo=[longitude, latitude]``. Note that Telegram seems
+     to have some heuristics to determine impossible locations, so changing
+     this value a lot quickly may not work as expected. You may also pass a
+     `InputGeoPoint` if you find the order confusing.
+     */
+    async click({
+        sharePhone = false,
+        shareGeo = [0, 0],
+        password,
+    }: {
+        sharePhone?: boolean | string | Api.InputMediaContact;
+        shareGeo?: [number, number] | Api.InputMediaGeoPoint;
+        password?: string;
+    }) {
         if (this.button instanceof Api.KeyboardButton) {
             return this._client.sendMessage(this._chat, {
                 message: this.button.text,
                 parseMode: undefined,
             });
         } else if (this.button instanceof Api.KeyboardButtonCallback) {
+            let encryptedPassword;
+            if (password != undefined) {
+                const pwd = await this.client.invoke(
+                    new Api.account.GetPassword()
+                );
+                encryptedPassword = await computeCheck(pwd, password);
+            }
             const request = new Api.messages.GetBotCallbackAnswer({
                 peer: this._chat,
                 msgId: this._msgId,
                 data: this.button.data,
+                password: encryptedPassword,
             });
             try {
                 return await this._client.invoke(request);
@@ -105,14 +152,16 @@ export class MessageButton {
                     "cannot click on phone buttons unless sharePhone=true"
                 );
             }
-
-            const me = (await this._client.getMe()) as Api.User;
-            const phoneMedia = new Api.InputMediaContact({
-                phoneNumber: me.phone || "",
-                firstName: me.firstName || "",
-                lastName: me.lastName || "",
-                vcard: "",
-            });
+            if (sharePhone == true || typeof sharePhone == "string") {
+                const me = (await this._client.getMe()) as Api.User;
+                sharePhone = new Api.InputMediaContact({
+                    phoneNumber:
+                        (sharePhone == true ? me.phone : sharePhone) || "",
+                    firstName: me.firstName || "",
+                    lastName: me.lastName || "",
+                    vcard: "",
+                });
+            }
             throw new Error("Not supported for now");
             // TODO
             //return this._client.sendFile(this._chat, phoneMedia);
@@ -122,12 +171,6 @@ export class MessageButton {
                     "cannot click on geo buttons unless shareGeo=[longitude, latitude]"
                 );
             }
-            let geoMedia = new Api.InputMediaGeoPoint({
-                geoPoint: new Api.InputGeoPoint({
-                    lat: shareGeo[0],
-                    long: shareGeo[1],
-                }),
-            });
             throw new Error("Not supported for now");
             // TODO
 

+ 19 - 2
package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "telegram",
-  "version": "2.4.6",
+  "version": "2.5.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "telegram",
-      "version": "2.4.6",
+      "version": "2.5.0",
       "license": "MIT",
       "dependencies": {
         "@cryptography/aes": "^0.1.1",
@@ -44,6 +44,7 @@
         "ts-loader": "^8.0.16",
         "ts-node": "^9.1.1",
         "typedoc": "^0.22.11",
+        "typedoc-plugin-missing-exports": "^0.22.6",
         "typescript": "^4.2.4",
         "webpack": "^5.21.2",
         "webpack-cli": "^4.5.0"
@@ -7624,6 +7625,15 @@
         "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x"
       }
     },
+    "node_modules/typedoc-plugin-missing-exports": {
+      "version": "0.22.6",
+      "resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-0.22.6.tgz",
+      "integrity": "sha512-1uguGQqa+c5f33nWS3v1mm0uAx4Ii1lw4Kx2zQksmYFKNEWTmrmMXbMNBoBg4wu0p4dFCNC7JIWPoRzpNS6pFA==",
+      "dev": true,
+      "peerDependencies": {
+        "typedoc": "0.22.x"
+      }
+    },
     "node_modules/typescript": {
       "version": "4.4.3",
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz",
@@ -14408,6 +14418,13 @@
         "shiki": "^0.10.0"
       }
     },
+    "typedoc-plugin-missing-exports": {
+      "version": "0.22.6",
+      "resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-0.22.6.tgz",
+      "integrity": "sha512-1uguGQqa+c5f33nWS3v1mm0uAx4Ii1lw4Kx2zQksmYFKNEWTmrmMXbMNBoBg4wu0p4dFCNC7JIWPoRzpNS6pFA==",
+      "dev": true,
+      "requires": {}
+    },
     "typescript": {
       "version": "4.4.3",
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz",

+ 3 - 2
package.json

@@ -1,6 +1,6 @@
 {
   "name": "telegram",
-  "version": "2.4.6",
+  "version": "2.5.0",
   "description": "NodeJS/Browser MTProto API Telegram client library,",
   "main": "index.js",
   "types": "index.d.ts",
@@ -48,7 +48,8 @@
     "typedoc": "^0.22.11",
     "typescript": "^4.2.4",
     "webpack": "^5.21.2",
-    "webpack-cli": "^4.5.0"
+    "webpack-cli": "^4.5.0",
+    "typedoc-plugin-missing-exports": "^0.22.6"
   },
   "dependencies": {
     "@cryptography/aes": "^0.1.1",

+ 1 - 10
type_doc.js

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