Skip to content

Commit

Permalink
Add ticket presets
Browse files Browse the repository at this point in the history
  • Loading branch information
Rian8337 committed Feb 5, 2024
1 parent 4f7378b commit 0cdb3fd
Show file tree
Hide file tree
Showing 16 changed files with 329 additions and 5 deletions.
9 changes: 9 additions & 0 deletions src/database/AliceDBCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { DanCourseLeaderboardScoreCollectionManager } from "./managers/aliceDb/D
import { DanCourseScoreCollectionManager } from "./managers/aliceDb/DanCourseScoreCollectionManager";
import { RecentPlaysCollectionManager } from "./managers/aliceDb/RecentPlaysCollectionManager";
import { SupportTicketCollectionManager } from "./managers/aliceDb/SupportTicketCollectionManager";
import { SupportTicketPresetCollectionManager } from "./managers/aliceDb/SupportTicketPresetCollectionManager";

/**
* Contains collections from Alice DB.
Expand Down Expand Up @@ -187,6 +188,11 @@ export class AliceDBCollection {
*/
readonly supportTicket: SupportTicketCollectionManager;

/**
* The database collection for support ticket presets.
*/
readonly supportTicketPreset: SupportTicketPresetCollectionManager;

/**
* @param aliceDb The database that is only used by this bot (my database).
*/
Expand Down Expand Up @@ -281,5 +287,8 @@ export class AliceDBCollection {
this.supportTicket = new SupportTicketCollectionManager(
aliceDb.collection("supportticket"),
);
this.supportTicketPreset = new SupportTicketPresetCollectionManager(
aliceDb.collection("supportticketpreset"),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { DatabaseSupportTicketPreset } from "@alice-structures/database/aliceDb/DatabaseSupportTicketPreset";
import { DatabaseCollectionManager } from "../DatabaseCollectionManager";
import { SupportTicketPreset } from "@alice-database/utils/aliceDb/SupportTicketPreset";
import { ApplicationCommandOptionChoiceData } from "discord.js";

export class SupportTicketPresetCollectionManager extends DatabaseCollectionManager<
DatabaseSupportTicketPreset,
SupportTicketPreset
> {
protected override readonly utilityInstance = SupportTicketPreset;

override get defaultDocument(): DatabaseSupportTicketPreset {
return {
id: 0,
name: "",
title: "",
description: "",
};
}

/**
* Searches presets based on its name for autocomplete response.
*
* @param name The name to search.
* @param amount The maximum amount of names to return. Defaults to 25.
* @returns The name of the presets that match the query.
*/
async searchPresets(
name: string | RegExp,
amount: number = 25,
): Promise<ApplicationCommandOptionChoiceData<string>[]> {
let regExp: RegExp;

try {
regExp = new RegExp(name, "i");
} catch {
return [];
}

const result = await this.collection
.find({ name: regExp }, { projection: { _id: 0, name: 1 } })
.limit(amount)
.toArray();

return result.map((v) => {
return {
name: v.name,
value: v.name,
};
});
}
}
24 changes: 24 additions & 0 deletions src/database/utils/aliceDb/SupportTicketPreset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { DatabaseSupportTicketPreset } from "@alice-structures/database/aliceDb/DatabaseSupportTicketPreset";
import { Manager } from "@alice-utils/base/Manager";
import { ObjectId } from "mongodb";

export class SupportTicketPreset
extends Manager
implements DatabaseSupportTicketPreset
{
readonly id: number;
readonly name: string;
readonly title: string;
readonly description: string;
readonly _id?: ObjectId;

constructor(data: DatabaseSupportTicketPreset) {
super();

this._id = data._id;
this.id = data.id;
this.name = data.name;
this.title = data.title;
this.description = data.description;
}
}
2 changes: 2 additions & 0 deletions src/events/interactionCreate/utils/runCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ export const run: EventUtil["run"] = async (
),
});

consola.error(e);

client.emit("error", e);
});
};
Expand Down
14 changes: 14 additions & 0 deletions src/interactions/autocomplete/General/ticket/ticket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { DatabaseManager } from "@alice-database/DatabaseManager";
import { AutocompleteHandler } from "@alice-structures/core/AutocompleteHandler";

export const run: AutocompleteHandler["run"] = async (_, interaction) => {
interaction.respond(
await DatabaseManager.aliceDb.collections.supportTicketPreset.searchPresets(
interaction.options.getFocused(),
),
);
};

export const config: AutocompleteHandler["config"] = {
name: "ticket",
};
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ export const run: ButtonCommand["run"] = async (_, interaction) => {

export const config: ButtonCommand["config"] = {
cooldown: 300,
instantDeferInDebug: false,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { DatabaseManager } from "@alice-database/DatabaseManager";
import { CreateSupportTicketWithPresetLocalization } from "@alice-localization/interactions/buttons/Support Ticket/createSupportTicketWithPreset/CreateSupportTicketWithPresetLocalization";
import { ButtonCommand } from "@alice-structures/core/ButtonCommand";
import { MessageCreator } from "@alice-utils/creators/MessageCreator";
import { ModalCreator } from "@alice-utils/creators/ModalCreator";
import { SelectMenuCreator } from "@alice-utils/creators/SelectMenuCreator";
import { CommandHelper } from "@alice-utils/helpers/CommandHelper";
import { InteractionHelper } from "@alice-utils/helpers/InteractionHelper";
import { TextInputBuilder, TextInputStyle } from "discord.js";

export const run: ButtonCommand["run"] = async (_, interaction) => {
const dbManager = DatabaseManager.aliceDb.collections.supportTicketPreset;
const language = await CommandHelper.getLocale(interaction);
const localization = new CreateSupportTicketWithPresetLocalization(
language,
);

await InteractionHelper.deferReply(interaction);

const ticketPresetsSearch = await dbManager.get(
"id",
{},
{ projection: { _id: 0, id: 1, name: 1 } },
);

if (ticketPresetsSearch.size === 0) {
return InteractionHelper.reply(interaction, {
content: MessageCreator.createReject(
localization.getTranslation("noTicketPresetsExist"),
),
});
}

const selectMenuInteraction =
await SelectMenuCreator.createStringSelectMenu(
interaction,
{
content: MessageCreator.createWarn(
localization.getTranslation("selectPresetPrompt"),
),
},
ticketPresetsSearch.map((v) => {
return {
value: v.id.toString(),
label: v.name,
};
}),
[interaction.user.id],
60,
);

if (!selectMenuInteraction) {
return;
}

const preset = await dbManager.getOne(
{
id: parseInt(selectMenuInteraction.values[0]),
},
{ projection: { _id: 0, title: 1, description: 1 } },
);
if (!preset) {
return InteractionHelper.reply(interaction, {
content: MessageCreator.createReject(
localization.getTranslation("presetNotFound"),
),
});
}

await ModalCreator.createModal(
selectMenuInteraction,
"ticket-create",
localization.getTranslation("modalTitle"),
new TextInputBuilder()
.setCustomId("title")
.setRequired(true)
.setStyle(TextInputStyle.Short)
.setMaxLength(100)
.setPlaceholder(
localization.getTranslation("modalTitlePlaceholder"),
)
.setLabel(localization.getTranslation("modalTitleLabel"))
.setValue(preset.title),
new TextInputBuilder()
.setCustomId("description")
.setRequired(true)
.setStyle(TextInputStyle.Paragraph)
.setMaxLength(1500)
.setPlaceholder(
localization.getTranslation("modalDescriptionPlaceholder"),
)
.setLabel(localization.getTranslation("modalDescriptionLabel"))
.setValue(preset.description),
);

// Delete the left-over select menu, which is a reply to the original interaction.
await interaction.deleteReply();
};

export const config: ButtonCommand["config"] = {
cooldown: 300,
replyEphemeral: true,
};
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,5 @@ export const run: ButtonCommand["run"] = async (_, interaction) => {
export const config: ButtonCommand["config"] = {
cooldown: 5,
replyEphemeral: true,
instantDeferInDebug: false,
};
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
import { DatabaseManager } from "@alice-database/DatabaseManager";
import { TicketLocalization } from "@alice-localization/interactions/commands/General/ticket/TicketLocalization";
import { SlashSubcommand } from "@alice-structures/core/SlashSubcommand";
import { MessageCreator } from "@alice-utils/creators/MessageCreator";
import { ModalCreator } from "@alice-utils/creators/ModalCreator";
import { CommandHelper } from "@alice-utils/helpers/CommandHelper";
import { InteractionHelper } from "@alice-utils/helpers/InteractionHelper";
import { TextInputBuilder, TextInputStyle } from "discord.js";

export const run: SlashSubcommand<true>["run"] = async (_, interaction) => {
const localization = new TicketLocalization(
await CommandHelper.getLocale(interaction),
);

// TODO: ticket presets
const presetName = interaction.options.getString("preset");

let prefilledTitle = "";
let prefilledDescription = "";

if (presetName) {
const preset =
await DatabaseManager.aliceDb.collections.supportTicketPreset.getOne(
{ name: presetName },
{ projection: { _id: 0, title: 1, description: 1 } },
);

if (!preset) {
return InteractionHelper.reply(interaction, {
content: MessageCreator.createReject(
localization.getTranslation("presetNotFound"),
),
});
}

prefilledTitle = preset.title;
prefilledDescription = preset.description;
}

ModalCreator.createModal(
interaction,
Expand All @@ -20,19 +45,30 @@ export const run: SlashSubcommand<true>["run"] = async (_, interaction) => {
.setRequired(true)
.setStyle(TextInputStyle.Short)
.setMaxLength(100)
.setLabel(localization.getTranslation("ticketModalTitleLabel")),
.setPlaceholder(
localization.getTranslation("ticketModalTitlePlaceholder"),
)
.setLabel(localization.getTranslation("ticketModalTitleLabel"))
.setValue(prefilledTitle),
new TextInputBuilder()
.setCustomId("description")
.setRequired(true)
.setStyle(TextInputStyle.Paragraph)
.setMaxLength(1500)
.setPlaceholder(
localization.getTranslation(
"ticketModalDescriptionPlaceholder",
),
)
.setLabel(
localization.getTranslation("ticketModalDescriptionLabel"),
),
)
.setValue(prefilledDescription),
);
};

export const config: SlashSubcommand["config"] = {
permissions: [],
cooldown: 30,
instantDeferInDebug: false,
};
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,6 @@ export const run: SlashSubcommand<true>["run"] = async (_, interaction) => {

export const config: SlashSubcommand["config"] = {
permissions: [],
cooldown: 5,
instantDeferInDebug: false,
};
12 changes: 10 additions & 2 deletions src/interactions/commands/General/ticket/ticket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ export const config: SlashCommand["config"] = {
name: "create",
type: ApplicationCommandOptionType.Subcommand,
description: "Creates a new ticket.",
options: [
{
name: "preset",
type: ApplicationCommandOptionType.String,
description: "The preset to use.",
autocomplete: true,
},
],
},
{
name: "edit",
Expand Down Expand Up @@ -81,7 +89,7 @@ export const config: SlashCommand["config"] = {
name: "author",
type: ApplicationCommandOptionType.User,
description:
"The user who originally opened the ticket. If unspecified, will default to the ticket in the channel.",
"The user who opened the ticket. If unspecified, will default to the ticket in the channel.",
},
{
name: "id",
Expand Down Expand Up @@ -130,7 +138,7 @@ export const config: SlashCommand["config"] = {
name: "author",
type: ApplicationCommandOptionType.User,
description:
"The user who originally opened the ticket. If unspecified, will default to the ticket in the channel.",
"The user who opened the ticket. If unspecified, will default to the ticket in the channel.",
},
{
name: "id",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Localization } from "@alice-localization/base/Localization";
import { Translations } from "@alice-localization/base/Translations";
import { CreateSupportTicketWithPresetENTranslation } from "./translations/CreateSupportTicketWithPresetENTranslation";

export interface CreateSupportTicketWithPresetStrings {
readonly noTicketPresetsExist: string;
readonly selectPresetPrompt: string;
readonly presetNotFound: string;
readonly modalTitle: string;
readonly modalTitleLabel: string;
readonly modalTitlePlaceholder: string;
readonly modalDescriptionLabel: string;
readonly modalDescriptionPlaceholder: string;
}

/**
* Localizations for the `createSupportTicketWithPreset` button command.
*/
export class CreateSupportTicketWithPresetLocalization extends Localization<CreateSupportTicketWithPresetStrings> {
protected override readonly localizations: Readonly<
Translations<CreateSupportTicketWithPresetStrings>
> = {
en: new CreateSupportTicketWithPresetENTranslation(),
};
}
Loading

0 comments on commit 0cdb3fd

Please sign in to comment.