From 47ad4c3a478b9cf57e24bc97ec03540444e3607b Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Tue, 11 Apr 2023 15:08:36 +0100 Subject: [PATCH] refactor: clean up jsdoc for methods feat: add missing methods from API Closes #69 --- src/classes/Channel.ts | 43 +++-- src/classes/Server.ts | 255 ++++++++++++++++++++++++++- src/classes/ServerMember.ts | 33 +++- src/classes/User.ts | 106 +++++++++++ src/collections/ChannelCollection.ts | 17 +- src/collections/MessageCollection.ts | 9 + src/collections/ServerCollection.ts | 20 +++ 7 files changed, 468 insertions(+), 15 deletions(-) diff --git a/src/classes/Channel.ts b/src/classes/Channel.ts index 6a087bc6..c6512157 100644 --- a/src/classes/Channel.ts +++ b/src/classes/Channel.ts @@ -349,22 +349,22 @@ export class Channel { /** * Edit a channel - * @param data Edit data + * @param data Changes */ async edit(data: DataEditChannel) { await this.#collection.client.api.patch(`/channels/${this.id as ""}`, data); } /** - * Delete a channel - * @param leave_silently Whether to not send a message on leave + * Delete or leave a channel + * @param leaveSilently Whether to not send a message on leave * @param noRequest Whether to not send a request - * @requires `DM`, `Group`, `TextChannel`, `VoiceChannel` + * @requires `DirectMessage`, `Group`, `TextChannel`, `VoiceChannel` */ - async delete(leave_silently?: boolean, noRequest?: boolean) { + async delete(leaveSilently?: boolean, noRequest?: boolean) { if (!noRequest) await this.#collection.client.api.delete(`/channels/${this.id as ""}`, { - leave_silently, + leave_silently: leaveSilently, }); if (this.type === "DirectMessage") { @@ -378,6 +378,7 @@ export class Channel { /** * Add a user to a group * @param user_id ID of the target user + * @requires `Group` */ async addMember(user_id: string) { return await this.#collection.client.api.put( @@ -388,16 +389,19 @@ export class Channel { /** * Remove a user from a group * @param user_id ID of the target user + * @requires `Group` */ async removeMember(user_id: string) { return await this.#collection.client.api.delete( `/channels/${this.id as ""}/recipients/${user_id as ""}` ); } + /** * Send a message * @param data Either the message as a string or message sending route data - * @returns The message + * @requires `SavedMessages`, `DirectMessage`, `Group`, `TextChannel` + * @returns Sent message */ async sendMessage( data: string | DataMessageSend, @@ -426,7 +430,8 @@ export class Channel { /** * Fetch a message by its ID * @param messageId ID of the target message - * @returns The message + * @requires `SavedMessages`, `DirectMessage`, `Group`, `TextChannel` + * @returns Message */ async fetchMessage(messageId: string) { const message = await this.#collection.client.api.get( @@ -439,7 +444,8 @@ export class Channel { /** * Fetch multiple messages from a channel * @param params Message fetching route data - * @returns The messages + * @requires `SavedMessages`, `DirectMessage`, `Group`, `TextChannel` + * @returns Messages */ async fetchMessages( params?: Omit< @@ -463,6 +469,7 @@ export class Channel { /** * Fetch multiple messages from a channel including the users that sent them * @param params Message fetching route data + * @requires `SavedMessages`, `DirectMessage`, `Group`, `TextChannel` * @returns Object including messages and users */ async fetchMessagesWithUsers( @@ -495,7 +502,8 @@ export class Channel { /** * Search for messages * @param params Message searching route data - * @returns The messages + * @requires `SavedMessages`, `DirectMessage`, `Group`, `TextChannel` + * @returns Messages */ async search(params: Omit) { const messages = (await this.#collection.client.api.post( @@ -511,7 +519,8 @@ export class Channel { /** * Search for messages including the users that sent them * @param params Message searching route data - * @returns The messages + * @requires `SavedMessages`, `DirectMessage`, `Group`, `TextChannel` + * @returns Object including messages and users */ async searchWithUsers(params: Omit) { const data = (await this.#collection.client.api.post( @@ -535,17 +544,23 @@ export class Channel { }; } + /** + * Delete many messages by their IDs + * @param ids List of message IDs + * @requires `SavedMessages`, `DirectMessage`, `Group`, `TextChannel` + */ async deleteMessages(ids: string[]) { await this.#collection.client.api.delete( `/channels/${this.id as ""}/messages/bulk`, { - data: { ids }, + ids, } ); } /** * Create an invite to the channel + * @requires `TextChannel`, `VoiceChannel` * @returns Newly created invite code */ async createInvite() { @@ -561,6 +576,7 @@ export class Channel { * Mark a channel as read * @param message Last read message or its ID * @param skipRateLimiter Whether to skip the internal rate limiter + * @requires `SavedMessages`, `DirectMessage`, `Group`, `TextChannel` */ async ack(message?: Message | string, skipRateLimiter?: boolean) { const lastMessageId = @@ -606,6 +622,7 @@ export class Channel { * Set role permissions * @param role_id Role Id, set to 'default' to affect all users * @param permissions Permission value + * @requires `Group`, `TextChannel`, `VoiceChannel` */ async setPermissions(role_id = "default", permissions: Override) { return await this.#collection.client.api.put( @@ -616,6 +633,7 @@ export class Channel { /** * Start typing in this channel + * @requires `DirectMessage`, `Group`, `TextChannel` */ startTyping() { this.#collection.client.events.send({ @@ -626,6 +644,7 @@ export class Channel { /** * Stop typing in this channel + * @requires `DirectMessage`, `Group`, `TextChannel` */ stopTyping() { this.#collection.client.events.send({ diff --git a/src/classes/Server.ts b/src/classes/Server.ts index 706e3f86..ca668868 100644 --- a/src/classes/Server.ts +++ b/src/classes/Server.ts @@ -1,6 +1,14 @@ -import type { Category } from "revolt-api"; +import type { + Category, + DataBanCreate, + DataCreateChannel, + DataEditRole, + DataEditServer, + Override, +} from "revolt-api"; import { decodeTime } from "ulid"; +import { ServerMember, User } from ".."; import { ServerCollection } from "../collections"; import { bitwiseAndEq, calculatePermission } from "../permissions/calculator"; import { Permission } from "../permissions/definitions"; @@ -304,4 +312,249 @@ export class Server { ...permission.map((x) => Permission[x]) ); } + + /** + * Create a channel + * @param data Channel create route data + * @returns The newly-created channel + */ + async createChannel(data: DataCreateChannel) { + let channel = await this.#collection.client.api.post( + `/servers/${this.id as ""}/channels`, + data + ); + + return this.#collection.client.channels.getOrCreate(channel._id, channel); + } + + /** + * Edit a server + * @param data Changes + */ + async edit(data: DataEditServer) { + await this.#collection.client.api.patch(`/servers/${this.id as ""}`, data); + } + + /** + * Delete or leave a server + * @param leaveSilently Whether to not send a message on leave + * @param noRequest Whether to not send a request + */ + async delete(leaveSilently?: boolean, avoidReq?: boolean) { + if (!avoidReq) + await this.#collection.client.api.delete(`/servers/${this.id as ""}`, { + leave_silently: leaveSilently, + }); + + this.#collection.delete(this.id); + } + + /** + * Mark a server as read + */ + async ack() { + await this.#collection.client.api.put(`/servers/${this.id}/ack`); + } + + /** + * Ban user from this server + * @param user User + * @param options Ban options + */ + async banUser(user: string | User | ServerMember, options?: DataBanCreate) { + const userId = + user instanceof User + ? user.id + : user instanceof ServerMember + ? user.id.user + : user; + + return await this.#collection.client.api.put( + `/servers/${this.id as ""}/bans/${userId}`, + options + ); + } + + /** + * Kick user from this server + * @param user User + */ + async kickUser(user: string | User | ServerMember) { + const userId = + user instanceof User + ? user.id + : user instanceof ServerMember + ? user.id.user + : user; + + return await this.#collection.client.api.delete( + `/servers/${this.id as ""}/members/${userId}` + ); + } + + /** + * Pardon user's ban + * @param user User + */ + async unbanUser(user: string | User) { + const userId = user instanceof User ? user.id : user; + return await this.#collection.client.api.delete( + `/servers/${this.id as ""}/bans/${userId}` + ); + } + + /** + * Fetch a server's invites + * @returns An array of the server's invites + */ + async fetchInvites() { + return await this.#collection.client.api.get( + `/servers/${this.id as ""}/invites` + ); + } + + /** + * Fetch a server's bans + * @returns An array of the server's bans. + */ + async fetchBans() { + return await this.#collection.client.api.get( + `/servers/${this.id as ""}/bans` + ); + } + + /** + * Set role permissions + * @param roleId Role Id, set to 'default' to affect all users + * @param permissions Permission value + */ + async setPermissions(roleId = "default", permissions: Override | number) { + return await this.#collection.client.api.put( + `/servers/${this.id as ""}/permissions/${roleId as ""}`, + { permissions: permissions as Override } + ); + } + + /** + * Create role + * @param name Role name + */ + async createRole(name: string) { + return await this.#collection.client.api.post( + `/servers/${this.id as ""}/roles`, + { + name, + } + ); + } + + /** + * Edit a role + * @param roleId Role ID + * @param data Role editing route data + */ + async editRole(roleId: string, data: DataEditRole) { + return await this.#collection.client.api.patch( + `/servers/${this.id as ""}/roles/${roleId as ""}`, + data + ); + } + + /** + * Delete role + * @param roleId Role ID + */ + async deleteRole(roleId: string) { + return await this.#collection.client.api.delete( + `/servers/${this.id as ""}/roles/${roleId as ""}` + ); + } + + /** + * Fetch a server member + * @param user User + * @returns Server member object + */ + async fetchMember(user: User | string) { + const userId = typeof user === "string" ? user : user.id; + const existing = this.#collection.client.serverMembers.getByKey({ + server: this.id, + user: userId, + }); + + if (existing) return existing; + + const member = await this.#collection.client.api.get( + `/servers/${this.id as ""}/members/${userId as ""}` + ); + + return this.#collection.client.serverMembers.getOrCreate( + member._id, + member + ); + } + + /** + * Optimised member fetch route + * @param excludeOffline + */ + async syncMembers(excludeOffline?: boolean) { + const data = await this.#collection.client.api.get( + `/servers/${this.id as ""}/members`, + { exclude_offline: excludeOffline } + ); + + if (excludeOffline) { + for (let i = 0; i < data.users.length; i++) { + const user = data.users[i]; + if (user.online) { + this.#collection.client.users.getOrCreate(user._id, user); + this.#collection.client.serverMembers.getOrCreate( + data.members[i]._id, + data.members[i] + ); + } + } + } else { + for (let i = 0; i < data.users.length; i++) { + this.#collection.client.users.getOrCreate( + data.users[i]._id, + data.users[i] + ); + this.#collection.client.serverMembers.getOrCreate( + data.members[i]._id, + data.members[i] + ); + } + } + } + + /** + * Fetch a server's members + * @returns List of the server's members and their user objects + */ + async fetchMembers() { + const data = await this.#collection.client.api.get( + `/servers/${this.id as ""}/members` + ); + + return { + members: data.members.map((member) => + this.#collection.client.serverMembers.getOrCreate(member._id, member) + ), + users: data.users.map((user) => + this.#collection.client.users.getOrCreate(user._id, user) + ), + }; + } + + /** + * Fetch a server's emoji + * @returns List of server emoji + */ + async fetchEmojis() { + return await this.#collection.client.api.get( + `/servers/${this.id as ""}/emojis` + ); + } } diff --git a/src/classes/ServerMember.ts b/src/classes/ServerMember.ts index 800bf3bd..6412124c 100644 --- a/src/classes/ServerMember.ts +++ b/src/classes/ServerMember.ts @@ -1,4 +1,9 @@ -import type { MemberCompositeKey } from "revolt-api"; +import type { + DataBanCreate, + DataEditMessage, + DataMemberEdit, + MemberCompositeKey, +} from "revolt-api"; import { ServerMemberCollection } from "../collections"; import { bitwiseAndEq, calculatePermission } from "../permissions/calculator"; @@ -200,4 +205,30 @@ export class ServerMember { this.user?.animatedAvatarURL ); } + + /** + * Edit a member + * @param data Changes + */ + async edit(data: DataMemberEdit) { + await this.#collection.client.api.patch( + `/servers/${this.id.server as ""}/members/${this.id.user as ""}`, + data + ); + } + + /** + * Ban this member from the server + * @param options Ban options + */ + async ban(options: DataBanCreate) { + this.server?.banUser(this, options); + } + + /** + * Kick this member from the server + */ + async kick() { + this.server?.kickUser(this); + } } diff --git a/src/classes/User.ts b/src/classes/User.ts index 2394b16d..9decb301 100644 --- a/src/classes/User.ts +++ b/src/classes/User.ts @@ -1,3 +1,4 @@ +import { DataEditUser } from "revolt-api"; import { decodeTime } from "ulid"; import { UserCollection } from "../collections"; @@ -164,4 +165,109 @@ export class User { return permissions; } + + /** + * Edit the user + * @param data Changes + */ + async edit(data: DataEditUser) { + await this.#collection.client.api.patch( + `/users/${ + this.id === this.#collection.client.user?.id ? "@me" : this.id + }`, + data + ); + } + + /** + * Change the username of the current user + * @param username New username + * @param password Current password + */ + async changeUsername(username: string, password: string) { + return await this.#collection.client.api.patch("/users/@me/username", { + username, + password, + }); + } + + /** + * Open a DM with a user + * @returns DM Channel + */ + async openDM() { + let dm = [...this.#collection.client.channels.values()].find( + (x) => x.type === "DirectMessage" && x.recipient == this + ); + + if (dm) { + if (!dm.active) { + this.#collection.client.channels.updateUnderlyingObject( + dm.id, + "active", + true + ); + } + } else { + const data = await this.#collection.client.api.get( + `/users/${this.id as ""}/dm` + ); + + dm = this.#collection.client.channels.getOrCreate(data._id, data)!; + } + + return dm; + } + + /** + * Send a friend request to a user + */ + async addFriend() { + const user = await this.#collection.client.api.post(`/users/friend`, { + username: this.username, + }); + + return this.#collection.getOrCreate(user._id, user); + } + + /** + * Remove a user from the friend list + */ + async removeFriend() { + await this.#collection.client.api.delete(`/users/${this.id as ""}/friend`); + } + + /** + * Block a user + */ + async blockUser() { + await this.#collection.client.api.put(`/users/${this.id as ""}/block`); + } + + /** + * Unblock a user + */ + async unblockUser() { + await this.#collection.client.api.delete(`/users/${this.id as ""}/block`); + } + + /** + * Fetch the profile of a user + * @returns The profile of the user + */ + async fetchProfile() { + return await this.#collection.client.api.get( + `/users/${this.id as ""}/profile` + ); + } + + /** + * Fetch the mutual connections of the current user and a target user + * @returns The mutual connections of the current user and a target user + */ + async fetchMutual() { + return await this.#collection.client.api.get( + `/users/${this.id as ""}/mutual` + ); + } } diff --git a/src/collections/ChannelCollection.ts b/src/collections/ChannelCollection.ts index e804fd3b..2cc35319 100644 --- a/src/collections/ChannelCollection.ts +++ b/src/collections/ChannelCollection.ts @@ -1,4 +1,4 @@ -import { API, Channel } from ".."; +import { API, Channel, User } from ".."; import { HydratedChannel } from "../hydration"; import { ClassCollection } from "."; @@ -62,4 +62,19 @@ export class ChannelCollection extends ClassCollection< return instance; } } + + /** + * Create a group + * @param name Group name + * @param users Users to add + * @returns The newly-created group + */ + async createGroup(name: string, users: (User | string)[]) { + const group = await this.client.api.post(`/channels/create`, { + name, + users: users.map((user) => (user instanceof User ? user.id : user)), + }); + + return this.getOrCreate(group._id, group, true); + } } diff --git a/src/collections/MessageCollection.ts b/src/collections/MessageCollection.ts index 9fabdd1a..b1b7563b 100644 --- a/src/collections/MessageCollection.ts +++ b/src/collections/MessageCollection.ts @@ -57,4 +57,13 @@ export class MessageCollection extends ClassCollection< return instance; } } + + /** + * Globally fetch messages + * @requires Admin + * @param query Message query + */ + async queryMessages(query: API.MessageQuery) { + return this.client.api.post("/admin/messages", query); + } } diff --git a/src/collections/ServerCollection.ts b/src/collections/ServerCollection.ts index 151ab58b..70201155 100644 --- a/src/collections/ServerCollection.ts +++ b/src/collections/ServerCollection.ts @@ -1,3 +1,5 @@ +import { DataCreateServer } from "revolt-api"; + import { API, Server } from ".."; import { HydratedServer } from "../hydration"; @@ -49,4 +51,22 @@ export class ServerCollection extends ClassCollection { return instance; } } + + /** + * Create a server + * @param data Server options + * @returns The newly-created server + */ + async createServer(data: DataCreateServer) { + const { server, channels } = await this.client.api.post( + `/servers/create`, + data + ); + + for (const channel of channels) { + this.client.channels.getOrCreate(channel._id, channel); + } + + return this.getOrCreate(server._id, server, true); + } }