1
0
Эх сурвалжийг харах

Add delete message helper method
Better error messages

painor 4 жил өмнө
parent
commit
57f94a355d

+ 13 - 2
gramjs/Utils.ts

@@ -7,6 +7,17 @@ import mime from "mime-types";
 import type { ParseInterface } from "./client/messageParse";
 import { MarkdownParser } from "./extensions/markdown";
 import { CustomFile } from "./client/uploads";
+
+/**
+ * Turns the given iterable into chunks of the specified size,
+ * which is 100 by default since that's what Telegram uses the most.
+ */
+export function* chunks<T>(arr: T[], size = 100): Generator<T[]> {
+    for (let i = 0; i < arr.length; i += size) {
+        yield arr.slice(i, i + size);
+    }
+}
+
 import TypeInputFile = Api.TypeInputFile;
 import { HTMLParser } from "./extensions/html";
 
@@ -32,7 +43,7 @@ const VALID_USERNAME_RE = new RegExp(
 
 function _raiseCastFail(entity: EntityLike, target: any): never {
     let toWrite = entity;
-    if (typeof entity === "object") {
+    if (typeof entity === "object" && "className" in entity) {
         toWrite = entity.className;
     }
     throw new Error(`Cannot cast ${toWrite} to any kind of ${target}`);
@@ -47,7 +58,7 @@ function _raiseCastFail(entity: EntityLike, target: any): never {
  cannot be used for general purposes, and thus is not returned to avoid
  any issues which can derive from invalid access hashes.
 
- Note that ``check_hash`` **is ignored** if an input peer is already
+ Note that ``checkHash`` **is ignored** if an input peer is already
  passed since in that case we assume the user knows what they're doing.
  This is key to getting entities by explicitly passing ``hash = 0``.
 

+ 1 - 1
gramjs/Version.ts

@@ -1 +1 @@
-export const version = "1.7.12";
+export const version = "1.7.15";

+ 38 - 2
gramjs/client/TelegramClient.ts

@@ -11,7 +11,7 @@ import * as uploadMethods from "./uploads";
 import * as userMethods from "./users";
 import * as chatMethods from "./chats";
 import * as dialogMethods from "./dialogs";
-import type { ButtonLike, Entity, EntityLike } from "../define";
+import type { ButtonLike, Entity, EntityLike, MessageIDLike } from "../define";
 import { Api } from "../tl";
 import { sanitizeParseMode } from "../Utils";
 import type { EventBuilder } from "../events/common";
@@ -659,6 +659,38 @@ export class TelegramClient extends TelegramBaseClient {
         return messageMethods.editMessage(this, entity, editMessageParams);
     }
 
+    /**
+     * Deletes the given messages, optionally "for everyone".
+     *
+     * See also {@link Message.delete}`.
+     *
+     * @remarks This method does **not** validate that the message IDs belong to the chat that you passed! It's possible for the method to delete messages from different private chats and small group chats at once, so make sure to pass the right IDs.
+     * @param entity - From who the message will be deleted. This can actually be `undefined` for normal chats, but **must** be present for channels and megagroups.
+     * @param messageIds - The IDs (or ID) or messages to be deleted.
+     * @param revoke - Whether the message should be deleted for everyone or not.
+     * By default it has the opposite behaviour of official clients,
+     * and it will delete the message for everyone.
+     * Disabling this has no effect on channels or megagroups,
+     * since it will unconditionally delete the message for everyone.
+     * @return
+     * A list of {@link AffectedMessages}, each item being the result for the delete calls of the messages in chunks of 100 each.
+     * @example
+     *  ```ts
+     *  await client.deleteMessages(chat, messages);
+     *
+     *  await client.deleteMessages(chat, messages, {revoke:false});
+     *  ```
+     */
+    deleteMessages(
+        entity: EntityLike | undefined,
+        messageIds: MessageIDLike[],
+        { revoke = true }
+    ) {
+        return messageMethods.deleteMessages(this, entity, messageIds, {
+            revoke: revoke,
+        });
+    }
+
     //endregion
     //region dialogs
 
@@ -1080,7 +1112,11 @@ export class TelegramClient extends TelegramBaseClient {
         if (
             !(await this._sender.connect(
                 connection,
-                _dispatchUpdate.bind(this)
+                (updateState: UpdateConnectionState) => {
+                    _dispatchUpdate(this, {
+                        update: updateState,
+                    });
+                }
             ))
         ) {
             return;

+ 54 - 0
gramjs/client/messages.ts

@@ -849,4 +849,58 @@ export async function editMessage(
     return client._getResponseMessage(request, result, entity) as Message;
 }
 
+/** @hidden */
+export async function deleteMessages(
+    client: TelegramClient,
+    entity: EntityLike | undefined,
+    messageIds: MessageIDLike[],
+    { revoke = false }
+) {
+    let ty = _EntityType.USER;
+    if (entity) {
+        entity = await client.getInputEntity(entity);
+        ty = _entityType(entity);
+    }
+    const ids: number[] = [];
+    for (const messageId of messageIds) {
+        if (
+            messageId instanceof Api.Message ||
+            messageId instanceof Api.MessageService ||
+            messageId instanceof Api.MessageEmpty
+        ) {
+            ids.push(messageId.id);
+        } else if (typeof messageId === "number") {
+            ids.push(messageId);
+        } else {
+            throw new Error(`Cannot convert ${messageId} to an integer`);
+        }
+    }
+    const results = [];
+
+    if (ty == _EntityType.CHANNEL) {
+        for (const chunk of utils.chunks(ids)) {
+            results.push(
+                client.invoke(
+                    new Api.channels.DeleteMessages({
+                        channel: entity,
+                        id: chunk,
+                    })
+                )
+            );
+        }
+    } else {
+        for (const chunk of utils.chunks(ids)) {
+            results.push(
+                client.invoke(
+                    new Api.messages.DeleteMessages({
+                        id: chunk,
+                        revoke: revoke,
+                    })
+                )
+            );
+        }
+    }
+    return Promise.all(results);
+}
+
 // TODO do the rest

+ 5 - 2
gramjs/client/uploads.ts

@@ -1,4 +1,5 @@
 import { Api } from "../tl";
+import { Message } from "../tl/custom/message";
 
 import { TelegramClient } from "./TelegramClient";
 import { generateRandomBytes, readBigIntFromBuffer, sleep } from "../Helpers";
@@ -317,7 +318,9 @@ async function _fileToMedia(
             }
         }
         if (!createdFile) {
-            throw new Error(`Could not create file from ${JSON.stringify(file)}`);
+            throw new Error(
+                `Could not create file from ${JSON.stringify(file)}`
+            );
         }
         fileHandle = await uploadFile(client, {
             file: createdFile,
@@ -481,7 +484,7 @@ export async function sendFile(
     });
     // todo get message
     const result = client.invoke(request);
-    return client._getResponseMessage(request, result, entity);
+    return client._getResponseMessage(request, result, entity) as Message;
 }
 
 function fileToBuffer(file: File | CustomFile) {

+ 4 - 2
gramjs/client/users.ts

@@ -23,7 +23,9 @@ export async function invoke<R extends Api.AnyRequest>(
         throw new Error("You can only invoke MTProtoRequests");
     }
     if (client._sender == undefined) {
-        throw new Error("Cannot send requests while disconnected");
+        throw new Error(
+            "Cannot send requests while disconnected. You need to call .connect()"
+        );
     }
     await request.resolve(client, utils);
     client._lastRequest = new Date().getTime();
@@ -304,7 +306,7 @@ export async function getInputEntity(
         }
     }
     throw new Error(
-        `Could not find the input entity for ${peer}.
+        `Could not find the input entity for ${JSON.stringify(peer)}.
          Please read https://` +
             "docs.telethon.dev/en/latest/concepts/entities.html to" +
             " find out more details."

+ 3 - 1
gramjs/network/MTProtoSender.ts

@@ -271,7 +271,9 @@ export class MTProtoSender {
      */
     send(request: Api.AnyRequest): any {
         if (!this._userConnected) {
-            throw new Error("Cannot send requests while disconnected");
+            throw new Error(
+                "Cannot send requests while disconnected. You need to call .connect()"
+            );
         }
         const state = new RequestState(request);
         this._sendQueue.append(state);

+ 6 - 3
gramjs/tl/api.js

@@ -112,7 +112,7 @@ function extractParams(fileContent) {
     return [constructors, functions];
 }
 
-function argToBytes(x, type) {
+function argToBytes(x, type, argName) {
     switch (type) {
         case "int":
             const i = Buffer.alloc(4);
@@ -141,6 +141,9 @@ function argToBytes(x, type) {
         case "date":
             return serializeDate(x);
         default:
+            if (x === undefined || typeof x.getBytes !== "function") {
+                throw new Error(`Required object ${argName} is undefined`);
+            }
             return x.getBytes();
     }
 }
@@ -434,7 +437,7 @@ function createClasses(classesType, params) {
                                 l,
                                 Buffer.concat(
                                     this[arg].map((x) =>
-                                        argToBytes(x, argsConfig[arg].type)
+                                        argToBytes(x, argsConfig[arg].type, arg)
                                     )
                                 )
                             );
@@ -465,7 +468,7 @@ function createClasses(classesType, params) {
                             }
                         } else {
                             buffers.push(
-                                argToBytes(this[arg], argsConfig[arg].type)
+                                argToBytes(this[arg], argsConfig[arg].type, arg)
                             );
 
                             if (

+ 9 - 7
gramjs/tl/custom/message.ts

@@ -782,15 +782,17 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
         return this._client.editMessage((await this.getInputChat())!, param);
     }
 
-    // TODO add delete messages
-    /*
-    async delete(args) {
+    async delete({ revoke = false }) {
         if (this._client) {
-            args.entity = await this.getInputChat();
-            args.messages = [this.id];
-            return this._client.deleteMessages(args);
+            return this._client.deleteMessages(
+                await this.getInputChat(),
+                [this.id],
+                {
+                    revoke,
+                }
+            );
         }
-    }*/
+    }
 
     async downloadMedia(params: DownloadFileParams) {
         if (this._client) return this._client.downloadMedia(this, params);