Skip to content

Commit

Permalink
v0.26.6
Browse files Browse the repository at this point in the history
- Add Ratings ticket
- Add new cmd ticketstats
  • Loading branch information
LucasB25 committed Jul 25, 2024
1 parent 625ad6b commit bf3d333
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 3 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "AikouTicket",
"version": "0.25.5",
"version": "0.26.6",
"description": "A simple AikouTicket bot for discord",
"type": "module",
"main": "dist/index.js",
Expand Down
Binary file modified prisma/aikouticket.db
Binary file not shown.
5 changes: 5 additions & 0 deletions prisma/example.mongodb.schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ model ticketsGuild {
selectMenuOptions String
}

model ticketStats {
channelId String @id @map("_id")
rating Float @default(0)
}

model ticketsInfo {
id String @id @default(cuid()) @map("_id")
channelId String @unique
Expand Down
5 changes: 5 additions & 0 deletions prisma/example.postgresql.schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ model ticketsGuild {
selectMenuOptions String
}

model ticketStats {
channelId String @id
rating Float @default(0)
}

model ticketsInfo {
id String @id @default(uuid())
channelId String @unique
Expand Down
5 changes: 5 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ model ticketsGuild {
selectMenuOptions String
}

model ticketStats {
channelId String @id
rating Float @default(0)
}

model ticketsInfo {
id String @id @default(uuid())
channelId String @unique
Expand Down
58 changes: 58 additions & 0 deletions src/commands/tickets/Ticketstats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { type Bot, Command, type Context } from "../../structures/index.js";
import { EmbedBuilder } from "discord.js";

export default class TicketStats extends Command {
constructor(client: Bot) {
super(client, {
name: "ticketstats",
nameLocalizations: {
fr: "statistiquestickets",
},
description: {
content: "Get the current ticket statistics",
usage: "ticketstats",
examples: ["ticketstats"],
},
descriptionLocalizations: {
fr: "Obtenez les statistiques actuelles des tickets.",
},
category: "general",
permissions: {
dev: false,
client: ["SendMessages", "ViewChannel", "EmbedLinks"],
user: ["ManageGuild"],
},
cooldown: 3,
options: [],
});
}

async run(_client: Bot, ctx: Context): Promise<void> {
try {
const allTicketStats = await this.client.db.getAllTicketStats();

if (allTicketStats.length === 0) {
await ctx.sendMessage({ content: "No ticket statistics available." });
return;
}

const totalRatings = allTicketStats.reduce((sum, stat) => sum + stat.rating, 0);
const averageRating = totalRatings / allTicketStats.length;

const embed = new EmbedBuilder()
.setTitle("Ticket Statistics")
.setColor("#FFD700")
.addFields(
{ name: "Total Tickets", value: `${allTicketStats.length}`, inline: true },
{ name: "Total Ratings", value: `${totalRatings}`, inline: true },
{ name: "Average Rating", value: `${averageRating.toFixed(2)}`, inline: true },
)
.setTimestamp();

await ctx.sendMessage({ embeds: [embed] });
} catch (error) {
this.client.logger.error(`Failed to retrieve ticket statistics: ${error.message}`);
await ctx.sendMessage({ content: "There was an error retrieving the ticket statistics." });
}
}
}
38 changes: 37 additions & 1 deletion src/database/server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PrismaClient, type ticketsGuild, type ticketsInfo } from "@prisma/client";
import { PrismaClient, type ticketsGuild, type ticketsInfo, type ticketStats } from "@prisma/client";

export default class ServerData {
private prisma: PrismaClient;
Expand Down Expand Up @@ -70,4 +70,40 @@ export default class ServerData {
return [];
});
}

public async getTicketStats(channelId: string): Promise<ticketStats | null> {
return await this.prisma.ticketStats.findUnique({ where: { channelId } });
}

public async saveTicketStats(channelId: string, rating: number = 0): Promise<void> {
await this.prisma.ticketStats
.upsert({
where: { channelId },
update: { rating },
create: { channelId, rating },
})
.catch((error) => console.error("Error saving ticket stats:", error));
}

public async updateTicketStats(channelId: string, rating: number): Promise<void> {
await this.prisma.ticketStats
.update({
where: { channelId },
data: { rating },
})
.catch((error) => console.error(`Error updating ticket stats for channel ${channelId}:`, error));
}

public async deleteTicketStats(channelId: string): Promise<void> {
await this.prisma.ticketStats
.delete({ where: { channelId } })
.catch((error) => console.error("Error deleting ticket stats:", error));
}

public async getAllTicketStats(): Promise<ticketStats[]> {
return this.prisma.ticketStats.findMany().catch((error) => {
console.error("Error retrieving all ticket stats:", error);
return [];
});
}
}
58 changes: 57 additions & 1 deletion src/events/client/InteractionCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export default class InteractionCreate extends Event {
await this.handleSelectMenuInteraction(interaction);
} else if (interaction.isButton()) {
await this.handleButtonInteraction(interaction);
} else if (interaction.isStringSelectMenu() && interaction.customId.startsWith("ratingMenu-")) {
await this.handleRatingSelectMenu(interaction);
}
} catch (error) {
this.client.logger.error(`Failed to handle interaction: ${error.message}`);
Expand Down Expand Up @@ -259,6 +261,7 @@ export default class InteractionCreate extends Event {
const creator = interaction.guild.members.cache.find((member) => member.user.username === ticket.creator);
if (enableNotifyTicketCreator && creator) {
await this.notifyTicketCreator(interaction, creator, reason, ticketChannel);
await this.sendRatingMenu(creator, ticketChannel);
} else if (!creator) {
this.client.logger.error(`Failed to find creator of ticket ${ticketChannel.id}.`);
}
Expand Down Expand Up @@ -298,6 +301,7 @@ export default class InteractionCreate extends Event {
const creator = interaction.guild.members.cache.find((member) => member.user.username === ticket.creator);
if (enableNotifyTicketCreator && creator) {
await this.notifyTicketCreator(interaction, creator, reason, ticketChannel);
await this.sendRatingMenu(creator, ticketChannel);
} else if (!creator) {
this.client.logger.error(`Failed to find creator of ticket ${ticketChannel.id}.`);
}
Expand Down Expand Up @@ -347,13 +351,65 @@ export default class InteractionCreate extends Event {
.setThumbnail(interaction.guild.iconURL({ format: "png", size: 1024 }))
.setFooter({ text: "Ticket System", iconURL: interaction.user.displayAvatarURL({ extension: "png", size: 1024 }) })
.setTimestamp();

await user.send({ embeds: [embed] });
} catch (error) {
this.client.logger.error("Failed to send DM to ticket creator:", error);
}
}

private async sendRatingMenu(user: any, ticketChannel: TextChannel): Promise<void> {
const ratingMenu = new StringSelectMenuBuilder()
.setCustomId(`ratingMenu-${ticketChannel.id}`)
.setPlaceholder("Rate your support ticket experience")
.addOptions(
{ label: "⭐ 1 Star", value: "1" },
{ label: "⭐⭐ 2 Stars", value: "2" },
{ label: "⭐⭐⭐ 3 Stars", value: "3" },
{ label: "⭐⭐⭐⭐ 4 Stars", value: "4" },
{ label: "⭐⭐⭐⭐⭐ 5 Stars", value: "5" },
);

const row = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(ratingMenu);

const embed = new EmbedBuilder()
.setColor("#FFD700")
.setTitle("Rate Your Ticket Experience")
.setDescription("Please rate your experience with our support team by selecting a rating below.");

try {
await user.send({ embeds: [embed], components: [row] });
} catch (error) {
this.client.logger.error("Failed to send rating menu:", error);
}
}

private async handleRatingSelectMenu(interaction: SelectMenuInteraction): Promise<void> {
const channelId = interaction.customId.split("-")[1];
const rating = parseInt(interaction.values[0], 10);

if (isNaN(rating) || rating < 1 || rating > 5) {
await interaction.reply({
content: "Invalid rating. Please select a rating between 1 and 5 stars.",
ephemeral: true,
});
return;
}

try {
await this.client.db.updateTicketStats(channelId, rating);
await interaction.reply({
content: "Thank you for rating your support ticket experience!",
ephemeral: true,
});
} catch (error) {
this.client.logger.error("Failed to save ticket rating:", error);
await interaction.reply({
content: `An error occurred: ${error.message}`,
ephemeral: true,
});
}
}

private async handleTranscriptTicketButton(interaction: any): Promise<void> {
const ticketChannel = interaction.channel;
await LogsManager.logTicketTranscript(interaction, this.client, ticketChannel);
Expand Down
1 change: 1 addition & 0 deletions src/utils/TicketManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export class TicketManager {

await LogsManager.logTicketCreation(interaction, categoryLabel, client, channel);
await client.db.saveTicketInfo(channel.id, userName);
await client.db.saveTicketStats(channel.id);

return channel;
} catch (error) {
Expand Down

0 comments on commit bf3d333

Please sign in to comment.