feat: default select menu values (#9867)

* feat: default select menu values

* feat(Message): support

* fix: fix crashes when an array is supplied and remove assertion

* docs(transformResolved): `BaseChannel` is the correct type

* refactor: prefer assignment

* chore: export function again

* fix(Util): fix circular dependency

* refactor(MentionableSelectMenu): clone in method

* docs: remove semicolon

* feat(MentionableSelectMenu): add `addDefaultValues()`

* refactor: reduce overhead

* types: adjust `channel`

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
This commit is contained in:
Jaw0r3k
2023-11-12 17:32:41 +01:00
committed by GitHub
parent b5e23ec2ec
commit 4ff3ea4a1b
10 changed files with 320 additions and 73 deletions

View File

@@ -2,6 +2,7 @@
const CommandInteraction = require('./CommandInteraction');
const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver');
const { transformResolved } = require('../util/Util');
/**
* Represents a command interaction.
@@ -18,7 +19,7 @@ class ChatInputCommandInteraction extends CommandInteraction {
this.options = new CommandInteractionOptionResolver(
this.client,
data.data.options?.map(option => this.transformOption(option, data.data.resolved)) ?? [],
this.transformResolved(data.data.resolved ?? {}),
transformResolved({ client: this.client, guild: this.guild, channel: this.channel }, data.data.resolved),
);
}

View File

@@ -1,6 +1,5 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const Attachment = require('./Attachment');
const BaseInteraction = require('./BaseInteraction');
const InteractionWebhook = require('./InteractionWebhook');
@@ -91,62 +90,6 @@ class CommandInteraction extends BaseInteraction {
* @property {Collection<Snowflake, Attachment>} [attachments] The resolved attachments
*/
/**
* Transforms the resolved received from the API.
* @param {APIInteractionDataResolved} resolved The received resolved objects
* @returns {CommandInteractionResolvedData}
* @private
*/
transformResolved({ members, users, channels, roles, messages, attachments }) {
const result = {};
if (members) {
result.members = new Collection();
for (const [id, member] of Object.entries(members)) {
const user = users[id];
result.members.set(id, this.guild?.members._add({ user, ...member }) ?? member);
}
}
if (users) {
result.users = new Collection();
for (const user of Object.values(users)) {
result.users.set(user.id, this.client.users._add(user));
}
}
if (roles) {
result.roles = new Collection();
for (const role of Object.values(roles)) {
result.roles.set(role.id, this.guild?.roles._add(role) ?? role);
}
}
if (channels) {
result.channels = new Collection();
for (const channel of Object.values(channels)) {
result.channels.set(channel.id, this.client.channels._add(channel, this.guild) ?? channel);
}
}
if (messages) {
result.messages = new Collection();
for (const message of Object.values(messages)) {
result.messages.set(message.id, this.channel?.messages?._add(message) ?? message);
}
}
if (attachments) {
result.attachments = new Collection();
for (const attachment of Object.values(attachments)) {
const patched = new Attachment(attachment);
result.attachments.set(attachment.id, patched);
}
}
return result;
}
/**
* Represents an option of a received command interaction.
* @typedef {Object} CommandInteractionOption

View File

@@ -4,6 +4,7 @@ const { lazy } = require('@discordjs/util');
const { ApplicationCommandOptionType } = require('discord-api-types/v10');
const CommandInteraction = require('./CommandInteraction');
const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver');
const { transformResolved } = require('../util/Util');
const getMessage = lazy(() => require('./Message').Message);
@@ -21,7 +22,7 @@ class ContextMenuCommandInteraction extends CommandInteraction {
this.options = new CommandInteractionOptionResolver(
this.client,
this.resolveContextMenuOptions(data.data),
this.transformResolved(data.data.resolved),
transformResolved({ client: this.client, guild: this.guild, channel: this.channel }, data.data.resolved),
);
/**

View File

@@ -25,7 +25,7 @@ const { createComponent } = require('../util/Components');
const { NonSystemMessageTypes, MaxBulkDeletableMessageAge, DeletableMessageTypes } = require('../util/Constants');
const MessageFlagsBitField = require('../util/MessageFlagsBitField');
const PermissionsBitField = require('../util/PermissionsBitField');
const { cleanContent, resolvePartialEmoji } = require('../util/Util');
const { cleanContent, resolvePartialEmoji, transformResolved } = require('../util/Util');
/**
* Represents a message on Discord.
@@ -223,6 +223,19 @@ class Message extends Base {
this.roleSubscriptionData ??= null;
}
if ('resolved' in data) {
/**
* Resolved data from auto-populated select menus.
* @typedef {Object} CommandInteractionResolvedData
*/
this.resolved = transformResolved(
{ client: this.client, guild: this.guild, channel: this.channel },
data.resolved,
);
} else {
this.resolved ??= null;
}
// Discord sends null if the message has not been edited
if (data.edited_timestamp) {
/**

View File

@@ -409,6 +409,75 @@ function parseWebhookURL(url) {
};
}
/**
* Supportive data for interaction resolved data.
* @typedef {Object} SupportingInteractionResolvedData
* @property {Client} client The client
* @property {Guild} [guild] A guild
* @property {GuildTextBasedChannel} [channel] A channel
* @private
*/
/**
* Transforms the resolved data received from the API.
* @param {SupportingInteractionResolvedData} supportingData Data to support the transformation
* @param {APIInteractionDataResolved} [data] The received resolved objects
* @returns {CommandInteractionResolvedData}
* @private
*/
function transformResolved(
{ client, guild, channel },
{ members, users, channels, roles, messages, attachments } = {},
) {
const result = {};
if (members) {
result.members = new Collection();
for (const [id, member] of Object.entries(members)) {
const user = users[id];
result.members.set(id, guild?.members._add({ user, ...member }) ?? member);
}
}
if (users) {
result.users = new Collection();
for (const user of Object.values(users)) {
result.users.set(user.id, client.users._add(user));
}
}
if (roles) {
result.roles = new Collection();
for (const role of Object.values(roles)) {
result.roles.set(role.id, guild?.roles._add(role) ?? role);
}
}
if (channels) {
result.channels = new Collection();
for (const apiChannel of Object.values(channels)) {
result.channels.set(apiChannel.id, client.channels._add(apiChannel, guild) ?? apiChannel);
}
}
if (messages) {
result.messages = new Collection();
for (const message of Object.values(messages)) {
result.messages.set(message.id, channel?.messages?._add(message) ?? message);
}
}
if (attachments) {
result.attachments = new Collection();
for (const attachment of Object.values(attachments)) {
const patched = new Attachment(attachment);
result.attachments.set(attachment.id, patched);
}
}
return result;
}
module.exports = {
flatten,
fetchRecommendedShardCount,
@@ -426,7 +495,9 @@ module.exports = {
cleanContent,
cleanCodeBlockContent,
parseWebhookURL,
transformResolved,
};
// Fixes Circular
const Attachment = require('../structures/Attachment');
const GuildChannel = require('../structures/GuildChannel');