В настоящее время я работаю над ботом Discord, я новичок в TypeScript, и мне нужна помощь с моим классом команд...
Это мой текущий класс:
export class Command {
constructor(
public options: {
...
data: ApplicationCommandData & {
type: ApplicationCommandType;
contexts?: Context[];
integration_types?: IntegrationTypes[];
};
execute: (options: { client: DiscordClient; interaction: CommandInteraction }) => any;
...
}
) {}
}
Вот как его использовать:
export default new Command({
data: {
name: 'ping',
description: 'Replies with Pong!',
type: ApplicationCommandType.ChatInput,
contexts: [Context.GUILD, Context.BOT_DM, Context.PRIVATE_CHANNEL],
integration_types: [IntegrationTypes.GUILD_INSTALL, IntegrationTypes.USER_INSTALL],
},
execute({ interaction}) {
interaction.reply({ content: 'Pong!', ephemeral: true });
},
});
Проблема в том, что независимо от типа (User
, Message
или ChatInput
) команды, взаимодействие всегда будет просто базовым CommandInteraction
, и из нее невозможно получить строковые параметры (interaction.options.getString()
). Мне пришлось бы поставить if (!interaction.isChatInputCommand()) return;
вверху каждой ChatInputCommand, которая использует строковые параметры.
Теперь мне нужна помощь в создании универсального класса (если возможно) для решения этой проблемы. Я хочу, чтобы тип моего класса/команды определял тип взаимодействия функции выполнения.
Вот грубый (не работающий) пример того, что я имею в виду:
export class Command<Type> {
constructor(
public options: {
...
data: ApplicationCommandData & {
type: Type;
contexts?: Context[];
integration_types?: IntegrationTypes[];
};
execute: (options: { client: DiscordClient; interaction: interactions[type] }) => any;
...
}
) {}
}
Я хочу, чтобы тип был взят из свойства command.options.data.type
и использовался для прохождения через объект/массив взаимодействий, чтобы получить соответствующее взаимодействие для типа команды!
Пожалуйста, дайте мне знать, если это возможно.
В противном случае мне просто придется сделать по 3 класса для каждого типа команды (User
, Message
, ChatInput
)...
Я перепробовал много разных вещей, но в итоге не нашел решения.
Решил эту проблему с помощью
Это мой новый класс:
import {
ApplicationCommandType,
AutocompleteInteraction,
ChatInputCommandInteraction,
MessageContextMenuCommandInteraction,
UserContextMenuCommandInteraction,
type RESTPostAPIApplicationCommandsJSONBody,
} from 'discord.js';
import { DiscordClient } from './client';
export enum Contexts {
GUILD = 0,
BOT_DM = 1,
PRIVATE_CHANNEL = 2,
}
export enum IntegrationTypes {
GUILD_INSTALL = 0,
USER_INSTALL = 1,
}
type InteractionType<T extends ApplicationCommandType> = T extends ApplicationCommandType.ChatInput
? ChatInputCommandInteraction
: T extends ApplicationCommandType.Message
? MessageContextMenuCommandInteraction
: T extends ApplicationCommandType.User
? UserContextMenuCommandInteraction
: never;
export class Command<T extends ApplicationCommandType = ApplicationCommandType.ChatInput> {
constructor(
public options: {
developerOnly?: boolean; // If command is for developer only, it cannot be used by anyone else
cooldown?: number; // Cooldown between command executes per user (in milliseconds)
data: RESTPostAPIApplicationCommandsJSONBody & {
type: T;
contexts?: Contexts[];
integration_types?: IntegrationTypes[];
};
autocomplete?: (options: { client: DiscordClient; interaction: AutocompleteInteraction }) => any;
execute: (options: { client: DiscordClient; interaction: InteractionType<T> }) => any;
}
) {}
}
И это автоматически дает мне правильный тип взаимодействия для моих взаимодействий в зависимости от типа команды.
Вот пример ChatInputCommand и UserContextMenuCommand:
import { ActionRowBuilder, ApplicationCommandType, ButtonBuilder, ButtonStyle, Colors, EmbedBuilder } from 'discord.js';
import { Command, Contexts, IntegrationTypes } from 'classes/command';
export default new Command({
data: {
name: 'ping',
description: 'Replies with Pong!',
type: ApplicationCommandType.ChatInput,
contexts: [Contexts.GUILD, Contexts.BOT_DM, Contexts.PRIVATE_CHANNEL],
integration_types: [IntegrationTypes.GUILD_INSTALL, IntegrationTypes.USER_INSTALL],
},
async execute({ interaction, client }) {
const sent = await interaction.reply({ content: 'Pinging...', fetchReply: true });
await interaction.editReply({
content: '',
embeds: [
new EmbedBuilder()
.setColor(Colors.Blurple)
.setTitle(i18next.t('Pong!', { lng }))
.addFields(
{ name: 'Websocket Hearbeat', value: `${client.ws.ping}` },
{ name: 'Roundtrip Latency', value: `${sent.createdTimestamp - interaction.createdTimestamp}ms` }
),
],
});
},
});
import { ApplicationCommandType, Colors, EmbedBuilder } from 'discord.js';
import { Command, Contexts, IntegrationTypes } from 'classes/command';
export default new Command({
data: {
name: 'Avatar & Banner',
type: ApplicationCommandType.User,
contexts: [Contexts.GUILD, Contexts.BOT_DM, Contexts.PRIVATE_CHANNEL],
integration_types: [IntegrationTypes.GUILD_INSTALL, IntegrationTypes.USER_INSTALL],
},
async execute({ interaction, client }) {
await interaction.deferReply({ ephemeral: true });
try {
const user = await client.users.fetch(interaction.targetId, { force: true });
if (!user) return interaction.editReply('Could not find user');
const member = await interaction.guild?.members.fetch(user.id);
const embeds: EmbedBuilder[] = [
new EmbedBuilder()
.setImage(user.displayAvatarURL({ size: 4096 })),
];
if (user.banner)
embeds.push(
new EmbedBuilder()
.setImage(user.bannerURL({ size: 4096 })!)
);
if (member && member.avatar)
embeds.push(
new EmbedBuilder()
.setImage(member.displayAvatarURL({ size: 4096 }))
);
interaction.editReply({ embeds });
} catch (err) {
interaction.editReply('Something went wrong');
}
},
});
Дополнительную информацию об исходном коде см. проверьте мой репозиторий GitHub: https://github.com/CuteNikki/discord-bot