Skip to content

Commit

Permalink
feat: forwarding messages
Browse files Browse the repository at this point in the history
  • Loading branch information
TAEMBO committed Jan 1, 2025
1 parent bc3a0c8 commit 3601a22
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 18 deletions.
31 changes: 29 additions & 2 deletions packages/discord.js/src/managers/MessageManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

const { Collection } = require('@discordjs/collection');
const { makeURLSearchParams } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v10');
const { MessageReferenceType, Routes } = require('discord-api-types/v10');
const CachedManager = require('./CachedManager');
const { DiscordjsTypeError, ErrorCodes } = require('../errors');
const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors');
const { Message } = require('../structures/Message');
const MessagePayload = require('../structures/MessagePayload');
const { MakeCacheOverrideSymbol } = require('../util/Symbols');
Expand Down Expand Up @@ -209,6 +209,33 @@ class MessageManager extends CachedManager {
return this.cache.get(data.id) ?? this._add(data);
}

/**
* Forwards a message to this manager's channel.
* @param {Message|MessageReference} reference The message to forward
* @returns {Promise<Message>}
*/
async forward(reference) {
if (!reference) throw new DiscordjsError(ErrorCodes.MessageReferenceMissing);
const message_id = this.resolveId(reference.messageId);
if (!message_id) throw new DiscordjsError(ErrorCodes.MessageReferenceMissing);
const channel_id = this.client.channels.resolveId(reference.channelId);
if (!channel_id) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'ChannelResolvable');
const guild_id = this.client.guilds.resolveId(reference.guildId);

const data = await this.client.rest.post(Routes.channelMessages(this.channel.id), {
body: {
message_reference: {
message_id,
channel_id,
guild_id,
type: MessageReferenceType.Forward,
},
},
});

return this.cache.get(data.id) ?? this._add(data);
}

/**
* Pins a message to the channel's pinned messages, even if it's not cached.
* @param {MessageResolvable} message The message to pin
Expand Down
24 changes: 21 additions & 3 deletions packages/discord.js/src/structures/Message.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const {
ChannelType,
MessageType,
MessageFlags,
MessageReferenceType,
PermissionFlagsBits,
} = require('discord-api-types/v10');
const Attachment = require('./Attachment');
Expand All @@ -20,7 +21,7 @@ const MessagePayload = require('./MessagePayload');
const { Poll } = require('./Poll.js');
const ReactionCollector = require('./ReactionCollector');
const { Sticker } = require('./Sticker');
const { DiscordjsError, ErrorCodes } = require('../errors');
const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors');
const ReactionManager = require('../managers/ReactionManager');
const { createComponent } = require('../util/Components');
const { NonSystemMessageTypes, MaxBulkDeletableMessageAge, UndeletableMessageTypes } = require('../util/Constants');
Expand Down Expand Up @@ -767,6 +768,20 @@ class Message extends Base {
return message;
}

/**
* Forwards this message.
* @param {ChannelResolvable} channel The channel to forward this message to
* @returns {Promise<Message>}
*/
async forward(channel) {
const resolvedChannel = this.client.channels.resolve(channel);

if (!resolvedChannel) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'ChannelResolvable');

const message = await resolvedChannel.messages.forward(this);
return message;
}

/**
* Whether the message is crosspostable by the client user
* @type {boolean}
Expand Down Expand Up @@ -920,8 +935,11 @@ class Message extends Base {
data = options;
} else {
data = MessagePayload.create(this, options, {
reply: {
messageReference: this,
messageReference: {
messageId: this.id,
channelId: this.channelId,
guildId: this.guildId,
type: MessageReferenceType.Default,
failIfNotExists: options?.failIfNotExists ?? this.client.options.failIfNotExists,
},
});
Expand Down
14 changes: 10 additions & 4 deletions packages/discord.js/src/structures/MessagePayload.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,19 @@ class MessagePayload {
}

let message_reference;
if (typeof this.options.reply === 'object') {
const reference = this.options.reply.messageReference;
const message_id = this.isMessage ? (reference.id ?? reference) : this.target.messages.resolveId(reference);
if (this.options.messageReference) {
const reference = this.options.messageReference;
const message_id = this.target.messages.resolveId(reference.messageId);
const channel_id = this.target.client.channels.resolveId(reference.channelId);
const guild_id = this.target.client.guilds.resolveId(reference.guildId);

if (message_id) {
message_reference = {
message_id,
fail_if_not_exists: this.options.reply.failIfNotExists ?? this.target.client.options.failIfNotExists,
channel_id,
guild_id,
type: reference.type,
fail_if_not_exists: reference.failIfNotExists ?? this.target.client.options.failIfNotExists,
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class TextBasedChannel {
/**
* The options for sending a message.
* @typedef {BaseMessageCreateOptions} MessageCreateOptions
* @property {ReplyOptions} [reply] The options for replying to a message
* @property {MessageReference|MessageResolvable} [messageReference] The options for a reference to a message
*/

/**
Expand Down
14 changes: 6 additions & 8 deletions packages/discord.js/typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2242,6 +2242,7 @@ export class Message<InGuild extends boolean = boolean> extends Base {
public equals(message: Message, rawData: unknown): boolean;
public fetchReference(): Promise<OmitPartialGroupDMChannel<Message<InGuild>>>;
public fetchWebhook(): Promise<Webhook>;
public forward(channel: TextBasedChannelResolvable): Promise<Message>;
public crosspost(): Promise<OmitPartialGroupDMChannel<Message<InGuild>>>;
public fetch(force?: boolean): Promise<OmitPartialGroupDMChannel<Message<InGuild>>>;
public pin(reason?: string): Promise<OmitPartialGroupDMChannel<Message<InGuild>>>;
Expand Down Expand Up @@ -4423,6 +4424,7 @@ export abstract class MessageManager<InGuild extends boolean = boolean> extends
public fetch(options: MessageResolvable | FetchMessageOptions): Promise<Message<InGuild>>;
public fetch(options?: FetchMessagesOptions): Promise<Collection<Snowflake, Message<InGuild>>>;
public fetchPinned(cache?: boolean): Promise<Collection<Snowflake, Message<InGuild>>>;
public forward(reference: Omit<MessageReference, 'type'>): Promise<Message<InGuild>>;
public react(message: MessageResolvable, emoji: EmojiIdentifierResolvable): Promise<void>;
public pin(message: MessageResolvable, reason?: string): Promise<void>;
public unpin(message: MessageResolvable, reason?: string): Promise<void>;
Expand Down Expand Up @@ -6394,7 +6396,7 @@ export interface MessageCreateOptions extends BaseMessageOptionsWithPoll {
tts?: boolean;
nonce?: string | number;
enforceNonce?: boolean;
reply?: ReplyOptions;
messageReference?: MessageReference & { failIfNotExists?: boolean };
stickers?: readonly StickerResolvable[];
flags?: BitFieldResolvable<
Extract<MessageFlagsString, 'SuppressEmbeds' | 'SuppressNotifications'>,
Expand Down Expand Up @@ -6640,12 +6642,7 @@ export interface ReactionCollectorOptions extends CollectorOptions<[MessageReact
maxUsers?: number;
}

export interface ReplyOptions {
messageReference: MessageResolvable;
failIfNotExists?: boolean;
}

export interface MessageReplyOptions extends Omit<MessageCreateOptions, 'reply'> {
export interface MessageReplyOptions extends Omit<MessageCreateOptions, 'messageReference'> {
failIfNotExists?: boolean;
}

Expand Down Expand Up @@ -6924,7 +6921,8 @@ export interface WebhookFetchMessageOptions {
threadId?: Snowflake;
}

export interface WebhookMessageCreateOptions extends Omit<MessageCreateOptions, 'nonce' | 'reply' | 'stickers'> {
export interface WebhookMessageCreateOptions
extends Omit<MessageCreateOptions, 'nonce' | 'messageReference' | 'stickers'> {
username?: string;
avatarURL?: string;
threadId?: Snowflake;
Expand Down

0 comments on commit 3601a22

Please sign in to comment.