diff --git a/src/helpers/interactions/send_interaction_response.ts b/src/helpers/interactions/send_interaction_response.ts index 65cdf4d7a..9a7e8e890 100644 --- a/src/helpers/interactions/send_interaction_response.ts +++ b/src/helpers/interactions/send_interaction_response.ts @@ -39,12 +39,60 @@ export async function sendInteractionResponse( return await bot.rest.runMethod(bot.rest, "post", bot.constants.endpoints.WEBHOOK(bot.applicationId, token), { content: options.data.content, tts: options.data.tts, - embeds: options.data.embeds, + embeds: options.data.embeds?.map((embed) => ({ + title: embed.title, + type: embed.type, + description: embed.description, + url: embed.url, + timestamp: embed.timestamp, + color: embed.color, + footer: embed.footer + ? { + text: embed.footer.text, + icon_url: embed.footer.iconUrl, + proxy_icon_url: embed.footer.proxyIconUrl, + } + : undefined, + image: embed.image + ? { + url: embed.image.url, + proxy_url: embed.image.proxyUrl, + height: embed.image.height, + width: embed.image.width, + } + : undefined, + thumbnail: embed.thumbnail + ? { + url: embed.thumbnail.url, + proxy_url: embed.thumbnail.proxyUrl, + height: embed.thumbnail.height, + width: embed.thumbnail.width, + } + : undefined, + video: embed.video + ? { + url: embed.video.url, + proxy_url: embed.video.proxyUrl, + height: embed.video.height, + width: embed.video.width, + } + : undefined, + provider: embed.provider, + author: embed.author + ? { + name: embed.author.name, + url: embed.author.url, + icon_url: embed.author.iconUrl, + proxy_icon_url: embed.author.proxyIconUrl, + } + : undefined, + fields: embed.fields, + })), allowed_mentions: { - parse: allowedMentions.parse, - roles: allowedMentions.roles, - users: allowedMentions.users, - replied_user: allowedMentions.repliedUser, + parse: allowedMentions?.parse, + roles: allowedMentions?.roles, + users: allowedMentions?.users, + replied_user: allowedMentions?.repliedUser, }, file: options.data.file, // TODO: Snakelize components?? @@ -67,12 +115,60 @@ export async function sendInteractionResponse( { content: options.data.content, tts: options.data.tts, - embeds: options.data.embeds, + embeds: options.data.embeds?.map((embed) => ({ + title: embed.title, + type: embed.type, + description: embed.description, + url: embed.url, + timestamp: embed.timestamp, + color: embed.color, + footer: embed.footer + ? { + text: embed.footer.text, + icon_url: embed.footer.iconUrl, + proxy_icon_url: embed.footer.proxyIconUrl, + } + : undefined, + image: embed.image + ? { + url: embed.image.url, + proxy_url: embed.image.proxyUrl, + height: embed.image.height, + width: embed.image.width, + } + : undefined, + thumbnail: embed.thumbnail + ? { + url: embed.thumbnail.url, + proxy_url: embed.thumbnail.proxyUrl, + height: embed.thumbnail.height, + width: embed.thumbnail.width, + } + : undefined, + video: embed.video + ? { + url: embed.video.url, + proxy_url: embed.video.proxyUrl, + height: embed.video.height, + width: embed.video.width, + } + : undefined, + provider: embed.provider, + author: embed.author + ? { + name: embed.author.name, + url: embed.author.url, + icon_url: embed.author.iconUrl, + proxy_icon_url: embed.author.proxyIconUrl, + } + : undefined, + fields: embed.fields, + })), allowed_mentions: { - parse: allowedMentions.parse, - roles: allowedMentions.roles, - users: allowedMentions.users, - replied_user: allowedMentions.repliedUser, + parse: allowedMentions?.parse, + roles: allowedMentions?.roles, + users: allowedMentions?.users, + replied_user: allowedMentions?.repliedUser, }, file: options.data.file, // TODO: Snakelize components?? diff --git a/src/transformers/interaction.ts b/src/transformers/interaction.ts index e7dc45590..a104aad1c 100644 --- a/src/transformers/interaction.ts +++ b/src/transformers/interaction.ts @@ -22,6 +22,8 @@ export function transformInteraction(bot: Bot, payload: SnakeCasedPropertiesDeep message: payload.message ? bot.transformers.message(bot, payload.message) : undefined, channelId: payload.channel_id ? bot.transformers.snowflake(payload.channel_id) : undefined, member: payload.member && guildId ? bot.transformers.member(bot, payload.member, guildId, user.id) : undefined, + // TODO: CamelCase INTERACTION DATA + data: payload.data, }; } diff --git a/tests/helpers/messages/deleteMessages.ts b/tests/helpers/messages/deleteMessages.ts new file mode 100644 index 000000000..592417057 --- /dev/null +++ b/tests/helpers/messages/deleteMessages.ts @@ -0,0 +1,36 @@ +import { Bot } from "../../../src/bot.ts"; +import { assertExists } from "../../deps.ts"; +import { delayUntil } from "../../utils.ts"; + +async function ifItFailsBlameWolf(bot: Bot, channelId: bigint, reason?: string) { + const message = await bot.helpers.sendMessage(channelId, "Hello World!"); + const secondMessage = await bot.helpers.sendMessage(channelId, "Hello World 2!"); + + // Assertions + assertExists(message); + assertExists(secondMessage); + // Delay the execution by to allow MESSAGE_CREATE event to be processed + await delayUntil(10000, () => bot.cache.messages.has(message.id) && bot.cache.messages.has(secondMessage.id)); + // Make sure the message was created. + if (!bot.cache.messages.has(message.id) || !bot.cache.messages.has(secondMessage.id)) { + throw new Error(`The message seemed to be sent but it was not cached. Reason: ${reason}`); + } + + // Delete the messages now + await bot.helpers.deleteMessages(channelId, [message.id, secondMessage.id], reason); + + // Wait to give it time for MESSAGE_DELETE event + await delayUntil(10000, () => !bot.cache.messages.has(message.id) && !bot.cache.messages.has(secondMessage.id)); + // Make sure they are gone from cache + if (bot.cache.messages.has(message.id) || bot.cache.messages.has(secondMessage.id)) { + throw new Error("The messages should have been deleted but they are still in cache."); + } +} + +export async function deleteMessagesWithoutReasonTest(bot: Bot, channelId: bigint, t: Deno.TestContext) { + await ifItFailsBlameWolf(bot, channelId); +} + +export async function deleteMessagesWithReasonTest(bot: Bot, channelId: bigint, t: Deno.TestContext) { + await ifItFailsBlameWolf(bot, channelId, "with a reason"); +} diff --git a/tests/helpers/messages/getMessage.ts b/tests/helpers/messages/getMessage.ts new file mode 100644 index 000000000..58c037322 --- /dev/null +++ b/tests/helpers/messages/getMessage.ts @@ -0,0 +1,22 @@ +import { Bot } from "../../../src/bot.ts"; +import { assertEquals, assertExists } from "../../deps.ts"; +import { delayUntil } from "../../utils.ts"; + +export async function getMessageTest(bot: Bot, channelId: bigint, t: Deno.TestContext) { + const message = await bot.helpers.sendMessage(channelId, "Hello World!"); + + // Assertions + assertExists(message); + // Delay the execution by to allow MESSAGE_CREATE event to be processed + await delayUntil(10000, () => bot.cache.messages.has(message.id)); + // Make sure the message was created. + if (!bot.cache.messages.has(message.id)) { + throw new Error("The message seemed to be sent but it was not cached. Reason: ${reason}"); + } + + // Fetch the message + const fetchedMessage = await bot.helpers.getMessage(channelId, message.id); + // Check if getMessage has worked + assertEquals(fetchedMessage.id, message.id); + assertEquals(fetchedMessage.content, message.content); +} diff --git a/tests/helpers/messages/getMessages.ts b/tests/helpers/messages/getMessages.ts new file mode 100644 index 000000000..187a044a3 --- /dev/null +++ b/tests/helpers/messages/getMessages.ts @@ -0,0 +1,38 @@ +import { Bot } from "../../../src/bot.ts"; +import { assertEquals, assertExists } from "../../deps.ts"; +import { delayUntil } from "../../utils.ts"; + +export async function getMessagesTest(bot: Bot, channelId: bigint, t: Deno.TestContext) { + const message = await bot.helpers.sendMessage(channelId, "Hello World!"); + const secondMessage = await bot.helpers.sendMessage(channelId, "Hello World 2!"); + const thirdMessage = await bot.helpers.sendMessage(channelId, "Hello World 3!"); + + // Assertions + assertExists(message); + assertExists(secondMessage); + assertExists(thirdMessage); + // Delay the execution by to allow MESSAGE_CREATE event to be processed + await delayUntil( + 10000, + () => + bot.cache.messages.has(message.id) && + bot.cache.messages.has(secondMessage.id) && + bot.cache.messages.has(thirdMessage.id) + ); + // Make sure the messages was created. + if ( + !bot.cache.messages.has(message.id) || + !bot.cache.messages.has(secondMessage.id) || + !bot.cache.messages.has(thirdMessage.id) + ) { + throw new Error("The message seemed to be sent but it was not cached."); + } + + // Fetch the messages + const fetchedMessages = await bot.helpers.getMessages(channelId, { + after: message.id, + limit: 2, + }); + // Check if getMessage has worked + assertEquals(fetchedMessages?.length, 2); +} diff --git a/tests/helpers/messages/sendMessage.ts b/tests/helpers/messages/sendMessage.ts new file mode 100644 index 000000000..47e471111 --- /dev/null +++ b/tests/helpers/messages/sendMessage.ts @@ -0,0 +1,89 @@ +import { Bot } from "../../../src/bot.ts"; +import { assertExists } from "../../deps.ts"; +import { delayUntil } from "../../utils.ts"; +import { CreateMessage } from "../../../src/types/messages/create_message.ts"; +import { DiscordMessageComponentTypes } from "https://raw.githubusercontent.com/discordeno/discordeno/cool-stuff/src/types/messages/components/message_component_types.ts"; +import { DiscordButtonStyles } from "https://raw.githubusercontent.com/discordeno/discordeno/cool-stuff/src/types/messages/components/button_styles.ts"; + +async function ifItFailsBlameWolf(bot: Bot, channelId: bigint, content: string | CreateMessage) { + const message = await bot.helpers.sendMessage(channelId, content); + // Assertions + assertExists(message); + // Delay the execution by to allow MESSAGE_CREATE event to be processed + await delayUntil(10000, () => bot.cache.messages.has(message.id)); + // Make sure the message was created. + if (!bot.cache.messages.has(message.id)) { + throw new Error("The message seemed to be sent but it was not cached."); + } +} + +export async function sendMessageWithTextTest(bot: Bot, channelId: bigint, t: Deno.TestContext) { + await ifItFailsBlameWolf(bot, channelId, "Hello World!"); +} + +export async function sendMessageWithComponents(bot: Bot, channelId: bigint, t: Deno.TestContext) { + await ifItFailsBlameWolf(bot, channelId, { + content: "Hello World!", + components: [ + { + type: DiscordMessageComponentTypes.ActionRow, + components: [ + { + type: DiscordMessageComponentTypes.Button, + label: "Doc", + style: DiscordButtonStyles.Link, + url: `https://discordeno.mod.land/`, + }, + { + type: DiscordMessageComponentTypes.Button, + label: "Server", + style: DiscordButtonStyles.Link, + url: `https://discord.gg/ddeno`, + }, + ], + }, + { + type: DiscordMessageComponentTypes.ActionRow, + components: [ + { + type: DiscordMessageComponentTypes.Button, + label: "Hi", + customId: `hi`, + style: DiscordButtonStyles.Primary, + }, + ], + }, + ], + }); +} + +export async function sendMessageWithEmbedsTest(bot: Bot, channelId: bigint, t: Deno.TestContext) { + await ifItFailsBlameWolf(bot, channelId, { + embeds: [ + { + title: "Hello World", + description: + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam at cursus libero. Sed egestas nec ligula sit amet sollicitudin. Curabitur.", + color: 0x00ff00, + footer: { + text: "Discordeno Best Lib", + }, + author: { + name: "Cacahe", + }, + }, + { + title: "Goodbye World", + description: + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean libero enim, blandit tincidunt magna non, auctor pellentesque lacus. Nulla diam.", + color: 0x0000ff, + footer: { + text: "Discordeno Best Lib", + }, + author: { + name: "Wolf", + }, + }, + ], + }); +} diff --git a/tests/local.ts b/tests/local.ts index 5858743f4..bba4ce566 100644 --- a/tests/local.ts +++ b/tests/local.ts @@ -1,4 +1,4 @@ import "./local/snowflake.ts"; -import "./util/validateLength.ts"; -import "./util/hasProperty.ts"; +import "./util/validate_length.ts"; +import "./util/utils.ts"; import "./util/hash.ts"; diff --git a/tests/mod.ts b/tests/mod.ts index 6ba923f98..0927d5ee2 100644 --- a/tests/mod.ts +++ b/tests/mod.ts @@ -2,11 +2,19 @@ import { TOKEN } from "../configs.ts"; import { createBot, createEventHandlers, DiscordChannelTypes, startBot, stopBot } from "../mod.ts"; import { assertEquals, assertExists } from "./deps.ts"; import { deleteMessageWithReasonTest, deleteMessageWithoutReasonTest } from "./helpers/messages/deleteMessage.ts"; +import { getMessagesTest } from "./helpers/messages/getMessages.ts"; +import { deleteMessagesWithoutReasonTest, deleteMessagesWithReasonTest } from "./helpers/messages/deleteMessages.ts"; import { editMessageTest } from "./helpers/messages/editMessage.ts"; import { delayUntil } from "./utils.ts"; +import { + sendMessageWithComponents, + sendMessageWithEmbedsTest, + sendMessageWithTextTest, +} from "./helpers/messages/sendMessage.ts"; // CONDUCT LOCAL TESTS FIRST BEFORE RUNNING API TEST import "./local.ts"; +import { getMessageTest } from "./helpers/messages/getMessage.ts"; Deno.test("[Bot] - Starting Tests", async (t) => { // CHANGE TO TRUE WHEN DEBUGGING SANITIZATION ERRORS @@ -77,20 +85,29 @@ Deno.test("[Bot] - Starting Tests", async (t) => { // ALL MESSAGE RELATED TESTS THAT DEPEND ON AN EXISTING CHANNEL await t.step("Message related tests", async (t) => { - const message = await bot.helpers.sendMessage(channel.id, "Testing"); - - // Assertions - assertExists(message); - - // Delay the execution to allow MESSAGE_CREATE event to be processed - await delayUntil(10000, () => bot.cache.messages.has(message.id)); - - if (!bot.cache.messages.has(message.id)) { - throw new Error("The message seemed to be sent but it was not cached."); - } - // CONDUCT ALL TESTS RELATED TO A MESSAGE HERE await Promise.all([ + t.step({ + name: "[message] send message with text", + fn: async (t) => { + await sendMessageWithTextTest(bot, channel.id, t); + }, + ...sanitizeMode, + }), + t.step({ + name: "[message] send message with embeds", + fn: async (t) => { + await sendMessageWithEmbedsTest(bot, channel.id, t); + }, + ...sanitizeMode, + }), + t.step({ + name: "[message] send message with components", + fn: async (t) => { + await sendMessageWithComponents(bot, channel.id, t); + }, + ...sanitizeMode, + }), t.step({ name: "[message] delete message without a reason", fn: async (t) => { @@ -105,6 +122,34 @@ Deno.test("[Bot] - Starting Tests", async (t) => { }, ...sanitizeMode, }), + t.step({ + name: "[message] delete messages without a reason", + fn: async (t) => { + await deleteMessagesWithoutReasonTest(bot, channel.id, t); + }, + ...sanitizeMode, + }), + t.step({ + name: "[message] delete messages with a reason", + fn: async (t) => { + await deleteMessagesWithReasonTest(bot, channel.id, t); + }, + ...sanitizeMode, + }), + t.step({ + name: "[message] fetch a message", + fn: async (t) => { + await getMessageTest(bot, channel.id, t); + }, + ...sanitizeMode, + }), + t.step({ + name: "[message] fetch messages", + fn: async (t) => { + await getMessagesTest(bot, channel.id, t); + }, + ...sanitizeMode, + }), t.step({ name: "[message] edit message", fn: async (t) => { diff --git a/tests/util/hasProperty.ts b/tests/util/utils.ts similarity index 51% rename from tests/util/hasProperty.ts rename to tests/util/utils.ts index 7cf0bd412..8ec25bee2 100644 --- a/tests/util/hasProperty.ts +++ b/tests/util/utils.ts @@ -1,5 +1,8 @@ -import { hasProperty } from "../../src/util/utils.ts"; +import { hasProperty, delay } from "../../src/util/utils.ts"; import { assertEquals } from "../deps.ts"; + +// hasProperty + const obj = { prop: "lts372005" }; Deno.test({ name: "[utils] hasProperty does HAVE property", @@ -13,3 +16,15 @@ Deno.test({ assertEquals(hasProperty(obj, "lts372005"), false); }, }); + +// delay + +Deno.test({ + name: "[utils] delay 2000 ms", + async fn() { + const before = Date.now(); + await delay(2000); + const after = Date.now(); + if (after - before < 2000) throw new Error(`delay(2000) delayed ${after - before}ms`); + }, +}); diff --git a/tests/util/validateLength.ts b/tests/util/validate_length.ts similarity index 100% rename from tests/util/validateLength.ts rename to tests/util/validate_length.ts