diff --git a/src/Client.ts b/src/Client.ts index 7f8d40fe..9af66609 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -68,6 +68,7 @@ type Events = { serverCreate(server: Server): void; serverUpdate(server: Server, previousServer: HydratedServer): void; serverDelete(server: HydratedServer): void; + serverLeave(server: HydratedServer): void; serverRoleUpdate(server: Server, roleId: string, previousRole: Role): void; serverRoleDelete(server: Server, roleId: string, role: Role): void; diff --git a/src/classes/Bot.ts b/src/classes/Bot.ts index 5f06c1a9..f266ee8c 100644 --- a/src/classes/Bot.ts +++ b/src/classes/Bot.ts @@ -28,6 +28,13 @@ export class Bot { return `<@${this.id}>`; } + /** + * Whether this object exists + */ + get $exists() { + return !!this.#collection.getUnderlyingObject(this.id).id; + } + /** * Time when this user created their account */ diff --git a/src/classes/Channel.ts b/src/classes/Channel.ts index 2a738193..580f0f3a 100644 --- a/src/classes/Channel.ts +++ b/src/classes/Channel.ts @@ -42,6 +42,13 @@ export class Channel { return `<#${this.id}>`; } + /** + * Whether this object exists + */ + get $exists() { + return !!this.#collection.getUnderlyingObject(this.id).id; + } + /** * Time when this server was created */ diff --git a/src/classes/ChannelUnread.ts b/src/classes/ChannelUnread.ts index 9d5cefb2..6f6dabf9 100644 --- a/src/classes/ChannelUnread.ts +++ b/src/classes/ChannelUnread.ts @@ -17,6 +17,13 @@ export class ChannelUnread { this.id = id; } + /** + * Whether this object exists + */ + get $exists() { + return !!this.#collection.getUnderlyingObject(this.id).id; + } + /** * Last read message id */ diff --git a/src/classes/ChannelWebhook.ts b/src/classes/ChannelWebhook.ts index ab88c974..860bb5d7 100644 --- a/src/classes/ChannelWebhook.ts +++ b/src/classes/ChannelWebhook.ts @@ -20,6 +20,13 @@ export class ChannelWebhook { this.id = id; } + /** + * Whether this object exists + */ + get $exists() { + return !!this.#collection.getUnderlyingObject(this.id).id; + } + /** * Webhook name */ diff --git a/src/classes/Emoji.ts b/src/classes/Emoji.ts index 5dab359e..9c02950d 100644 --- a/src/classes/Emoji.ts +++ b/src/classes/Emoji.ts @@ -27,6 +27,13 @@ export class Emoji { return `:${this.id}:`; } + /** + * Whether this object exists + */ + get $exists() { + return !!this.#collection.getUnderlyingObject(this.id).id; + } + /** * Time when this emoji was created */ diff --git a/src/classes/Message.ts b/src/classes/Message.ts index 8e1f04b9..be88bddf 100644 --- a/src/classes/Message.ts +++ b/src/classes/Message.ts @@ -25,6 +25,13 @@ export class Message { this.id = id; } + /** + * Whether this object exists + */ + get $exists() { + return !!this.#collection.getUnderlyingObject(this.id).id; + } + /** * Time when this message was posted */ diff --git a/src/classes/Server.ts b/src/classes/Server.ts index 4339cc22..ee01517c 100644 --- a/src/classes/Server.ts +++ b/src/classes/Server.ts @@ -47,6 +47,13 @@ export class Server { return `<%${this.id}>`; } + /** + * Whether this object exists + */ + get $exists() { + return !!this.#collection.getUnderlyingObject(this.id).id; + } + /** * Time when this server was created */ @@ -378,6 +385,33 @@ export class Server { ); } + /** + * Delete the underlying server + * @param leaveEvent Whether we are leaving + */ + $delete(leaveEvent?: boolean) { + batch(() => { + const server = this.#collection.client.servers.getUnderlyingObject( + this.id + ); + + // Avoid race conditions + if (server.id) { + this.#collection.client.emit( + leaveEvent ? "serverLeave" : "serverDelete", + server + ); + + for (const channel of this.channelIds) { + this.#collection.client.channels.delete(channel); + } + + this.#collection.delete(this.id); + } + // TODO: delete members, emoji, etc + }); + } + /** * Delete or leave a server * @param leaveSilently Whether to not send a message on leave @@ -387,7 +421,7 @@ export class Server { leave_silently: leaveSilently, }); - this.#collection.delete(this.id); + this.$delete(); } /** @@ -432,13 +466,6 @@ export class 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}` ); diff --git a/src/classes/ServerMember.ts b/src/classes/ServerMember.ts index 9b791daf..9f0d13d4 100644 --- a/src/classes/ServerMember.ts +++ b/src/classes/ServerMember.ts @@ -45,6 +45,13 @@ export class ServerMember { return `<@${this.id.user}>`; } + /** + * Whether this object exists + */ + get $exists() { + return !this.#collection.getUnderlyingObject(key(this.id)).id; + } + /** * Server this member belongs to */ diff --git a/src/classes/Session.ts b/src/classes/Session.ts index e8420890..c9e00b72 100644 --- a/src/classes/Session.ts +++ b/src/classes/Session.ts @@ -27,6 +27,13 @@ export class Session { return this.name; } + /** + * Whether this object exists + */ + get $exists() { + return !!this.#collection.getUnderlyingObject(this.id).id; + } + /** * Time when this session was created */ diff --git a/src/classes/User.ts b/src/classes/User.ts index 35cfb2f9..a22d6320 100644 --- a/src/classes/User.ts +++ b/src/classes/User.ts @@ -29,6 +29,13 @@ export class User { return `<@${this.id}>`; } + /** + * Whether this object exists + */ + get $exists() { + return !!this.#collection.getUnderlyingObject(this.id).id; + } + /** * Time when this user created their account */ diff --git a/src/events/v1.ts b/src/events/v1.ts index f46a544f..bad68e1d 100644 --- a/src/events/v1.ts +++ b/src/events/v1.ts @@ -202,6 +202,10 @@ export async function handleEvent( event: ServerMessage, setReady: Setter ) { + if (client.options.debug) { + console.debug("[S->C]", event); + } + switch (event.type) { case "Bulk": { for (const item of event.v) { @@ -554,16 +558,10 @@ export async function handleEvent( break; } case "ServerDelete": { - if (client.servers.getOrPartial(event.id)) { - batch(() => { - const server = client.servers.getUnderlyingObject(event.id); - client.emit("serverDelete", server); - client.servers.delete(event.id); - - for (const channel of server.channelIds) { - client.channels.delete(channel); - } - }); + const server = client.servers.getOrPartial(event.id); + if (server) { + // TODO: server should tell us if it's a leave or delete on our end + server.$delete(); } break; } @@ -603,6 +601,12 @@ export async function handleEvent( }; if (!client.serverMembers.hasByKey(id)) { + if (!client.users.has(id.user)) { + if (client.options.eagerFetching) { + await client.users.fetch(id.user); + } + } + client.emit( "serverMemberJoin", client.serverMembers.getOrCreate(id, { @@ -653,6 +657,19 @@ export async function handleEvent( break; } case "ServerMemberLeave": { + if (event.user) { + handleEvent( + client, + { + type: "ServerDelete", + id: event.id, + }, + setReady + ); + + return; + } + const id = { server: event.id, user: event.user, @@ -662,6 +679,7 @@ export async function handleEvent( const member = client.serverMembers.getUnderlyingObject( id.server + id.user ); + client.emit("serverMemberLeave", member); client.serverMembers.delete(id.server + id.user); }