From da1c62f2f03be728606caf75cbdeeb7add8515c5 Mon Sep 17 00:00:00 2001 From: itsMapleLeaf <19603573+itsMapleLeaf@users.noreply.github.com> Date: Sat, 28 Oct 2023 14:34:09 -0500 Subject: [PATCH] public interface tweaks and such --- .../library/core/reacord-discord-js.ts | 158 +++++------------- .../renderers/interaction-reply-renderer.ts | 17 +- .../src/content/guides/0-getting-started.md | 13 +- .../src/content/guides/1-sending-messages.md | 30 ++-- 4 files changed, 75 insertions(+), 143 deletions(-) diff --git a/packages/reacord/library/core/reacord-discord-js.ts b/packages/reacord/library/core/reacord-discord-js.ts index 3f6dadc..7c846f6 100644 --- a/packages/reacord/library/core/reacord-discord-js.ts +++ b/packages/reacord/library/core/reacord-discord-js.ts @@ -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, ): 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) diff --git a/packages/reacord/library/internal/renderers/interaction-reply-renderer.ts b/packages/reacord/library/internal/renderers/interaction-reply-renderer.ts index b1c986c..e521286 100644 --- a/packages/reacord/library/internal/renderers/interaction-reply-renderer.ts +++ b/packages/reacord/library/internal/renderers/interaction-reply-renderer.ts @@ -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() +export type InteractionReplyRendererImplementation = { + interactionId: string + reply: (options: MessageOptions) => Promise + followUp: (options: MessageOptions) => Promise +} + export class InteractionReplyRenderer extends Renderer { - constructor(private interaction: Interaction) { + constructor(private implementation: InteractionReplyRendererImplementation) { super() } protected createMessage(options: MessageOptions): Promise { - 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) } } diff --git a/packages/website/src/content/guides/0-getting-started.md b/packages/website/src/content/guides/0-getting-started.md index 4a46233..c87e251 100644 --- a/packages/website/src/content/guides/0-getting-started.md +++ b/packages/website/src/content/guides/0-getting-started.md @@ -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 ``` diff --git a/packages/website/src/content/guides/1-sending-messages.md b/packages/website/src/content/guides/1-sending-messages.md index 7ef8520..330a8a2 100644 --- a/packages/website/src/content/guides/1-sending-messages.md +++ b/packages/website/src/content/guides/1-sending-messages.md @@ -36,7 +36,6 @@ function Uptime() { client.on("ready", () => { const instance = reacord.createChannelMessage(channel) - instance.render() }) ``` @@ -48,26 +47,25 @@ const Hello = ({ subject }) => <>Hello, {subject}! client.on("ready", () => { const instance = reacord.createChannelMessage(channel) - instance.render() instance.render() }) ``` -## 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() +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. -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.