mirror of
https://github.com/Hessenuk/DiscordTickets.git
synced 2024-11-17 09:23:07 +02:00
feat: feedback, start of close requests
This commit is contained in:
parent
d7e1b05586
commit
8bf01aa520
@ -101,10 +101,9 @@ model Feedback {
|
||||
createdAt DateTime @default(now())
|
||||
guild Guild @relation(fields: [guildId], references: [id], onDelete: Cascade)
|
||||
guildId String @db.VarChar(19)
|
||||
id Int @id @default(autoincrement())
|
||||
rating Int
|
||||
ticket Ticket @relation(fields: [ticketId], references: [id])
|
||||
ticketId String @unique @db.VarChar(19)
|
||||
ticketId String @id @db.VarChar(19)
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
userId String? @db.VarChar(19)
|
||||
|
||||
@ -199,10 +198,9 @@ model Ticket {
|
||||
createdAt DateTime @default(now())
|
||||
createdBy User @relation(name: "TicketsCreatedByUser", fields: [createdById], references: [id])
|
||||
createdById String @db.VarChar(19)
|
||||
feedback Feedback?
|
||||
feedbackId Int?
|
||||
firstResponseAt DateTime?
|
||||
deleted Boolean @default(false)
|
||||
feedback Feedback?
|
||||
firstResponseAt DateTime?
|
||||
guild Guild @relation(fields: [guildId], references: [id], onDelete: Cascade)
|
||||
guildId String @db.VarChar(19)
|
||||
id String @id @db.VarChar(19)
|
||||
|
@ -101,10 +101,9 @@ model Feedback {
|
||||
createdAt DateTime @default(now())
|
||||
guild Guild @relation(fields: [guildId], references: [id], onDelete: Cascade)
|
||||
guildId String @db.VarChar(19)
|
||||
id Int @id @default(autoincrement())
|
||||
rating Int
|
||||
ticket Ticket @relation(fields: [ticketId], references: [id])
|
||||
ticketId String @unique @db.VarChar(19)
|
||||
ticketId String @id @db.VarChar(19)
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
userId String? @db.VarChar(19)
|
||||
|
||||
@ -199,10 +198,9 @@ model Ticket {
|
||||
createdAt DateTime @default(now())
|
||||
createdBy User @relation(name: "TicketsCreatedByUser", fields: [createdById], references: [id])
|
||||
createdById String @db.VarChar(19)
|
||||
feedback Feedback?
|
||||
feedbackId Int?
|
||||
firstResponseAt DateTime?
|
||||
deleted Boolean @default(false)
|
||||
feedback Feedback?
|
||||
firstResponseAt DateTime?
|
||||
guild Guild @relation(fields: [guildId], references: [id], onDelete: Cascade)
|
||||
guildId String @db.VarChar(19)
|
||||
id String @id @db.VarChar(19)
|
||||
|
@ -101,10 +101,9 @@ model Feedback {
|
||||
createdAt DateTime @default(now())
|
||||
guild Guild @relation(fields: [guildId], references: [id], onDelete: Cascade)
|
||||
guildId String
|
||||
id Int @id @default(autoincrement())
|
||||
rating Int
|
||||
ticket Ticket @relation(fields: [ticketId], references: [id])
|
||||
ticketId String @unique
|
||||
ticketId String @id
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
userId String?
|
||||
|
||||
@ -199,10 +198,9 @@ model Ticket {
|
||||
createdAt DateTime @default(now())
|
||||
createdBy User @relation(name: "TicketsCreatedByUser", fields: [createdById], references: [id])
|
||||
createdById String
|
||||
feedback Feedback?
|
||||
feedbackId Int?
|
||||
firstResponseAt DateTime?
|
||||
deleted Boolean @default(false)
|
||||
feedback Feedback?
|
||||
firstResponseAt DateTime?
|
||||
guild Guild @relation(fields: [guildId], references: [id], onDelete: Cascade)
|
||||
guildId String
|
||||
id String @id
|
||||
|
@ -43,8 +43,8 @@
|
||||
"@fastify/http-proxy": "^8.4.0",
|
||||
"@fastify/jwt": "^5.0.1",
|
||||
"@fastify/oauth2": "^5.1.0",
|
||||
"@prisma/client": "^4.8.0",
|
||||
"cryptr": "^6.0.3",
|
||||
"@prisma/client": "^4.8.1",
|
||||
"cryptr": "^6.1.0",
|
||||
"discord.js": "^14.7.1",
|
||||
"dotenv": "^16.0.3",
|
||||
"express": "^4.18.2",
|
||||
@ -60,7 +60,7 @@
|
||||
"node-emoji": "^1.11.0",
|
||||
"object-diffy": "^1.0.4",
|
||||
"pad": "^3.2.0",
|
||||
"prisma": "^4.8.0",
|
||||
"prisma": "^4.8.1",
|
||||
"semver": "^7.3.8",
|
||||
"terminal-link": "^2.1.1",
|
||||
"yaml": "^1.10.2"
|
||||
|
@ -1,4 +1,5 @@
|
||||
const { Button } = require('@eartharoid/dbf');
|
||||
const { isStaff } = require('../lib/users');
|
||||
|
||||
module.exports = class CloseButton extends Button {
|
||||
constructor(client, options) {
|
||||
@ -16,6 +17,29 @@ module.exports = class CloseButton extends Button {
|
||||
/** @type {import("client")} */
|
||||
const client = this.client;
|
||||
|
||||
await interaction.deferReply();
|
||||
if (id.accepted === undefined) {
|
||||
await client.tickets.beforeRequestClose(interaction);
|
||||
} else {
|
||||
// {
|
||||
// action: 'close',
|
||||
// expect: staff ? 'user' : 'staff',
|
||||
// reason: interaction.options?.getString('reason', false) || null, // ?. because it could be a button interaction
|
||||
// requestedBy: interaction.user.id,
|
||||
// }
|
||||
|
||||
await interaction.deferReply();
|
||||
const ticket = await client.prisma.ticket.findUnique({
|
||||
include: { guild: true },
|
||||
where: { id: interaction.channel.id },
|
||||
});
|
||||
|
||||
if (id.expect === 'staff' && !await isStaff(interaction.guild, interaction.user.id)) {
|
||||
return;
|
||||
} else if (interaction.user.id !== ticket.createdById) {
|
||||
return;
|
||||
// if user and expect user (or is creator), feedback modal (if enabled)
|
||||
// otherwise add "Give feedback" button in DM message (if enabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
@ -2,8 +2,8 @@ const { Button } = require('@eartharoid/dbf');
|
||||
const {
|
||||
ActionRowBuilder,
|
||||
ModalBuilder,
|
||||
SelectMenuBuilder,
|
||||
SelectMenuOptionBuilder,
|
||||
StringSelectMenuBuilder,
|
||||
StringSelectMenuOptionBuilder,
|
||||
TextInputBuilder,
|
||||
TextInputStyle,
|
||||
} = require('discord.js');
|
||||
@ -86,14 +86,14 @@ module.exports = class EditButton extends Button {
|
||||
} else if (a.question.type === 'MENU') {
|
||||
return new ActionRowBuilder()
|
||||
.setComponents(
|
||||
new SelectMenuBuilder()
|
||||
new StringSelectMenuBuilder()
|
||||
.setCustomId(a.question.id)
|
||||
.setPlaceholder(a.question.placeholder || a.question.label)
|
||||
.setMaxValues(a.question.maxLength)
|
||||
.setMinValues(a.question.minLength)
|
||||
.setOptions(
|
||||
a.question.options.map((o, i) => {
|
||||
const builder = new SelectMenuOptionBuilder()
|
||||
const builder = new StringSelectMenuOptionBuilder()
|
||||
.setValue(String(i))
|
||||
.setLabel(o.label);
|
||||
if (o.description) builder.setDescription(o.description);
|
||||
|
@ -1,6 +1,7 @@
|
||||
const { FrameworkClient } = require('@eartharoid/dbf');
|
||||
const {
|
||||
GatewayIntentBits, Partials,
|
||||
GatewayIntentBits,
|
||||
Partials,
|
||||
} = require('discord.js');
|
||||
const { PrismaClient } = require('@prisma/client');
|
||||
const Keyv = require('keyv');
|
||||
|
@ -30,6 +30,8 @@ module.exports = class CloseSlashCommand extends SlashCommand {
|
||||
* @param {import("discord.js").ChatInputCommandInteraction} interaction
|
||||
*/
|
||||
async run(interaction) {
|
||||
|
||||
/** @type {import("client")} */
|
||||
const client = this.client;
|
||||
await client.tickets.beforeRequestClose(interaction);
|
||||
}
|
||||
};
|
@ -228,7 +228,5 @@ module.exports = class ForceCloseSlashCommand extends SlashCommand {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: close (reason)
|
||||
}
|
||||
};
|
@ -85,8 +85,8 @@ module.exports = class MoveSlashCommand extends SlashCommand {
|
||||
where: { id: ticket.id },
|
||||
});
|
||||
|
||||
const $oldCategory = client.tickets.$.categories[ticket.categoryId];
|
||||
const $newCategory = client.tickets.$.categories[newCategory.id];
|
||||
const $oldCategory = client.tickets.$count.categories[ticket.categoryId];
|
||||
const $newCategory = client.tickets.$count.categories[newCategory.id];
|
||||
|
||||
$oldCategory.total--;
|
||||
$oldCategory[ticket.createdById]--;
|
||||
|
@ -44,7 +44,7 @@ module.exports = class TransferSlashCommand extends SlashCommand {
|
||||
|
||||
let ticket = await client.prisma.ticket.findUnique({ where: { id: interaction.channel.id } });
|
||||
const from = ticket.createdById;
|
||||
console.log(1)
|
||||
|
||||
ticket = await client.prisma.ticket.update({
|
||||
data: {
|
||||
createdBy: {
|
||||
|
@ -1,6 +1,9 @@
|
||||
buttons:
|
||||
accept_close_request:
|
||||
emoji: ✅
|
||||
text: Accept
|
||||
cancel:
|
||||
emoji: 🚫
|
||||
emoji: ✖️
|
||||
text: Cancel
|
||||
claim:
|
||||
emoji: 🙌
|
||||
@ -17,6 +20,9 @@ buttons:
|
||||
edit:
|
||||
emoji: ✏️
|
||||
text: Edit
|
||||
reject_close_request:
|
||||
emoji: ✖️
|
||||
text: Reject
|
||||
unclaim:
|
||||
emoji: ♻️
|
||||
text: Release
|
||||
@ -328,7 +334,13 @@ misc:
|
||||
title: ❌ That ticket category doesn't exist
|
||||
modals:
|
||||
feedback:
|
||||
title: Feedback
|
||||
comment:
|
||||
label: Comment
|
||||
placeholder: Do you have any additional feedback?
|
||||
rating:
|
||||
label: Rating
|
||||
placeholder: 1-5
|
||||
title: How did we do?
|
||||
topic:
|
||||
label: Topic
|
||||
placeholder: What is this ticket about?
|
||||
@ -336,6 +348,20 @@ ticket:
|
||||
answers:
|
||||
no_value: "*No response*"
|
||||
claimed: 🙌 {user} has claimed this ticket.
|
||||
close:
|
||||
forbidden:
|
||||
description: You don't have permission to close this ticket.
|
||||
title: ❌ Error
|
||||
staff_request:
|
||||
archived: |
|
||||
|
||||
The messages in this channel will be archived for future reference.
|
||||
description: |
|
||||
{requestedBy} wants to close this ticket.
|
||||
Click "Accept" to close it now, or "Reject" if you still need help.
|
||||
title: ❓ Can this ticket be closed?
|
||||
user_request:
|
||||
title: ❓ {requestedBy} wants to close this ticket
|
||||
created:
|
||||
description: "Your ticket channel has been created: {channel}."
|
||||
title: ✅ Ticket created
|
||||
|
@ -22,10 +22,10 @@ module.exports = async client => {
|
||||
let cooldowns = 0;
|
||||
for (const category of categories) {
|
||||
ticketCount += category.tickets.length;
|
||||
client.tickets.$.categories[category.id] = { total: category.tickets.length };
|
||||
client.tickets.$count.categories[category.id] = { total: category.tickets.length };
|
||||
for (const ticket of category.tickets) {
|
||||
if (client.tickets.$.categories[category.id][ticket.createdById]) client.tickets.$.categories[category.id][ticket.createdById]++;
|
||||
else client.tickets.$.categories[category.id][ticket.createdById] = 1;
|
||||
if (client.tickets.$count.categories[category.id][ticket.createdById]) client.tickets.$count.categories[category.id][ticket.createdById]++;
|
||||
else client.tickets.$count.categories[category.id][ticket.createdById] = 1;
|
||||
/** @type {import("discord.js").Guild} */
|
||||
const guild = client.guilds.cache.get(ticket.guildId);
|
||||
if (guild && guild.available && !client.channels.cache.has(ticket.id)) {
|
||||
|
@ -16,6 +16,7 @@ const emoji = require('node-emoji');
|
||||
const ms = require('ms');
|
||||
const ExtendedEmbedBuilder = require('../embed');
|
||||
const { logTicketEvent } = require('../logging');
|
||||
const { isStaff } = require('../users');
|
||||
const { Collection } = require('discord.js');
|
||||
const Cryptr = require('cryptr');
|
||||
const { encrypt } = new Cryptr(process.env.ENCRYPTION_KEY);
|
||||
@ -30,14 +31,21 @@ module.exports = class TicketManager {
|
||||
/** @type {import("client")} */
|
||||
this.client = client;
|
||||
this.archiver = new TicketArchiver(client);
|
||||
this.$ = { categories: {} };
|
||||
this.$count = { categories: {} };
|
||||
this.$stale = new Collection();
|
||||
}
|
||||
|
||||
async getCategory(categoryId) {
|
||||
/**
|
||||
* Retrieve cached category data
|
||||
* @param {string} categoryId the category ID
|
||||
* @param {boolean} force bypass & update the cache?
|
||||
* @returns {Promise<CategoryGuildQuestions>}
|
||||
*/
|
||||
async getCategory(categoryId, force) {
|
||||
const cacheKey = `cache/category+guild+questions:${categoryId}`;
|
||||
/** @type {CategoryGuildQuestions} */
|
||||
let category = await this.client.keyv.get(cacheKey);
|
||||
if (!category) {
|
||||
if (!category || force) {
|
||||
category = await this.client.prisma.category.findUnique({
|
||||
include: {
|
||||
guild: true,
|
||||
@ -45,16 +53,16 @@ module.exports = class TicketManager {
|
||||
},
|
||||
where: { id: categoryId },
|
||||
});
|
||||
this.client.keyv.set(cacheKey, category, ms('5m'));
|
||||
await this.client.keyv.set(cacheKey, category, ms('12h'));
|
||||
}
|
||||
return category;
|
||||
}
|
||||
|
||||
// TODO: update when a ticket is closed or moved
|
||||
async getTotalCount(categoryId) {
|
||||
const category = this.$.categories[categoryId];
|
||||
if (!category) this.$.categories[categoryId] = {};
|
||||
let count = this.$.categories[categoryId].total;
|
||||
const category = this.$count.categories[categoryId];
|
||||
if (!category) this.$count.categories[categoryId] = {};
|
||||
let count = this.$count.categories[categoryId].total;
|
||||
if (!count) {
|
||||
count = await this.client.prisma.ticket.count({
|
||||
where: {
|
||||
@ -62,16 +70,16 @@ module.exports = class TicketManager {
|
||||
open: true,
|
||||
},
|
||||
});
|
||||
this.$.categories[categoryId].total = count;
|
||||
this.$count.categories[categoryId].total = count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// TODO: update when a ticket is closed or moved
|
||||
async getMemberCount(categoryId, memberId) {
|
||||
const category = this.$.categories[categoryId];
|
||||
if (!category) this.$.categories[categoryId] = {};
|
||||
let count = this.$.categories[categoryId][memberId];
|
||||
const category = this.$count.categories[categoryId];
|
||||
if (!category) this.$count.categories[categoryId] = {};
|
||||
let count = this.$count.categories[categoryId][memberId];
|
||||
if (!count) {
|
||||
count = await this.client.prisma.ticket.count({
|
||||
where: {
|
||||
@ -80,7 +88,7 @@ module.exports = class TicketManager {
|
||||
open: true,
|
||||
},
|
||||
});
|
||||
this.$.categories[categoryId][memberId] = count;
|
||||
this.$count.categories[categoryId][memberId] = count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
@ -308,17 +316,15 @@ module.exports = class TicketManager {
|
||||
}) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
const cacheKey = `cache/category+guild+questions:${categoryId}`;
|
||||
/** @type {CategoryGuildQuestions} */
|
||||
const category = await this.client.keyv.get(cacheKey);
|
||||
const category = await this.getCategory(categoryId);
|
||||
|
||||
let answers;
|
||||
if (interaction.isModalSubmit()) {
|
||||
if (action === 'questions') {
|
||||
answers = category.questions.map(q => ({
|
||||
answers = category.questions.filter(q => q.type === 'TEXT').map(q => ({
|
||||
questionId: q.id,
|
||||
userId: interaction.user.id,
|
||||
value: interaction.fields.getTextInputValue(q.id) ? cryptr.encrypt(interaction.fields.getTextInputValue(q.id)) : '',
|
||||
value: interaction.fields.getTextInputValue(q.id) ? encrypt(interaction.fields.getTextInputValue(q.id)) : '',
|
||||
}));
|
||||
if (category.customTopic) topic = interaction.fields.getTextInputValue(category.customTopic);
|
||||
} else if (action === 'topic') {
|
||||
@ -568,7 +574,7 @@ module.exports = class TicketManager {
|
||||
id: channel.id,
|
||||
number,
|
||||
openingMessageId: sent.id,
|
||||
topic: topic ? cryptr.encrypt(topic) : null,
|
||||
topic: topic ? encrypt(topic) : null,
|
||||
};
|
||||
if (referencesTicketId) data.referencesTicket = { connect: { id: referencesTicketId } };
|
||||
if (answers) data.questionAnswers = { createMany: { data: answers } };
|
||||
@ -587,8 +593,8 @@ module.exports = class TicketManager {
|
||||
|
||||
try {
|
||||
const ticket = await this.client.prisma.ticket.create({ data });
|
||||
this.$.categories[categoryId].total++;
|
||||
this.$.categories[categoryId][creator.id]++;
|
||||
this.$count.categories[categoryId].total++;
|
||||
this.$count.categories[categoryId][creator.id]++;
|
||||
|
||||
if (category.cooldown) {
|
||||
const cacheKey = `cooldowns/category-member:${category.id}-${ticket.createdById}`;
|
||||
@ -805,15 +811,170 @@ module.exports = class TicketManager {
|
||||
/**
|
||||
* @param {import("discord.js").ChatInputCommandInteraction|import("discord.js").ButtonInteraction} interaction
|
||||
*/
|
||||
async requestClose(interaction) {
|
||||
async beforeRequestClose(interaction) {
|
||||
const ticket = await this.client.prisma.ticket.findUnique({
|
||||
include: {
|
||||
category: true,
|
||||
category: { select: { enableFeedback: true } },
|
||||
feedback: { select: { id: true } },
|
||||
guild: true,
|
||||
},
|
||||
where: { id: interaction.channel.id },
|
||||
});
|
||||
|
||||
if (!ticket) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
const {
|
||||
errorColour,
|
||||
locale,
|
||||
} = await this.client.prisma.guild.findUnique({
|
||||
select: {
|
||||
errorColour: true,
|
||||
locale: true,
|
||||
},
|
||||
where: { id: interaction.guild.id },
|
||||
});
|
||||
const getMessage = this.client.i18n.getLocale(locale);
|
||||
return await interaction.editReply({
|
||||
embeds: [
|
||||
new ExtendedEmbedBuilder()
|
||||
.setColor(errorColour)
|
||||
.setTitle(getMessage('misc.not_ticket.title'))
|
||||
.setDescription(getMessage('misc.not_ticket.description')),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
const getMessage = this.client.i18n.getLocale(ticket.guild.locale);
|
||||
const staff = await isStaff(interaction.guild, interaction.user.id);
|
||||
const reason = interaction.options?.getString('reason', false) || null; // ?. because it could be a button interaction)
|
||||
|
||||
if (ticket.createdById !== interaction.user.id && !staff) {
|
||||
return await interaction.editReply({
|
||||
embeds: [
|
||||
new ExtendedEmbedBuilder()
|
||||
.setColor(ticket.guild.errorColour)
|
||||
.setTitle(getMessage('ticket.close.forbidden.title'))
|
||||
.setDescription(getMessage('ticket.close.forbidden.description')),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (ticket.createdById === interaction.user.id && ticket.category.enableFeedback && !ticket.feedback) {
|
||||
return await interaction.showModal(
|
||||
new ModalBuilder()
|
||||
.setCustomId(JSON.stringify({
|
||||
action: 'feedback',
|
||||
reason,
|
||||
}))
|
||||
.setTitle(getMessage('modals.feedback.title'))
|
||||
.setComponents(
|
||||
new ActionRowBuilder()
|
||||
.setComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('rating')
|
||||
.setLabel(getMessage('modals.feedback.rating.label'))
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setMaxLength(3)
|
||||
.setMinLength(1)
|
||||
.setPlaceholder(getMessage('modals.feedback.rating.placeholder'))
|
||||
.setRequired(false),
|
||||
),
|
||||
new ActionRowBuilder()
|
||||
.setComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('comment')
|
||||
.setLabel(getMessage('modals.feedback.comment.label'))
|
||||
.setStyle(TextInputStyle.Paragraph)
|
||||
.setMaxLength(1000)
|
||||
.setMinLength(4)
|
||||
.setPlaceholder(getMessage('modals.feedback.comment.placeholder'))
|
||||
.setRequired(false),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// defer asap
|
||||
await interaction.deferReply();
|
||||
|
||||
// if the creator isn't in the guild , close the ticket immediately
|
||||
// (although leaving should cause the ticket to be closed anyway)
|
||||
try {
|
||||
await interaction.guild.members.fetch(ticket.createdById);
|
||||
} catch {
|
||||
return this.close(ticket.id, true, reason);
|
||||
}
|
||||
|
||||
await this.requestClose(interaction, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("discord.js").ChatInputCommandInteraction|import("discord.js").ButtonInteraction|import("discord.js").ModalSubmitInteraction} interaction
|
||||
*/
|
||||
async requestClose(interaction, reason) {
|
||||
// interaction could be command, button. or modal
|
||||
const ticket = await this.client.prisma.ticket.findUnique({
|
||||
include: { guild: true },
|
||||
where: { id: interaction.channel.id },
|
||||
});
|
||||
const getMessage = this.client.i18n.getLocale(ticket.guild.locale);
|
||||
const staff = await isStaff(interaction.guild, interaction.user.id);
|
||||
const closeButtonId = {
|
||||
action: 'close',
|
||||
expect: staff ? 'user' : 'staff',
|
||||
};
|
||||
const embed = new ExtendedEmbedBuilder()
|
||||
.setColor(ticket.guild.primaryColour)
|
||||
.setTitle(getMessage(`ticket.close.${staff ? 'staff' : 'user'}_request.title`, { requestedBy: interaction.member.displayName }));
|
||||
|
||||
if (staff) {
|
||||
embed.setDescription(
|
||||
getMessage('ticket.close.staff_request.description', { requestedBy: interaction.user.toString() }) +
|
||||
(ticket.guild.archive ? getMessage('ticket.close.staff_request.archived') : ''),
|
||||
);
|
||||
}
|
||||
|
||||
const sent = await interaction.editReply({
|
||||
components: [
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId(JSON.stringify({
|
||||
accepted: true,
|
||||
...closeButtonId,
|
||||
}))
|
||||
.setStyle(ButtonStyle.Success)
|
||||
.setEmoji(getMessage('buttons.accept_close_request.emoji'))
|
||||
.setLabel(getMessage('buttons.accept_close_request.text')),
|
||||
new ButtonBuilder()
|
||||
.setCustomId(JSON.stringify({
|
||||
accepted: false,
|
||||
...closeButtonId,
|
||||
}))
|
||||
.setStyle(ButtonStyle.Danger)
|
||||
.setEmoji(getMessage('buttons.reject_close_request.emoji'))
|
||||
.setLabel(getMessage('buttons.reject_close_request.text')),
|
||||
),
|
||||
],
|
||||
content: staff ? `<@${ticket.createdById}>` : '', // ticket.category.pingRoles.map(r => `<@&${r}>`).join(' ')
|
||||
embeds: [embed],
|
||||
});
|
||||
|
||||
this.$stale.set(ticket.id, {
|
||||
closeAt: ticket.guild.autoClose ? Date.now() + ticket.guild.autoClose : null,
|
||||
closedBy: interaction.user.id, // null if set as stale due to inactivity
|
||||
message: sent,
|
||||
reason,
|
||||
staleSince: Date.now(),
|
||||
});
|
||||
|
||||
if (ticket.priority && ticket.priority !== 'LOW') {
|
||||
await this.client.prisma.ticket.update({
|
||||
data: { priority: 'LOW' },
|
||||
where: { id: ticket.id },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -822,10 +983,11 @@ module.exports = class TicketManager {
|
||||
* @param {boolean} skip
|
||||
* @param {string} reason
|
||||
*/
|
||||
async final(ticketId, skip, reason) {
|
||||
async close(ticketId, skip, reason) {
|
||||
// TODO: update cache/cat count
|
||||
// TODO: update cache/member count
|
||||
// TODO: set messageCount on ticket
|
||||
// TODO: pinnedMessages, closedBy, closedAt
|
||||
// delete
|
||||
}
|
||||
};
|
@ -108,5 +108,13 @@ module.exports = class extends Listener {
|
||||
send();
|
||||
setInterval(() => send(), ms('12h'));
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
// TODO: check lastMessageAt and set stale
|
||||
|
||||
for (const [ticketId, $] of client.tickets.$stale) {
|
||||
// ⌛
|
||||
}
|
||||
}, ms('5m'));
|
||||
}
|
||||
};
|
||||
|
@ -8,5 +8,24 @@ module.exports = class FeedbackModal extends Modal {
|
||||
});
|
||||
}
|
||||
|
||||
async run(id, interaction) { }
|
||||
async run(id, interaction) {
|
||||
/** @type {import("client")} */
|
||||
const client = this.client;
|
||||
|
||||
await interaction.deferReply();
|
||||
await client.prisma.ticket.update({
|
||||
data: {
|
||||
feedback: {
|
||||
create: {
|
||||
comment: interaction.fields.getTextInputValue('comment'),
|
||||
guild: { connect: { id: interaction.guild.id } },
|
||||
rating: parseInt(interaction.fields.getTextInputValue('rating')) || null,
|
||||
user: { connect: { id: interaction.user.id } },
|
||||
},
|
||||
},
|
||||
},
|
||||
where: { id: interaction.channel.id },
|
||||
});
|
||||
await client.tickets.requestClose(interaction, id.reason);
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user