Skip to content

Commit

Permalink
feat: add start of message payload (#118)
Browse files Browse the repository at this point in the history
  • Loading branch information
thewilloftheshadow authored Sep 21, 2024
1 parent c20730b commit 9e087a2
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 60 deletions.
6 changes: 6 additions & 0 deletions .changeset/chilly-owls-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@buape/carbon": minor
---

feat: add MessagePayload for replies and message sending
This will allow you to pass just a string to reply with as the content, or the entire message payload.
2 changes: 1 addition & 1 deletion apps/rocko/src/commands/testing/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class PingButton extends Button {
style = ButtonStyle.Primary

async run(interaction: ButtonInteraction) {
await interaction.reply({ content: "OMG YOU CLICKED THE BUTTON" })
await interaction.reply("OMG YOU CLICKED THE BUTTON")
}
}

Expand Down
11 changes: 5 additions & 6 deletions packages/carbon/src/abstracts/BaseComponentInteraction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import {
Routes
} from "discord-api-types/v10"
import type { Client } from "../classes/Client.js"
import { splitCustomId } from "../utils.js"
import { serializePayload, splitCustomId } from "../utils.js"
import {
BaseInteraction,
type InteractionDefaults,
type InteractionReplyData,
type InteractionReplyOptions
} from "./BaseInteraction.js"
import type { MessagePayload } from "../types.js"

export class BaseComponentInteraction extends BaseInteraction<APIMessageComponentInteraction> {
customId: string
Expand Down Expand Up @@ -49,18 +49,17 @@ export class BaseComponentInteraction extends BaseInteraction<APIMessageComponen
* Update the original message of the component
*/
async update(
data: InteractionReplyData,
data: MessagePayload,
options: Pick<InteractionReplyOptions, "files"> = {}
) {
const serialized = serializePayload(data)
await this.client.rest.post(
Routes.interactionCallback(this.rawData.id, this.rawData.token),
{
body: {
type: InteractionResponseType.UpdateMessage,
data: {
...data,
embeds: data.embeds?.map((embed) => embed.serialize()),
components: data.components?.map((row) => row.serialize())
...serialized
}
} as RESTPostAPIInteractionCallbackJSONBody,
files: options.files
Expand Down
7 changes: 4 additions & 3 deletions packages/carbon/src/abstracts/BaseGuildChannel.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
type APIGuildChannel,
type APIMessage,
type GuildChannelType,
type RESTGetAPIGuildInvitesResult,
type RESTPostAPIChannelInviteJSONBody,
Expand All @@ -11,6 +10,8 @@ import { Guild } from "../structures/Guild.js"
import type { GuildCategoryChannel } from "../structures/GuildCategoryChannel.js"
import type { IfPartial } from "../utils.js"
import { BaseChannel } from "./BaseChannel.js"
import type { MessagePayload } from "../types.js"
import { serializePayload } from "../utils.js"

export abstract class BaseGuildChannel<
Type extends GuildChannelType,
Expand Down Expand Up @@ -132,9 +133,9 @@ export abstract class BaseGuildChannel<
/**
* Send a message to the channel
*/
async send(message: APIMessage) {
async send(message: MessagePayload) {
this.client.rest.post(Routes.channelMessages(this.id), {
body: { ...message }
body: serializePayload(message)
})
}

Expand Down
54 changes: 15 additions & 39 deletions packages/carbon/src/abstracts/BaseInteraction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,12 @@ import {
Guild,
Message,
type Modal,
type Row,
User,
channelFactory
} from "../index.js"
import { GuildMember } from "../structures/GuildMember.js"

/**
* The data to reply to an interaction
*/
export type InteractionReplyData = {
/**
* The content of the message
*/
content?: string
/**
* The embeds of the message
*/
embeds?: Embed[]
/**
* The components to send in the message, listed in rows
*/
components?: Row[]
}
import { serializePayload } from "../utils.js"
import type { MessagePayload } from "../types.js"

/**
* Additional options for replying to an interaction
Expand Down Expand Up @@ -148,10 +131,8 @@ export abstract class BaseInteraction<T extends APIInteraction> extends Base {
* If the interaction is deferred, this will edit the original response.
* @param data The response data
*/
async reply(
data: InteractionReplyData,
options: InteractionReplyOptions = {}
) {
async reply(data: MessagePayload, options: InteractionReplyOptions = {}) {
const serialized = serializePayload(data)
if (this._deferred) {
await this.client.rest.patch(
Routes.webhookMessage(
Expand All @@ -161,10 +142,7 @@ export abstract class BaseInteraction<T extends APIInteraction> extends Base {
),
{
body: {
...data,
embeds: data.embeds?.map((embed) => embed.serialize()),
components: data.components?.map((row) => row.serialize()),
flags: options.ephemeral ? 64 : undefined
...serialized
} as RESTPatchAPIInteractionOriginalResponseJSONBody,
files: options.files
}
Expand All @@ -176,10 +154,10 @@ export abstract class BaseInteraction<T extends APIInteraction> extends Base {
body: {
type: InteractionResponseType.ChannelMessageWithSource,
data: {
...data,
embeds: data.embeds?.map((embed) => embed.serialize()),
components: data.components?.map((row) => row.serialize()),
flags: options.ephemeral ? 64 : undefined
...serializePayload(data),
flags: options.ephemeral
? 64 | ("flags" in serialized ? (serialized.flags ?? 0) : 0)
: undefined
}
} as RESTPostAPIInteractionCallbackJSONBody,
files: options.files
Expand Down Expand Up @@ -228,18 +206,16 @@ export abstract class BaseInteraction<T extends APIInteraction> extends Base {
/**
* Send a followup message to the interaction
*/
async followUp(
reply: InteractionReplyData,
options: InteractionReplyOptions = {}
) {
async followUp(reply: MessagePayload, options: InteractionReplyOptions = {}) {
const serialized = serializePayload(reply)
await this.client.rest.post(
Routes.webhook(this.client.options.clientId, this.rawData.token),
{
body: {
...reply,
embeds: reply.embeds?.map((embed) => embed.serialize()),
components: reply.components?.map((row) => row.serialize()),
flags: options.ephemeral ? 64 : undefined
...serialized,
flags: options.ephemeral
? 64 | ("flags" in serialized ? (serialized.flags ?? 0) : 0)
: undefined
} as RESTPostAPIInteractionFollowupJSONBody,
files: options.files
}
Expand Down
4 changes: 2 additions & 2 deletions packages/carbon/src/abstracts/GuildThreadOnlyChannel.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type {
APIGuildForumDefaultReactionEmoji,
APIGuildForumTag,
APIMessage,
APIThreadOnlyChannel,
ChannelType,
SortOrderType,
Expand All @@ -10,6 +9,7 @@ import type {
import { GuildThreadChannel } from "../structures/GuildThreadChannel.js"
import type { IfPartial } from "../utils.js"
import { BaseGuildChannel } from "./BaseGuildChannel.js"
import type { MessagePayload } from "../types.js"

export abstract class GuildThreadOnlyChannel<
Type extends ChannelType.GuildForum | ChannelType.GuildMedia,
Expand Down Expand Up @@ -83,7 +83,7 @@ export abstract class GuildThreadOnlyChannel<
* @remarks
* This is an alias for {@link GuildThreadChannel.send} that will fetch the channel, but if you already have the channel, you can use {@link GuildThreadChannel.send} instead.
*/
async sendToPost(message: APIMessage, postId: string): Promise<void> {
async sendToPost(message: MessagePayload, postId: string): Promise<void> {
const channel = new GuildThreadChannel<ThreadChannelType, true>(
this.client,
postId
Expand Down
8 changes: 4 additions & 4 deletions packages/carbon/src/structures/DmChannel.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
type APIDMChannel,
type APIMessage,
type ChannelType,
Routes
} from "discord-api-types/v10"
import { BaseChannel } from "../abstracts/BaseChannel.js"
import type { IfPartial } from "../utils.js"
import { serializePayload, type IfPartial } from "../utils.js"
import type { MessagePayload } from "../types.js"

/**
* Represents a DM between two users.
Expand All @@ -27,9 +27,9 @@ export class DmChannel<IsPartial extends boolean = false> extends BaseChannel<
/**
* Send a message to the channel
*/
async send(message: APIMessage) {
async send(message: MessagePayload) {
this.client.rest.post(Routes.channelMessages(this.id), {
body: { ...message }
body: serializePayload(message)
})
}
}
9 changes: 4 additions & 5 deletions packages/carbon/src/structures/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import {
type APIDMChannel,
type APIMessage,
type APIUser,
type RESTPostAPIChannelMessageJSONBody,
Routes,
type UserFlags
} from "discord-api-types/v10"
import { Base } from "../abstracts/Base.js"
import type { Client } from "../classes/Client.js"
import type { IfPartial } from "../utils.js"
import { Message } from "./Message.js"
import { serializePayload } from "../utils.js"
import type { MessagePayload } from "../types.js"

export class User<IsPartial extends boolean = false> extends Base {
constructor(
Expand Down Expand Up @@ -172,14 +173,12 @@ export class User<IsPartial extends boolean = false> extends Base {
/**
* Send a message to this user.
*/
async send(data: RESTPostAPIChannelMessageJSONBody) {
async send(data: MessagePayload) {
const dmChannel = await this.createDm(this.id)
const message = (await this.client.rest.post(
Routes.channelMessages(dmChannel.id),
{
body: {
...data
}
body: serializePayload(data)
}
)) as APIMessage
return new Message(this.client, message)
Expand Down
39 changes: 39 additions & 0 deletions packages/carbon/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { Embed } from "./classes/Embed.js"
import type { Row } from "./classes/Row.js"

/**
* The data that is sent to Discord when sending a message.
* If you pass just a string, it will be treated as the content of the message.
*/
export type MessagePayload =
| {
/**
* The content of the message
*/
content?: string
/**
* The embeds of the message
*/
embeds?: Embed[]
/**
* The components to send in the message, listed in rows
*/
components?: Row[]
/**
* The settings for which mentions are allowed in the message
*/
allowedMentions?: {
parse?: ["roles", "users", "everyone"]
roles?: string[]
users?: string[]
}
/**
* The flags for the message
*/
flags?: number
/**
* Whether the message should be TTS
*/
tts?: boolean
}
| string
11 changes: 11 additions & 0 deletions packages/carbon/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export type IfPartial<T, U, V = U | undefined> = T extends true ? V : U
import type { MessagePayload } from "./types.js"

export const splitCustomId = (
customId: string
Expand Down Expand Up @@ -80,3 +81,13 @@ export function concatUint8Arrays(
merged.set(arr2, arr1.length)
return merged
}

export const serializePayload = (payload: MessagePayload) => {
return typeof payload === "string"
? { content: payload }
: {
...payload,
embeds: payload.embeds?.map((embed) => embed.serialize()),
components: payload.components?.map((row) => row.serialize())
}
}

0 comments on commit 9e087a2

Please sign in to comment.