pass around a client promise so renderers can await login

This commit is contained in:
itsMapleLeaf
2022-08-05 11:44:23 -05:00
parent f58ec8d776
commit 3bd0b33750
4 changed files with 46 additions and 30 deletions

View File

@@ -0,0 +1,13 @@
import type { ClientOptions } from "discord.js"
import { Client } from "discord.js"
import { once } from "node:events"
export async function createDiscordClient(
token: string,
options: ClientOptions,
) {
const client = new Client(options)
await client.login(token)
const [readyClient] = await once(client, "ready")
return readyClient
}

View File

@@ -1,11 +1,11 @@
import type { APIInteraction } from "discord.js"
import type { APIInteraction, Client } from "discord.js"
import {
Client,
GatewayDispatchEvents,
GatewayIntentBits,
InteractionType,
} from "discord.js"
import * as React from "react"
import { createDiscordClient } from "./create-discord-client"
import type { ReacordInstance } from "./reacord-instance.js"
import { ReacordInstancePrivate } from "./reacord-instance.js"
import { InstanceProvider } from "./react/instance-context"
@@ -54,9 +54,9 @@ export type InteractionInfo = {
*/
export class ReacordClient {
private readonly config: Required<ReacordConfig>
private readonly client: Client
private readonly discordClientPromise: Promise<Client<true>>
private instances: ReacordInstancePrivate[] = []
private destroyed = false
destroyed = false
constructor(config: ReacordConfig) {
this.config = {
@@ -64,31 +64,34 @@ export class ReacordClient {
maxInstances: config.maxInstances ?? 50,
}
this.client = new Client({ intents: [GatewayIntentBits.Guilds] })
this.client.login(this.config.token).catch(console.error)
this.discordClientPromise = createDiscordClient(this.config.token, {
intents: [GatewayIntentBits.Guilds],
})
this.client.ws.on(
GatewayDispatchEvents.InteractionCreate,
(interaction: APIInteraction) => {
if (interaction.type !== InteractionType.MessageComponent) return
for (const instance of this.instances) {
instance.handleInteraction(interaction, this)
}
},
)
this.discordClientPromise
.then((client) => {
client.ws.on(
GatewayDispatchEvents.InteractionCreate,
(interaction: APIInteraction) => {
if (interaction.type !== InteractionType.MessageComponent) return
for (const instance of this.instances) {
instance.handleInteraction(interaction, this)
}
},
)
return client
})
.catch(console.error)
}
send(channelId: string, initialContent?: React.ReactNode): ReacordInstance {
send(channelId: string, initialContent?: React.ReactNode) {
return this.createInstance(
new ChannelMessageRenderer(channelId, this.client),
new ChannelMessageRenderer(channelId, this.discordClientPromise),
initialContent,
)
}
reply(
interaction: InteractionInfo,
initialContent?: React.ReactNode,
): ReacordInstance {
reply(interaction: InteractionInfo, initialContent?: React.ReactNode) {
return this.createInstance(
new InteractionReplyRenderer(interaction),
initialContent,
@@ -98,7 +101,7 @@ export class ReacordClient {
ephemeralReply(
interaction: InteractionInfo,
initialContent?: React.ReactNode,
): ReacordInstance {
) {
return this.createInstance(
new EphemeralInteractionReplyRenderer(interaction),
initialContent,
@@ -106,14 +109,11 @@ export class ReacordClient {
}
destroy() {
this.client.destroy()
void this.discordClientPromise.then((client) => client.destroy())
this.destroyed = true
}
private createInstance(
renderer: Renderer,
initialContent?: React.ReactNode,
): ReacordInstance {
private createInstance(renderer: Renderer, initialContent?: React.ReactNode) {
if (this.destroyed) throw new Error("ReacordClient is destroyed")
const instance = new ReacordInstancePrivate(renderer)

View File

@@ -4,6 +4,7 @@ import type {
APIMessageComponentSelectMenuInteraction,
} from "discord.js"
import { ComponentType } from "discord.js"
import type * as React from "react"
import { Node } from "./node"
import type { ReacordClient } from "./reacord-client"
import { ButtonNode } from "./react/button"

View File

@@ -19,15 +19,17 @@ export class ChannelMessageRenderer implements Renderer {
constructor(
private readonly channelId: string,
private readonly client: Client,
private readonly client: Promise<Client<true>>,
) {}
private async getChannel(): Promise<TextChannel> {
if (this.channel) return this.channel
const client = await this.client
const channel =
this.client.channels.cache.get(this.channelId) ??
(await this.client.channels.fetch(this.channelId))
client.channels.cache.get(this.channelId) ??
(await client.channels.fetch(this.channelId))
if (!(channel instanceof TextChannel)) {
throw new TypeError(`Channel ${this.channelId} is not a text channel`)