DiscordJS 命令的 TypeScript 通用类

问题描述 投票:0回答:1

我目前正在开发一个 Discord Bot,我对 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
)...

我尝试了很多不同的东西,但最终找不到解决方案。

javascript typescript class generics discord.js
1个回答
0
投票

在 ChatGPT 的帮助下解决了这个问题

这是我的新班级:

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

© www.soinside.com 2019 - 2024. All rights reserved.