This commit is contained in:
parent
c6b458199c
commit
904842ae32
9 changed files with 340 additions and 79 deletions
|
@ -1,10 +1,10 @@
|
||||||
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js";
|
import { CommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||||
import { Command } from "../type/command";
|
import { Command } from "../type/command";
|
||||||
import EffectHelper from "../helpers/EffectHelper";
|
import { EffectChoices } from "../constants/EffectDetails";
|
||||||
import { EffectDetails } from "../constants/EffectDetails";
|
|
||||||
import TimeLengthInput from "../helpers/TimeLengthInput";
|
|
||||||
import EmbedColours from "../constants/EmbedColours";
|
|
||||||
import AppLogger from "../client/appLogger";
|
import AppLogger from "../client/appLogger";
|
||||||
|
import List from "./effects/List";
|
||||||
|
import Use from "./effects/Use";
|
||||||
|
import Buy from "./effects/Buy";
|
||||||
|
|
||||||
export default class Effects extends Command {
|
export default class Effects extends Command {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -27,9 +27,19 @@ export default class Effects extends Command {
|
||||||
.setName("id")
|
.setName("id")
|
||||||
.setDescription("The effect id to use")
|
.setDescription("The effect id to use")
|
||||||
.setRequired(true)
|
.setRequired(true)
|
||||||
.setChoices([
|
.setChoices(EffectChoices)))
|
||||||
{ name: "Unclaimed Chance Up", value: "unclaimed" },
|
.addSubcommand(x => x
|
||||||
])));
|
.setName("buy")
|
||||||
|
.setDescription("Buy more effects")
|
||||||
|
.addStringOption(y => y
|
||||||
|
.setName("id")
|
||||||
|
.setDescription("The effect id to buy")
|
||||||
|
.setRequired(true)
|
||||||
|
.setChoices(EffectChoices))
|
||||||
|
.addNumberOption(y => y
|
||||||
|
.setName("quantity")
|
||||||
|
.setDescription("The amount to buy")
|
||||||
|
.setMinValue(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async execute(interaction: CommandInteraction) {
|
public override async execute(interaction: CommandInteraction) {
|
||||||
|
@ -39,80 +49,16 @@ export default class Effects extends Command {
|
||||||
|
|
||||||
switch (subcommand) {
|
switch (subcommand) {
|
||||||
case "list":
|
case "list":
|
||||||
await this.List(interaction);
|
await List(interaction);
|
||||||
break;
|
break;
|
||||||
case "use":
|
case "use":
|
||||||
await this.Use(interaction);
|
await Use(interaction);
|
||||||
break;
|
break;
|
||||||
|
case "buy":
|
||||||
|
await Buy(interaction);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
AppLogger.LogError("Commands/Effects", `Invalid subcommand: ${subcommand}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async List(interaction: CommandInteraction) {
|
|
||||||
const pageOption = interaction.options.get("page");
|
|
||||||
|
|
||||||
const page = !isNaN(Number(pageOption?.value)) ? Number(pageOption?.value) : 1;
|
|
||||||
|
|
||||||
const result = await EffectHelper.GenerateEffectEmbed(interaction.user.id, page);
|
|
||||||
|
|
||||||
await interaction.reply({
|
|
||||||
embeds: [ result.embed ],
|
|
||||||
components: [ result.row ],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Use(interaction: CommandInteraction) {
|
|
||||||
const id = interaction.options.get("id", true).value!.toString();
|
|
||||||
|
|
||||||
const effectDetail = EffectDetails.get(id);
|
|
||||||
|
|
||||||
if (!effectDetail) {
|
|
||||||
AppLogger.LogWarn("Commands/Effects", `Unable to find effect details for ${id}`);
|
|
||||||
|
|
||||||
await interaction.reply("Unable to find effect!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const canUseEffect = await EffectHelper.CanUseEffect(interaction.user.id, id);
|
|
||||||
|
|
||||||
if (!canUseEffect) {
|
|
||||||
await interaction.reply("Unable to use effect! Please make sure you have it in your inventory and is not on cooldown");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeLengthInput = TimeLengthInput.ConvertFromMilliseconds(effectDetail.duration);
|
|
||||||
|
|
||||||
const embed = new EmbedBuilder()
|
|
||||||
.setTitle("Effect Confirmation")
|
|
||||||
.setDescription("Would you like to use this effect?")
|
|
||||||
.setColor(EmbedColours.Ok)
|
|
||||||
.addFields([
|
|
||||||
{
|
|
||||||
name: "Effect",
|
|
||||||
value: effectDetail.friendlyName,
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Length",
|
|
||||||
value: timeLengthInput.GetLengthShort(),
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const row = new ActionRowBuilder<ButtonBuilder>()
|
|
||||||
.addComponents([
|
|
||||||
new ButtonBuilder()
|
|
||||||
.setLabel("Confirm")
|
|
||||||
.setCustomId(`effects use confirm ${effectDetail.id}`)
|
|
||||||
.setStyle(ButtonStyle.Primary),
|
|
||||||
new ButtonBuilder()
|
|
||||||
.setLabel("Cancel")
|
|
||||||
.setCustomId(`effects use cancel ${effectDetail.id}`)
|
|
||||||
.setStyle(ButtonStyle.Danger),
|
|
||||||
]);
|
|
||||||
|
|
||||||
await interaction.reply({
|
|
||||||
embeds: [ embed ],
|
|
||||||
components: [ row ],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
4
src/commands/effects/Buy.ts
Normal file
4
src/commands/effects/Buy.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import { CommandInteraction } from "discord.js";
|
||||||
|
|
||||||
|
export default async function Buy(interaction: CommandInteraction) {
|
||||||
|
}
|
15
src/commands/effects/List.ts
Normal file
15
src/commands/effects/List.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { CommandInteraction } from "discord.js";
|
||||||
|
import EffectHelper from "../../helpers/EffectHelper";
|
||||||
|
|
||||||
|
export default async function List(interaction: CommandInteraction) {
|
||||||
|
const pageOption = interaction.options.get("page");
|
||||||
|
|
||||||
|
const page = !isNaN(Number(pageOption?.value)) ? Number(pageOption?.value) : 1;
|
||||||
|
|
||||||
|
const result = await EffectHelper.GenerateEffectEmbed(interaction.user.id, page);
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
embeds: [ result.embed ],
|
||||||
|
components: [ result.row ],
|
||||||
|
});
|
||||||
|
}
|
62
src/commands/effects/Use.ts
Normal file
62
src/commands/effects/Use.ts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder } from "discord.js";
|
||||||
|
import { EffectDetails } from "../../constants/EffectDetails";
|
||||||
|
import AppLogger from "../../client/appLogger";
|
||||||
|
import EffectHelper from "../../helpers/EffectHelper";
|
||||||
|
import TimeLengthInput from "../../helpers/TimeLengthInput";
|
||||||
|
import EmbedColours from "../../constants/EmbedColours";
|
||||||
|
|
||||||
|
export default async function Use(interaction: CommandInteraction) {
|
||||||
|
const id = interaction.options.get("id", true).value!.toString();
|
||||||
|
|
||||||
|
const effectDetail = EffectDetails.get(id);
|
||||||
|
|
||||||
|
if (!effectDetail) {
|
||||||
|
AppLogger.LogWarn("Commands/Effects", `Unable to find effect details for ${id}`);
|
||||||
|
|
||||||
|
await interaction.reply("Unable to find effect!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const canUseEffect = await EffectHelper.CanUseEffect(interaction.user.id, id);
|
||||||
|
|
||||||
|
if (!canUseEffect) {
|
||||||
|
await interaction.reply("Unable to use effect! Please make sure you have it in your inventory and is not on cooldown");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeLengthInput = TimeLengthInput.ConvertFromMilliseconds(effectDetail.duration);
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle("Effect Confirmation")
|
||||||
|
.setDescription("Would you like to use this effect?")
|
||||||
|
.setColor(EmbedColours.Ok)
|
||||||
|
.addFields([
|
||||||
|
{
|
||||||
|
name: "Effect",
|
||||||
|
value: effectDetail.friendlyName,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Length",
|
||||||
|
value: timeLengthInput.GetLengthShort(),
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const row = new ActionRowBuilder<ButtonBuilder>()
|
||||||
|
.addComponents([
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setLabel("Confirm")
|
||||||
|
.setCustomId(`effects use confirm ${effectDetail.id}`)
|
||||||
|
.setStyle(ButtonStyle.Primary),
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setLabel("Cancel")
|
||||||
|
.setCustomId(`effects use cancel ${effectDetail.id}`)
|
||||||
|
.setStyle(ButtonStyle.Danger),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
embeds: [ embed ],
|
||||||
|
components: [ row ],
|
||||||
|
});
|
||||||
|
}
|
|
@ -17,3 +17,7 @@ class EffectDetail {
|
||||||
export const EffectDetails = new Map<string, EffectDetail>([
|
export const EffectDetails = new Map<string, EffectDetail>([
|
||||||
[ "unclaimed", new EffectDetail("unclaimed", "Unclaimed Chance Up", 10 * 60 * 1000, 100, 3 * 60 * 60 * 1000) ],
|
[ "unclaimed", new EffectDetail("unclaimed", "Unclaimed Chance Up", 10 * 60 * 1000, 100, 3 * 60 * 60 * 1000) ],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
export const EffectChoices = [
|
||||||
|
{ name: "Unclaimed Chance Up", value: "unclaimed" },
|
||||||
|
];
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { CommandInteraction } from "../../__types__/discord.js";
|
||||||
|
|
||||||
|
export default function GenerateCommandInteractionMock(options?: {
|
||||||
|
subcommand?: string,
|
||||||
|
}): CommandInteraction {
|
||||||
|
return {
|
||||||
|
isChatInputCommand: jest.fn().mockReturnValue(true),
|
||||||
|
options: {
|
||||||
|
getSubcommand: jest.fn().mockReturnValue(options?.subcommand),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -15,3 +15,10 @@ export type ButtonInteraction = {
|
||||||
} | null,
|
} | null,
|
||||||
customId: string,
|
customId: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CommandInteraction = {
|
||||||
|
isChatInputCommand: jest.Func,
|
||||||
|
options: {
|
||||||
|
getSubcommand: jest.Func,
|
||||||
|
},
|
||||||
|
}
|
106
tests/commands/__snapshots__/effects.test.ts.snap
Normal file
106
tests/commands/__snapshots__/effects.test.ts.snap
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`EXPECT CommandBuilder to be defined 1`] = `
|
||||||
|
{
|
||||||
|
"contexts": undefined,
|
||||||
|
"default_member_permissions": undefined,
|
||||||
|
"default_permission": undefined,
|
||||||
|
"description": "Effects",
|
||||||
|
"description_localizations": undefined,
|
||||||
|
"dm_permission": undefined,
|
||||||
|
"integration_types": undefined,
|
||||||
|
"name": "effects",
|
||||||
|
"name_localizations": undefined,
|
||||||
|
"nsfw": undefined,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"description": "List all effects I have",
|
||||||
|
"description_localizations": undefined,
|
||||||
|
"name": "list",
|
||||||
|
"name_localizations": undefined,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"autocomplete": undefined,
|
||||||
|
"choices": undefined,
|
||||||
|
"description": "The page number",
|
||||||
|
"description_localizations": undefined,
|
||||||
|
"max_value": undefined,
|
||||||
|
"min_value": 1,
|
||||||
|
"name": "page",
|
||||||
|
"name_localizations": undefined,
|
||||||
|
"required": false,
|
||||||
|
"type": 10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"type": 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Use an effect in your inventory",
|
||||||
|
"description_localizations": undefined,
|
||||||
|
"name": "use",
|
||||||
|
"name_localizations": undefined,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"autocomplete": undefined,
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"name": "Unclaimed Chance Up",
|
||||||
|
"name_localizations": undefined,
|
||||||
|
"value": "unclaimed",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"description": "The effect id to use",
|
||||||
|
"description_localizations": undefined,
|
||||||
|
"max_length": undefined,
|
||||||
|
"min_length": undefined,
|
||||||
|
"name": "id",
|
||||||
|
"name_localizations": undefined,
|
||||||
|
"required": true,
|
||||||
|
"type": 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"type": 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Buy more effects",
|
||||||
|
"description_localizations": undefined,
|
||||||
|
"name": "buy",
|
||||||
|
"name_localizations": undefined,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"autocomplete": undefined,
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"name": "Unclaimed Chance Up",
|
||||||
|
"name_localizations": undefined,
|
||||||
|
"value": "unclaimed",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"description": "The effect id to buy",
|
||||||
|
"description_localizations": undefined,
|
||||||
|
"max_length": undefined,
|
||||||
|
"min_length": undefined,
|
||||||
|
"name": "id",
|
||||||
|
"name_localizations": undefined,
|
||||||
|
"required": true,
|
||||||
|
"type": 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autocomplete": undefined,
|
||||||
|
"choices": undefined,
|
||||||
|
"description": "The amount to buy",
|
||||||
|
"description_localizations": undefined,
|
||||||
|
"max_value": undefined,
|
||||||
|
"min_value": 1,
|
||||||
|
"name": "quantity",
|
||||||
|
"name_localizations": undefined,
|
||||||
|
"required": false,
|
||||||
|
"type": 10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"type": 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"type": 1,
|
||||||
|
}
|
||||||
|
`;
|
105
tests/commands/effects.test.ts
Normal file
105
tests/commands/effects.test.ts
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import Effects from "../../src/commands/effects";
|
||||||
|
import List from "../../src/commands/effects/List";
|
||||||
|
import Use from "../../src/commands/effects/Use";
|
||||||
|
import Buy from "../../src/commands/effects/Buy";
|
||||||
|
import AppLogger from "../../src/client/appLogger";
|
||||||
|
import GenerateCommandInteractionMock from "../__functions__/discord.js/GenerateCommandInteractionMock";
|
||||||
|
import { CommandInteraction } from "discord.js";
|
||||||
|
|
||||||
|
jest.mock("../../src/commands/effects/List");
|
||||||
|
jest.mock("../../src/commands/effects/Use");
|
||||||
|
jest.mock("../../src/commands/effects/Buy");
|
||||||
|
jest.mock("../../src/client/appLogger");
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("EXPECT CommandBuilder to be defined", async () => {
|
||||||
|
// Act
|
||||||
|
const effects = new Effects();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(effects.CommandBuilder).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("execute", () => {
|
||||||
|
test("GIVEN interaction subcommand is list, EXPECT buy function called", async () => {
|
||||||
|
// Arrange
|
||||||
|
const interaction = GenerateCommandInteractionMock({
|
||||||
|
subcommand: "list",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const effects = new Effects();
|
||||||
|
await effects.execute(interaction as unknown as CommandInteraction);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(List).toHaveBeenCalledTimes(1);
|
||||||
|
expect(List).toHaveBeenCalledWith(interaction);
|
||||||
|
|
||||||
|
expect(Use).not.toHaveBeenCalled();
|
||||||
|
expect(Buy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
expect(AppLogger.LogError).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("GIVEN interaction subcommand is use, EXPECT buy function called", async () => {
|
||||||
|
// Arrange
|
||||||
|
const interaction = GenerateCommandInteractionMock({
|
||||||
|
subcommand: "use",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const effects = new Effects();
|
||||||
|
await effects.execute(interaction as unknown as CommandInteraction);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(Use).toHaveBeenCalledTimes(1);
|
||||||
|
expect(Use).toHaveBeenCalledWith(interaction);
|
||||||
|
|
||||||
|
expect(List).not.toHaveBeenCalled();
|
||||||
|
expect(Buy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
expect(AppLogger.LogError).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("GIVEN interaction subcommand is buy, EXPECT buy function called", async () => {
|
||||||
|
// Arrange
|
||||||
|
const interaction = GenerateCommandInteractionMock({
|
||||||
|
subcommand: "buy",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const effects = new Effects();
|
||||||
|
await effects.execute(interaction as unknown as CommandInteraction);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(Buy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(Buy).toHaveBeenCalledWith(interaction);
|
||||||
|
|
||||||
|
expect(List).not.toHaveBeenCalled();
|
||||||
|
expect(Use).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
expect(AppLogger.LogError).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("GIVEN interaction subcommand is invalid, EXPECT error logged", async () => {
|
||||||
|
// Arrange
|
||||||
|
const interaction = GenerateCommandInteractionMock({
|
||||||
|
subcommand: "invalid",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const effects = new Effects();
|
||||||
|
await effects.execute(interaction as unknown as CommandInteraction);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(AppLogger.LogError).toHaveBeenCalledTimes(1);
|
||||||
|
expect(AppLogger.LogError).toHaveBeenCalledWith("Commands/Effects", "Invalid subcommand: invalid");
|
||||||
|
|
||||||
|
expect(List).not.toHaveBeenCalled();
|
||||||
|
expect(Use).not.toHaveBeenCalled();
|
||||||
|
expect(Buy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue