From 36f5433207b5b836c4d9dede7be625ad752a3d9e Mon Sep 17 00:00:00 2001
From: abdulrahman1s <abdulrahman.8alah@gmail.com>
Date: Mon, 30 May 2022 02:02:49 +0200
Subject: [PATCH] feat(TextBasedChannel): Add #bulkDelete

---
 import.json                                   |   2 +-
 scripts/build_npm.ts                          |   4 +-
 src/managers/MessageManager.ts                | 150 +++++++-----------
 src/structures/DMChannel.ts                   |  20 ++-
 src/structures/GroupChannel.ts                |   7 +
 src/structures/NotesChannel.ts                |  14 +-
 src/structures/TextChannel.ts                 |  14 +-
 src/structures/interfaces/TextBasedChannel.ts |  11 +-
 8 files changed, 117 insertions(+), 105 deletions(-)

diff --git a/import.json b/import.json
index aec5955..1483516 100644
--- a/import.json
+++ b/import.json
@@ -4,6 +4,6 @@
         "events": "https://deno.land/std@0.132.0/node/events.ts",
         "crypto": "https://deno.land/std@0.132.0/node/crypto.ts",
         "collection": "https://esm.sh/@discordjs/collection@0.6.0",
-        "@revoltio/rest": "https://deno.land/x/revoltio_rest@v1.1.0/mod.ts"
+        "@revoltio/rest": "https://deno.land/x/revoltio_rest@v1.1.2/mod.ts"
     }
 }
\ No newline at end of file
diff --git a/scripts/build_npm.ts b/scripts/build_npm.ts
index 9937133..0445b52 100644
--- a/scripts/build_npm.ts
+++ b/scripts/build_npm.ts
@@ -21,9 +21,9 @@ await build({
   },
   importMap: './import.json',
   mappings: {
-    'https://deno.land/x/revoltio_rest@v1.1.0/mod.ts': {
+    'https://deno.land/x/revoltio_rest@v1.1.2/mod.ts': {
       name: '@revoltio/rest',
-      version: '^1.1.0',
+      version: '^1.1.2',
     },
   },
   package: {
diff --git a/src/managers/MessageManager.ts b/src/managers/MessageManager.ts
index 3fef6c8..788d1d9 100644
--- a/src/managers/MessageManager.ts
+++ b/src/managers/MessageManager.ts
@@ -1,11 +1,7 @@
-import type {
-  Member as APIMember,
-  Message as APIMessage,
-  User as APIUser,
-} from 'revolt-api-types';
+import type { Message as APIMessage } from 'revolt-api-types';
 import { BaseManager } from './BaseManager.ts';
 import { TypeError } from '../errors/mod.ts';
-import { Channel, Message, ServerMember, User } from '../structures/mod.ts';
+import { Channel, Message } from '../structures/mod.ts';
 import { Collection, UUID } from '../util/mod.ts';
 
 export type MessageResolvable = Message | APIMessage | string;
@@ -20,20 +16,21 @@ export interface MessageOptions {
   attachments?: string[];
 }
 
-export interface SearchMessageQuery {
+export interface MessageSearchOptions {
   query: string;
   limit?: number;
   before?: string;
   after?: string;
   sort?: 'Relevance' | 'Latest' | 'Oldest';
-  include_users?: boolean;
 }
 
-type SearchResultWithUsers = {
-  users: Collection<string, User>;
-  messages: Collection<string, Message>;
-  members: Collection<string, ServerMember>;
-};
+export interface MessageQueryOptions {
+  limit?: number;
+  before?: string;
+  after?: string;
+  sort?: 'Relevance' | 'Latest' | 'Oldest';
+  nearby?: string;
+}
 
 export class MessageManager extends BaseManager<Message, APIMessage> {
   holds = Message;
@@ -41,24 +38,6 @@ export class MessageManager extends BaseManager<Message, APIMessage> {
     super(channel.client);
   }
 
-  private async _fetchId(messageId: string) {
-    const data = await this.client.api.get(
-      `/channels/${this.channel.id}/messages/${messageId}`,
-    ) as APIMessage;
-    return this._add(data);
-  }
-
-  private async _fetchMany(withUsers = true) {
-    const { messages } = await this.client.api.get(
-      `/channels/${this.channel.id}/messages?include_users=${withUsers}`,
-    );
-    return (messages as APIMessage[]).reduce((coll, cur) => {
-      const msg = this._add(cur);
-      coll.set(msg.id, msg);
-      return coll;
-    }, new Collection<string, Message>());
-  }
-
   async send(options: MessageOptions | string): Promise<Message> {
     const { content, replies, attachments }: MessageOptions =
       typeof options === 'object' ? { ...options } : { content: options };
@@ -86,6 +65,25 @@ export class MessageManager extends BaseManager<Message, APIMessage> {
     await this.client.api.put(`/channels/${this.channel.id}/ack/${id}`);
   }
 
+  async bulkDelete(
+    messages: MessageResolvable[] | number | Collection<string, Message>,
+  ): Promise<void> {
+    let ids: string[] = [];
+
+    if (typeof messages === 'number') {
+      messages = await this.fetch(messages);
+      ids = [...messages.keys()];
+    } else if (messages instanceof Collection) {
+      ids = [...messages.keys()];
+    } else {
+      ids = messages.map((m) => this.resolveId(m)!).filter(Boolean);
+    }
+
+    await this.client.api.delete(`/channels/${this.channel.id}/messages/bulk`, {
+      body: { ids },
+    });
+  }
+
   async delete(message: MessageResolvable): Promise<void> {
     const id = this.resolveId(message);
     if (!id) {
@@ -108,56 +106,12 @@ export class MessageManager extends BaseManager<Message, APIMessage> {
   }
 
   async search(
-    query: SearchMessageQuery & { include_users: true },
-  ): Promise<SearchResultWithUsers>;
-  async search(query: SearchMessageQuery): Promise<Collection<string, Message>>;
-  async search(
-    query: SearchMessageQuery,
-  ): Promise<Collection<string, Message> | SearchResultWithUsers> {
-    if (query.include_users) {
-      const response =
-        (await this.client.api.post(`/channels/${this.channel.id}/search`, {
-          body: query,
-        })) as {
-          users: APIUser[];
-          messages: APIMessage[];
-          members: APIMember[];
-        };
-
-      const users = response.users.reduce((coll, cur) => {
-        const user = this.client.users._add(cur);
-        coll.set(user.id, user);
-        return coll;
-      }, new Collection<string, User>());
-
-      const messages = response.messages.reduce((coll, cur) => {
-        const msg = this._add(cur);
-        coll.set(msg.id, msg);
-        return coll;
-      }, new Collection<string, Message>());
-
-      const server = this.client.servers.cache.get(
-        response.members[0]?._id.server,
-      );
-
-      if (server) {
-        const members = response.members.reduce((coll, cur) => {
-          const member = server!.members._add(cur);
-          coll.set(cur._id.user, member);
-          return coll;
-        }, new Collection<string, ServerMember>());
-
-        return { messages, users, members };
-      }
-
-      return { messages, users, members: new Collection() };
-    }
-
+    query: MessageSearchOptions,
+  ): Promise<Collection<string, Message>> {
     const response =
       (await this.client.api.post(`/channels/${this.channel.id}/search`, {
-        body: query,
+        query: query as Required<MessageSearchOptions>,
       })) as APIMessage[];
-
     return response.reduce((coll, cur) => {
       const msg = this._add(cur);
       coll.set(msg.id, msg);
@@ -166,23 +120,29 @@ export class MessageManager extends BaseManager<Message, APIMessage> {
   }
 
   fetch(messageId: string): Promise<Message>;
-  fetch(options: { includeUsers: true }): Promise<{
-    users: Collection<string, User>;
-    messages: Collection<string, Message>;
-  }>;
-  fetch(
-    options?: { includeUsers?: false },
-  ): Promise<Collection<string, Message>>;
-  fetch(options?: string | { includeUsers?: boolean }): Promise<
-    | Collection<string, Message>
-    | {
-      users: Collection<string, User>;
-      messages: Collection<string, Message>;
+  fetch(query: MessageQueryOptions): Promise<Collection<string, Message>>;
+  fetch(limit: number): Promise<Collection<string, Message>>;
+  async fetch(
+    query?: string | MessageQueryOptions | number,
+  ): Promise<Collection<string, Message> | Message> {
+    if (typeof query === 'string') {
+      const data = await this.client.api.get(
+        `/channels/${this.channel.id}/messages/${query}`,
+      ) as APIMessage;
+      return this._add(data);
     }
-    | Message
-  > {
-    return typeof options === 'string'
-      ? this._fetchId(options)
-      : this._fetchMany(options?.['includeUsers']);
+
+    if (typeof query === 'number') query = { limit: query };
+
+    const messages = await this.client.api.get(
+      `/channels/${this.channel.id}/messages`,
+      { query: query as Required<MessageQueryOptions> },
+    );
+
+    return (messages as APIMessage[]).reduce((coll, cur) => {
+      const msg = this._add(cur);
+      coll.set(msg.id, msg);
+      return coll;
+    }, new Collection<string, Message>());
   }
 }
diff --git a/src/structures/DMChannel.ts b/src/structures/DMChannel.ts
index 78ded86..350d8b0 100644
--- a/src/structures/DMChannel.ts
+++ b/src/structures/DMChannel.ts
@@ -1,8 +1,17 @@
 import type { Channel as APIChannel } from 'revolt-api-types';
 import { Channel, Message } from './mod.ts';
 import { TextBasedChannel } from './interfaces/mod.ts';
-import { Client, MessageManager, MessageOptions } from '../lib.ts';
-import { ChannelTypes, DEFAULT_PERMISSION_DM } from '../util/mod.ts';
+import {
+  Client,
+  MessageManager,
+  MessageOptions,
+  MessageResolvable,
+} from '../lib.ts';
+import {
+  ChannelTypes,
+  Collection,
+  DEFAULT_PERMISSION_DM,
+} from '../util/mod.ts';
 
 type APIDirectChannel = Extract<APIChannel, { channel_type: 'DirectMessage' }>;
 
@@ -27,6 +36,13 @@ export class DMChannel extends Channel<APIDirectChannel>
 
     return this;
   }
+
+  bulkDelete(
+    messages: MessageResolvable[] | Collection<string, Message> | number,
+  ): Promise<void> {
+    return this.messages.bulkDelete(messages);
+  }
+
   send(options: MessageOptions | string): Promise<Message> {
     return this.messages.send(options);
   }
diff --git a/src/structures/GroupChannel.ts b/src/structures/GroupChannel.ts
index b6994ef..cf26197 100644
--- a/src/structures/GroupChannel.ts
+++ b/src/structures/GroupChannel.ts
@@ -7,6 +7,7 @@ import { Channel } from './Channel.ts';
 import {
   MessageManager,
   MessageOptions,
+  MessageResolvable,
   UserResolvable,
 } from '../managers/mod.ts';
 import { ChannelPermissions, ChannelTypes, Collection } from '../util/mod.ts';
@@ -74,6 +75,12 @@ export class GroupChannel extends Channel<APIGroupChannel>
     return this.messages.cache.get(this.lastMessageId) ?? null;
   }
 
+  bulkDelete(
+    messages: MessageResolvable[] | Collection<string, Message> | number,
+  ): Promise<void> {
+    return this.messages.bulkDelete(messages);
+  }
+
   async add(user: UserResolvable): Promise<void> {
     const id = this.client.users.resolveId(user);
     if (!id) throw new TypeError('INVALID_TYPE', 'user', 'UserResolvable');
diff --git a/src/structures/NotesChannel.ts b/src/structures/NotesChannel.ts
index c9d7198..4020ba9 100644
--- a/src/structures/NotesChannel.ts
+++ b/src/structures/NotesChannel.ts
@@ -2,8 +2,12 @@ import type { Channel as APIChannel } from 'revolt-api-types';
 import { Channel, Message, User } from './mod.ts';
 import { TextBasedChannel } from './interfaces/mod.ts';
 import { Client } from '../client/Client.ts';
-import { MessageManager, MessageOptions } from '../managers/mod.ts';
-import { ChannelTypes } from '../util/mod.ts';
+import {
+  MessageManager,
+  MessageOptions,
+  MessageResolvable,
+} from '../managers/mod.ts';
+import { ChannelTypes, Collection } from '../util/mod.ts';
 
 type APINotesChannel = Extract<APIChannel, { channel_type: 'SavedMessages' }>;
 
@@ -32,6 +36,12 @@ export class NotesChannel extends Channel<APINotesChannel>
     return this.messages.send(options);
   }
 
+  bulkDelete(
+    messages: MessageResolvable[] | Collection<string, Message> | number,
+  ): Promise<void> {
+    return this.messages.bulkDelete(messages);
+  }
+
   get lastMessage(): Message | null {
     if (!this.lastMessageId) return null;
     return this.messages.cache.get(this.lastMessageId) ?? null;
diff --git a/src/structures/TextChannel.ts b/src/structures/TextChannel.ts
index 5172f8f..3218b83 100644
--- a/src/structures/TextChannel.ts
+++ b/src/structures/TextChannel.ts
@@ -2,8 +2,12 @@ import type { Channel as APIChannel } from 'revolt-api-types';
 import { Message, ServerChannel } from './mod.ts';
 import { TextBasedChannel } from './interfaces/mod.ts';
 import { Client } from '../client/Client.ts';
-import { MessageManager, MessageOptions } from '../managers/mod.ts';
-import { ChannelTypes } from '../util/mod.ts';
+import {
+  MessageManager,
+  MessageOptions,
+  MessageResolvable,
+} from '../managers/mod.ts';
+import { ChannelTypes, Collection } from '../util/mod.ts';
 
 type APITextChannel = Extract<APIChannel, { channel_type: 'TextChannel' }>;
 
@@ -29,6 +33,12 @@ export class TextChannel extends ServerChannel<APITextChannel>
     return this.messages.send(options);
   }
 
+  bulkDelete(
+    messages: MessageResolvable[] | Collection<string, Message> | number,
+  ): Promise<void> {
+    return this.messages.bulkDelete(messages);
+  }
+
   get lastMessage(): Message | null {
     if (!this.lastMessageId) return null;
     return this.messages.cache.get(this.lastMessageId) ?? null;
diff --git a/src/structures/interfaces/TextBasedChannel.ts b/src/structures/interfaces/TextBasedChannel.ts
index d729c78..b2ea6d7 100644
--- a/src/structures/interfaces/TextBasedChannel.ts
+++ b/src/structures/interfaces/TextBasedChannel.ts
@@ -1,8 +1,17 @@
-import type { Message, MessageManager, MessageOptions } from '../../lib.ts';
+import type {
+  Collection,
+  Message,
+  MessageManager,
+  MessageOptions,
+  MessageResolvable,
+} from '../../lib.ts';
 
 export interface TextBasedChannel {
   messages: MessageManager;
   lastMessageId: string | null;
   lastMessage: Message | null;
   send(options: MessageOptions | string): Promise<Message>;
+  bulkDelete(
+    messages: MessageResolvable[] | Collection<string, Message> | number,
+  ): Promise<void>;
 }