diff --git a/packages/builders/__tests__/messages/formatters.test.ts b/packages/builders/__tests__/messages/formatters.test.ts index febe7c4e3f95..92a291f81ac7 100644 --- a/packages/builders/__tests__/messages/formatters.test.ts +++ b/packages/builders/__tests__/messages/formatters.test.ts @@ -3,6 +3,7 @@ import { describe, test, expect, vitest } from 'vitest'; import { blockQuote, bold, + channelLink, channelMention, codeBlock, Faces, @@ -11,6 +12,7 @@ import { hyperlink, inlineCode, italic, + messageLink, quote, roleMention, spoiler, @@ -150,6 +152,34 @@ describe('Message formatters', () => { }); }); + describe('channelLink', () => { + test('GIVEN channelId THEN returns "https://discord.com/channels/@me/${channelId}"', () => { + expect<'https://discord.com/channels/@me/123456789012345678'>(channelLink('123456789012345678')).toEqual( + 'https://discord.com/channels/@me/123456789012345678', + ); + }); + + test('GIVEN channelId WITH guildId THEN returns "https://discord.com/channels/${guildId}/${channelId}"', () => { + expect<'https://discord.com/channels/987654321987654/123456789012345678'>( + channelLink('123456789012345678', '987654321987654'), + ).toEqual('https://discord.com/channels/987654321987654/123456789012345678'); + }); + }); + + describe('messageLink', () => { + test('GIVEN channelId AND messageId THEN returns "https://discord.com/channels/@me/${channelId}/${messageId}"', () => { + expect<'https://discord.com/channels/@me/123456789012345678/102938475657483'>( + messageLink('123456789012345678', '102938475657483'), + ).toEqual('https://discord.com/channels/@me/123456789012345678/102938475657483'); + }); + + test('GIVEN channelId AND messageId WITH guildId THEN returns "https://discord.com/channels/${guildId}/${channelId}/${messageId}"', () => { + expect<'https://discord.com/channels/987654321987654/123456789012345678/102938475657483'>( + messageLink('123456789012345678', '102938475657483', '987654321987654'), + ).toEqual('https://discord.com/channels/987654321987654/123456789012345678/102938475657483'); + }); + }); + describe('time', () => { test('GIVEN no arguments THEN returns ""', () => { vitest.useFakeTimers(); diff --git a/packages/builders/src/messages/formatters.ts b/packages/builders/src/messages/formatters.ts index e5bf6dfd0969..b2822c87730f 100644 --- a/packages/builders/src/messages/formatters.ts +++ b/packages/builders/src/messages/formatters.ts @@ -207,6 +207,63 @@ export function formatEmoji(emojiId: C, animated = false): return `<${animated ? 'a' : ''}:_:${emojiId}>`; } +/** + * Formats a channel link for a direct message channel. + * + * @param channelId - The channel's id + */ +export function channelLink(channelId: C): `https://discord.com/channels/@me/${C}`; + +/** + * Formats a channel link for a guild channel. + * + * @param channelId - The channel's id + * @param guildId - The guild's id + */ +export function channelLink( + channelId: C, + guildId: G, +): `https://discord.com/channels/${G}/${C}`; + +export function channelLink( + channelId: C, + guildId?: G, +): `https://discord.com/channels/@me/${C}` | `https://discord.com/channels/${G}/${C}` { + return `https://discord.com/channels/${guildId ?? '@me'}/${channelId}`; +} + +/** + * Formats a message link for a direct message channel. + * + * @param channelId - The channel's id + * @param messageId - The message's id + */ +export function messageLink( + channelId: C, + messageId: M, +): `https://discord.com/channels/@me/${C}/${M}`; + +/** + * Formats a message link for a guild channel. + * + * @param channelId - The channel's id + * @param messageId - The message's id + * @param guildId - The guild's id + */ +export function messageLink( + channelId: C, + messageId: M, + guildId: G, +): `https://discord.com/channels/${G}/${C}/${M}`; + +export function messageLink( + channelId: C, + messageId: M, + guildId?: G, +): `https://discord.com/channels/@me/${C}/${M}` | `https://discord.com/channels/${G}/${C}/${M}` { + return `${typeof guildId === 'undefined' ? channelLink(channelId) : channelLink(channelId, guildId)}/${messageId}`; +} + /** * Formats a date into a short date-time string * diff --git a/packages/discord.js/src/structures/BaseChannel.js b/packages/discord.js/src/structures/BaseChannel.js index 76ed7c7e9a2d..49d611436e19 100644 --- a/packages/discord.js/src/structures/BaseChannel.js +++ b/packages/discord.js/src/structures/BaseChannel.js @@ -1,5 +1,6 @@ 'use strict'; +const { channelLink } = require('@discordjs/builders'); const { DiscordSnowflake } = require('@sapphire/snowflake'); const { ChannelType, Routes } = require('discord-api-types/v10'); const Base = require('./Base'); @@ -55,7 +56,7 @@ class BaseChannel extends Base { * @readonly */ get url() { - return `https://discord.com/channels/${this.isDMBased() ? '@me' : this.guildId}/${this.id}`; + return this.isDMBased() ? channelLink(this.id) : channelLink(this.id, this.guildId); } /** diff --git a/packages/discord.js/src/structures/Message.js b/packages/discord.js/src/structures/Message.js index 5e4753021f9d..2ce960c03aa8 100644 --- a/packages/discord.js/src/structures/Message.js +++ b/packages/discord.js/src/structures/Message.js @@ -1,5 +1,6 @@ 'use strict'; +const { messageLink } = require('@discordjs/builders'); const { Collection } = require('@discordjs/collection'); const { DiscordSnowflake } = require('@sapphire/snowflake'); const { @@ -439,7 +440,7 @@ class Message extends Base { * @readonly */ get url() { - return `https://discord.com/channels/${this.guildId ?? '@me'}/${this.channelId}/${this.id}`; + return this.inGuild() ? messageLink(this.channelId, this.id, this.guildId) : messageLink(this.channelId, this.id); } /** diff --git a/packages/discord.js/src/util/Formatters.js b/packages/discord.js/src/util/Formatters.js index 7a188286e8ca..9d4c28240c92 100644 --- a/packages/discord.js/src/util/Formatters.js +++ b/packages/discord.js/src/util/Formatters.js @@ -132,6 +132,23 @@ const { * @returns {string} */ +/** + * Formats a channel link for a channel. + * @method channelLink + * @param {Snowflake} channelId The id of the channel + * @param {Snowflake} [guildId] The id of the guild + * @returns {string} + */ + +/** + * Formats a message link for a channel. + * @method messageLink + * @param {Snowflake} channelId The id of the channel + * @param {Snowflake} messageId The id of the message + * @param {Snowflake} [guildId] The id of the guild + * @returns {string} + */ + /** * A message formatting timestamp style, as defined in * [here](https://discord.com/developers/docs/reference#message-formatting-timestamp-styles).