8.4 DEV 1
Changed JSON to SQLlite Added Suggestion Forums Added /suggestadmin
This commit is contained in:
1 parent
c6db929d12
commit
a05d65099b
40 files changed
+1562
-1522
No files matched your search
@@ -5,7 +5,7 @@ const yaml = require("js-yaml");
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
const Command = require("../../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.Help.Command,
|
||||
@@ -55,7 +55,7 @@ module.exports = new Command({
|
||||
});
|
||||
|
||||
interaction.reply({
|
||||
ephemeral: true,
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
embeds: [HelpEmbed1],
|
||||
});
|
||||
},
|
||||
@@ -18,7 +18,7 @@ const msgconfig = yaml.load(
|
||||
fs.readFileSync("./Configs/messages.yml", "utf8")
|
||||
);
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
const Command = require("../../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.Info.Command,
|
||||
@@ -10,7 +10,7 @@ const supportbot = yaml.load(
|
||||
);
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
const Command = require("../../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.Ping.Command,
|
||||
@@ -0,0 +1,296 @@
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ModalBuilder,
|
||||
TextInputBuilder,
|
||||
TextInputStyle,
|
||||
ApplicationCommandType,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
ApplicationCommandOptionType
|
||||
} = require("discord.js");
|
||||
const fs = require("fs");
|
||||
const yaml = require("js-yaml");
|
||||
const db = require("../../Structures/Database.js");
|
||||
const Command = require("../../Structures/Command.js");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
|
||||
const clockedInUsers = new Set();
|
||||
|
||||
function updateClockedInStatus(embed, clockedIn) {
|
||||
embed.data.fields = embed.data.fields.map(f =>
|
||||
f.name === "Clocked In Status"
|
||||
? { ...f, value: clockedIn ? "✅ Clocked In" : "❌ Clocked Out" }
|
||||
: f
|
||||
);
|
||||
return embed;
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.Profile.Command,
|
||||
description: cmdconfig.Profile.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
permissions: cmdconfig.Profile.Permission,
|
||||
options: [
|
||||
{
|
||||
name: "user",
|
||||
description: "The user to view the profile of",
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: false
|
||||
}
|
||||
],
|
||||
|
||||
async run(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
const viewingUser = interaction.options.getUser("user") || interaction.user;
|
||||
const viewingUserId = viewingUser.id;
|
||||
const interactionUserId = interaction.user.id;
|
||||
|
||||
const member = await interaction.guild.members.fetch(viewingUserId);
|
||||
const { getRole } = interaction.client;
|
||||
const Staff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
|
||||
const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
|
||||
if (!Staff || !Admin) {
|
||||
return interaction.editReply({
|
||||
content: "Some roles seem to be missing!\nPlease check for errors when starting the bot.",
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
const isStaff = member.roles.cache.has(Staff.id) || member.roles.cache.has(Admin.id);
|
||||
const isOwnProfile = interactionUserId === viewingUserId;
|
||||
|
||||
const profileData = db.getProfile(viewingUserId);
|
||||
let { bio, timezone, clockedIn } = profileData;
|
||||
clockedIn = Boolean(clockedIn);
|
||||
|
||||
let profileEmbed = new EmbedBuilder()
|
||||
.setTitle(`${viewingUser.username}'s Profile`)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.setThumbnail(viewingUser.displayAvatarURL())
|
||||
.addFields(
|
||||
{ name: "Bio", value: bio || "No bio set.", inline: false },
|
||||
{ name: "Timezone", value: timezone || "No timezone set.", inline: true }
|
||||
);
|
||||
|
||||
if (isStaff) {
|
||||
profileEmbed.addFields({
|
||||
name: "Clocked In Status",
|
||||
value: clockedIn ? "✅ Clocked In" : "❌ Clocked Out",
|
||||
inline: true
|
||||
});
|
||||
}
|
||||
|
||||
const buttonRow = new ActionRowBuilder();
|
||||
if (isOwnProfile) {
|
||||
buttonRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("editProfile")
|
||||
.setLabel("Edit Profile")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
if (isStaff) {
|
||||
buttonRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("clockInOut")
|
||||
.setLabel(clockedIn ? "Clock Out" : "Clock In")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isStaff && supportbot.Ticket.ClaimTickets.Enabled) {
|
||||
buttonRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("viewTicketStats")
|
||||
.setLabel("View Ticket Stats")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
}
|
||||
|
||||
await interaction.editReply({
|
||||
embeds: [profileEmbed],
|
||||
components: buttonRow.components.length ? [buttonRow] : []
|
||||
});
|
||||
|
||||
const filter = i =>
|
||||
["editProfile", "clockInOut", "viewTicketStats", "showOpenTickets", "backToStats", "backToProfile"]
|
||||
.includes(i.customId) &&
|
||||
i.user.id === interactionUserId;
|
||||
|
||||
const collector = interaction.channel.createMessageComponentCollector({ filter, time: 60000 });
|
||||
|
||||
collector.on("collect", async i => {
|
||||
if (i.customId === "editProfile") {
|
||||
const modal = new ModalBuilder()
|
||||
.setCustomId("editProfileModal")
|
||||
.setTitle("Edit Profile")
|
||||
.addComponents(
|
||||
new ActionRowBuilder().addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId("bio")
|
||||
.setLabel("Bio")
|
||||
.setStyle(TextInputStyle.Paragraph)
|
||||
.setPlaceholder("Enter your bio")
|
||||
.setValue(String(bio || ""))
|
||||
.setRequired(false)
|
||||
),
|
||||
new ActionRowBuilder().addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId("timezone")
|
||||
.setLabel("Timezone")
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setPlaceholder("Enter your timezone")
|
||||
.setValue(String(timezone || ""))
|
||||
.setRequired(false)
|
||||
)
|
||||
);
|
||||
|
||||
return i.showModal(modal);
|
||||
}
|
||||
|
||||
if (i.customId === "clockInOut") {
|
||||
const newStatus = !clockedIn;
|
||||
db.setClockedIn(viewingUserId, newStatus);
|
||||
clockedIn = newStatus;
|
||||
profileEmbed = updateClockedInStatus(profileEmbed, newStatus);
|
||||
|
||||
if (newStatus) clockedInUsers.add(viewingUserId);
|
||||
else clockedInUsers.delete(viewingUserId);
|
||||
|
||||
const updatedRow = new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("editProfile")
|
||||
.setLabel("Edit Profile")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
if (isStaff) {
|
||||
updatedRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("clockInOut")
|
||||
.setLabel(newStatus ? "Clock Out" : "Clock In")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
if (supportbot.Ticket.ClaimTickets.Enabled) {
|
||||
updatedRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("viewTicketStats")
|
||||
.setLabel("View Ticket Stats")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await i.update({ embeds: [profileEmbed], components: [updatedRow] });
|
||||
|
||||
const clockEmbed = new EmbedBuilder()
|
||||
.setTitle(newStatus ? "Clocked In!" : "Clocked Out!")
|
||||
.setDescription(newStatus ? "You have successfully clocked in." : "You have successfully clocked out.")
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
return interaction.followUp({ embeds: [clockEmbed], ephemeral: true });
|
||||
}
|
||||
|
||||
if (i.customId === "viewTicketStats") {
|
||||
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketsClaimed = TicketData.tickets.filter(t => t.claimedBy === viewingUserId);
|
||||
const ticketsOpen = ticketsClaimed.filter(t => t.open);
|
||||
const totalTickets = ticketsClaimed.length;
|
||||
|
||||
let totalResponseTime = 0;
|
||||
let responseCount = 0;
|
||||
ticketsClaimed.forEach(t => {
|
||||
if (t.claimedAt && t.createdAt) {
|
||||
totalResponseTime += new Date(t.claimedAt) - new Date(t.createdAt);
|
||||
responseCount++;
|
||||
}
|
||||
});
|
||||
|
||||
const avgMinutes = responseCount ? Math.round(totalResponseTime / responseCount / 60000) : 0;
|
||||
|
||||
const statsEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.TicketStats.Title)
|
||||
.setDescription(`> Ticket stats for <@${viewingUserId}>`)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.addFields(
|
||||
{ name: msgconfig.TicketStats.OpenTickets, value: `${ticketsOpen.length}`, inline: true },
|
||||
{ name: msgconfig.TicketStats.TotalTickets, value: `${totalTickets}`, inline: true },
|
||||
{ name: msgconfig.TicketStats.ResponseTime, value: `${avgMinutes} minutes`, inline: false }
|
||||
);
|
||||
|
||||
const statsRow = new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder().setCustomId("showOpenTickets").setLabel("Show Open Tickets").setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder().setCustomId("backToProfile").setLabel("Back to Profile").setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
return i.update({ embeds: [statsEmbed], components: [statsRow] });
|
||||
}
|
||||
|
||||
if (i.customId === "showOpenTickets") {
|
||||
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketsOpen = TicketData.tickets.filter(t => t.claimedBy === viewingUserId && t.open);
|
||||
|
||||
const openEmbed = new EmbedBuilder()
|
||||
.setTitle("Active Tickets")
|
||||
.setDescription(ticketsOpen.map(t => `<#${t.id}>`).join("\n") || "No active tickets.")
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const backRow = new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder().setCustomId("backToStats").setLabel("Back to Stats").setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
return i.update({ embeds: [openEmbed], components: [backRow] });
|
||||
}
|
||||
|
||||
if (i.customId === "backToStats") {
|
||||
|
||||
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketsClaimed = TicketData.tickets.filter(t => t.claimedBy === viewingUserId);
|
||||
const ticketsOpen = ticketsClaimed.filter(t => t.open);
|
||||
const totalTickets = ticketsClaimed.length;
|
||||
|
||||
let totalResponseTime = 0;
|
||||
let responseCount = 0;
|
||||
ticketsClaimed.forEach(t => {
|
||||
if (t.claimedAt && t.createdAt) {
|
||||
totalResponseTime += new Date(t.claimedAt) - new Date(t.createdAt);
|
||||
responseCount++;
|
||||
}
|
||||
});
|
||||
const avgMinutes = responseCount ? Math.round(totalResponseTime / responseCount / 60000) : 0;
|
||||
|
||||
const statsEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.TicketStats.Title)
|
||||
.setDescription(`> Ticket stats for <@${viewingUserId}>`)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.addFields(
|
||||
{ name: msgconfig.TicketStats.OpenTickets, value: `${ticketsOpen.length}`, inline: true },
|
||||
{ name: msgconfig.TicketStats.TotalTickets, value: `${totalTickets}`, inline: true },
|
||||
{ name: msgconfig.TicketStats.ResponseTime, value: `${avgMinutes} minutes`, inline: false }
|
||||
);
|
||||
|
||||
const statsRow = new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder().setCustomId("showOpenTickets").setLabel("Show Open Tickets").setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder().setCustomId("backToProfile").setLabel("Back to Profile").setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
return i.update({ embeds: [statsEmbed], components: [statsRow] });
|
||||
}
|
||||
|
||||
if (i.customId === "backToProfile") {
|
||||
return i.update({
|
||||
embeds: [profileEmbed],
|
||||
components: buttonRow.components.length ? [buttonRow] : []
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports.clockedInUsers = clockedInUsers;
|
||||
@@ -18,7 +18,7 @@ const msgconfig = yaml.load(
|
||||
fs.readFileSync("./Configs/messages.yml", "utf8")
|
||||
)
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
const Command = require("../../Structures/Command.js");
|
||||
const translate = require("@vitalets/google-translate-api");
|
||||
|
||||
module.exports = new Command({
|
||||
@@ -89,7 +89,7 @@ module.exports = new Command({
|
||||
"Click the title to see which 2 letter language codes are valid."
|
||||
),
|
||||
],
|
||||
ephemeral: true,
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,147 @@
|
||||
const fs = require("fs");
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
const path = require("path");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
|
||||
const Command = require("../../Structures/Command.js");
|
||||
const DB = require("../../Structures/Database.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: "suggest",
|
||||
description: cmdconfig.Suggestion?.Description || "Submit a new suggestion",
|
||||
type: Discord.ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: "text",
|
||||
description: "Your suggestion text",
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
required: true
|
||||
}
|
||||
],
|
||||
|
||||
async run(interaction) {
|
||||
const suggestion = interaction.options.getString("text");
|
||||
const mode = supportbot.Suggestions.Mode || "Channel";
|
||||
|
||||
const embed = new Discord.EmbedBuilder()
|
||||
.addFields(
|
||||
{ name: "Suggestion", value: suggestion, inline: true },
|
||||
{ name: "From", value: `<@${interaction.user.id}>` },
|
||||
{ name: `${supportbot.Suggestions.UpvoteEmoji} ${supportbot.Suggestions.UpvoteTitle}`, value: "0", inline: true },
|
||||
{ name: `${supportbot.Suggestions.DownvoteEmoji} ${supportbot.Suggestions.DownvoteTitle}`, value: "0", inline: true }
|
||||
)
|
||||
.setThumbnail(interaction.user.displayAvatarURL())
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const row = new Discord.ActionRowBuilder().addComponents(
|
||||
new Discord.ButtonBuilder()
|
||||
.setCustomId("upvote")
|
||||
.setEmoji(supportbot.Suggestions.UpvoteEmoji)
|
||||
.setStyle(supportbot.Suggestions.Buttons.Upvote),
|
||||
new Discord.ButtonBuilder()
|
||||
.setCustomId("downvote")
|
||||
.setEmoji(supportbot.Suggestions.DownvoteEmoji)
|
||||
.setStyle(supportbot.Suggestions.Buttons.Downvote),
|
||||
new Discord.ButtonBuilder()
|
||||
.setCustomId("removevote")
|
||||
.setLabel(supportbot.Suggestions.Buttons.RemoveVote_Title)
|
||||
.setStyle(supportbot.Suggestions.Buttons.RemoveVote)
|
||||
);
|
||||
|
||||
if (mode === "Forum") {
|
||||
const forum = await interaction.guild.channels
|
||||
.fetch(supportbot.Suggestions.ForumChannel)
|
||||
.catch(() => null);
|
||||
|
||||
if (!forum || forum.type !== Discord.ChannelType.GuildForum) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("⚠ Missing Forum Channel")
|
||||
.setDescription(msgconfig.Error.MissingChannel)
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const appliedTags = supportbot.Suggestions.Tags?.Created
|
||||
? [supportbot.Suggestions.Tags.Created]
|
||||
: [];
|
||||
|
||||
const thread = await forum.threads.create({
|
||||
name: suggestion.slice(0, 100),
|
||||
message: {
|
||||
content: "\u200B",
|
||||
embeds: [embed],
|
||||
components: [row]
|
||||
},
|
||||
appliedTags,
|
||||
autoArchiveDuration: Discord.ThreadAutoArchiveDuration.OneDay,
|
||||
reason: "New Suggestion"
|
||||
});
|
||||
|
||||
embed.setFooter({
|
||||
text: `${supportbot.Embed.Footer} | Suggestion ID: ${thread.id}`,
|
||||
iconURL: interaction.user.displayAvatarURL()
|
||||
});
|
||||
const starter = await thread.fetchStarterMessage();
|
||||
await starter.edit({ embeds: [embed], components: [row] });
|
||||
|
||||
DB.add(thread.id, interaction.user.id, suggestion);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.Suggestions.Sent_Title)
|
||||
.setDescription(`${msgconfig.Suggestions.Sent}`)
|
||||
.addFields({ name: 'Posted in:', value: `<#${forum.id}>` })
|
||||
.setColor(supportbot.Embed.Colours.Success)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- Channel Mode ----------
|
||||
const channel = await interaction.guild.channels
|
||||
.fetch(supportbot.Suggestions.Channel)
|
||||
.catch(() => null);
|
||||
|
||||
if (!channel) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("⚠ Missing Suggestion Channel")
|
||||
.setDescription(msgconfig.Error.MissingChannel)
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const message = await channel.send({ embeds: [embed], components: [row] });
|
||||
|
||||
embed.setFooter({
|
||||
text: `${supportbot.Embed.Footer} | Suggestion ID: ${message.id}`,
|
||||
iconURL: interaction.user.displayAvatarURL()
|
||||
});
|
||||
await message.edit({ embeds: [embed], components: [row] });
|
||||
|
||||
DB.add(message.id, interaction.user.id, suggestion);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.Suggestions.Sent_Title)
|
||||
.setDescription(`${msgconfig.Suggestions.Sent}`)
|
||||
.addFields({ name: 'Posted in:', value: `<#${channel.id}>` })
|
||||
.setColor(supportbot.Embed.Colours.Success)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,183 @@
|
||||
const Discord = require("discord.js");
|
||||
const fs = require("fs");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
const SuggestionDB = require("../../Structures/Database.js");
|
||||
const Command = require("../../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.SuggestionAdmin.Command,
|
||||
description: cmdconfig.SuggestionAdmin.Description,
|
||||
type: Discord.ApplicationCommandType.ChatInput,
|
||||
permissions: cmdconfig.SuggestionAdmin.Permission,
|
||||
options: [
|
||||
{
|
||||
name: "status",
|
||||
description: "New status",
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
required: true,
|
||||
choices: [
|
||||
{ name: "Solved", value: "Solved" },
|
||||
{ name: "Denied", value: "Denied" },
|
||||
{ name: "Considering", value: "Considering" }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "id",
|
||||
description: "Message ID (required in Channel mode)",
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
required: false
|
||||
}
|
||||
],
|
||||
|
||||
async run(interaction) {
|
||||
const status = interaction.options.getString("status");
|
||||
const idOption = interaction.options.getString("id");
|
||||
const mode = supportbot.Suggestions.Mode || "Channel";
|
||||
|
||||
const { getRole } = interaction.client;
|
||||
const staffRole = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
|
||||
const adminRole = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
if (
|
||||
!interaction.member.roles.cache.has(staffRole?.id) &&
|
||||
!interaction.member.roles.cache.has(adminRole?.id)
|
||||
) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Invalid Permissions!")
|
||||
.setDescription(
|
||||
`${msgconfig.Error.IncorrectPerms}\nRequired: ${supportbot.Roles.StaffMember.Staff} or ${supportbot.Roles.StaffMember.Admin}`
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.Warn)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
if (mode === "Forum") {
|
||||
const thread = interaction.channel;
|
||||
if (!thread.isThread() || thread.parentId !== supportbot.Suggestions.ForumChannel) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Not a Suggestion Thread")
|
||||
.setDescription(msgconfig.Error.MissingChannel)
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const tagMap = {
|
||||
Solved: supportbot.Suggestions.Tags.Solved,
|
||||
Denied: supportbot.Suggestions.Tags.Denied,
|
||||
Considering: supportbot.Suggestions.Tags.Considering
|
||||
};
|
||||
const tagId = tagMap[status];
|
||||
if (!tagId) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Tag Missing")
|
||||
.setDescription("No forum tag configured for this status.")
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
await thread.setAppliedTags([tagId]).catch(() => null);
|
||||
SuggestionDB.setStatus(thread.id, status);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Suggestion Updated")
|
||||
.setDescription(`Status set to **${status}**.`)
|
||||
.setColor(supportbot.Embed.Colours.Success)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
if (!idOption) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Message ID Required")
|
||||
.setDescription("Provide the suggestion’s message ID when in Channel mode.")
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const channel = await interaction.guild.channels
|
||||
.fetch(supportbot.Suggestions.Channel)
|
||||
.catch(() => null);
|
||||
if (!channel) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Missing Suggestion Channel")
|
||||
.setDescription(msgconfig.Error.MissingChannel)
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const targetMsg = await channel.messages.fetch(idOption).catch(() => null);
|
||||
if (!targetMsg) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Message Not Found")
|
||||
.setDescription("Could not fetch a suggestion with that ID.")
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const oldEmbed = targetMsg.embeds[0];
|
||||
if (!oldEmbed) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("No Embed Found")
|
||||
.setDescription("The target message has no suggestion embed.")
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const colorMap = {
|
||||
Solved: supportbot.Embed.Colours.Success,
|
||||
Denied: supportbot.Embed.Colours.Error,
|
||||
Considering: supportbot.Embed.Colours.Warn
|
||||
};
|
||||
|
||||
const newEmbed = Discord.EmbedBuilder.from(oldEmbed)
|
||||
.setColor(colorMap[status])
|
||||
.addFields({ name: "Status", value: status, inline: true });
|
||||
|
||||
await targetMsg.edit({ embeds: [newEmbed], components: targetMsg.components });
|
||||
SuggestionDB.setStatus(idOption, status);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Suggestion Updated")
|
||||
.setDescription(`Suggestion **${idOption}** marked as **${status}**.`)
|
||||
.setColor(supportbot.Embed.Colours.Success)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,119 @@
|
||||
const fs = require("fs");
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ApplicationCommandOptionType,
|
||||
ApplicationCommandType,
|
||||
ChannelType,
|
||||
MessageFlags,
|
||||
} = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
const Command = require("../../Structures/Command.js");
|
||||
const db = require("../../Structures/Database.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.ForceAddUser.Command,
|
||||
description: cmdconfig.ForceAddUser.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: "user",
|
||||
description: "The user to forcefully add",
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
permissions: cmdconfig.ForceAddUser.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
const userToAdd = interaction.options.getUser("user");
|
||||
const ticketChannel = interaction.channel;
|
||||
|
||||
if (
|
||||
(supportbot.Ticket.TicketType === "threads" && !ticketChannel.isThread()) ||
|
||||
(supportbot.Ticket.TicketType === "channels" && ticketChannel.type !== ChannelType.GuildText)
|
||||
) {
|
||||
const onlyInTicket = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.NotInTicket_Title)
|
||||
.setDescription(msgconfig.ForceAddUser.NotInTicket_Description)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [onlyInTicket],
|
||||
flags: MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const ticketRow = db.getTicket(ticketChannel.id);
|
||||
if (!ticketRow) {
|
||||
const notFound = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.NotInTicket_Title)
|
||||
.setDescription(msgconfig.ForceAddUser.NotInTicket_Description)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [notFound],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
const botMember = await ticketChannel.members.fetch(interaction.client.user.id).catch(() => null);
|
||||
if (!botMember) await ticketChannel.join();
|
||||
await new Promise(res => setTimeout(res, 1000));
|
||||
await ticketChannel.members.add(userToAdd.id);
|
||||
} else if (supportbot.Ticket.TicketType === "channels") {
|
||||
await ticketChannel.permissionOverwrites.create(userToAdd.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
}
|
||||
|
||||
db.addUserToTicket(ticketChannel.id, userToAdd.id);
|
||||
|
||||
const addedEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.Added_Title)
|
||||
.setDescription(
|
||||
msgconfig.ForceAddUser.Added_Description.replace("%username%", userToAdd.username)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [addedEmbed],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
|
||||
const addedToTicketEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.AddedToTicket_Title)
|
||||
.setDescription(
|
||||
msgconfig.ForceAddUser.AddedToTicket_Description.replace(
|
||||
"%channel_link%",
|
||||
`[${ticketChannel.name}](https://discord.com/channels/${ticketChannel.guild.id}/${ticketChannel.id})`
|
||||
)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
await userToAdd.send({
|
||||
embeds: [addedToTicketEmbed],
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error adding user to the ticket:", err);
|
||||
|
||||
const errorEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.Error_Title)
|
||||
.setDescription(msgconfig.ForceAddUser.Error_Adding)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [errorEmbed],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -2,11 +2,13 @@ const fs = require("fs");
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const db = require("../../Structures/Database.js");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
const Command = require("../../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.CloseTicket.Command,
|
||||
@@ -57,18 +59,9 @@ module.exports = new Command({
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
let tickets;
|
||||
try {
|
||||
tickets = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
} catch (err) {
|
||||
console.error("Error reading ticket data file:", err);
|
||||
return interaction.followUp({ content: "There was an error loading ticket data." });
|
||||
}
|
||||
|
||||
let TicketData = tickets.tickets.findIndex((t) => t.id === interaction.channel.id);
|
||||
let ticket = tickets.tickets[TicketData];
|
||||
|
||||
if (TicketData === -1) {
|
||||
const ticket = db.getTicket(interaction.channel.id);
|
||||
|
||||
if (!ticket) {
|
||||
const Exists = new Discord.EmbedBuilder()
|
||||
.setTitle("No Ticket Found!")
|
||||
.setDescription(msgconfig.Error.NoValidTicket)
|
||||
@@ -120,9 +113,9 @@ module.exports = new Command({
|
||||
await interaction.followUp({ embeds: [reviewEmbed] });
|
||||
}
|
||||
|
||||
await handleCloseTicket(interaction, reason, ticket, TicketData);
|
||||
await handleCloseTicket(interaction, reason, ticket);
|
||||
} else {
|
||||
await handleCloseTicket(interaction, reason, ticket, TicketData);
|
||||
await handleCloseTicket(interaction, reason, ticket);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -150,7 +143,7 @@ async function collectReviewRating(interaction, ticketUserId) {
|
||||
content: `<@${ticketUserId}>`,
|
||||
embeds: [reviewPrompt],
|
||||
components: [stars],
|
||||
ephemeral: true,
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
|
||||
const starRating = await interaction.channel.awaitMessageComponent({
|
||||
@@ -170,7 +163,7 @@ async function collectReviewComment(interaction, ticketUserId) {
|
||||
await interaction.followUp({
|
||||
content: `<@${ticketUserId}>`,
|
||||
embeds: [commentPrompt],
|
||||
ephemeral: true,
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
|
||||
const filter = (response) => response.author.id === ticketUserId;
|
||||
@@ -179,18 +172,10 @@ async function collectReviewComment(interaction, ticketUserId) {
|
||||
const comment = commentCollection.first()?.content;
|
||||
return comment && comment.toLowerCase() !== "no" ? comment : "No Comment Provided";
|
||||
}
|
||||
async function handleCloseTicket(interaction, reason, ticket, TicketData) {
|
||||
async function handleCloseTicket(interaction, reason, ticket) {
|
||||
const { getChannel } = interaction.client;
|
||||
|
||||
let tickets;
|
||||
try {
|
||||
tickets = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
} catch (err) {
|
||||
console.error("Error reading ticket data:", err);
|
||||
return interaction.followUp("Failed to load ticket data.");
|
||||
}
|
||||
|
||||
let tUser = interaction.client.users.cache.get(ticket.user);
|
||||
let tUser = interaction.client.users.cache.get(ticket.user_id);
|
||||
let transcriptChannel = await getChannel(supportbot.Ticket.Log.TicketDataLog, interaction.guild);
|
||||
|
||||
if (!transcriptChannel) {
|
||||
@@ -199,7 +184,6 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) {
|
||||
}
|
||||
|
||||
try {
|
||||
// Fetch all messages from the channel
|
||||
let allMessages = [];
|
||||
let lastId = null;
|
||||
|
||||
@@ -218,10 +202,8 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) {
|
||||
if (messages.size < 100) break;
|
||||
}
|
||||
|
||||
// Sort messages by timestamp
|
||||
allMessages.sort((a, b) => a.createdTimestamp - b.createdTimestamp);
|
||||
|
||||
// Create transcript data
|
||||
const transcriptData = allMessages.map(msg => ({
|
||||
content: msg.content || "No content",
|
||||
username: msg.author.username,
|
||||
@@ -239,10 +221,7 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) {
|
||||
}))
|
||||
}));
|
||||
|
||||
// Update ticket status
|
||||
tickets.tickets[TicketData].open = false;
|
||||
tickets.tickets[TicketData].messages = transcriptData;
|
||||
fs.writeFileSync("./Data/TicketData.json", JSON.stringify(tickets, null, 4));
|
||||
db.updateTicketStatus(interaction.channel.id, "closed");
|
||||
|
||||
const transcriptEmbed = new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.TicketLog.Title)
|
||||
@@ -250,7 +229,7 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) {
|
||||
.setFooter({ text: supportbot.Embed.Footer, iconURL: interaction.user.displayAvatarURL() })
|
||||
.setDescription(
|
||||
`> **Ticket:** ${interaction.channel.name} (\`${interaction.channel.id}\`)\n` +
|
||||
`> **User:** ${tUser?.tag || "Unknown User"} (\`${tUser?.id || ticket.user}\`)\n` +
|
||||
`> **User:** ${tUser?.tag || "Unknown User"} (\`${tUser?.id || ticket.user_id}\`)` +
|
||||
`> **Closed by:** <@${interaction.user.id}>\n` +
|
||||
`> **Message Count:** ${transcriptData.length}`
|
||||
)
|
||||
@@ -264,6 +243,12 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) {
|
||||
},
|
||||
reason
|
||||
);
|
||||
|
||||
const transcriptDir = "./Data/Transcripts";
|
||||
|
||||
if (!fs.existsSync(transcriptDir)) {
|
||||
fs.mkdirSync(transcriptDir, { recursive: true });
|
||||
}
|
||||
|
||||
const fileName = `${interaction.channel.id}-transcript.html`;
|
||||
fs.writeFileSync(`./Data/Transcripts/${fileName}`, html);
|
||||
@@ -288,19 +273,14 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) {
|
||||
}
|
||||
|
||||
function parseMarkdown(content) {
|
||||
// Convert **bold** text to <strong> tags
|
||||
content = content.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
|
||||
|
||||
// Convert _italic_ text to <em> tags
|
||||
content = content.replace(/_(.*?)_/g, '<em>$1</em>');
|
||||
|
||||
// Convert inline `code` to <code> tags
|
||||
content = content.replace(/`(.*?)`/g, '<code>$1</code>');
|
||||
|
||||
// Convert [text](link) to <a> tags
|
||||
content = content.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank">$1</a>');
|
||||
|
||||
// Convert multiline code blocks ```code``` to <pre><code> tags
|
||||
content = content.replace(/```(.*?)```/gs, '<pre><code>$1</code></pre>');
|
||||
|
||||
return content;
|
||||
@@ -4,6 +4,7 @@ const {
|
||||
ApplicationCommandOptionType,
|
||||
ApplicationCommandType,
|
||||
ChannelType,
|
||||
MessageFlags,
|
||||
} = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
@@ -11,7 +12,8 @@ const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
const Command = require("../../Structures/Command.js");
|
||||
const db = require("../../Structures/Database.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.ForceAddUser.Command,
|
||||
@@ -30,7 +32,6 @@ module.exports = new Command({
|
||||
async run(interaction) {
|
||||
const userToAdd = interaction.options.getUser("user");
|
||||
const ticketChannel = interaction.channel;
|
||||
const ticketDataPath = "./Data/TicketData.json";
|
||||
|
||||
if (
|
||||
(supportbot.Ticket.TicketType === "threads" && !ticketChannel.isThread()) ||
|
||||
@@ -43,54 +44,38 @@ module.exports = new Command({
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [onlyInTicket],
|
||||
ephemeral: true,
|
||||
flags: MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const ticketRow = db.getTicket(ticketChannel.id);
|
||||
if (!ticketRow) {
|
||||
const notFound = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.NotInTicket_Title)
|
||||
.setDescription(msgconfig.ForceAddUser.NotInTicket_Description)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [notFound],
|
||||
flags: MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
try {
|
||||
// Explicitly ensure the bot is in the thread
|
||||
const botMember = await ticketChannel.members.fetch(interaction.client.user.id).catch(() => null);
|
||||
if (!botMember) {
|
||||
await ticketChannel.join();
|
||||
}
|
||||
|
||||
// Delay to ensure API has processed the bot's membership
|
||||
await new Promise(res => setTimeout(res, 1000));
|
||||
|
||||
// Attempt to add the user to the thread
|
||||
await ticketChannel.members.add(userToAdd.id);
|
||||
|
||||
console.log("User successfully added to the thread.");
|
||||
} catch (err) {
|
||||
console.error("Error adding user to the thread:", err.message);
|
||||
const errorEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.Error_Title)
|
||||
.setDescription(`An error occurred: ${err.message}`)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [errorEmbed],
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
const botMember = await ticketChannel.members.fetch(interaction.client.user.id).catch(() => null);
|
||||
if (!botMember) await ticketChannel.join();
|
||||
await new Promise(res => setTimeout(res, 1000));
|
||||
await ticketChannel.members.add(userToAdd.id);
|
||||
} else if (supportbot.Ticket.TicketType === "channels") {
|
||||
await ticketChannel.permissionOverwrites.create(userToAdd.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const ticketData = JSON.parse(fs.readFileSync(ticketDataPath, "utf8"));
|
||||
const ticketIndex = ticketData.tickets.findIndex((t) => t.id === ticketChannel.id);
|
||||
if (ticketIndex !== -1) {
|
||||
ticketData.tickets[ticketIndex].subUsers.push(userToAdd.id);
|
||||
fs.writeFileSync(ticketDataPath, JSON.stringify(ticketData, null, 4));
|
||||
}
|
||||
db.addUserToTicket(ticketChannel.id, userToAdd.id);
|
||||
|
||||
const addedEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.Added_Title)
|
||||
@@ -101,7 +86,7 @@ module.exports = new Command({
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [addedEmbed],
|
||||
ephemeral: true,
|
||||
flags: MessageFlags.Ephemeral,
|
||||
});
|
||||
|
||||
const addedToTicketEmbed = new EmbedBuilder()
|
||||
@@ -127,7 +112,7 @@ module.exports = new Command({
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [errorEmbed],
|
||||
ephemeral: true,
|
||||
flags: MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -0,0 +1,108 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Remove user from a ticket
|
||||
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ApplicationCommandOptionType,
|
||||
ApplicationCommandType,
|
||||
ChannelType,
|
||||
MessageFlags,
|
||||
} = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
const fs = require("fs");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
const Command = require("../../Structures/Command.js");
|
||||
const db = require("../../Structures/Database.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.RemoveUser.Command,
|
||||
description: cmdconfig.RemoveUser.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: "user",
|
||||
description: "The user to remove",
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
permissions: cmdconfig.RemoveUser.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
const { getRole } = interaction.client;
|
||||
const SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
|
||||
const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
|
||||
if (!SupportStaff || !Admin) {
|
||||
return interaction.reply("Missing staff/admin roles in config.");
|
||||
}
|
||||
if (
|
||||
!interaction.member.roles.cache.has(SupportStaff.id) &&
|
||||
!interaction.member.roles.cache.has(Admin.id)
|
||||
) {
|
||||
const NoPerms = new EmbedBuilder()
|
||||
.setTitle("Invalid Permissions!")
|
||||
.setDescription(
|
||||
`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
return interaction.reply({ embeds: [NoPerms], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
const userToRemove = interaction.options.getUser("user");
|
||||
const ticketChannel = interaction.channel;
|
||||
|
||||
if (
|
||||
(supportbot.Ticket.TicketType === "threads" && !ticketChannel.isThread()) ||
|
||||
(supportbot.Ticket.TicketType === "channels" && ticketChannel.type !== ChannelType.GuildText)
|
||||
) {
|
||||
const onlyInTicket = new EmbedBuilder()
|
||||
.setTitle(msgconfig.RemoveUser.NotInTicket_Title)
|
||||
.setDescription(msgconfig.RemoveUser.NotInTicket_Description)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
return interaction.reply({ embeds: [onlyInTicket], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
try {
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await ticketChannel.members.remove(userToRemove.id);
|
||||
} else {
|
||||
await ticketChannel.permissionOverwrites.delete(userToRemove.id);
|
||||
}
|
||||
|
||||
db.removeUserFromTicket(ticketChannel.id, userToRemove.id);
|
||||
|
||||
const removedEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.RemoveUser.Removed_Title)
|
||||
.setDescription(msgconfig.RemoveUser.Removed_Message)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
await interaction.reply({ embeds: [removedEmbed], flags: MessageFlags.Ephemeral });
|
||||
|
||||
const removedFromTicketEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.RemoveUser.RemovedFromTicket_Title)
|
||||
.setDescription(
|
||||
msgconfig.RemoveUser.RemovedFromTicket_Description.replace(
|
||||
"%channel_link%",
|
||||
`[${ticketChannel.name}](https://discord.com/channels/${ticketChannel.guild.id}/${ticketChannel.id})`
|
||||
)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
await userToRemove.send({ embeds: [removedFromTicketEmbed] }).catch(() => {});
|
||||
} catch (err) {
|
||||
console.error("Error removing user from the ticket:", err);
|
||||
|
||||
const errorEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.RemoveUser.Error_Title)
|
||||
.setDescription(msgconfig.RemoveUser.Error_Removing)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
await interaction.reply({ embeds: [errorEmbed], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -10,16 +10,19 @@ const {
|
||||
StringSelectMenuOptionBuilder,
|
||||
ButtonStyle,
|
||||
MessageCollector,
|
||||
MessageFlags,
|
||||
} = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const db = require("../../Structures/Database.js");
|
||||
|
||||
const panelconfig = yaml.load(fs.readFileSync("./Configs/ticket-panel.yml", "utf8"));
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
const TicketNumberID = require("../Structures/TicketID.js");
|
||||
const Command = require("../../Structures/Command.js");
|
||||
const TicketNumberID = require("../../Structures/TicketID.js");
|
||||
|
||||
async function isBlacklisted(userId) {
|
||||
let blacklistData;
|
||||
@@ -40,16 +43,11 @@ async function isBlacklisted(userId) {
|
||||
|
||||
async function getClockedInUsers(guild) {
|
||||
const clockedInUsers = new Set();
|
||||
|
||||
const profilesDir = './Data/Profiles';
|
||||
const profileFiles = fs.readdirSync(profilesDir);
|
||||
|
||||
for (const file of profileFiles) {
|
||||
const profileData = JSON.parse(fs.readFileSync(`${profilesDir}/${file}`, "utf8"));
|
||||
if (profileData.clockedIn) {
|
||||
const userId = file.replace('.json', '');
|
||||
const member = await guild.members.fetch(userId);
|
||||
if (member) clockedInUsers.add(member.id);
|
||||
const rows = guild.members.cache.map(m => m.id);
|
||||
for (const id of rows) {
|
||||
const profile = db.getProfile(id);
|
||||
if (profile && profile.clockedIn) {
|
||||
clockedInUsers.add(id);
|
||||
}
|
||||
}
|
||||
return clockedInUsers;
|
||||
@@ -61,14 +59,11 @@ async function askTicketQuestions(ticketChannel, interaction, questions) {
|
||||
|
||||
const sendNextQuestion = async () => {
|
||||
if (questionIndex < questions.length) {
|
||||
|
||||
const questionsEmbed = new EmbedBuilder()
|
||||
.setDescription(`**${questionIndex + 1}.** ${questions[questionIndex]}`)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const questionMessage = await ticketChannel.send({
|
||||
embeds: [questionsEmbed],
|
||||
});
|
||||
const questionMessage = await ticketChannel.send({ embeds: [questionsEmbed] });
|
||||
|
||||
const collector = new MessageCollector(ticketChannel, {
|
||||
filter: (m) => m.author.id === interaction.user.id,
|
||||
@@ -83,12 +78,12 @@ async function askTicketQuestions(ticketChannel, interaction, questions) {
|
||||
await questionMessage.delete();
|
||||
sendNextQuestion();
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
const summaryEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.TicketQuestions.Details.Title)
|
||||
.setDescription(msgconfig.Ticket.TicketQuestions.Details.Description.replace('%user%', interaction.user.id))
|
||||
.setDescription(
|
||||
msgconfig.Ticket.TicketQuestions.Details.Description.replace("%user%", interaction.user.id)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.addFields(
|
||||
answers.map((a) => ({
|
||||
@@ -99,18 +94,14 @@ async function askTicketQuestions(ticketChannel, interaction, questions) {
|
||||
)
|
||||
.setTimestamp();
|
||||
|
||||
await ticketChannel.send({
|
||||
embeds: [summaryEmbed]
|
||||
});
|
||||
await ticketChannel.send({ embeds: [summaryEmbed] });
|
||||
}
|
||||
};
|
||||
|
||||
// Start asking the first question
|
||||
sendNextQuestion();
|
||||
}
|
||||
|
||||
async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, attempts = 0) {
|
||||
|
||||
try {
|
||||
const clockedInUsers = await getClockedInUsers(interaction.guild);
|
||||
|
||||
@@ -124,7 +115,7 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte
|
||||
|
||||
const claimButton = new ButtonBuilder()
|
||||
.setCustomId(`claimticket-${ticketChannel.id}`)
|
||||
.setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle || 'Claim Ticket')
|
||||
.setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle || "Claim Ticket")
|
||||
.setStyle(ButtonStyle.Primary);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(claimButton);
|
||||
@@ -135,20 +126,25 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte
|
||||
components: [row],
|
||||
});
|
||||
|
||||
const filter = i => i.customId === `claimticket-${ticketChannel.id}` && i.member.roles.cache.has(Staff.id);
|
||||
const filter = (i) =>
|
||||
i.customId === `claimticket-${ticketChannel.id}` &&
|
||||
i.member.roles.cache.has(Staff.id);
|
||||
const collector = claimMessage.createMessageComponentCollector({ filter, time: 120000 });
|
||||
|
||||
collector.on('collect', async i => {
|
||||
collector.on("collect", async (i) => {
|
||||
await ticketChannel.send({ content: `<@${i.user.id}> has claimed the ticket.` });
|
||||
});
|
||||
|
||||
collector.on('end', async collected => {
|
||||
collector.on("end", async (collected) => {
|
||||
if (collected.size === 0) {
|
||||
await claimMessage.delete();
|
||||
|
||||
await claimChannel.send({
|
||||
content: `Ticket is still unclaimed. \nCC: [<@&${Staff.id}> <@&${Admin.id}]`,
|
||||
embeds: [new EmbedBuilder().setDescription(`Ticket is still unclaimed: <#${ticketChannel.id}>`).setColor(supportbot.Embed.Colours.Warn)],
|
||||
content: `Ticket is still unclaimed. \nCC: [<@&${Staff.id}> <@&${Admin.id}>]`,
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setDescription(`Ticket is still unclaimed: <#${ticketChannel.id}>`)
|
||||
.setColor(supportbot.Embed.Colours.Warn),
|
||||
],
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -161,22 +157,27 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte
|
||||
}
|
||||
|
||||
const clockedInArray = Array.from(clockedInUsers);
|
||||
const assignedUserId = clockedInArray[Math.floor(Math.random() * clockedInArray.length)];
|
||||
const assignedUserId =
|
||||
clockedInArray[Math.floor(Math.random() * clockedInArray.length)];
|
||||
const assignedUser = await interaction.guild.members.fetch(assignedUserId);
|
||||
|
||||
const claimEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle)
|
||||
.setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage.replace('%user%', interaction.user.id))
|
||||
.setDescription(
|
||||
msgconfig.Ticket.ClaimTickets.ClaimMessage.replace("%user%", interaction.user.id)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const claimButton = new ButtonBuilder()
|
||||
.setCustomId(`claimticket-${ticketChannel.id}`)
|
||||
.setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle || 'Claim Ticket')
|
||||
.setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle || "Claim Ticket")
|
||||
.setStyle(ButtonStyle.Primary);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(claimButton);
|
||||
|
||||
const claimChannel = await interaction.guild.channels.cache.get(supportbot.Ticket.ClaimTickets.Channel);
|
||||
const claimChannel = await interaction.guild.channels.cache.get(
|
||||
supportbot.Ticket.ClaimTickets.Channel
|
||||
);
|
||||
if (!claimChannel) {
|
||||
console.error("Claim channel not found");
|
||||
return null;
|
||||
@@ -188,21 +189,28 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte
|
||||
components: [row],
|
||||
});
|
||||
|
||||
const filter = i => i.customId === `claimticket-${ticketChannel.id}` && i.user.id === assignedUserId;
|
||||
const filter = (i) =>
|
||||
i.customId === `claimticket-${ticketChannel.id}` && i.user.id === assignedUserId;
|
||||
const collector = claimMessage.createMessageComponentCollector({ filter, time: 120000 });
|
||||
|
||||
collector.on('collect', async i => {
|
||||
await ticketChannel.send({ content: `<@${assignedUserId}>` }).then(msg => setTimeout(() => msg.delete(), 5000));
|
||||
collector.on("collect", async (i) => {
|
||||
await ticketChannel
|
||||
.send({ content: `<@${assignedUserId}>` })
|
||||
.then((msg) => setTimeout(() => msg.delete(), 5000));
|
||||
});
|
||||
|
||||
collector.on('end', async collected => {
|
||||
collector.on("end", async (collected) => {
|
||||
if (collected.size === 0) {
|
||||
await claimMessage.delete();
|
||||
|
||||
if (attempts >= 1) {
|
||||
await claimChannel.send({
|
||||
content: `No one claimed the ticket. Tagging roles: <@&${Staff.id}> <@&${Admin.id}>`,
|
||||
embeds: [new EmbedBuilder().setDescription(`Ticket is still unclaimed: <#${ticketChannel.id}>`).setColor(supportbot.Embed.Colours.Warn)],
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setDescription(`Ticket is still unclaimed: <#${ticketChannel.id}>`)
|
||||
.setColor(supportbot.Embed.Colours.Warn),
|
||||
],
|
||||
});
|
||||
|
||||
const claimEmbed = new EmbedBuilder()
|
||||
@@ -223,17 +231,24 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte
|
||||
components: [row],
|
||||
});
|
||||
|
||||
const filter = i => i.customId === `claimticket-${ticketChannel.id}` && i.member.roles.cache.has(Staff.id);
|
||||
const collector = claimMessage.createMessageComponentCollector({ filter, time: 120000 });
|
||||
const filter = (i) =>
|
||||
i.customId === `claimticket-${ticketChannel.id}` &&
|
||||
i.member.roles.cache.has(Staff.id);
|
||||
const collector = claimMessage.createMessageComponentCollector({
|
||||
filter,
|
||||
time: 120000,
|
||||
});
|
||||
|
||||
collector.on('collect', async i => {
|
||||
collector.on("collect", async (i) => {
|
||||
console.log(`Ticket ${ticketChannel.id} claimed by user ${i.user.id}`);
|
||||
await ticketChannel.member.add(i.user.id);
|
||||
});
|
||||
|
||||
collector.on('end', async collected => {
|
||||
collector.on("end", async (collected) => {
|
||||
if (collected.size === 0) {
|
||||
console.log(`No one claimed the ticket (${ticketChannel.id}), notifying all staff`);
|
||||
console.log(
|
||||
`No one claimed the ticket (${ticketChannel.id}), notifying all staff`
|
||||
);
|
||||
await claimMessage.delete();
|
||||
|
||||
await claimChannel.send({
|
||||
@@ -244,7 +259,9 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte
|
||||
} else {
|
||||
const ReassigningEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.ClaimTickets.ReassigningTitle)
|
||||
.setDescription(msgconfig.Ticket.ClaimTickets.ReassigningMessage.replace('%user%', assignedUserId))
|
||||
.setDescription(
|
||||
msgconfig.Ticket.ClaimTickets.ReassigningMessage.replace("%user%", assignedUserId)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
await claimChannel.send({ embeds: [ReassigningEmbed], components: [] });
|
||||
@@ -256,9 +273,6 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte
|
||||
return assignedUserId;
|
||||
} catch (error) {
|
||||
console.error("Error in assignTicketToUser function:", error);
|
||||
if (error instanceof CombinedError) {
|
||||
console.error("CombinedError details:", error.errors);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -283,16 +297,17 @@ module.exports = new Command({
|
||||
if (await isBlacklisted(interaction.user.id)) {
|
||||
return interaction.reply({
|
||||
content: msgconfig.Ticket.Blacklisted,
|
||||
ephemeral: true,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
let TicketReason = null;
|
||||
if (supportbot.Ticket.TicketReason) {
|
||||
TicketReason = interaction.reason || null; // Retrieve the reason from the interaction object
|
||||
TicketReason = interaction.reason || null;
|
||||
}
|
||||
|
||||
let TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
let TicketData = { tickets: db.getAllTickets() };
|
||||
|
||||
const { getRole, getChannel } = interaction.client;
|
||||
let User = interaction.guild.members.cache.get(interaction.user.id);
|
||||
|
||||
@@ -308,7 +323,7 @@ module.exports = new Command({
|
||||
color: supportbot.Embed.Colours.Warn,
|
||||
},
|
||||
],
|
||||
ephemeral: true,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
@@ -326,7 +341,7 @@ module.exports = new Command({
|
||||
) {
|
||||
return await interaction.reply({
|
||||
embeds: [TicketExists],
|
||||
ephemeral: true,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
@@ -335,7 +350,7 @@ module.exports = new Command({
|
||||
if (!Staff || !Admin)
|
||||
return interaction.reply({
|
||||
content: "Some roles seem to be missing!\nPlease check for errors when starting the bot.",
|
||||
ephemeral: true,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
let ticketChannel;
|
||||
@@ -361,7 +376,7 @@ module.exports = new Command({
|
||||
if (!category) {
|
||||
return interaction.reply({
|
||||
content: "The ticket category does not exist!",
|
||||
ephemeral: true,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
@@ -372,7 +387,6 @@ module.exports = new Command({
|
||||
reason: 'support ticket',
|
||||
});
|
||||
|
||||
// Move to secondary category if the primary one is full
|
||||
if (category.children.size >= 50) {
|
||||
const secondaryCategory = interaction.guild.channels.cache.find(
|
||||
(c) => c.name === supportbot.Ticket.TicketChannelsCategory2 || c.id === supportbot.Ticket.TicketChannelsCategory2
|
||||
@@ -383,13 +397,12 @@ module.exports = new Command({
|
||||
} else {
|
||||
return interaction.reply({
|
||||
content: "The secondary ticket category does not exist!",
|
||||
ephemeral: true,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assign ticket to a user if claiming tickets is enabled
|
||||
if (supportbot.Ticket.ClaimTickets.Enabled) {
|
||||
const assignedUserId = await assignTicketToUser(ticketChannel, Staff, Admin, interaction);
|
||||
if (!assignedUserId) {
|
||||
@@ -418,17 +431,8 @@ module.exports = new Command({
|
||||
ViewChannel: false,
|
||||
});
|
||||
}
|
||||
|
||||
// Ask questions to the user after the ticket is opened
|
||||
if (supportbot.Ticket.Questions.Enabled) {
|
||||
const questions = supportbot.Ticket.Questions.List || [];
|
||||
if (questions.length > 0) {
|
||||
await askTicketQuestions(ticketChannel, interaction, questions);
|
||||
}
|
||||
}
|
||||
|
||||
// Save ticket data
|
||||
TicketData.tickets.push({
|
||||
db.addTicket({
|
||||
id: ticketChannel.id,
|
||||
name: ticketChannel.name,
|
||||
user: interaction.user.id,
|
||||
@@ -439,16 +443,8 @@ module.exports = new Command({
|
||||
createdAt: new Date().toISOString(),
|
||||
claimedAt: null,
|
||||
});
|
||||
TicketData.tickets = db.getAllTickets();
|
||||
|
||||
fs.writeFileSync(
|
||||
"./Data/TicketData.json",
|
||||
JSON.stringify(TicketData, null, 4),
|
||||
(err) => {
|
||||
if (err) console.error(err);
|
||||
}
|
||||
);
|
||||
|
||||
// Notify the user that the ticket was created
|
||||
const CreatedTicket = new EmbedBuilder()
|
||||
.setDescription(
|
||||
msgconfig.Ticket.TicketCreatedAlert.replace(/%ticketauthor%/g, interaction.user.id)
|
||||
@@ -457,9 +453,8 @@ module.exports = new Command({
|
||||
.replace(/%ticketreason%/g, TicketSubject)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
await interaction.reply({ embeds: [CreatedTicket], ephemeral: true });
|
||||
await interaction.reply({ embeds: [CreatedTicket], flags: MessageFlags.Ephemeral });
|
||||
|
||||
// Create the ticket message
|
||||
const TicketMessage = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: msgconfig.Ticket.TicketAuthorTitle.replace(/%ticketauthor%/g, interaction.user.id)
|
||||
@@ -486,7 +481,6 @@ module.exports = new Command({
|
||||
);
|
||||
}
|
||||
|
||||
// Create the control panel
|
||||
const SelectMenus = new StringSelectMenuBuilder()
|
||||
.setCustomId("ticketcontrolpanel")
|
||||
.setPlaceholder("Ticket Control Panel")
|
||||
@@ -542,6 +536,13 @@ module.exports = new Command({
|
||||
components: [row2],
|
||||
});
|
||||
|
||||
if (supportbot.Ticket.Questions.Enabled) {
|
||||
const questions = supportbot.Ticket.Questions.List || [];
|
||||
|
||||
if (questions.length > 0) {
|
||||
await askTicketQuestions(ticketChannel, interaction, questions);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error in run method:", error);
|
||||
@@ -0,0 +1,113 @@
|
||||
const fs = require("fs");
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ApplicationCommandType,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
MessageFlags,
|
||||
} = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const panelconfig = yaml.load(fs.readFileSync("./Configs/ticket-panel.yml", "utf8"));
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
const Command = require("../../Structures/Command.js");
|
||||
const db = require("../../Structures/Database.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.TicketPanel.Command,
|
||||
description: cmdconfig.TicketPanel.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
options: [],
|
||||
permissions: cmdconfig.TicketPanel.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
if (!panelconfig.Panel) {
|
||||
console.log(
|
||||
"\u001b[31m",
|
||||
`[TICKET PANEL]`,
|
||||
"\u001b[33m",
|
||||
"Ticket Panel is not setup, You can set this in", `\u001b[31m`, '/Configs/ticket-panel.yml', `\n `,
|
||||
);
|
||||
return interaction.reply({
|
||||
content: "Ticket Panel is not set up. Please configure it in `/Configs/ticket-panel.yml`.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
const { getChannel } = interaction.client;
|
||||
|
||||
const channel = await getChannel(
|
||||
supportbot.Ticket.TicketHome,
|
||||
interaction.guild
|
||||
);
|
||||
|
||||
if (!channel) {
|
||||
console.log(`[TICKET PANEL] ${channelName} channel not found. Please check your config file.`);
|
||||
return interaction.reply({
|
||||
content: `${channelName} channel not found. Please check your config file.`,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
const panelRow = db.getTicketPanel();
|
||||
if (panelRow) {
|
||||
try {
|
||||
await channel.messages.fetch(panelRow.message_id);
|
||||
return interaction.reply({
|
||||
content: "Ticket panel message already exists.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
let embed = new EmbedBuilder()
|
||||
.setTitle(panelconfig.PanelTitle)
|
||||
.setColor(panelconfig.PanelColour)
|
||||
.setFooter({
|
||||
text: supportbot.Embed.Footer,
|
||||
iconURL: interaction.user.displayAvatarURL(),
|
||||
});
|
||||
|
||||
if (panelconfig.TicketPanel_Description) {
|
||||
embed.setDescription(panelconfig.PanelMessage);
|
||||
}
|
||||
|
||||
if (panelconfig.TicketPanel_Thumbnail) {
|
||||
embed.setThumbnail(panelconfig.PanelThumbnail);
|
||||
}
|
||||
|
||||
if (panelconfig.TicketPanel_Image) {
|
||||
embed.setImage(panelconfig.PanelImage);
|
||||
}
|
||||
|
||||
const createTicketButton = new ButtonBuilder()
|
||||
.setCustomId("createticket")
|
||||
.setLabel(panelconfig.Button.Text)
|
||||
.setEmoji(panelconfig.Button.Emoji)
|
||||
.setStyle(panelconfig.Button.Color);
|
||||
|
||||
let row = new ActionRowBuilder().addComponents(createTicketButton);
|
||||
|
||||
try {
|
||||
const message = await channel.send({
|
||||
embeds: [embed],
|
||||
components: [row],
|
||||
});
|
||||
|
||||
db.saveTicketPanel(message.id, channel.id);
|
||||
|
||||
return interaction.reply({
|
||||
content: "Ticket panel message has been sent!",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("Error sending message:", e);
|
||||
return interaction.reply({
|
||||
content: "An error occurred while sending the ticket panel message.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -9,9 +9,10 @@ const {
|
||||
TextInputBuilder,
|
||||
TextInputStyle,
|
||||
ButtonStyle,
|
||||
ComponentType
|
||||
ComponentType,
|
||||
MessageFlags,
|
||||
} = require('discord.js');
|
||||
const Command = require('../Structures/Command.js');
|
||||
const Command = require('../../Structures/Command.js');
|
||||
const fs = require('fs');
|
||||
const yaml = require('js-yaml');
|
||||
|
||||
@@ -19,7 +20,6 @@ const supportbot = yaml.load(fs.readFileSync('./Configs/supportbot.yml', 'utf8')
|
||||
const cmdconfig = yaml.load(fs.readFileSync('./Configs/commands.yml', 'utf8'));
|
||||
const msgconfig = yaml.load(fs.readFileSync('./Configs/messages.yml', 'utf8'));
|
||||
|
||||
// Helper function to validate and convert hex color
|
||||
function parseColor(color) {
|
||||
if (!color) return 0x5865F2;
|
||||
if (typeof color === 'number') return color;
|
||||
@@ -46,7 +46,6 @@ module.exports = new Command({
|
||||
permissions: cmdconfig.Embed.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
// Permission check
|
||||
const { getRole } = interaction.client;
|
||||
let SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
|
||||
let Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
@@ -61,12 +60,11 @@ module.exports = new Command({
|
||||
.setTitle('Invalid Permissions!')
|
||||
.setDescription(`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``)
|
||||
.setColor(0xFF0000);
|
||||
return interaction.reply({ embeds: [NoPerms], ephemeral: true });
|
||||
return interaction.reply({ embeds: [NoPerms], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
const channel = interaction.options.getChannel('channel');
|
||||
|
||||
// Initial message state
|
||||
const messageData = {
|
||||
type: 'embed',
|
||||
title: 'New Embed',
|
||||
@@ -79,7 +77,6 @@ module.exports = new Command({
|
||||
timestamp: false
|
||||
};
|
||||
|
||||
// Create initial embed
|
||||
function createEmbed() {
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(messageData.title)
|
||||
@@ -105,7 +102,6 @@ module.exports = new Command({
|
||||
return embed;
|
||||
}
|
||||
|
||||
// Create type selector
|
||||
const typeRow = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new StringSelectMenuBuilder()
|
||||
@@ -125,12 +121,10 @@ module.exports = new Command({
|
||||
])
|
||||
);
|
||||
|
||||
// Create button rows function
|
||||
function createButtons(messageType) {
|
||||
const buttons = [];
|
||||
|
||||
if (messageType === 'embed') {
|
||||
// Only show these buttons for embed mode
|
||||
const row1 = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
@@ -173,7 +167,6 @@ module.exports = new Command({
|
||||
|
||||
buttons.push(row1, row2);
|
||||
} else {
|
||||
// Text mode only shows edit text button
|
||||
const textRow = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
@@ -198,7 +191,6 @@ module.exports = new Command({
|
||||
return buttons;
|
||||
}
|
||||
|
||||
// Function to update the message preview
|
||||
async function updatePreview(i) {
|
||||
if (messageData.type === 'embed') {
|
||||
await i.update({
|
||||
@@ -207,7 +199,6 @@ module.exports = new Command({
|
||||
components: [typeRow, ...createButtons('embed')]
|
||||
});
|
||||
} else {
|
||||
// For text mode, just show the text directly
|
||||
await i.update({
|
||||
content: messageData.description,
|
||||
embeds: [],
|
||||
@@ -216,15 +207,13 @@ module.exports = new Command({
|
||||
}
|
||||
}
|
||||
|
||||
// Send initial message
|
||||
const message = await interaction.reply({
|
||||
content: messageData.type === 'text' ? messageData.description : 'Customize your message:',
|
||||
components: [typeRow, ...createButtons(messageData.type)],
|
||||
embeds: messageData.type === 'embed' ? [createEmbed()] : [],
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
// Create collector for interactions
|
||||
const collector = message.createMessageComponentCollector({
|
||||
time: 900000
|
||||
});
|
||||
@@ -233,7 +222,7 @@ module.exports = new Command({
|
||||
if (i.user.id !== interaction.user.id) {
|
||||
return i.reply({
|
||||
content: 'You cannot edit this message.',
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
@@ -422,7 +411,7 @@ module.exports = new Command({
|
||||
} catch (error) {
|
||||
await i.reply({
|
||||
content: 'Failed to send message. Please check channel permissions.',
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
break;
|
||||
@@ -1,11 +1,11 @@
|
||||
const fs = require("fs");
|
||||
const { ApplicationCommandOptionType, ApplicationCommandType, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType } = require("discord.js");
|
||||
const { ApplicationCommandOptionType, ApplicationCommandType, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType, MessageFlags } = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
const Command = require("../../Structures/Command.js");
|
||||
|
||||
// Function to chunk array into smaller arrays
|
||||
function chunkArray(arr, size) {
|
||||
@@ -116,7 +116,7 @@ module.exports = new Command({
|
||||
const alreadyBlacklistedEmbed = new EmbedBuilder()
|
||||
.setDescription(msgconfig.Mod.TicketBlacklist.Add.AlreadyBlacklisted.replace("{userTag}", user.tag) || ":x: User is already blacklisted.")
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
return interaction.reply({ embeds: [alreadyBlacklistedEmbed], ephemeral: true });
|
||||
return interaction.reply({ embeds: [alreadyBlacklistedEmbed], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
blacklistedUsers.push(user.id);
|
||||
@@ -141,7 +141,7 @@ module.exports = new Command({
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [successEmbed],
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
} else if (subcommand === cmdconfig.Mod.TicketBlacklist.Remove.Command) {
|
||||
@@ -149,7 +149,7 @@ module.exports = new Command({
|
||||
const notBlacklistedEmbed = new EmbedBuilder()
|
||||
.setDescription(msgconfig.Mod.TicketBlacklist.Remove.NotBlacklisted.replace("{userTag}", user.tag) || ":x: User is not blacklisted.")
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
return interaction.reply({ embeds: [notBlacklistedEmbed], ephemeral: true });
|
||||
return interaction.reply({ embeds: [notBlacklistedEmbed], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
blacklistedUsers = blacklistedUsers.filter(id => id !== user.id);
|
||||
@@ -172,14 +172,14 @@ module.exports = new Command({
|
||||
blacklistChannel.send({ embeds: [blacklistLogEmbed] });
|
||||
}
|
||||
|
||||
return interaction.reply({ embeds: [removedEmbed], ephemeral: true });
|
||||
return interaction.reply({ embeds: [removedEmbed], flags: MessageFlags.Ephemeral });
|
||||
|
||||
} else if (subcommand === "view") {
|
||||
if (blacklistedUsers.length === 0) {
|
||||
const noBlacklistedUsersEmbed = new EmbedBuilder()
|
||||
.setDescription(msgconfig.Mod.TicketBlacklist.View.NoBlacklistedUsers || "No users are blacklisted.")
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
return interaction.reply({ embeds: [noBlacklistedUsersEmbed], ephemeral: true });
|
||||
return interaction.reply({ embeds: [noBlacklistedUsersEmbed], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
const chunkedUsers = chunkArray(blacklistedUsers, 5);
|
||||
@@ -210,7 +210,7 @@ module.exports = new Command({
|
||||
const response = await interaction.reply({
|
||||
embeds: [createEmbed(currentPage)],
|
||||
components: chunkedUsers.length > 1 ? [buttons] : [],
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
if (chunkedUsers.length <= 1) return;
|
||||
@@ -224,7 +224,7 @@ module.exports = new Command({
|
||||
if (i.user.id !== interaction.user.id) {
|
||||
return i.reply({
|
||||
content: 'You cannot use these buttons.',
|
||||
ephemeral: true
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ module.exports = new Command({
|
||||
const errorEmbed = new EmbedBuilder()
|
||||
.setDescription(msgconfig.Error.ActionFailed || "An error occurred while processing your request.")
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
interaction.reply({ embeds: [errorEmbed], ephemeral: true });
|
||||
interaction.reply({ embeds: [errorEmbed], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,33 +1,35 @@
|
||||
const { Command } = require('../Structures/Addon.js');
|
||||
const { Command } = require('../../Structures/Addon.js');
|
||||
const Discord = require("discord.js");
|
||||
const fs = require("fs");
|
||||
const yaml = require("js-yaml");
|
||||
const path = require("path");
|
||||
const db = require("../../Structures/Database.js");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
const settingsFilePath = path.resolve(__dirname, '../Data/settings.json');
|
||||
|
||||
function loadSettings() {
|
||||
try {
|
||||
if (fs.existsSync(settingsFilePath)) {
|
||||
const data = fs.readFileSync(settingsFilePath, 'utf8');
|
||||
return JSON.parse(data);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error reading settings file:", error);
|
||||
return {};
|
||||
}
|
||||
async function loadSettings() {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.get("SELECT data FROM settings WHERE id = 1", (err, row) => {
|
||||
if (err) return reject(err);
|
||||
if (!row) return resolve({});
|
||||
try {
|
||||
resolve(JSON.parse(row.data));
|
||||
} catch (e) {
|
||||
resolve({});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function saveSettings(settings) {
|
||||
try {
|
||||
fs.writeFileSync(settingsFilePath, JSON.stringify(settings, null, 2), 'utf8');
|
||||
} catch (error) {
|
||||
console.error("Error writing settings file:", error);
|
||||
}
|
||||
async function saveSettings(settings) {
|
||||
const data = JSON.stringify(settings);
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run(
|
||||
"INSERT INTO settings (id, data) VALUES (1, ?) ON CONFLICT(id) DO UPDATE SET data = excluded.data",
|
||||
[data],
|
||||
(err) => (err ? reject(err) : resolve())
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
@@ -119,7 +121,7 @@ module.exports = new Command({
|
||||
|
||||
async run(interaction) {
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
const settings = loadSettings();
|
||||
const settings = await loadSettings();
|
||||
|
||||
if (subcommand === 'setstatus') {
|
||||
const status = interaction.options.getString('status');
|
||||
@@ -131,15 +133,15 @@ module.exports = new Command({
|
||||
});
|
||||
|
||||
settings.status = status;
|
||||
saveSettings(settings);
|
||||
await saveSettings(settings);
|
||||
|
||||
const embed = new Discord.EmbedBuilder()
|
||||
.setDescription(`Status set to **${status.charAt(0).toUpperCase() + status.slice(1)}**`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
return interaction.reply({ embeds: [embed], ephemeral: true });
|
||||
return interaction.reply({ embeds: [embed], flags: Discord.MessageFlags.Ephemeral });
|
||||
} catch (error) {
|
||||
return interaction.reply({ content: `Failed to set status: ${error.message}`, ephemeral: true });
|
||||
return interaction.reply({ content: `Failed to set status: ${error.message}`, flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,15 +156,15 @@ module.exports = new Command({
|
||||
});
|
||||
|
||||
settings.activity = { type: activityType, message: activityMessage };
|
||||
saveSettings(settings);
|
||||
await saveSettings(settings);
|
||||
|
||||
const embed = new Discord.EmbedBuilder()
|
||||
.setDescription(`Activity set to **${activityType.toLowerCase()} ${activityMessage}**`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
return interaction.reply({ embeds: [embed], ephemeral: true });
|
||||
return interaction.reply({ embeds: [embed], flags: Discord.MessageFlags.Ephemeral });
|
||||
} catch (error) {
|
||||
return interaction.reply({ content: `Failed to set activity: ${error.message}`, ephemeral: true });
|
||||
return interaction.reply({ content: `Failed to set activity: ${error.message}`, flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
} else if (subcommand === 'streammode') {
|
||||
const toggle = interaction.options.getString('toggle');
|
||||
@@ -170,7 +172,7 @@ module.exports = new Command({
|
||||
|
||||
if (toggle === 'on') {
|
||||
if (!twitchUrl) {
|
||||
return interaction.reply({ content: 'Please provide a Twitch URL to stream.', ephemeral: true });
|
||||
return interaction.reply({ content: 'Please provide a Twitch URL to stream.', flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -180,15 +182,15 @@ module.exports = new Command({
|
||||
});
|
||||
|
||||
settings.streaming = { active: true, url: twitchUrl };
|
||||
saveSettings(settings);
|
||||
await saveSettings(settings);
|
||||
|
||||
const embed = new Discord.EmbedBuilder()
|
||||
.setDescription(`**Streaming mode enabled!**\nStreaming: [Twitch](${twitchUrl})`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
return interaction.reply({ embeds: [embed], ephemeral: true });
|
||||
return interaction.reply({ embeds: [embed], flags: Discord.MessageFlags.Ephemeral });
|
||||
} catch (error) {
|
||||
return interaction.reply({ content: `Failed to enable streaming mode: ${error.message}`, ephemeral: true });
|
||||
return interaction.reply({ content: `Failed to enable streaming mode: ${error.message}`, flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
} else if (toggle === 'off') {
|
||||
try {
|
||||
@@ -198,15 +200,15 @@ module.exports = new Command({
|
||||
});
|
||||
|
||||
settings.streaming = { active: false };
|
||||
saveSettings(settings);
|
||||
await saveSettings(settings);
|
||||
|
||||
const embed = new Discord.EmbedBuilder()
|
||||
.setDescription(`**Streaming mode disabled!**`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
return interaction.reply({ embeds: [embed], ephemeral: true });
|
||||
return interaction.reply({ embeds: [embed], flags: Discord.MessageFlags.Ephemeral });
|
||||
} catch (error) {
|
||||
return interaction.reply({ content: `Failed to disable streaming mode: ${error.message}`, ephemeral: true });
|
||||
return interaction.reply({ content: `Failed to disable streaming mode: ${error.message}`, flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
}
|
||||
} else if (subcommand === 'setnickname') {
|
||||
@@ -218,18 +220,18 @@ module.exports = new Command({
|
||||
await botMember.setNickname(nickname);
|
||||
|
||||
settings.nickname = nickname;
|
||||
saveSettings(settings);
|
||||
await saveSettings(settings);
|
||||
|
||||
const embed = new Discord.EmbedBuilder()
|
||||
.setDescription(`Bot nickname set to **${nickname}**`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
return interaction.reply({ embeds: [embed], ephemeral: true });
|
||||
return interaction.reply({ embeds: [embed], flags: Discord.MessageFlags.Ephemeral });
|
||||
} catch (error) {
|
||||
return interaction.reply({ content: `Failed to set nickname: ${error.message}`, ephemeral: true });
|
||||
return interaction.reply({ content: `Failed to set nickname: ${error.message}`, flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
} else {
|
||||
return interaction.reply({ content: 'Invalid command usage.', ephemeral: true });
|
||||
return interaction.reply({ content: 'Invalid command usage.', flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -1,217 +0,0 @@
|
||||
const fs = require("fs");
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ApplicationCommandOptionType,
|
||||
ApplicationCommandType,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
ChannelType,
|
||||
} = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.AddUser.Command,
|
||||
description: cmdconfig.AddUser.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: "user",
|
||||
description: "The user to add",
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
permissions: cmdconfig.AddUser.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
const userToAdd = interaction.options.getUser("user");
|
||||
const ticketChannel = interaction.channel;
|
||||
const ticketDataPath = "./Data/TicketData.json";
|
||||
|
||||
if (
|
||||
(supportbot.Ticket.TicketType === "threads" && !ticketChannel.isThread()) ||
|
||||
(supportbot.Ticket.TicketType === "channels" && ticketChannel.type !== ChannelType.GuildText)
|
||||
) {
|
||||
const onlyInTicket = new EmbedBuilder()
|
||||
.setTitle(msgconfig.AddUser.NotInTicket_Title)
|
||||
.setDescription(msgconfig.AddUser.NotInTicket_Description)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [onlyInTicket],
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
const ticketInviteEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.AddUser.Invite_Title)
|
||||
.setDescription(msgconfig.AddUser.Invite_Description.replace("%username%", userToAdd.username))
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [ticketInviteEmbed],
|
||||
ephemeral: true,
|
||||
});
|
||||
|
||||
try {
|
||||
const dmEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.AddUser.DM_Title)
|
||||
.setDescription(msgconfig.AddUser.DM_Description.replace("%channelname%", ticketChannel.name))
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const acceptButton = new ButtonBuilder()
|
||||
.setCustomId("accept_invite")
|
||||
.setLabel(msgconfig.AddUser.Accept_Label)
|
||||
.setEmoji("✅")
|
||||
.setStyle(ButtonStyle.Primary);
|
||||
|
||||
const rejectButton = new ButtonBuilder()
|
||||
.setCustomId("reject_invite")
|
||||
.setLabel(msgconfig.AddUser.Reject_Label)
|
||||
.setEmoji("❌")
|
||||
.setStyle(ButtonStyle.Secondary);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(acceptButton, rejectButton);
|
||||
|
||||
const dmMessage = await userToAdd.send({
|
||||
embeds: [dmEmbed],
|
||||
components: [row],
|
||||
});
|
||||
|
||||
const filter = (i) => i.user.id === userToAdd.id;
|
||||
const collector = dmMessage.createMessageComponentCollector({
|
||||
filter,
|
||||
time: 60000,
|
||||
});
|
||||
|
||||
collector.on("collect", async (i) => {
|
||||
if (i.customId === "accept_invite") {
|
||||
try {
|
||||
// Add user to thread or channel based on TicketType
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await ticketChannel.members.add(userToAdd.id); // For threads
|
||||
} else if (supportbot.Ticket.TicketType === "channels") {
|
||||
await ticketChannel.permissionOverwrites.create(userToAdd.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
}); // For channels
|
||||
}
|
||||
|
||||
const ticketData = JSON.parse(fs.readFileSync(ticketDataPath, "utf8"));
|
||||
const ticketIndex = ticketData.tickets.findIndex((t) => t.id === ticketChannel.id);
|
||||
if (ticketIndex !== -1) {
|
||||
ticketData.tickets[ticketIndex].subUsers.push(userToAdd.id);
|
||||
fs.writeFileSync(ticketDataPath, JSON.stringify(ticketData, null, 4));
|
||||
}
|
||||
|
||||
const acceptedEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.AddUser.Accepted_Title)
|
||||
.setDescription(msgconfig.AddUser.Accepted_Message)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
await i.update({
|
||||
embeds: [acceptedEmbed],
|
||||
components: [],
|
||||
});
|
||||
|
||||
// Send the added user ticket message in the ticket channel
|
||||
|
||||
const addeduserTicket = new EmbedBuilder()
|
||||
.setTitle(msgconfig.AddUser.Added_Title)
|
||||
.setDescription(msgconfig.AddUser.Added_Description.replace('%username%', userToAdd.username))
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
await ticketChannel.send({
|
||||
embeds: [addeduserTicket],
|
||||
});
|
||||
|
||||
|
||||
const addedToTicketEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.AddUser.AddedToTicket_Title)
|
||||
.setDescription(msgconfig.AddUser.AddedToTicket_Description.replace(
|
||||
"%channel_link%",
|
||||
`[${ticketChannel.name}](https://discord.com/channels/${ticketChannel.guild.id}/${ticketChannel.id})`
|
||||
))
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const ticketLinkButton = new ButtonBuilder()
|
||||
.setLabel(msgconfig.AddUser.ViewTicket_Label)
|
||||
.setURL(`https://discord.com/channels/${ticketChannel.guild.id}/${ticketChannel.id}`)
|
||||
.setStyle(ButtonStyle.Link);
|
||||
|
||||
await userToAdd.send({
|
||||
embeds: [addedToTicketEmbed],
|
||||
components: [new ActionRowBuilder().addComponents(ticketLinkButton)],
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error("Error adding user to the ticket:", err);
|
||||
|
||||
const errorEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.AddUser.Error_Title)
|
||||
.setDescription(msgconfig.AddUser.Error_Adding)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
await i.update({
|
||||
embeds: [errorEmbed],
|
||||
components: [],
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const declinedEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.AddUser.Declined_Title)
|
||||
.setDescription(msgconfig.AddUser.Declined_Message)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
await i.update({
|
||||
embeds: [declinedEmbed],
|
||||
components: [],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
collector.on("end", (collected) => {
|
||||
if (collected.size === 0) {
|
||||
userToAdd.send({
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setTitle(msgconfig.AddUser.NoResponse_Title)
|
||||
.setDescription(msgconfig.AddUser.NoResponse_Message)
|
||||
.setColor(supportbot.Embed.Colours.Warning),
|
||||
],
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.code === 50007) {
|
||||
const errorEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.AddUser.DM_Error_Title)
|
||||
.setDescription(msgconfig.AddUser.DM_Error_Description.replace("%username%", userToAdd.username))
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
await ticketChannel.send({
|
||||
embeds: [errorEmbed],
|
||||
});
|
||||
} else {
|
||||
console.error("Error sending DM:", error);
|
||||
|
||||
const unexpectedErrorEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.AddUser.Unexpected_Error_Title)
|
||||
.setDescription(msgconfig.AddUser.Unexpected_Error_Description.replace("%error%", error.message))
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
await ticketChannel.send({
|
||||
embeds: [unexpectedErrorEmbed],
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,320 +0,0 @@
|
||||
const { EmbedBuilder, ModalBuilder, TextInputBuilder, TextInputStyle, ApplicationCommandType, ActionRowBuilder, ButtonBuilder, ButtonStyle, ApplicationCommandOptionType } = require("discord.js");
|
||||
const fs = require("fs");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
|
||||
const clockedInUsers = new Set();
|
||||
|
||||
function updateClockedInStatus(profileEmbed, clockedIn) {
|
||||
profileEmbed.data.fields = profileEmbed.data.fields.map(field => {
|
||||
if (field.name === 'Clocked In Status') {
|
||||
field.value = clockedIn ? '✅ Clocked In' : '❌ Clocked Out';
|
||||
}
|
||||
return field;
|
||||
});
|
||||
return profileEmbed;
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.Profile.Command,
|
||||
description: cmdconfig.Profile.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
permissions: cmdconfig.Profile.Permission,
|
||||
options: [
|
||||
{
|
||||
name: 'user',
|
||||
description: 'The user to view the profile of',
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: false
|
||||
}
|
||||
],
|
||||
|
||||
async run(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
const userOption = interaction.options.getUser('user');
|
||||
const viewingUser = userOption || interaction.user;
|
||||
const viewingUserId = viewingUser.id;
|
||||
const interactionUserId = interaction.user.id;
|
||||
|
||||
const user = interaction.guild.members.cache.get(viewingUserId);
|
||||
const { getRole, getChannel } = interaction.client;
|
||||
|
||||
const Staff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
|
||||
const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
|
||||
if (!Staff || !Admin) {
|
||||
return interaction.editReply({
|
||||
content: "Some roles seem to be missing!\nPlease check for errors when starting the bot.",
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
const isStaff = user.roles.cache.has(Staff.id) || user.roles.cache.has(Admin.id);
|
||||
const isOwnProfile = interactionUserId === viewingUserId;
|
||||
|
||||
const profilePath = `./Data/Profiles/${viewingUserId}.json`;
|
||||
let profileData;
|
||||
|
||||
if (fs.existsSync(profilePath)) {
|
||||
profileData = JSON.parse(fs.readFileSync(profilePath, "utf8"));
|
||||
} else {
|
||||
profileData = {
|
||||
bio: "",
|
||||
timezone: "",
|
||||
clockedIn: false
|
||||
};
|
||||
fs.writeFileSync(profilePath, JSON.stringify(profileData, null, 2));
|
||||
}
|
||||
|
||||
const { bio, timezone, clockedIn } = profileData;
|
||||
|
||||
let profileEmbed = new EmbedBuilder()
|
||||
.setTitle(`${viewingUser.username}'s Profile`)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.setThumbnail(viewingUser.displayAvatarURL())
|
||||
.addFields(
|
||||
{ name: 'Bio', value: bio || 'No bio set.', inline: false },
|
||||
{ name: 'Timezone', value: timezone || 'No timezone set.', inline: true }
|
||||
);
|
||||
|
||||
if (isStaff) {
|
||||
profileEmbed.addFields({ name: 'Clocked In Status', value: clockedIn ? '✅ Clocked In' : '❌ Clocked Out', inline: true });
|
||||
}
|
||||
|
||||
const buttonRow = new ActionRowBuilder();
|
||||
if (isOwnProfile) {
|
||||
buttonRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('editProfile')
|
||||
.setLabel('Edit Profile')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
if (isStaff) {
|
||||
buttonRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('clockInOut')
|
||||
.setLabel(clockedIn ? 'Clock Out' : 'Clock In')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isStaff && supportbot.Ticket.ClaimTickets.Enabled) {
|
||||
buttonRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('viewTicketStats')
|
||||
.setLabel('View Ticket Stats')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
}
|
||||
|
||||
await interaction.editReply({ embeds: [profileEmbed], components: buttonRow.components.length > 0 ? [buttonRow] : [] });
|
||||
|
||||
const filter = i => (
|
||||
['editProfile', 'clockInOut', 'viewTicketStats', 'showOpenTickets', 'backToStats', 'backToProfile'].includes(i.customId)
|
||||
&& i.user.id === interactionUserId
|
||||
);
|
||||
|
||||
const collector = interaction.channel.createMessageComponentCollector({ filter, time: 60000 });
|
||||
|
||||
collector.on('collect', async i => {
|
||||
if (i.customId === 'editProfile') {
|
||||
const modal = new ModalBuilder()
|
||||
.setCustomId('editProfileModal')
|
||||
.setTitle('Edit Profile')
|
||||
.addComponents(
|
||||
new ActionRowBuilder().addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('bio')
|
||||
.setLabel('Bio')
|
||||
.setStyle(TextInputStyle.Paragraph)
|
||||
.setPlaceholder('Enter your bio')
|
||||
.setValue(String(bio))
|
||||
.setRequired(false)
|
||||
),
|
||||
new ActionRowBuilder().addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('timezone')
|
||||
.setLabel('Timezone')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setPlaceholder('Enter your timezone')
|
||||
.setValue(String(timezone))
|
||||
.setRequired(false)
|
||||
)
|
||||
);
|
||||
|
||||
await i.showModal(modal);
|
||||
}
|
||||
|
||||
if (i.customId === 'clockInOut') {
|
||||
profileData.clockedIn = !profileData.clockedIn;
|
||||
fs.writeFileSync(profilePath, JSON.stringify(profileData, null, 2));
|
||||
profileEmbed = updateClockedInStatus(profileEmbed, profileData.clockedIn);
|
||||
|
||||
if (profileData.clockedIn) {
|
||||
clockedInUsers.add(viewingUserId);
|
||||
} else {
|
||||
clockedInUsers.delete(viewingUserId);
|
||||
}
|
||||
|
||||
const updatedButtonRow = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('editProfile')
|
||||
.setLabel('Edit Profile')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
if (isStaff) {
|
||||
updatedButtonRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('clockInOut')
|
||||
.setLabel(profileData.clockedIn ? 'Clock Out' : 'Clock In')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
if (supportbot.Ticket.ClaimTickets.Enabled) {
|
||||
updatedButtonRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('viewTicketStats')
|
||||
.setLabel('View Ticket Stats')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await i.update({ embeds: [profileEmbed], components: [updatedButtonRow] });
|
||||
|
||||
let clockedinout = new EmbedBuilder()
|
||||
.setTitle(profileData.clockedIn ? 'Clocked In!' : 'Clocked Out!')
|
||||
.setDescription(profileData.clockedIn ? 'You have successfully clocked in.' : 'You have successfully clocked out.')
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
|
||||
await interaction.followUp({ embeds: [clockedinout], ephemeral: true });
|
||||
}
|
||||
|
||||
if (i.customId === 'viewTicketStats') {
|
||||
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketsClaimed = TicketData.tickets.filter(t => t.claimedBy === viewingUserId);
|
||||
const ticketsOpen = ticketsClaimed.filter(t => t.open);
|
||||
const totalTickets = ticketsClaimed.length;
|
||||
|
||||
let totalResponseTime = 0;
|
||||
let responseCount = 0;
|
||||
|
||||
ticketsClaimed.forEach(ticket => {
|
||||
if (ticket.claimedAt && ticket.createdAt) {
|
||||
const responseTime = new Date(ticket.claimedAt) - new Date(ticket.createdAt);
|
||||
totalResponseTime += responseTime;
|
||||
responseCount++;
|
||||
}
|
||||
});
|
||||
|
||||
const averageResponseTime = responseCount ? totalResponseTime / responseCount : 0;
|
||||
const averageResponseTimeMinutes = Math.round(averageResponseTime / 60000);
|
||||
|
||||
const statsEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.TicketStats.Title)
|
||||
.setDescription(`> Ticket stats for <@${viewingUserId}>`)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.addFields(
|
||||
{ name: msgconfig.TicketStats.OpenTickets, value: `${ticketsOpen.length}`, inline: true },
|
||||
{ name: msgconfig.TicketStats.TotalTickets, value: `${totalTickets}`, inline: true },
|
||||
{ name: msgconfig.TicketStats.ResponseTime, value: `${averageResponseTimeMinutes} minutes`, inline: false }
|
||||
);
|
||||
|
||||
const statsButtonRow = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('showOpenTickets')
|
||||
.setLabel('Show Open Tickets')
|
||||
.setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder()
|
||||
.setCustomId('backToProfile')
|
||||
.setLabel('Back to Profile')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
await i.update({ embeds: [statsEmbed], components: [statsButtonRow] });
|
||||
}
|
||||
|
||||
if (i.customId === 'showOpenTickets') {
|
||||
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketsOpen = TicketData.tickets.filter(t => t.claimedBy === viewingUserId && t.open);
|
||||
|
||||
const openTicketsEmbed = new EmbedBuilder()
|
||||
.setTitle('Active Tickets')
|
||||
.setDescription(ticketsOpen.map(t => `<#${t.id}>`).join('\n') || 'No active tickets.')
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const openTicketsButtonRow = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('backToStats')
|
||||
.setLabel('Back to Stats')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
await i.update({ embeds: [openTicketsEmbed], components: [openTicketsButtonRow] });
|
||||
}
|
||||
|
||||
if (i.customId === 'backToStats') {
|
||||
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketsClaimed = TicketData.tickets.filter(t => t.claimedBy === viewingUserId);
|
||||
const ticketsOpen = ticketsClaimed.filter(t => t.open);
|
||||
const totalTickets = ticketsClaimed.length;
|
||||
|
||||
let totalResponseTime = 0;
|
||||
let responseCount = 0;
|
||||
|
||||
ticketsClaimed.forEach(ticket => {
|
||||
if (ticket.claimedAt && ticket.createdAt) {
|
||||
const responseTime = new Date(ticket.claimedAt) - new Date(ticket.createdAt);
|
||||
totalResponseTime += responseTime;
|
||||
responseCount++;
|
||||
}
|
||||
});
|
||||
|
||||
const averageResponseTime = responseCount ? totalResponseTime / responseCount : 0;
|
||||
const averageResponseTimeMinutes = Math.round(averageResponseTime / 60000);
|
||||
|
||||
const statsEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.TicketStats.Title)
|
||||
.setDescription(`> Ticket stats for <@${viewingUserId}>`)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.addFields(
|
||||
{ name: msgconfig.TicketStats.OpenTickets, value: `${ticketsOpen.length}`, inline: true },
|
||||
{ name: msgconfig.TicketStats.TotalTickets, value: `${totalTickets}`, inline: true },
|
||||
{ name: msgconfig.TicketStats.ResponseTime, value: `${averageResponseTimeMinutes} minutes`, inline: false }
|
||||
);
|
||||
|
||||
const statsButtonRow = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('showOpenTickets')
|
||||
.setLabel('Show Open Tickets')
|
||||
.setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder()
|
||||
.setCustomId('backToProfile')
|
||||
.setLabel('Back to Profile')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
await i.update({ embeds: [statsEmbed], components: [statsButtonRow] });
|
||||
}
|
||||
|
||||
if (i.customId === 'backToProfile') {
|
||||
await i.update({ embeds: [profileEmbed], components: buttonRow.components.length > 0 ? [buttonRow] : [] });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports.clockedInUsers = clockedInUsers;
|
||||
@@ -1,134 +0,0 @@
|
||||
const fs = require("fs");
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ApplicationCommandOptionType,
|
||||
ApplicationCommandType,
|
||||
ChannelType,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
} = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.RemoveUser.Command,
|
||||
description: cmdconfig.RemoveUser.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: "user",
|
||||
description: "The user to remove",
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
permissions: cmdconfig.RemoveUser.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
|
||||
const { getRole } = interaction.client;
|
||||
let SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
|
||||
let Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
if (!SupportStaff || !Admin) {
|
||||
return interaction.reply(
|
||||
"Some roles seem to be missing!\nPlease check for errors when starting the bot."
|
||||
);
|
||||
}
|
||||
|
||||
const NoPerms = new EmbedBuilder()
|
||||
.setTitle("Invalid Permissions!")
|
||||
.setDescription(
|
||||
`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
|
||||
if (
|
||||
!interaction.member.roles.cache.has(SupportStaff.id) &&
|
||||
!interaction.member.roles.cache.has(Admin.id)
|
||||
) {
|
||||
return interaction.reply({ embeds: [NoPerms] });
|
||||
}
|
||||
|
||||
const userToRemove = interaction.options.getUser("user");
|
||||
const ticketChannel = interaction.channel;
|
||||
const ticketDataPath = "./Data/TicketData.json";
|
||||
|
||||
// Check if the channel is a ticket thread or text channel based on TicketType
|
||||
if (
|
||||
(supportbot.Ticket.TicketType === "threads" && !ticketChannel.isThread()) ||
|
||||
(supportbot.Ticket.TicketType === "channels" && ticketChannel.type !== ChannelType.GuildText)
|
||||
) {
|
||||
const onlyInTicket = new EmbedBuilder()
|
||||
.setTitle(msgconfig.RemoveUser.NotInTicket_Title)
|
||||
.setDescription(msgconfig.RemoveUser.NotInTicket_Description)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [onlyInTicket],
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Remove user from thread or channel based on TicketType
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await ticketChannel.members.remove(userToRemove.id); // For threads
|
||||
} else if (supportbot.Ticket.TicketType === "channels") {
|
||||
await ticketChannel.permissionOverwrites.delete(userToRemove.id); // For channels
|
||||
}
|
||||
|
||||
// Update the ticket data
|
||||
const ticketData = JSON.parse(fs.readFileSync(ticketDataPath, "utf8"));
|
||||
const ticketIndex = ticketData.tickets.findIndex((t) => t.id === ticketChannel.id);
|
||||
if (ticketIndex !== -1) {
|
||||
const subUsers = ticketData.tickets[ticketIndex].subUsers;
|
||||
const userIndex = subUsers.indexOf(userToRemove.id);
|
||||
if (userIndex !== -1) {
|
||||
subUsers.splice(userIndex, 1);
|
||||
fs.writeFileSync(ticketDataPath, JSON.stringify(ticketData, null, 4));
|
||||
}
|
||||
}
|
||||
|
||||
const removedEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.RemoveUser.Removed_Title)
|
||||
.setDescription(msgconfig.RemoveUser.Removed_Message)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [removedEmbed],
|
||||
ephemeral: true,
|
||||
});
|
||||
|
||||
const removedFromTicketEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.RemoveUser.RemovedFromTicket_Title)
|
||||
.setDescription(msgconfig.RemoveUser.RemovedFromTicket_Description.replace(
|
||||
"%channel_link%",
|
||||
`[${ticketChannel.name}](https://discord.com/channels/${ticketChannel.guild.id}/${ticketChannel.id})`
|
||||
))
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
await userToRemove.send({
|
||||
embeds: [removedFromTicketEmbed],
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error("Error removing user from the ticket:", err);
|
||||
|
||||
const errorEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.RemoveUser.Error_Title)
|
||||
.setDescription(msgconfig.RemoveUser.Error_Removing)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [errorEmbed],
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,179 +0,0 @@
|
||||
const fs = require("fs");
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
// Load configurations
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
|
||||
// Ensure suggestions file exists and is correctly loaded
|
||||
let suggestions;
|
||||
try {
|
||||
suggestions = require("../Data/SuggestionData.json");
|
||||
} catch (error) {
|
||||
suggestions = {};
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.Suggestion.Command,
|
||||
description: cmdconfig.Suggestion.Description,
|
||||
type: Discord.ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: "suggestion",
|
||||
description: "Create a Suggestion",
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
permissions: cmdconfig.Suggestion.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
const { getChannel } = interaction.client;
|
||||
const mode = supportbot.Suggestions.Mode || "Standard";
|
||||
|
||||
let suggestion = interaction.options.getString("suggestion");
|
||||
|
||||
if (mode === "Forum") {
|
||||
const forumChannel = await interaction.guild.channels.fetch(supportbot.Suggestions.ForumChannel).catch(() => null);
|
||||
|
||||
const NoChannel = new Discord.EmbedBuilder()
|
||||
.setTitle("Missing Forum Channel!")
|
||||
.setDescription(msgconfig.Error.MissingChannel)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
if (!forumChannel || forumChannel.type !== Discord.ChannelType.GuildForum) {
|
||||
return interaction.reply({ embeds: [NoChannel], ephemeral: true });
|
||||
}
|
||||
|
||||
const SuggestForumEmbed = new Discord.EmbedBuilder()
|
||||
.addFields(
|
||||
{ name: "Suggestion", value: suggestion, inline: true },
|
||||
{ name: "From", value: `<@${interaction.user.id}>` },
|
||||
{ name: `${supportbot.Suggestions.UpvoteEmoji} ${supportbot.Suggestions.UpvoteTitle}`, value: "0", inline: true },
|
||||
{ name: `${supportbot.Suggestions.DownvoteEmoji} ${supportbot.Suggestions.DownvoteTitle}`, value: "0", inline: true }
|
||||
)
|
||||
.setThumbnail(interaction.user.displayAvatarURL())
|
||||
.setFooter({
|
||||
text: supportbot.Embed.Footer,
|
||||
iconURL: interaction.user.displayAvatarURL(),
|
||||
})
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const UpvoteForumButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("upvote")
|
||||
.setEmoji(supportbot.Suggestions.UpvoteEmoji)
|
||||
.setStyle(supportbot.Suggestions.Buttons.Upvote);
|
||||
|
||||
const DownvoteForumButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("downvote")
|
||||
.setEmoji(supportbot.Suggestions.DownvoteEmoji)
|
||||
.setStyle(supportbot.Suggestions.Buttons.Downvote);
|
||||
|
||||
const RemoveVoteForumButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("removevote")
|
||||
.setLabel(supportbot.Suggestions.Buttons.RemoveVote_Title)
|
||||
.setStyle(supportbot.Suggestions.Buttons.RemoveVote);
|
||||
|
||||
const row = new Discord.ActionRowBuilder().addComponents(UpvoteForumButton, DownvoteForumButton, RemoveVoteForumButton);
|
||||
|
||||
const post = await forumChannel.threads.create({
|
||||
name: suggestion.slice(0, 100),
|
||||
message: {
|
||||
embeds: [SuggestForumEmbed],
|
||||
components: [row],
|
||||
},
|
||||
reason: "New suggestion submitted",
|
||||
autoArchiveDuration: 1440,
|
||||
});
|
||||
|
||||
suggestions[post.id] = {
|
||||
suggestion: suggestion,
|
||||
author: interaction.user.id,
|
||||
upvotes: [],
|
||||
downvotes: []
|
||||
};
|
||||
|
||||
fs.writeFileSync("./Data/SuggestionData.json", JSON.stringify(suggestions, null, 2));
|
||||
|
||||
const successEmbed = new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.Suggestions.Sent_Title)
|
||||
.setDescription(msgconfig.Suggestions.Sent)
|
||||
.addFields({ name: "Posted in forum:", value: `<#${forumChannel.id}>` })
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
return interaction.reply({ embeds: [successEmbed], ephemeral: true });
|
||||
}
|
||||
|
||||
const suggestChannel = await getChannel(supportbot.Suggestions.Channel, interaction.guild);
|
||||
|
||||
const NoChannel = new Discord.EmbedBuilder()
|
||||
.setTitle("Missing Channel!")
|
||||
.setDescription(msgconfig.Error.MissingChannel)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
if (!suggestChannel) return interaction.reply({ embeds: [NoChannel] });
|
||||
|
||||
const SuggestEmbed = new Discord.EmbedBuilder()
|
||||
.addFields(
|
||||
{ name: "Suggestion", value: suggestion, inline: true },
|
||||
{ name: "From", value: `<@${interaction.user.id}>` },
|
||||
{ name: `${supportbot.Suggestions.UpvoteEmoji} ${supportbot.Suggestions.UpvoteTitle}`, value: "0", inline: true },
|
||||
{ name: `${supportbot.Suggestions.DownvoteEmoji} ${supportbot.Suggestions.DownvoteTitle}`, value: "0", inline: true }
|
||||
)
|
||||
.setThumbnail(interaction.user.displayAvatarURL())
|
||||
.setFooter({
|
||||
text: supportbot.Embed.Footer,
|
||||
iconURL: interaction.user.displayAvatarURL(),
|
||||
})
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const UpvoteButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("upvote")
|
||||
.setEmoji(supportbot.Suggestions.UpvoteEmoji)
|
||||
.setStyle(supportbot.Suggestions.Buttons.Upvote);
|
||||
|
||||
const DownvoteButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("downvote")
|
||||
.setEmoji(supportbot.Suggestions.DownvoteEmoji)
|
||||
.setStyle(supportbot.Suggestions.Buttons.Downvote);
|
||||
|
||||
const RemoveVoteButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("removevote")
|
||||
.setLabel(supportbot.Suggestions.Buttons.RemoveVote_Title)
|
||||
.setStyle(supportbot.Suggestions.Buttons.RemoveVote);
|
||||
|
||||
const row = new Discord.ActionRowBuilder().addComponents(UpvoteButton, DownvoteButton, RemoveVoteButton);
|
||||
|
||||
const suggestionMsg = await suggestChannel.send({ embeds: [SuggestEmbed], components: [row] });
|
||||
|
||||
suggestions[suggestionMsg.id] = {
|
||||
suggestion: suggestion,
|
||||
author: interaction.user.id,
|
||||
upvotes: [],
|
||||
downvotes: []
|
||||
};
|
||||
|
||||
fs.writeFileSync("./Data/SuggestionData.json", JSON.stringify(suggestions, null, 2));
|
||||
|
||||
if (supportbot.Suggestions.Threads.Enabled) {
|
||||
await suggestionMsg.startThread({
|
||||
name: supportbot.Suggestions.Threads.Title,
|
||||
autoArchiveDuration: 60,
|
||||
type: Discord.ChannelType.PublicThread,
|
||||
reason: supportbot.Suggestions.Threads.Reason,
|
||||
});
|
||||
}
|
||||
|
||||
const Submitted = new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.Suggestions.Sent_Title)
|
||||
.setDescription(msgconfig.Suggestions.Sent)
|
||||
.addFields({ name: "Sent to:", value: `<#${suggestChannel.id}>` })
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
await interaction.reply({ ephemeral: true, embeds: [Submitted] });
|
||||
},
|
||||
});
|
||||
@@ -1,120 +0,0 @@
|
||||
// ticketpanel.js
|
||||
const fs = require("fs");
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ApplicationCommandType,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
} = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const panelconfig = yaml.load(fs.readFileSync("./Configs/ticket-panel.yml", "utf8"));
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.TicketPanel.Command,
|
||||
description: cmdconfig.TicketPanel.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
options: [],
|
||||
permissions: cmdconfig.TicketPanel.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
if (!panelconfig.Panel) {
|
||||
console.log(
|
||||
"\u001b[31m",
|
||||
`[TICKET PANEL]`,
|
||||
"\u001b[33m",
|
||||
"Ticket Panel is not setup, You can set this in", `\u001b[31m`, '/Configs/ticket-panel.yml', `\n `,
|
||||
);
|
||||
return interaction.reply({
|
||||
content: "Ticket Panel is not set up. Please configure it in `/Configs/ticket-panel.yml`.",
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
const { getChannel } = interaction.client;
|
||||
|
||||
const channel = await getChannel(
|
||||
supportbot.Ticket.TicketHome,
|
||||
interaction.guild
|
||||
);
|
||||
|
||||
if (!channel) {
|
||||
console.log(`[TICKET PANEL] ${channelName} channel not found. Please check your config file.`);
|
||||
return interaction.reply({
|
||||
content: `${channelName} channel not found. Please check your config file.`,
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
const panelid = JSON.parse(fs.readFileSync("./Data/ticket-panel-id.json", "utf8"));
|
||||
|
||||
try {
|
||||
await channel.messages.fetch(panelid.TicketPanelID);
|
||||
return interaction.reply({
|
||||
content: "Ticket panel message already exists.",
|
||||
ephemeral: true,
|
||||
});
|
||||
} catch (error) {
|
||||
let embed = new EmbedBuilder()
|
||||
.setTitle(panelconfig.PanelTitle)
|
||||
.setColor(panelconfig.PanelColour)
|
||||
.setFooter({
|
||||
text: supportbot.Embed.Footer,
|
||||
iconURL: interaction.user.displayAvatarURL(),
|
||||
});
|
||||
|
||||
if (panelconfig.TicketPanel_Description) {
|
||||
embed.setDescription(panelconfig.PanelMessage);
|
||||
}
|
||||
|
||||
if (panelconfig.TicketPanel_Thumbnail) {
|
||||
embed.setThumbnail(panelconfig.PanelThumbnail);
|
||||
}
|
||||
|
||||
if (panelconfig.TicketPanel_Image) {
|
||||
embed.setImage(panelconfig.PanelImage);
|
||||
}
|
||||
|
||||
const createTicketButton = new ButtonBuilder()
|
||||
.setCustomId("createticket")
|
||||
.setLabel(panelconfig.Button.Text)
|
||||
.setEmoji(panelconfig.Button.Emoji)
|
||||
.setStyle(panelconfig.Button.Color);
|
||||
|
||||
let row = new ActionRowBuilder().addComponents(createTicketButton);
|
||||
|
||||
try {
|
||||
const message = await channel.send({
|
||||
embeds: [embed],
|
||||
components: [row],
|
||||
});
|
||||
|
||||
let data = {
|
||||
id: panelid.id,
|
||||
TicketPanelID: message.id,
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
"./Data/ticket-panel-id.json",
|
||||
JSON.stringify(data),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
return interaction.reply({
|
||||
content: "Ticket panel message has been sent!",
|
||||
ephemeral: true,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("Error sending message:", e);
|
||||
return interaction.reply({
|
||||
content: "An error occurred while sending the ticket panel message.",
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -73,6 +73,12 @@ Suggestion:
|
||||
Permission: ["SendMessages"]
|
||||
Enabled: true
|
||||
|
||||
SuggestionAdmin:
|
||||
Command: "suggestadmin"
|
||||
Description: "Update a suggestion’s status."
|
||||
Permission: ['SendMessages']
|
||||
# Disable "Suggestion" to disable this command.
|
||||
|
||||
Embed:
|
||||
Command: "embed"
|
||||
Description: "Build a discord embed."
|
||||
|
||||
+12
-6
@@ -21,7 +21,7 @@ SupportBot_Version: "v8.4-DEV-1"
|
||||
|
||||
General:
|
||||
Name: "SupportBot"
|
||||
Token: "DISCORD_BOT_TOKEN"
|
||||
Token: "BOT_TOKEN"
|
||||
|
||||
Addons:
|
||||
Enabled: true
|
||||
@@ -95,7 +95,7 @@ Leave:
|
||||
# ----------------------------
|
||||
|
||||
Suggestions:
|
||||
Mode: "Forum" # or Forum
|
||||
Mode: "Channel" # Channel or Forum
|
||||
ForumChannel: "1402655496477081671"
|
||||
Channel: "1295788900094316706"
|
||||
|
||||
@@ -129,13 +129,19 @@ Suggestions:
|
||||
# 3 - Green
|
||||
# 4 - Red
|
||||
|
||||
Tags:
|
||||
Created: "1418937323118137435"
|
||||
Considering: "1418937237156139130"
|
||||
Solved: "1418937168029679697"
|
||||
Denied: "1418937185469464647"
|
||||
|
||||
# -------------------------
|
||||
# Tickets
|
||||
# -------------------------
|
||||
|
||||
VoiceTickets:
|
||||
Name: "%username%'s VC"
|
||||
Category: "1297539636159315999"
|
||||
Category: "1419062819688550460"
|
||||
|
||||
Ticket:
|
||||
TicketType: "channels"
|
||||
@@ -151,9 +157,9 @@ Ticket:
|
||||
# - Channel where ticket threads are made. (only if ticket threads is enabled above.)
|
||||
|
||||
# For TicketType: channels
|
||||
TicketChannelsCategory: "1295789095070728253"
|
||||
TicketChannelsCategory2: "1295789210040537088" # This will be used if the first category has reached the max channel limit.
|
||||
TicketArchiveCategory: "1295789286322343946"
|
||||
TicketChannelsCategory: "1418988408193155112"
|
||||
TicketChannelsCategory2: "1418988456167870615" # This will be used if the first category has reached the max channel limit.
|
||||
TicketArchiveCategory: "1418988434600759348"
|
||||
# ---
|
||||
|
||||
Channel: "ticket-" # The prefix for ticket channel names
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"bio": "",
|
||||
"timezone": "",
|
||||
"clockedIn": true,
|
||||
"workingHours": 0,
|
||||
"lastClockInTime": null,
|
||||
"schedule": {
|
||||
"monday": {
|
||||
"start": "09:00",
|
||||
"end": "17:00"
|
||||
},
|
||||
"tuesday": {
|
||||
"start": "09:00",
|
||||
"end": "17:00"
|
||||
},
|
||||
"wednesday": {
|
||||
"start": "09:00",
|
||||
"end": "17:00"
|
||||
},
|
||||
"thursday": {
|
||||
"start": "09:00",
|
||||
"end": "17:00"
|
||||
},
|
||||
"friday": {
|
||||
"start": "09:00",
|
||||
"end": "17:00"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"bio": "",
|
||||
"timezone": "",
|
||||
"clockedIn": false
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"tickets": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"bio": "",
|
||||
"timezone": "",
|
||||
"clockedIn": false
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"id": 0,
|
||||
"TicketPanelID": "000000000000000"
|
||||
}
|
||||
+99
-198
@@ -7,13 +7,7 @@ const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
|
||||
const Event = require("../Structures/Event.js");
|
||||
|
||||
let suggestions;
|
||||
try {
|
||||
suggestions = require("../Data/SuggestionData.json");
|
||||
} catch (error) {
|
||||
suggestions = {};
|
||||
}
|
||||
const db = require("../Structures/Database.js");
|
||||
|
||||
module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
|
||||
@@ -83,7 +77,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
interaction.customId = `createticket-${reason}`;
|
||||
const cmd = client.commands.get(cmdconfig.OpenTicket.Command);
|
||||
if (cmd) {
|
||||
interaction.reason = reason; // Attach the reason to the interaction object
|
||||
interaction.reason = reason;
|
||||
await cmd.run(interaction);
|
||||
} else {
|
||||
console.error("Command not found.");
|
||||
@@ -93,31 +87,24 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
|
||||
if (interaction.customId === "renameTicketModal") {
|
||||
const newTicketName = interaction.fields.getTextInputValue("renameTicket");
|
||||
|
||||
// Fixing the ticketChannelId extraction and lookup
|
||||
const ticketChannelId = interaction.channel.id;
|
||||
|
||||
let ticketChannel;
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
ticketChannel = interaction.guild.channels.cache.get(ticketChannelId);
|
||||
} else if (supportbot.Ticket.TicketType === "channels") {
|
||||
ticketChannel = interaction.guild.channels.cache.get(ticketChannelId);
|
||||
}
|
||||
|
||||
if (!ticketChannel) {
|
||||
return interaction.reply({
|
||||
content: "Ticket channel not found.",
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await ticketChannel.setName(newTicketName);
|
||||
|
||||
const renamedsuccess = new Discord.EmbedBuilder()
|
||||
.setDescription(msgconfig.Ticket.RenamedSuccess)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [renamedsuccess],
|
||||
ephemeral: true,
|
||||
@@ -146,137 +133,107 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
break;
|
||||
|
||||
case "supportvc":
|
||||
const ticketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketInfo = ticketData.tickets.find(t => t.id === interaction.channel.id);
|
||||
|
||||
if (ticketInfo) {
|
||||
const user = interaction.guild.members.cache.get(ticketInfo.user) || ticketInfo.user;
|
||||
const claimedBy = interaction.guild.members.cache.get(ticketInfo.claimedBy) || ticketInfo.claimedBy;
|
||||
const Admin = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Admin);
|
||||
const Staff = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Staff);
|
||||
|
||||
const VCcategory = interaction.guild.channels.cache.find(
|
||||
(c) => c.name === supportbot.VoiceTickets.Category || c.id === supportbot.VoiceTickets.Category
|
||||
);
|
||||
|
||||
if (!VCcategory) {
|
||||
return interaction.reply({
|
||||
content: "The Support VC category does not exist!",
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
let username = user ? user.user.username : "Unknown User";
|
||||
|
||||
let SupportVoice = await interaction.guild.channels.create({
|
||||
name: supportbot.VoiceTickets.Name.replace(/%username%/g, username),
|
||||
type: Discord.ChannelType.GuildVoice,
|
||||
parent: VCcategory,
|
||||
permissionOverwrites: [
|
||||
{
|
||||
id: user,
|
||||
allow: [
|
||||
Discord.PermissionsBitField.Flags.Connect,
|
||||
Discord.PermissionsBitField.Flags.Speak,
|
||||
Discord.PermissionsBitField.Flags.ViewChannel,
|
||||
],
|
||||
},
|
||||
{
|
||||
id: Staff,
|
||||
allow: [
|
||||
Discord.PermissionsBitField.Flags.Connect,
|
||||
Discord.PermissionsBitField.Flags.Speak,
|
||||
Discord.PermissionsBitField.Flags.ViewChannel,
|
||||
],
|
||||
},
|
||||
{
|
||||
id: Admin,
|
||||
allow: [
|
||||
Discord.PermissionsBitField.Flags.Connect,
|
||||
Discord.PermissionsBitField.Flags.Speak,
|
||||
Discord.PermissionsBitField.Flags.ViewChannel,
|
||||
],
|
||||
},
|
||||
{
|
||||
id: interaction.guild.id,
|
||||
deny: [Discord.PermissionsBitField.Flags.ViewChannel],
|
||||
},
|
||||
],
|
||||
const ticketInfo = db.getTicket(interaction.channel.id);
|
||||
if (!ticketInfo) {
|
||||
return interaction.reply({
|
||||
content: "⚠️ This channel isn’t a valid ticket.",
|
||||
flags: Discord.MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
ticketInfo.voiceChannelId = SupportVoice.id;
|
||||
|
||||
fs.writeFileSync("./Data/TicketData.json", JSON.stringify(ticketData, null, 2));
|
||||
|
||||
const VCDeleteButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("deleteSupportVC")
|
||||
.setLabel(supportbot.Buttons.Voice.TicketDeleteText)
|
||||
.setEmoji(supportbot.Buttons.General.Delete)
|
||||
.setStyle(supportbot.Buttons.General.DeleteStyle);
|
||||
|
||||
const VCDeleteRow = new Discord.ActionRowBuilder().addComponents(VCDeleteButton);
|
||||
|
||||
const vcmadeembed = new Discord.EmbedBuilder()
|
||||
.setDescription(`> **Support VC Created:** <#${SupportVoice.id}>`)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [vcmadeembed],
|
||||
components: [VCDeleteRow],
|
||||
});
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
const user = interaction.guild.members.cache.get(ticketInfo.user_id) || ticketInfo.user_id;
|
||||
const username = user?.user?.username || "Unknown User";
|
||||
|
||||
const VCcategory = interaction.guild.channels.cache.find(
|
||||
(c) => c.name === supportbot.VoiceTickets.Category || c.id === supportbot.VoiceTickets.Category
|
||||
);
|
||||
|
||||
if (!VCcategory) {
|
||||
return interaction.reply({
|
||||
content: "⚠️ The Support VC category does not exist!",
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
const SupportVoice = await interaction.guild.channels.create({
|
||||
name: supportbot.VoiceTickets.Name.replace(/%username%/g, username),
|
||||
type: Discord.ChannelType.GuildVoice,
|
||||
parent: VCcategory,
|
||||
permissionOverwrites: [
|
||||
{
|
||||
id: interaction.guild.id,
|
||||
deny: [Discord.PermissionsBitField.Flags.Connect]
|
||||
},
|
||||
{
|
||||
id: user.id || user, // works if user is a Member or just ID
|
||||
allow: [
|
||||
Discord.PermissionsBitField.Flags.Connect,
|
||||
Discord.PermissionsBitField.Flags.Speak,
|
||||
Discord.PermissionsBitField.Flags.ViewChannel,
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
db.updateTicketVoice(ticketInfo.ticket_id, SupportVoice.id);
|
||||
|
||||
const vcmadeembed = new Discord.EmbedBuilder()
|
||||
.setDescription(`> **Support VC Created:** <#${SupportVoice.id}>`)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
const VCDeleteButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("deleteSupportVC")
|
||||
.setLabel(supportbot.Buttons.Voice.TicketDeleteText)
|
||||
.setEmoji(supportbot.Buttons.General.Delete)
|
||||
.setStyle(supportbot.Buttons.General.DeleteStyle);
|
||||
|
||||
const VCDeleteRow = new Discord.ActionRowBuilder().addComponents(VCDeleteButton);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [vcmadeembed],
|
||||
components: [VCDeleteRow],
|
||||
});
|
||||
break;
|
||||
|
||||
case "archiveticket":
|
||||
const arcembed = new Discord.EmbedBuilder()
|
||||
.setDescription(msgconfig.Ticket.TicketArchived)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
const CloseButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("confirmCloseTicket")
|
||||
.setLabel(supportbot.Ticket.Close.Confirmation_Button)
|
||||
.setEmoji(supportbot.Ticket.Close.Confirmation_Emoji)
|
||||
.setStyle(supportbot.Ticket.Close.Confirmation_Style);
|
||||
|
||||
const unarchiveButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("unarchiveTicket")
|
||||
.setLabel(supportbot.Buttons.Tickets.Unarchive)
|
||||
.setEmoji(supportbot.Buttons.Tickets.Unarchive_Emoji)
|
||||
.setStyle(supportbot.Buttons.Tickets.Unarchive_Style);
|
||||
|
||||
const ArchivedOptions = new Discord.ActionRowBuilder()
|
||||
.addComponents(CloseButton, unarchiveButton);
|
||||
|
||||
await interaction.channel.send({
|
||||
embeds: [arcembed],
|
||||
components: [ArchivedOptions],
|
||||
});
|
||||
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await interaction.channel.setArchived(true);
|
||||
}
|
||||
|
||||
if (supportbot.Ticket.TicketType === "channels") {
|
||||
const { getRole, getChannel } = interaction.client;
|
||||
await interaction.channel.setParent(supportbot.Ticket.TicketArchiveCategory);
|
||||
|
||||
const ticketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketInfo = ticketData.tickets.find(t => t.id === interaction.channel.id);
|
||||
|
||||
const tickets = db.getAllTickets();
|
||||
const ticketInfo = tickets.find(t => t.ticket_id === interaction.channel.id);
|
||||
if (ticketInfo) {
|
||||
const user = interaction.guild.members.cache.get(ticketInfo.user) || ticketInfo.user;
|
||||
const claimedBy = interaction.guild.members.cache.get(ticketInfo.claimedBy) || ticketInfo.claimedBy;
|
||||
const user = interaction.guild.members.cache.get(ticketInfo.user_id) || ticketInfo.user_id;
|
||||
const claimedBy = interaction.guild.members.cache.get(ticketInfo.claimed_by) || ticketInfo.claimed_by;
|
||||
const Admin = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Admin);
|
||||
const Staff = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Staff);
|
||||
|
||||
if (user && user.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(user.id, {
|
||||
ViewChannel: false,
|
||||
SendMessages: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (supportbot.ClaimTickets === true) {
|
||||
if (claimedBy && claimedBy.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(claimedBy.id, {
|
||||
@@ -286,7 +243,6 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (supportbot.ClaimTickets === false) {
|
||||
if (Staff && Staff.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(claimedBy.id, {
|
||||
@@ -296,7 +252,6 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (Admin && Admin.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(Admin.id, {
|
||||
ViewChannel: true,
|
||||
@@ -304,59 +259,47 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
}
|
||||
|
||||
await interaction.channel.permissionOverwrites.edit(interaction.guild.roles.everyone.id, {
|
||||
ViewChannel: false,
|
||||
SendMessages: false,
|
||||
ReadMessageHistory: false,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "lockticket":
|
||||
const lockembed = new Discord.EmbedBuilder()
|
||||
.setDescription(msgconfig.Ticket.TicketLocked)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
const unlockButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("unlockTicket")
|
||||
.setLabel(supportbot.Buttons.Tickets.Unlock)
|
||||
.setEmoji(supportbot.Buttons.Tickets.Unlock_Emoji)
|
||||
.setStyle(supportbot.Buttons.Tickets.Unlock_Style);
|
||||
|
||||
const LockedOptions = new Discord.ActionRowBuilder()
|
||||
.addComponents(unlockButton);
|
||||
|
||||
await interaction.channel.send({
|
||||
embeds: [lockembed],
|
||||
components: [LockedOptions],
|
||||
ephemeral: false,
|
||||
});
|
||||
|
||||
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await interaction.channel.setLocked(true);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "unlockTicket":
|
||||
const unlockEmbed = new Discord.EmbedBuilder()
|
||||
.setDescription(msgconfig.Ticket.TicketUnlocked)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
await interaction.channel.send({
|
||||
embeds: [unlockEmbed],
|
||||
ephemeral: false,
|
||||
});
|
||||
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await interaction.channel.setLocked(false);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "enableinvites":
|
||||
@@ -383,38 +326,29 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
);
|
||||
await interaction.showModal(modal);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (interaction.customId === 'deleteSupportVC') {
|
||||
const ticketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketInfo = ticketData.tickets.find(t => t.id === interaction.channel.id);
|
||||
|
||||
const ticketData = db.getAllTickets();
|
||||
const ticketInfo = ticketData.find(t => t.ticket_id === interaction.channel.id);
|
||||
if (ticketInfo && ticketInfo.voiceChannelId) {
|
||||
try {
|
||||
const voiceChannel = await interaction.guild.channels.fetch(ticketInfo.voiceChannelId).catch(() => null);
|
||||
|
||||
if (voiceChannel) {
|
||||
await voiceChannel.delete();
|
||||
|
||||
const successEmbed = new Discord.EmbedBuilder()
|
||||
.setDescription('✅ Support voice channel has been successfully deleted!')
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [successEmbed],
|
||||
ephemeral: true,
|
||||
});
|
||||
|
||||
ticketInfo.voiceChannelId = null;
|
||||
fs.writeFileSync("./Data/TicketData.json", JSON.stringify(ticketData, null, 2));
|
||||
} else {
|
||||
const errorEmbed = new Discord.EmbedBuilder()
|
||||
.setDescription('❌ The associated voice channel could not be found.')
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [errorEmbed],
|
||||
ephemeral: true,
|
||||
@@ -422,11 +356,9 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting the Voice Channel:', error);
|
||||
|
||||
const errorEmbed = new Discord.EmbedBuilder()
|
||||
.setDescription('❌ **There was an error trying to delete this voice channel. Please try again later.**')
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [errorEmbed],
|
||||
ephemeral: true,
|
||||
@@ -436,36 +368,28 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
const errorEmbed = new Discord.EmbedBuilder()
|
||||
.setDescription('⚠️ This button is not linked to a valid voice channel.')
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [errorEmbed],
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (interaction.isButton()) {
|
||||
if (interaction.customId === "unarchiveTicket") {
|
||||
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await interaction.channel.setArchived(false);
|
||||
}
|
||||
|
||||
if (supportbot.Ticket.TicketType === "channels") {
|
||||
const { getRole, getChannel } = interaction.client;
|
||||
await interaction.channel.setParent(supportbot.Ticket.TicketChannelsCategory);
|
||||
|
||||
const ticketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketInfo = ticketData.tickets.find(t => t.id === interaction.channel.id);
|
||||
|
||||
const ticketData = db.getAllTickets();
|
||||
const ticketInfo = ticketData.find(t => t.ticket_id === interaction.channel.id);
|
||||
if (ticketInfo) {
|
||||
const user = interaction.guild.members.cache.get(ticketInfo.user) || ticketInfo.user;
|
||||
const claimedBy = interaction.guild.members.cache.get(ticketInfo.claimedBy) || ticketInfo.claimedBy;
|
||||
const user = interaction.guild.members.cache.get(ticketInfo.user_id) || ticketInfo.user_id;
|
||||
const claimedBy = interaction.guild.members.cache.get(ticketInfo.claimed_by) || ticketInfo.claimed_by;
|
||||
const Admin = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Admin);
|
||||
const Staff = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Staff);
|
||||
|
||||
if (user && user.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(user.id, {
|
||||
ViewChannel: true,
|
||||
@@ -473,7 +397,6 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (supportbot.ClaimTickets === true) {
|
||||
if (claimedBy && claimedBy.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(claimedBy.id, {
|
||||
@@ -483,7 +406,6 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (supportbot.ClaimTickets === false) {
|
||||
if (Staff && Staff.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(claimedBy.id, {
|
||||
@@ -493,7 +415,6 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (Admin && Admin.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(Admin.id, {
|
||||
ViewChannel: true,
|
||||
@@ -501,7 +422,6 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
}
|
||||
|
||||
await interaction.channel.permissionOverwrites.edit(interaction.guild.roles.everyone.id, {
|
||||
ViewChannel: false,
|
||||
SendMessages: false,
|
||||
@@ -509,16 +429,13 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const unrcembed = new Discord.EmbedBuilder()
|
||||
.setDescription(msgconfig.Ticket.TicketUnarchived)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
await interaction.update({
|
||||
embeds: [unrcembed],
|
||||
components: [],
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if (interaction.customId === "createticket") {
|
||||
@@ -535,7 +452,6 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
.setRequired(true)
|
||||
)
|
||||
);
|
||||
|
||||
await interaction.showModal(modal);
|
||||
} else {
|
||||
try {
|
||||
@@ -566,7 +482,6 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
.setRequired(true)
|
||||
)
|
||||
);
|
||||
|
||||
await interaction.showModal(modal);
|
||||
} else {
|
||||
try {
|
||||
@@ -589,7 +504,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
});
|
||||
}
|
||||
|
||||
const { getRole, getChannel } = interaction.client;
|
||||
const { getRole } = interaction.client;
|
||||
const Staff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
|
||||
const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
|
||||
@@ -601,10 +516,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
|
||||
if (!Staff || !Admin) {
|
||||
return interaction.reply({
|
||||
embeds: [RolePerms],
|
||||
ephemeral: true,
|
||||
});
|
||||
return interaction.reply({ embeds: [RolePerms], ephemeral: true });
|
||||
}
|
||||
|
||||
const NoPermsClaim = new Discord.EmbedBuilder()
|
||||
@@ -615,65 +527,55 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
if (!interaction.member.roles.cache.has(Staff.id) && !interaction.member.roles.cache.has(Admin.id)) {
|
||||
return interaction.reply({
|
||||
embeds: [NoPermsClaim],
|
||||
ephemeral: true,
|
||||
});
|
||||
return interaction.reply({ embeds: [NoPermsClaim], ephemeral: true });
|
||||
}
|
||||
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await ticketChannel.members.add(interaction.user.id);
|
||||
|
||||
} else if (supportbot.Ticket.TicketType === "channels") {
|
||||
await ticketChannel.members.add(interaction.user.id);
|
||||
} else {
|
||||
await ticketChannel.permissionOverwrites.create(interaction.user.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
await ticketChannel.permissionOverwrites.create(Admin, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
await ticketChannel.permissionOverwrites.create(interaction.guild.roles.everyone, {
|
||||
ViewChannel: false,
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
const pingclaimedstaff = await ticketChannel.send(`<@${interaction.user.id}>`);
|
||||
setTimeout(() => pingclaimedstaff.delete(), 2000);
|
||||
|
||||
setTimeout(() => {
|
||||
pingclaimedstaff.delete();
|
||||
}, 2000);
|
||||
|
||||
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketIndex = TicketData.tickets.findIndex((t) => t.id === ticketChannelId);
|
||||
if (ticketIndex !== -1) {
|
||||
TicketData.tickets[ticketIndex].claimedBy = interaction.user.id;
|
||||
TicketData.tickets[ticketIndex].claimedAt = new Date().toISOString();
|
||||
fs.writeFileSync("./Data/TicketData.json", JSON.stringify(TicketData, null, 4));
|
||||
const ticketInfo = db.getTicket(ticketChannelId);
|
||||
if (ticketInfo) {
|
||||
db.updateTicketStatus(ticketChannelId, ticketInfo.status);
|
||||
ticketInfo.claimed_by = interaction.user.id;
|
||||
}
|
||||
|
||||
const claimMessage = await interaction.message.fetch();
|
||||
const claimedEmbed = new Discord.EmbedBuilder(claimMessage.embeds[0].data)
|
||||
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimedTitle)
|
||||
.setDescription(
|
||||
msgconfig.Ticket.ClaimTickets.ClaimMessage_Edit.replace("%user%", interaction.user.id).replace("%channel%", ticketChannel.id)
|
||||
msgconfig.Ticket.ClaimTickets.ClaimMessage_Edit
|
||||
.replace("%user%", `${interaction.user.id}`)
|
||||
.replace("%channel%", `${ticketChannel.id}`)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
const successfullyClaimed = new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimedTitle)
|
||||
.setDescription(msgconfig.Ticket.ClaimTickets.Claimed.replace("%channel%", ticketChannel.id))
|
||||
.setDescription(msgconfig.Ticket.ClaimTickets.Claimed
|
||||
.replace("%channel%", `${ticketChannel.id}`)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
await claimMessage.edit({ embeds: [claimedEmbed], components: [] });
|
||||
await interaction.reply({
|
||||
embeds: [successfullyClaimed],
|
||||
ephemeral: true,
|
||||
});
|
||||
await interaction.reply({ embeds: [successfullyClaimed], ephemeral: true });
|
||||
}
|
||||
|
||||
// SUGGESTION INTERACTIONS [START]
|
||||
@@ -777,7 +679,6 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Modal submission handler for editing profile
|
||||
if (interaction.type === Discord.InteractionType.ModalSubmit && interaction.customId === "editProfileModal") {
|
||||
const userId = interaction.user.id;
|
||||
const profilePath = `./Data/Profiles/${userId}.json`;
|
||||
|
||||
+2
-4
@@ -60,9 +60,7 @@ const logStartup = async (client) => {
|
||||
// Links section
|
||||
console.log(gradient.summer("┏━━━━━━━━━━━━━━━━━━━ Important Links ━━━━━━━━━━━━━━━━━━━┓"));
|
||||
console.log(gradient.summer("┃ ┃"));
|
||||
console.log(gradient.summer("┃ ") + "🌐 Website : " + gradient.cristal("https://emeraldsrv.com ┃"));
|
||||
console.log(gradient.summer("┃ ") + "📚 Docs : " + gradient.cristal("https://docs.emeraldsrv.com ┃"));
|
||||
console.log(gradient.summer("┃ ") + "💬 Community : " + gradient.cristal("https://community.emeraldsrv.com ┃"));
|
||||
console.log(gradient.summer("┃ ") + "📚 Docs : " + gradient.cristal("https://emerald-services.gitbook.io/ ┃"));
|
||||
console.log(gradient.summer("┃ ") + "🤝 Discord : " + gradient.cristal("https://dsc.gg/emerald-dev ┃"));
|
||||
console.log(gradient.summer("┃ ┃"));
|
||||
console.log(gradient.summer("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n"));
|
||||
@@ -123,7 +121,7 @@ const checkConfigs = async (client) => {
|
||||
console.log(gradient.morning("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n"));
|
||||
};
|
||||
|
||||
module.exports = new Event("ready", async (client) => {
|
||||
module.exports = new Event("clientReady", async (client) => {
|
||||
setActivity(client, supportbot?.Activity?.Type, supportbot?.Activity?.Status);
|
||||
await logStartup(client);
|
||||
await checkConfigs(client);
|
||||
|
||||
+41
-27
@@ -1,12 +1,27 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const Discord = require("discord.js");
|
||||
const { GatewayIntentBits, Partials } = require('discord.js');
|
||||
const yaml = require("js-yaml");
|
||||
const { Command, Event } = require('./Addon.js'); // Adjust the path as needed
|
||||
const { Command, Event } = require('./Addon.js');
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
function walk(dir, ext = ".js", fileList = []) {
|
||||
const files = fs.readdirSync(dir);
|
||||
for (const file of files) {
|
||||
const full = `${dir}/${file}`;
|
||||
const stat = fs.statSync(full);
|
||||
if (stat.isDirectory()) {
|
||||
walk(full, ext, fileList);
|
||||
} else if (file.endsWith(ext)) {
|
||||
fileList.push(full);
|
||||
}
|
||||
}
|
||||
return fileList;
|
||||
}
|
||||
|
||||
class Client extends Discord.Client {
|
||||
constructor() {
|
||||
super({
|
||||
@@ -23,7 +38,7 @@ class Client extends Discord.Client {
|
||||
}
|
||||
|
||||
async getChannel(channel, guild) {
|
||||
if (!channel) return null; // Add check for undefined channel
|
||||
if (!channel) return null;
|
||||
return guild.channels.cache.find(
|
||||
(c) =>
|
||||
(c.type === Discord.ChannelType.GuildText || c.type === Discord.ChannelType.GuildNews) &&
|
||||
@@ -32,14 +47,14 @@ class Client extends Discord.Client {
|
||||
}
|
||||
|
||||
async getRole(role, guild) {
|
||||
if (!role) return null; // Add check for undefined role
|
||||
if (!role) return null;
|
||||
return guild.roles.cache.find(
|
||||
(r) => r.id === role || (r.name && r.name.toLowerCase() === role.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
async getCategory(category, guild) {
|
||||
if (!category) return null; // Add check for undefined category
|
||||
if (!category) return null;
|
||||
return guild.channels.cache.find(
|
||||
(c) =>
|
||||
c.type === Discord.ChannelType.GuildCategory &&
|
||||
@@ -48,72 +63,75 @@ class Client extends Discord.Client {
|
||||
}
|
||||
|
||||
async start(token) {
|
||||
let tempCommandFiles = fs.readdirSync("./Commands").filter((file) => file.endsWith(".js"));
|
||||
let tempCommandFiles = walk("./Commands");
|
||||
|
||||
if (cmdconfig.Suggestion.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "suggest.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "suggest.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Suggestion.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "suggestadmin.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Help.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "help.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "help.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Info.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "info.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "info.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.OpenTicket.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "ticket.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "ticket.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.CloseTicket.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "close.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "close.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Embed.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "embed.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "embed.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Translate.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "translate.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "translate.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.UserInfo.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "userinfo.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "userinfo.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Ping.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "Ping.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "Ping.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.AddUser.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "addUser.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "addUser.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.RemoveUser.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "removeUser.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "removeUser.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.ForceAddUser.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "forceaddUser.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "forceaddUser.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Profile.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "profile.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "profile.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Settings.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "settings.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "settings.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Mod.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "mod.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "mod.js");
|
||||
}
|
||||
|
||||
const commandFiles = tempCommandFiles;
|
||||
const commands = commandFiles.map((file) => require(`../Commands/${file}`));
|
||||
const commands = commandFiles.map((file) => require(`../${file}`));
|
||||
|
||||
// Load main commands
|
||||
console.log(`\u001b[33m`, "――――――――――――――――――――――――――――――――――――――――――――");
|
||||
|
||||
commands.forEach((cmd) => {
|
||||
@@ -121,7 +139,6 @@ class Client extends Discord.Client {
|
||||
this.commands.set(cmd.name, cmd);
|
||||
});
|
||||
|
||||
// Load addon commands and events
|
||||
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬");
|
||||
|
||||
if (supportbot.General.Addons.Enabled) {
|
||||
@@ -133,7 +150,6 @@ class Client extends Discord.Client {
|
||||
return addon;
|
||||
});
|
||||
|
||||
// Addons
|
||||
console.log(" ");
|
||||
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
|
||||
|
||||
@@ -163,8 +179,6 @@ class Client extends Discord.Client {
|
||||
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
|
||||
}
|
||||
|
||||
// Register Slash Commands
|
||||
|
||||
this.once("ready", async () => {
|
||||
await this.guilds.cache.first()?.commands.set(this.commands);
|
||||
console.log(
|
||||
@@ -196,4 +210,4 @@ class Client extends Discord.Client {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Client;
|
||||
module.exports = Client;
|
||||
@@ -0,0 +1,227 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Database Structure
|
||||
|
||||
const Database = require("better-sqlite3");
|
||||
const path = require("path");
|
||||
|
||||
const db = new Database(path.join(__dirname, "../Data/supportbot.db"));
|
||||
|
||||
// ----- Core tables -----
|
||||
db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS suggestions (
|
||||
thread_id TEXT PRIMARY KEY,
|
||||
author_id TEXT,
|
||||
text TEXT,
|
||||
status TEXT DEFAULT 'Created'
|
||||
)
|
||||
`).run();
|
||||
|
||||
db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS profiles (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
bio TEXT,
|
||||
timezone TEXT,
|
||||
clockedIn INTEGER DEFAULT 0
|
||||
)
|
||||
`).run();
|
||||
|
||||
db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
ticket_id TEXT PRIMARY KEY,
|
||||
user_id TEXT,
|
||||
status TEXT DEFAULT 'open',
|
||||
created_at INTEGER,
|
||||
updated_at INTEGER,
|
||||
subject TEXT,
|
||||
description TEXT
|
||||
)
|
||||
`).run();
|
||||
|
||||
try {
|
||||
db.prepare("ALTER TABLE tickets ADD COLUMN voiceChannelId TEXT").run();
|
||||
} catch (e) {
|
||||
if (!String(e).includes("duplicate column name")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS ticket_panel (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
message_id TEXT,
|
||||
channel_id TEXT,
|
||||
created_at INTEGER
|
||||
)
|
||||
`).run();
|
||||
|
||||
db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS ticket_users (
|
||||
ticket_id TEXT,
|
||||
user_id TEXT,
|
||||
PRIMARY KEY(ticket_id, user_id)
|
||||
)
|
||||
`).run();
|
||||
|
||||
/* ---- NEW table for global bot settings ---- */
|
||||
db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
id INTEGER PRIMARY KEY,
|
||||
data TEXT
|
||||
)
|
||||
`).run();
|
||||
|
||||
module.exports = {
|
||||
// ----- Suggestions -----
|
||||
addSuggestion(threadId, authorId, text) {
|
||||
db.prepare(`
|
||||
INSERT INTO suggestions (thread_id, author_id, text)
|
||||
VALUES (@threadId, @authorId, @text)
|
||||
ON CONFLICT(thread_id) DO NOTHING
|
||||
`).run({ threadId, authorId, text });
|
||||
},
|
||||
|
||||
setSuggestionStatus(threadId, status) {
|
||||
db.prepare(`
|
||||
UPDATE suggestions SET status=@status WHERE thread_id=@threadId
|
||||
`).run({ threadId, status });
|
||||
},
|
||||
|
||||
getSuggestion(threadId) {
|
||||
return db.prepare(`
|
||||
SELECT * FROM suggestions WHERE thread_id=@threadId
|
||||
`).get({ threadId });
|
||||
},
|
||||
|
||||
// ----- Profiles -----
|
||||
getProfile(userId) {
|
||||
const row = db.prepare(`SELECT * FROM profiles WHERE user_id=?`).get(userId);
|
||||
if (!row) {
|
||||
db.prepare(`
|
||||
INSERT INTO profiles (user_id, bio, timezone, clockedIn)
|
||||
VALUES (?, '', '', 0)
|
||||
`).run(userId);
|
||||
return { user_id: userId, bio: "", timezone: "", clockedIn: 0 };
|
||||
}
|
||||
return row;
|
||||
},
|
||||
|
||||
updateProfile(userId, data) {
|
||||
const { bio = "", timezone = "", clockedIn = 0 } = data;
|
||||
db.prepare(`
|
||||
INSERT INTO profiles (user_id, bio, timezone, clockedIn)
|
||||
VALUES (@userId, @bio, @timezone, @clockedIn)
|
||||
ON CONFLICT(user_id) DO UPDATE SET
|
||||
bio = excluded.bio,
|
||||
timezone = excluded.timezone,
|
||||
clockedIn = excluded.clockedIn
|
||||
`).run({ userId, bio, timezone, clockedIn });
|
||||
},
|
||||
|
||||
setClockedIn(userId, value) {
|
||||
db.prepare(`UPDATE profiles SET clockedIn=? WHERE user_id=?`)
|
||||
.run(value ? 1 : 0, userId);
|
||||
},
|
||||
|
||||
// ----- Tickets -----
|
||||
addTicket(ticket) {
|
||||
const now = Date.now();
|
||||
db.prepare(`
|
||||
INSERT INTO tickets (ticket_id, user_id, subject, description, created_at, updated_at)
|
||||
VALUES (@ticket_id, @user_id, @subject, @description, @created_at, @updated_at)
|
||||
ON CONFLICT(ticket_id) DO NOTHING
|
||||
`).run({
|
||||
ticket_id: ticket.id,
|
||||
user_id: ticket.user,
|
||||
subject: ticket.reason || "",
|
||||
description: ticket.description || "",
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
});
|
||||
},
|
||||
|
||||
updateTicketVoice(ticketId, voiceChannelId) {
|
||||
db.prepare(`
|
||||
UPDATE tickets SET voiceChannelId=@voiceChannelId, updated_at=@updatedAt WHERE ticket_id=@ticketId
|
||||
`).run({ ticketId, voiceChannelId, updatedAt: Date.now() });
|
||||
},
|
||||
|
||||
getTicketVoice(ticketId) {
|
||||
return db.prepare(`SELECT voiceChannelId FROM tickets WHERE ticket_id=?`).get(ticketId);
|
||||
},
|
||||
|
||||
deleteTicketVoice(ticketId) {
|
||||
db.prepare(`
|
||||
UPDATE tickets SET voiceChannelId=NULL, updated_at=@updatedAt WHERE ticket_id=@ticketId
|
||||
`).run({ ticketId, updatedAt: Date.now() });
|
||||
},
|
||||
|
||||
updateTicketStatus(ticketId, status) {
|
||||
db.prepare(`
|
||||
UPDATE tickets SET status=@status, updated_at=@updatedAt WHERE ticket_id=@ticketId
|
||||
`).run({ status, updatedAt: Date.now(), ticketId });
|
||||
},
|
||||
|
||||
getTicket(ticketId) {
|
||||
return db.prepare(`
|
||||
SELECT * FROM tickets WHERE ticket_id=@ticketId
|
||||
`).get({ ticketId });
|
||||
},
|
||||
|
||||
getAllTickets() {
|
||||
return db.prepare(`SELECT * FROM tickets`).all();
|
||||
},
|
||||
|
||||
saveTicketPanel(messageId, channelId) {
|
||||
db.prepare(`
|
||||
INSERT INTO ticket_panel (message_id, channel_id, created_at)
|
||||
VALUES (@messageId, @channelId, @createdAt)
|
||||
`).run({ messageId, channelId, createdAt: Date.now() });
|
||||
},
|
||||
|
||||
getTicketPanel() {
|
||||
return db.prepare(`
|
||||
SELECT * FROM ticket_panel ORDER BY id DESC LIMIT 1
|
||||
`).get();
|
||||
},
|
||||
|
||||
// ----- Ticket participants -----
|
||||
addUserToTicket(ticketId, userId) {
|
||||
db.prepare(`
|
||||
INSERT INTO ticket_users (ticket_id, user_id)
|
||||
VALUES (?, ?)
|
||||
ON CONFLICT(ticket_id, user_id) DO NOTHING
|
||||
`).run(ticketId, userId);
|
||||
},
|
||||
|
||||
removeUserFromTicket(ticketId, userId) {
|
||||
db.prepare(`
|
||||
DELETE FROM ticket_users WHERE ticket_id=? AND user_id=?
|
||||
`).run(ticketId, userId);
|
||||
},
|
||||
|
||||
getTicketUsers(ticketId) {
|
||||
return db.prepare(`
|
||||
SELECT user_id FROM ticket_users WHERE ticket_id=?
|
||||
`).all(ticketId).map(r => r.user_id);
|
||||
},
|
||||
|
||||
// ----- Global Settings -----
|
||||
getSettings() {
|
||||
const row = db.prepare(`SELECT data FROM settings WHERE id=1`).get();
|
||||
if (!row) return {};
|
||||
try {
|
||||
return JSON.parse(row.data);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
|
||||
saveSettings(obj) {
|
||||
const data = JSON.stringify(obj);
|
||||
db.prepare(`
|
||||
INSERT INTO settings (id, data)
|
||||
VALUES (1, @data)
|
||||
ON CONFLICT(id) DO UPDATE SET data = excluded.data
|
||||
`).run({ data });
|
||||
}
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Event Structure
|
||||
|
||||
class Event {
|
||||
constructor(event, run) {
|
||||
this.event = event;
|
||||
|
||||
+12
-29
@@ -1,47 +1,30 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Ticket ID Structure
|
||||
|
||||
const fs = require("fs");
|
||||
const db = require("./Database")
|
||||
|
||||
class TicketNumberID {
|
||||
static async pad() {
|
||||
let num = await this.get();
|
||||
let n = num + "";
|
||||
await this.set(num + 1);
|
||||
return n.length >= 4 ? n : new Array(4 - n.length + 1).join("0") + n;
|
||||
let num = await this.get()
|
||||
let n = num + ""
|
||||
await this.set(num + 1)
|
||||
return n.length >= 4 ? n : new Array(4 - n.length + 1).join("0") + n
|
||||
}
|
||||
|
||||
static async get() {
|
||||
let ticketnumberID = await JSON.parse(
|
||||
fs.readFileSync("./Data/ticket-panel-id.json", "utf8")
|
||||
);
|
||||
if (!ticketnumberID.id) {
|
||||
return this.set(0);
|
||||
} else {
|
||||
return ticketnumberID.id;
|
||||
}
|
||||
const row = db.getTicketPanel()
|
||||
if (!row) return await this.set(0)
|
||||
return row.id
|
||||
}
|
||||
|
||||
static async set(value) {
|
||||
let ticketnumberID = await JSON.parse(
|
||||
fs.readFileSync("./Data/ticket-panel-id.json", "utf8")
|
||||
);
|
||||
|
||||
ticketnumberID.id = value;
|
||||
fs.writeFileSync(
|
||||
"./Data/ticket-panel-id.json",
|
||||
JSON.stringify(ticketnumberID, null, 4),
|
||||
(err) => {
|
||||
if (err) console.error(err);
|
||||
}
|
||||
);
|
||||
|
||||
return value;
|
||||
db.saveTicketPanel(`panel-${value}`, "none")
|
||||
return value
|
||||
}
|
||||
|
||||
static async reset() {
|
||||
return await this.set(0);
|
||||
return await this.set(0)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TicketNumberID;
|
||||
module.exports = TicketNumberID
|
||||
@@ -21,7 +21,10 @@
|
||||
"@paypal/checkout-server-sdk": "^1.0.3",
|
||||
"@vitalets/google-translate-api": "^8.0.0",
|
||||
"axios": "^1.7.2",
|
||||
"better-sqlite3": "^12.2.0",
|
||||
"discord.js": "^14.13.0",
|
||||
"figlet": "^1.9.3",
|
||||
"gradient-string": "^3.0.0",
|
||||
"js": "^0.1.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"moment": "^2.29.3",
|
||||
|
||||
Reference in new issue
Block a user