public interface tweaks and such

This commit is contained in:
itsMapleLeaf
2023-10-28 14:34:09 -05:00
parent cdc22b7916
commit da1c62f2f0
4 changed files with 75 additions and 143 deletions

View File

@@ -25,43 +25,6 @@ import type { ReacordInstance } from "./instance"
import type { ReacordConfig } from "./reacord"
import { Reacord } from "./reacord"
/**
* Options for the channel message.
*
* @see https://reacord.mapleleaf.dev/guides/sending-messages
*/
export interface LegacyCreateChannelMessageOptions
extends CreateChannelMessageOptions {
/**
* Send message as a reply. Requires the use of message event instead of
* channel id provided as argument.
*
* @deprecated Use reacord.createMessageReply()
*/
reply?: boolean
}
/**
* Options for the channel message.
*
* @see https://reacord.mapleleaf.dev/guides/sending-messages
*/
export interface CreateChannelMessageOptions {}
/**
* Options for the message reply method.
*
* @see https://reacord.mapleleaf.dev/guides/sending-messages
*/
export interface CreateMessageReplyOptions {}
/**
* Custom options for the interaction reply method.
*
* @see https://reacord.mapleleaf.dev/guides/sending-messages
*/
export type CreateInteractionReplyOptions = ReplyInfo
/**
* The Reacord adapter for Discord.js.
*
@@ -86,45 +49,31 @@ export class ReacordDiscordJs extends Reacord {
/**
* Sends a message to a channel.
*
* @param target - Discord channel object.
* @param [options] - Options for the channel message
* @param target Discord channel object.
* @param [options] Options for the channel message
* @see https://reacord.mapleleaf.dev/guides/sending-messages
* @see {@link Discord.MessageCreateOptions}
*/
public createChannelMessage(
target: Discord.Channel,
options: CreateChannelMessageOptions = {},
target: Discord.ChannelResolvable,
options: Discord.MessageCreateOptions = {},
): ReacordInstance {
return this.createInstance(
this.createChannelMessageRenderer(target, options),
)
}
/**
* Replies to a message by sending a message.
*
* @param message - Discord message event object.
* @param [options] - Options for the message reply method.
* @see https://reacord.mapleleaf.dev/guides/sending-messages
*/
public createMessageReply(
message: Discord.Message,
options: CreateMessageReplyOptions = {},
): ReacordInstance {
return this.createInstance(
this.createMessageReplyRenderer(message, options),
)
}
/**
* Replies to a command interaction by sending a message.
*
* @param interaction - Discord command interaction object.
* @param [options] - Custom options for the interaction reply method.
* @param interaction Discord command interaction object.
* @param [options] Custom options for the interaction reply method.
* @see https://reacord.mapleleaf.dev/guides/sending-messages
* @see {@link Discord.InteractionReplyOptions}
*/
public createInteractionReply(
interaction: Discord.CommandInteraction,
options: CreateInteractionReplyOptions = {},
options: Discord.InteractionReplyOptions = {},
): ReacordInstance {
return this.createInstance(
this.createInteractionReplyRenderer(interaction, options),
@@ -132,19 +81,17 @@ export class ReacordDiscordJs extends Reacord {
}
/**
* Sends a message to a channel. Alternatively replies to message event.
* Sends a message to a channel.
*
* @deprecated Use reacord.createChannelMessage() or
* reacord.createMessageReply() instead.
* @deprecated Use reacord.createChannelMessage() instead.
* @see https://reacord.mapleleaf.dev/guides/sending-messages
*/
public send(
event: string | Discord.Message,
channel: Discord.ChannelResolvable,
initialContent?: React.ReactNode,
options: LegacyCreateChannelMessageOptions = {},
): ReacordInstance {
return this.createInstance(
this.createMessageReplyRenderer(event, options),
this.createChannelMessageRenderer(channel, {}),
initialContent,
)
}
@@ -158,10 +105,9 @@ export class ReacordDiscordJs extends Reacord {
public reply(
interaction: Discord.CommandInteraction,
initialContent?: React.ReactNode,
options: CreateInteractionReplyOptions = {},
): ReacordInstance {
return this.createInstance(
this.createInteractionReplyRenderer(interaction, options),
this.createInteractionReplyRenderer(interaction, {}),
initialContent,
)
}
@@ -169,18 +115,16 @@ export class ReacordDiscordJs extends Reacord {
/**
* Sends an ephemeral message as a reply to a command interaction.
*
* @deprecated Use reacord.createInteractionReply(interaction, content, {
* ephemeral: true })
* @deprecated Use reacord.createInteractionReply(interaction, { ephemeral:
* true })
* @see https://reacord.mapleleaf.dev/guides/sending-messages
*/
public ephemeralReply(
interaction: Discord.CommandInteraction,
initialContent?: React.ReactNode,
options?: Omit<CreateInteractionReplyOptions, "ephemeral">,
): ReacordInstance {
return this.createInstance(
this.createInteractionReplyRenderer(interaction, {
...options,
ephemeral: true,
}),
initialContent,
@@ -188,49 +132,32 @@ export class ReacordDiscordJs extends Reacord {
}
private createChannelMessageRenderer(
channel: Discord.Channel,
_opts?: CreateMessageReplyOptions,
channelResolvable: Discord.ChannelResolvable,
messageCreateOptions?: Discord.MessageCreateOptions,
) {
return new ChannelMessageRenderer({
send: async (options) => {
if (!channel.isTextBased()) {
raise(`Channel ${channel.id} is not a text channel`)
send: async (messageOptions) => {
let channel = this.client.channels.resolve(channelResolvable)
if (!channel && typeof channelResolvable === "string") {
channel = await this.client.channels.fetch(channelResolvable)
}
const message = await channel.send(getDiscordMessageOptions(options))
return createReacordMessage(message)
},
})
}
private createMessageReplyRenderer(
event: string | Discord.Message,
opts: CreateChannelMessageOptions | LegacyCreateChannelMessageOptions,
) {
return new ChannelMessageRenderer({
send: async (options) => {
// Backwards compatible channelId api
// `event` is treated as MessageEvent depending on its type
const channel =
typeof event === "string"
? this.client.channels.cache.get(event) ??
(await this.client.channels.fetch(event)) ??
raise(`Channel ${event} not found`)
: event.channel
if (!channel) {
const id =
typeof channelResolvable === "string"
? channelResolvable
: channelResolvable.id
raise(`Channel ${id} not found`)
}
if (!channel.isTextBased()) {
raise(`Channel ${channel.id} is not a text channel`)
raise(`Channel ${channel.id} must be a text channel`)
}
if ("reply" in opts && opts.reply) {
if (typeof event === "string") {
raise("Cannot send reply with channel ID provided")
}
const message = await event.reply(getDiscordMessageOptions(options))
return createReacordMessage(message)
}
const message = await channel.send(getDiscordMessageOptions(options))
const message = await channel.send({
...getDiscordMessageOptions(messageOptions),
...messageCreateOptions,
})
return createReacordMessage(message)
},
})
@@ -240,23 +167,22 @@ export class ReacordDiscordJs extends Reacord {
interaction:
| Discord.CommandInteraction
| Discord.MessageComponentInteraction,
opts: CreateInteractionReplyOptions,
interactionReplyOptions: Discord.InteractionReplyOptions,
) {
return new InteractionReplyRenderer({
type: "command",
id: interaction.id,
reply: async (options) => {
interactionId: interaction.id,
reply: async (messageOptions) => {
const message = await interaction.reply({
...getDiscordMessageOptions(options),
...opts,
...getDiscordMessageOptions(messageOptions),
...interactionReplyOptions,
fetchReply: true,
})
return createReacordMessage(message)
},
followUp: async (options) => {
followUp: async (messageOptions) => {
const message = await interaction.followUp({
...getDiscordMessageOptions(options),
...opts,
...getDiscordMessageOptions(messageOptions),
...interactionReplyOptions,
fetchReply: true,
})
return createReacordMessage(message)

View File

@@ -1,4 +1,3 @@
import type { Interaction } from "../interaction"
import type { Message, MessageOptions } from "../message"
import { Renderer } from "./renderer"
@@ -6,17 +5,23 @@ import { Renderer } from "./renderer"
// so we know whether to call reply() or followUp()
const repliedInteractionIds = new Set<string>()
export type InteractionReplyRendererImplementation = {
interactionId: string
reply: (options: MessageOptions) => Promise<Message>
followUp: (options: MessageOptions) => Promise<Message>
}
export class InteractionReplyRenderer extends Renderer {
constructor(private interaction: Interaction) {
constructor(private implementation: InteractionReplyRendererImplementation) {
super()
}
protected createMessage(options: MessageOptions): Promise<Message> {
if (repliedInteractionIds.has(this.interaction.id)) {
return this.interaction.followUp(options)
if (repliedInteractionIds.has(this.implementation.interactionId)) {
return this.implementation.followUp(options)
}
repliedInteractionIds.add(this.interaction.id)
return this.interaction.reply(options)
repliedInteractionIds.add(this.implementation.interactionId)
return this.implementation.reply(options)
}
}

View File

@@ -6,7 +6,7 @@ slug: getting-started
# Getting Started
These guides assume some familiarity with JavaScript, [React](https://reactjs.org), [Discord.js](https://discord.js.org) and the [Discord API](https://discord.dev). Keep these pages as reference if you need it.
These guides assume some familiarity with [JavaScript](https://developer.mozilla.org/en-US/docs/Web/javascript), [React](https://reactjs.org), [Discord.js](https://discord.js.org) and the [Discord API](https://discord.dev). Keep these pages as reference if you need it.
## Setup from template
@@ -47,6 +47,13 @@ await client.login(process.env.BOT_TOKEN)
To use JSX in your code, run it with [tsx](https://npm.im/tsx):
```bash
npm install tsx
tsx main.tsx
npm install -D tsx
npx tsx main.tsx
```
For production, I recommend compiling it with [tsup](https://npm.im/tsup):
```bash
npm install -D tsup
npx tsup src/main.tsx --target node20
```

View File

@@ -36,7 +36,6 @@ function Uptime() {
client.on("ready", () => {
const instance = reacord.createChannelMessage(channel)
instance.render(<Uptime />)
})
```
@@ -48,26 +47,25 @@ const Hello = ({ subject }) => <>Hello, {subject}!</>
client.on("ready", () => {
const instance = reacord.createChannelMessage(channel)
instance.render(<Hello subject="World" />)
instance.render(<Hello subject="Moon" />)
})
```
## Replying to Messages
Instead of sending messages to a channel, you may want to reply to a specific message instead. To do this, create an instance using `.createMessageReply()` instead:
You can specify various options for the message:
```jsx
const Hello = ({ username }) => <>Hello, {username}!</>
client.on("messageCreate", (message) => {
reacord
.createMessageReply(message)
.render(<Hello username={message.author.displayName} />)
const instance = reacord.createChannelMessage(channel, {
tts: true,
reply: {
messageReference: someMessage.id,
},
flags: [MessageFlags.SuppressNotifications],
})
```
See the [Discord.js docs](https://discord.js.org/#/docs/discord.js/main/typedef/MessageCreateOptions) for all of the available options.
## Cleaning Up Instances
If you no longer want to use the instance, you can clean it up in a few ways:
@@ -91,7 +89,7 @@ const reacord = new ReacordDiscordJs(client, {
This section also applies to other kinds of application commands, such as context menu commands.
</aside>
To reply to a command interaction, use the `.createInteractionReply()` function. This function returns an instance that works the same way as the one from `.createChannelMessage()` and `.createMessageReply()`. Here's an example:
To reply to a command interaction, use the `.createInteractionReply()` function. This function returns an instance that works the same way as the one from `.createChannelMessage()`. Here's an example:
```jsx
import { Client } from "discord.js"
@@ -163,11 +161,7 @@ handleCommands(client, [
])
```
## Interaction Options
Just like `.createChannelMessage()` and `.createMessageReply()`, interaction replies provide a way to specify certain `interaction.reply()` options.
### Ephemeral Command Replies
## Ephemeral Command Replies
Ephemeral replies are replies that only appear for one user. To create them, use the `.createInteractionReply()` function and provide `ephemeral` option.
@@ -185,7 +179,7 @@ handleCommands(client, [
])
```
### Text-to-Speech Command Replies
## Text-to-Speech Command Replies
Additionally interaction replies may have `tts` option to turn on text-to-speech ability for the reply. To create such reply, use `.createInteractionReply()` function and provide `tts` option.