Forráskód Böngészése

Add friendly method for marking messages as read (#232)

* refactor: _pin function

- Move message param validate undefined before call client.getInputEntity so it doesn't make unnecessary network call.

* refactor: add function overloads

- Add client.pinMessage function overloads
- Add client.unpinMessage function overloads
- Add @remark to client.getEntity document

* feat: Add method for marks messages as read

- Add client.sendReadAcknowledge method
- Add Message.markRead convenience method
- Add document for client.sendReadAcknowledge

* refactor: rename client.sendReadAcknowledge to client.markAsRead

* refactor: rename markAsRead method
Man Nguyen 3 éve
szülő
commit
1eb63397d9
3 módosított fájl, 138 hozzáadás és 34 törlés
  1. 63 4
      gramjs/client/TelegramClient.ts
  2. 63 9
      gramjs/client/messages.ts
  3. 12 21
      gramjs/tl/custom/message.ts

+ 63 - 4
gramjs/client/TelegramClient.ts

@@ -796,7 +796,17 @@ export class TelegramClient extends TelegramBaseClient {
      */
     pinMessage(
         entity: EntityLike,
-        message?: MessageIDLike,
+        message?: undefined,
+        pinMessageParams?: messageMethods.UpdatePinMessageParams
+    ): Promise<Api.messages.AffectedHistory>;
+    pinMessage(
+        entity: EntityLike,
+        message: MessageIDLike,
+        pinMessageParams?: messageMethods.UpdatePinMessageParams
+    ): Promise<Api.Message>;
+    pinMessage(
+        entity: EntityLike,
+        message?: any,
         pinMessageParams?: messageMethods.UpdatePinMessageParams
     ) {
         return messageMethods.pinMessage(
@@ -831,7 +841,17 @@ export class TelegramClient extends TelegramBaseClient {
      */
     unpinMessage(
         entity: EntityLike,
-        message?: MessageIDLike,
+        message?: undefined,
+        pinMessageParams?: messageMethods.UpdatePinMessageParams
+    ): Promise<Api.messages.AffectedHistory>;
+    unpinMessage(
+        entity: EntityLike,
+        message: MessageIDLike,
+        pinMessageParams?: messageMethods.UpdatePinMessageParams
+    ): Promise<undefined>;
+    unpinMessage(
+        entity: EntityLike,
+        message?: any,
         unpinMessageParams?: messageMethods.UpdatePinMessageParams
     ) {
         return messageMethods.unpinMessage(
@@ -842,6 +862,44 @@ export class TelegramClient extends TelegramBaseClient {
         );
     }
 
+    /**
+     * Marks messages as read and optionally clears mentions. <br/>
+     * This effectively marks a message as read (or more than one) in the given conversation. <br />
+     * If a message or maximum ID is provided, all the messages up to and
+     * including such ID will be marked as read (for all messages whose ID ≤ max_id).
+     *
+     * See also {@link Message.markRead}`.
+     *
+     * @remarks If neither message nor maximum ID are provided, all messages will be marked as read by assuming that `max_id = 0`.
+     * @param entity - The chat where the message should be pinned.
+     * @param message - The message or the message ID to pin. If it's `undefined`, all messages will be unpinned instead.
+     * @param markAsReadParams - see {@link MarkAsReadParams}.
+     * @return boolean
+     * @example
+     *  ```ts
+     *  // using a Message object
+     *  const message = await client.sendMessage(chat, 'GramJS is awesome!');
+     *  await client.markAsRead(chat, message)
+     *  // ...or using the int ID of a Message
+     *  await client.markAsRead(chat, message.id);
+     *
+     *  // ...or passing a list of messages to mark as read
+     *  await client.markAsRead(chat, messages)
+     *  ```
+     */
+    markAsRead(
+        entity: EntityLike,
+        message?: MessageIDLike | MessageIDLike[],
+        markAsReadParams?: messageMethods.MarkAsReadParams
+    ) {
+        return messageMethods.markAsRead(
+            this,
+            entity,
+            message,
+            markAsReadParams
+        );
+    }
+
     //endregion
     //region dialogs
 
@@ -1012,7 +1070,7 @@ export class TelegramClient extends TelegramBaseClient {
     // region uploads
     /**
      * Uploads a file to Telegram's servers, without sending it.
-     * @remark generally it's better to use {@link sendFile} instead.
+     * @remarks generally it's better to use {@link sendFile} instead.
      * This method returns a handle (an instance of InputFile or InputFileBig, as required) which can be later used before it expires (they are usable during less than a day).<br/>
      * Uploading a file will simply return a "handle" to the file stored remotely in the Telegram servers,
      * which can be later used on. This will not upload the file to your own chat or any chat at all.
@@ -1071,7 +1129,7 @@ export class TelegramClient extends TelegramBaseClient {
      *  lastName:'',
      *  vcard:''
      *  }))
-     ```
+     * ```
      */
     sendFile(
         entity: EntityLike,
@@ -1150,6 +1208,7 @@ export class TelegramClient extends TelegramBaseClient {
     /**
      * Turns the given entity into a valid Telegram {@link Api.User}, {@link Api.Chat} or {@link Api.Channel}.<br/>
      * You can also pass a list or iterable of entities, and they will be efficiently fetched from the network.
+     * @remarks Telegram does not allow to get user profile by integer id if current client had never "saw" it.
      * @param entity - If a username is given, the username will be resolved making an API call every time.<br/>
      * Resolving usernames is an expensive operation and will start hitting flood waits around 50 usernames in a short period of time.<br/>
      * <br/>

+ 63 - 9
gramjs/client/messages.ts

@@ -583,6 +583,20 @@ export interface UpdatePinMessageParams {
     pmOneSide?: boolean;
 }
 
+/** Interface for mark message as read */
+export interface MarkAsReadParams {
+    /**
+     * Until which message should the read acknowledge be sent for. <br />
+     * This has priority over the `message` parameter.
+     */
+    maxId?: number;
+    /**
+     * Whether the mention badge should be cleared (so that there are no more mentions) or not for the given entity. <br />
+     * If no message is provided, this will be the only action taken.
+     */
+    clearMentions?: boolean;
+}
+
 /** @hidden */
 export function iterMessages(
     client: TelegramClient,
@@ -1040,19 +1054,18 @@ export async function _pin(
     pmOneSide: boolean = false
 ) {
     message = utils.getMessageId(message) || 0;
-    entity = await client.getInputEntity(entity);
-    let request:
-        | Api.messages.UnpinAllMessages
-        | Api.messages.UpdatePinnedMessage;
 
     if (message === 0) {
-        request = new Api.messages.UnpinAllMessages({
-            peer: entity,
-        });
-        return await client.invoke(request);
+        return await client.invoke(
+            new Api.messages.UnpinAllMessages({
+                peer: entity,
+            })
+        );
     }
 
-    request = new Api.messages.UpdatePinnedMessage({
+    entity = await client.getInputEntity(entity);
+
+    const request = new Api.messages.UpdatePinnedMessage({
         silent: !notify,
         unpin,
         pmOneside: pmOneSide,
@@ -1079,4 +1092,45 @@ export async function _pin(
     return client._getResponseMessage(request, result, entity) as Api.Message;
 }
 
+/** @hidden */
+export async function markAsRead(
+    client: TelegramClient,
+    entity: EntityLike,
+    message?: MessageIDLike | MessageIDLike[],
+    markAsReadParams?: MarkAsReadParams
+): Promise<boolean> {
+    let maxId: number = markAsReadParams?.maxId || 0;
+    const maxIdIsUndefined = markAsReadParams?.maxId === undefined;
+    if (maxIdIsUndefined) {
+        if (message) {
+            if (Array.isArray(message)) {
+                maxId = Math.max(
+                    ...message.map((v) => utils.getMessageId(v) as number)
+                );
+            } else {
+                maxId = utils.getMessageId(message) as number;
+            }
+        }
+    }
+
+    entity = await client.getInputEntity(entity);
+    if (markAsReadParams && !markAsReadParams.clearMentions) {
+        await client.invoke(new Api.messages.ReadMentions({ peer: entity }));
+        if (maxIdIsUndefined && message === undefined) {
+            return true;
+        }
+    }
+
+    if (_entityType(entity) === _EntityType.CHANNEL) {
+        return await client.invoke(
+            new Api.channels.ReadHistory({ channel: entity, maxId })
+        );
+    } else {
+        await client.invoke(
+            new Api.messages.ReadHistory({ peer: entity, maxId })
+        );
+        return true;
+    }
+}
+
 // TODO do the rest

+ 12 - 21
gramjs/tl/custom/message.ts

@@ -850,6 +850,18 @@ export class CustomMessage extends SenderGetter {
             return this._client.downloadMedia(this as any, params);
     }
 
+    async markAsRead() {
+        if (this._client) {
+            const entity = await this.getInputChat();
+            if (entity === undefined) {
+                throw Error(
+                    `Failed to mark message id ${this.id} as read due to cannot get input chat.`
+                );
+            }
+            return this._client.markAsRead(entity, this.id);
+        }
+    }
+
     /* TODO doesn't look good enough.
     async click({ i = undefined, j = undefined, text = undefined, filter = undefined, data = undefined }) {
         if (!this._client) return;
@@ -905,27 +917,6 @@ export class CustomMessage extends SenderGetter {
         if (!j) return this._buttonsFlat[i].click();
         else return this._buttons[i][j].click();
     }
-*/
-    /* TODO add missing friendly functions
-    async markRead() {
-        if (this._client) {
-            await this._client.sendReadAcknowledge({
-                entity: await this.getInputChat(),
-                maxId: this.id
-            });
-        }
-    }
-
-    async pin(notify = false) {
-        if (this._client) {
-            await this._client.pinMessage({
-                entity: await this.getInputChat(),
-                message: this.id,
-                notify: notify
-            });
-        }
-    }
-*/
     /*
         _setButtons(chat, bot) {
             // TODO: Implement MessageButton