Compare commits
1
Commits
v8.0
..
v8.1-Pre-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd65d0a9a7 |
No files matched your search
@@ -0,0 +1,26 @@
|
||||
const Discord = require("discord.js");
|
||||
const fs = require("fs");
|
||||
const yaml = require("js-yaml");
|
||||
const { Command } = require('../Structures/Addon.js'); // Adjust the path as needed
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
|
||||
module.exports = new Command({
|
||||
name: "addon",
|
||||
description: "This is a sample addon!",
|
||||
options: [],
|
||||
permissions: ["Administrator"],
|
||||
|
||||
async run(interaction) {
|
||||
let disableCommand = true;
|
||||
|
||||
const PingEmbed = new Discord.EmbedBuilder()
|
||||
.setDescription(
|
||||
`This is a sample addon.\n\nPong! \`${interaction.client.ws.ping} ms\``
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
interaction.reply({
|
||||
embeds: [PingEmbed],
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
const fs = require("fs");
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
const generateImage = require('../Structures/generateImage.js');
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const Command = require("../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: "image",
|
||||
description: "Create an image through SupportBot AI",
|
||||
type: Discord.ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: "prompt",
|
||||
description: "The prompt to generate an image from",
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "negativeprompt",
|
||||
description: "The negative prompt to exclude from the image",
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "cfgscale",
|
||||
description: "The cfg scale for the image generation",
|
||||
type: Discord.ApplicationCommandOptionType.Number,
|
||||
required: false,
|
||||
}
|
||||
],
|
||||
permissions: "SendMessages",
|
||||
|
||||
async run(interaction) {
|
||||
const prompt = interaction.options.getString("prompt");
|
||||
const negativePrompt = interaction.options.getString("negativeprompt") || "";
|
||||
const cfgScale = interaction.options.getNumber("cfgscale") || 7.5; // Default value
|
||||
|
||||
try {
|
||||
const imageUrl = await generateImage(prompt, negativePrompt, cfgScale);
|
||||
|
||||
const ImageEmbed = new Discord.EmbedBuilder()
|
||||
.setTitle("Generated Image")
|
||||
.setDescription(`Here is the image generated for your prompt: "${prompt}"`)
|
||||
.setImage(imageUrl)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.setFooter({
|
||||
text: supportbot.Embed.Footer,
|
||||
iconURL: interaction.user.displayAvatarURL(),
|
||||
});
|
||||
|
||||
if (!interaction.replied) {
|
||||
await interaction.reply({
|
||||
ephemeral: true,
|
||||
embeds: [ImageEmbed],
|
||||
});
|
||||
} else {
|
||||
await interaction.followUp({
|
||||
ephemeral: true,
|
||||
embeds: [ImageEmbed],
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error generating image:', error);
|
||||
if (!interaction.replied) {
|
||||
await interaction.reply({
|
||||
ephemeral: true,
|
||||
content: `An error occurred while generating the image: ${error.message}. Please try again later.`,
|
||||
});
|
||||
} else {
|
||||
await interaction.followUp({
|
||||
ephemeral: true,
|
||||
content: `An error occurred while generating the image: ${error.message}. Please try again later.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
+10
-21
@@ -1,17 +1,9 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Help Command
|
||||
|
||||
const fs = require("fs");
|
||||
const Discord = 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 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");
|
||||
|
||||
@@ -32,11 +24,17 @@ module.exports = new Command({
|
||||
);
|
||||
}
|
||||
|
||||
// Fetch all commands from the client's commands collection
|
||||
// Separate regular commands and addon commands
|
||||
let botCommands = "";
|
||||
let addonCommands = "";
|
||||
|
||||
interaction.client.commands.forEach((command) => {
|
||||
if (command.permissions) {
|
||||
botCommands += `\`${command.name}\` `;
|
||||
if (command.addon) {
|
||||
addonCommands += `\`${command.name}\` `;
|
||||
} else {
|
||||
botCommands += `\`${command.name}\` `;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -51,15 +49,6 @@ module.exports = new Command({
|
||||
inline: false,
|
||||
});
|
||||
|
||||
// Conditionally add Addons section
|
||||
if (supportbot.General.Addons.Enabled) {
|
||||
HelpEmbed1.addFields({
|
||||
name: "🛠️ Addons\n",
|
||||
value: "No addons available.",
|
||||
inline: false,
|
||||
});
|
||||
}
|
||||
|
||||
HelpEmbed1.setFooter({
|
||||
text: supportbot.Embed.Footer,
|
||||
iconURL: interaction.user.displayAvatarURL(),
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
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();
|
||||
|
||||
async function getRole(roleName, guild) {
|
||||
return guild.roles.cache.find(role => role.name === roleName);
|
||||
}
|
||||
|
||||
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: 'profile',
|
||||
description: 'Show user profile',
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
permissions: ["SendMessages"],
|
||||
options: [
|
||||
{
|
||||
name: 'user',
|
||||
description: 'The user to view the profile of',
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: false
|
||||
}
|
||||
],
|
||||
|
||||
async run(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true }); // Acknowledge the interaction immediately
|
||||
|
||||
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 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;
|
||||
+396
-253
@@ -8,7 +8,7 @@ const {
|
||||
ButtonBuilder,
|
||||
StringSelectMenuBuilder,
|
||||
StringSelectMenuOptionBuilder,
|
||||
GuildMemberRoleManager,
|
||||
ButtonStyle
|
||||
} = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
@@ -20,6 +20,191 @@ const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
const Command = require("../Structures/Command.js");
|
||||
const TicketNumberID = require("../Structures/TicketID.js");
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
return clockedInUsers;
|
||||
}
|
||||
|
||||
async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, attempts = 0) {
|
||||
console.log(`Assigning ticket attempt ${attempts}`);
|
||||
|
||||
try {
|
||||
const clockedInUsers = await getClockedInUsers(interaction.guild);
|
||||
console.log(`Clocked in users: ${Array.from(clockedInUsers).join(', ')}`);
|
||||
|
||||
if (clockedInUsers.size === 0 || attempts >= 2) {
|
||||
console.log("No clocked in users or max attempts reached");
|
||||
const claimChannel = await interaction.guild.channels.cache.get(supportbot.Ticket.ClaimTickets.Channel);
|
||||
if (claimChannel) {
|
||||
const claimEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle || 'Default Claim Title')
|
||||
.setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage || 'Default Claim Message')
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const claimButton = new ButtonBuilder()
|
||||
.setCustomId(`claimticket-${ticketChannel.id}`)
|
||||
.setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle || 'Claim Ticket')
|
||||
.setStyle(ButtonStyle.Primary);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(claimButton);
|
||||
|
||||
const claimMessage = await claimChannel.send({
|
||||
content: `<@&${Staff.id}> <@&${Admin.id}>`,
|
||||
embeds: [claimEmbed],
|
||||
components: [row],
|
||||
});
|
||||
|
||||
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 => {
|
||||
console.log(`Ticket claimed by user ${i.user.id}`);
|
||||
await ticketChannel.send({ content: `<@${i.user.id}> has claimed the ticket.` });
|
||||
});
|
||||
|
||||
collector.on('end', async collected => {
|
||||
if (collected.size === 0) {
|
||||
console.log("No one claimed the ticket, notifying admins");
|
||||
await claimMessage.delete();
|
||||
|
||||
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)],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
} else {
|
||||
console.error("Claim channel not found");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const clockedInArray = Array.from(clockedInUsers);
|
||||
const assignedUserId = clockedInArray[Math.floor(Math.random() * clockedInArray.length)];
|
||||
const assignedUser = await interaction.guild.members.fetch(assignedUserId);
|
||||
console.log(`Assigned user: ${assignedUserId}`);
|
||||
|
||||
const claimEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle || 'Default Claim Title')
|
||||
.setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage.replace('%user%', interaction.user.id) || 'Default Claim Message')
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const claimButton = new ButtonBuilder()
|
||||
.setCustomId(`claimticket-${ticketChannel.id}`)
|
||||
.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);
|
||||
if (!claimChannel) {
|
||||
console.error("Claim channel not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
const claimMessage = await claimChannel.send({
|
||||
content: `${assignedUser}`,
|
||||
embeds: [claimEmbed],
|
||||
components: [row],
|
||||
});
|
||||
|
||||
const filter = i => i.customId === `claimticket-${ticketChannel.id}` && i.user.id === assignedUserId;
|
||||
const collector = claimMessage.createMessageComponentCollector({ filter, time: 120000 });
|
||||
|
||||
collector.on('collect', async i => {
|
||||
console.log(`Ticket claimed by user ${i.user.id}`);
|
||||
await ticketChannel.send({ content: `<@${assignedUserId}>` });
|
||||
});
|
||||
|
||||
collector.on('end', async collected => {
|
||||
if (collected.size === 0) {
|
||||
console.log("No one claimed the ticket, attempting reassignment");
|
||||
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)],
|
||||
});
|
||||
|
||||
// Add a claim button for any staff member to click
|
||||
const claimEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle)
|
||||
.setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const claimButton = new ButtonBuilder()
|
||||
.setCustomId(`claimticket-${ticketChannel.id}`)
|
||||
.setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle)
|
||||
.setEmoji(supportbot.Ticket.ClaimTickets.ButtonEmoji)
|
||||
.setStyle(ButtonStyle.Primary);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(claimButton);
|
||||
|
||||
const claimMessage = await claimChannel.send({
|
||||
content: `<@&${Staff.id}> <@&${Admin.id}>`,
|
||||
embeds: [claimEmbed],
|
||||
components: [row],
|
||||
});
|
||||
|
||||
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 => {
|
||||
console.log(`Ticket ${ticketChannel.id} claimed by user ${i.user.id}`);
|
||||
await ticketChannel.member.add(i.user.id);
|
||||
});
|
||||
|
||||
collector.on('end', async collected => {
|
||||
if (collected.size === 0) {
|
||||
console.log(`No one claimed the ticket (${ticketChannel.id}), notifying all staff`);
|
||||
await claimMessage.delete();
|
||||
|
||||
await claimChannel.send({
|
||||
content: `No one claimed the ticket. Tagging roles: <@&${Staff.id}> <@&${Admin.id}>`,
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const ReassigningEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.ClaimTickets.ReassigningTitle)
|
||||
.setDescription(msgconfig.Ticket.ClaimTickets.ReassigningMessage.replace('%user%', assignedUserId))
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
await claimChannel.send({ embeds: [ReassigningEmbed], components: [] });
|
||||
await assignTicketToUser(ticketChannel, Staff, Admin, interaction, attempts + 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return assignedUserId;
|
||||
} catch (error) {
|
||||
console.error("Error in assignTicketToUser function:", error);
|
||||
if (error instanceof CombinedError) {
|
||||
console.error("CombinedError details:", error.errors);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.OpenTicket.Command,
|
||||
description: cmdconfig.OpenTicket.Description,
|
||||
@@ -34,275 +219,233 @@ module.exports = new Command({
|
||||
permissions: cmdconfig.OpenTicket.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
let department = interaction.customId?.split("-")[1] || null;
|
||||
let TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const { getRole, getChannel } = interaction.client;
|
||||
let User = interaction.guild.members.cache.get(interaction.user.id);
|
||||
try {
|
||||
let department = interaction.customId?.split("-")[1] || null;
|
||||
let TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const { getRole, getChannel } = interaction.client;
|
||||
let User = interaction.guild.members.cache.get(interaction.user.id);
|
||||
|
||||
if (
|
||||
supportbot.Ticket.TicketsPerUser &&
|
||||
TicketData.tickets.filter((t) => t.user === interaction.user.id && t.open).length >= supportbot.Ticket.TicketsPerUser
|
||||
) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
{
|
||||
title: "Too Many Tickets!",
|
||||
description: `You can't have more than ${supportbot.Ticket.TicketsPerUser} open tickets!`,
|
||||
color: supportbot.Embed.Colours.Warn,
|
||||
},
|
||||
],
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (User.roles.cache.has(supportbot.Roles.ModRoles.Blacklisted)) {
|
||||
return interaction.reply({
|
||||
content: msgconfig.Ticket.Blacklisted,
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (User.roles.cache.has(supportbot.Roles.ModRoles.Muted)) {
|
||||
return interaction.reply({
|
||||
content: msgconfig.Ticket.Muted,
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
let ticketNumberID = await TicketNumberID.pad();
|
||||
const TicketSubject = interaction.options?.getString("reason") || msgconfig.Ticket.InvalidSubject;
|
||||
|
||||
const TicketExists = new EmbedBuilder()
|
||||
.setTitle("Ticket Exists!")
|
||||
.setDescription(msgconfig.Ticket.TicketExists);
|
||||
|
||||
if (
|
||||
await interaction.guild.channels.cache.find(
|
||||
(ticketChannel) => ticketChannel.name === `${supportbot.Ticket.Channel}${ticketNumberID}`
|
||||
)
|
||||
) {
|
||||
return await interaction.reply({
|
||||
embeds: [TicketExists],
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
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.reply({
|
||||
content: "Some roles seem to be missing!\nPlease check for errors when starting the bot.",
|
||||
ephemeral: true,
|
||||
});
|
||||
|
||||
const TicketCategory = interaction.guild.channels.cache.find(
|
||||
(c) => c.name === supportbot.Ticket.TicketCategory || c.id === supportbot.Ticket.TicketCategory
|
||||
);
|
||||
|
||||
const channel = await getChannel(supportbot.Ticket.TicketCategory, interaction.guild);
|
||||
const ticketChannel = await channel.threads.create({
|
||||
name: `${supportbot.Ticket.Channel}${ticketNumberID}`,
|
||||
type: ChannelType.PrivateThread,
|
||||
parent: TicketCategory,
|
||||
autoArchiveDuration: 60,
|
||||
reason: 'support ticket',
|
||||
});
|
||||
|
||||
if (supportbot.Ticket.ClaimTickets.Enabled === false) {
|
||||
const InvStaff = await ticketChannel.send(`<@&${Staff.id}> <@&${Admin.id}>`)
|
||||
|
||||
setTimeout(() => {
|
||||
InvStaff.delete();
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
// TICKET CLAIMING SYSTEM [START]
|
||||
|
||||
if (supportbot.Ticket.ClaimTickets.Enabled) {
|
||||
const InvStaff1 = await ticketChannel.send(`<@&${Admin.id}>`)
|
||||
|
||||
setTimeout(() => {
|
||||
InvStaff1.delete();
|
||||
}, 2000)
|
||||
|
||||
const claimChannel = await getChannel(
|
||||
supportbot.Ticket.ClaimTickets.Channel,
|
||||
interaction.guild
|
||||
);
|
||||
|
||||
const claimEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle)
|
||||
.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)
|
||||
.setStyle(supportbot.Ticket.ClaimTickets.Button);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(claimButton);
|
||||
|
||||
await claimChannel.send({
|
||||
content: `<@&${Staff.id}>`,
|
||||
embeds: [claimEmbed],
|
||||
components: [row]
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// TICKET CLAIMING SYSTEM [END]
|
||||
|
||||
if (!department) {
|
||||
await ticketChannel.members.add(interaction.user.id);
|
||||
}
|
||||
|
||||
TicketData.tickets.push({
|
||||
id: ticketChannel.id,
|
||||
name: ticketChannel.name,
|
||||
user: interaction.user.id,
|
||||
number: ticketNumberID,
|
||||
reason: TicketSubject,
|
||||
open: true,
|
||||
subUsers: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
claimedAt: null, // Will be updated when claimed
|
||||
});
|
||||
|
||||
fs.writeFileSync(
|
||||
"./Data/TicketData.json",
|
||||
JSON.stringify(TicketData, null, 4),
|
||||
(err) => {
|
||||
if (err) console.error(err);
|
||||
if (
|
||||
supportbot.Ticket.TicketsPerUser &&
|
||||
TicketData.tickets.filter((t) => t.user === interaction.user.id && t.open).length >= supportbot.Ticket.TicketsPerUser
|
||||
) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
{
|
||||
title: "Too Many Tickets!",
|
||||
description: `You can't have more than ${supportbot.Ticket.TicketsPerUser} open tickets!`,
|
||||
color: supportbot.Embed.Colours.Warn,
|
||||
},
|
||||
],
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const CreatedTicket = new EmbedBuilder()
|
||||
.setDescription(
|
||||
msgconfig.Ticket.TicketCreatedAlert.replace(/%ticketauthor%/g, interaction.user.id)
|
||||
.replace(/%ticketid%/g, ticketChannel.id)
|
||||
.replace(/%ticketusername%/g, interaction.user.username)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
await interaction.reply({ embeds: [CreatedTicket], ephemeral: true });
|
||||
if (User.roles.cache.has(supportbot.Roles.ModRoles.Blacklisted)) {
|
||||
return interaction.reply({
|
||||
content: msgconfig.Ticket.Blacklisted,
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (supportbot.Ticket.AllowMentions) {
|
||||
await ticketChannel.send(`${interaction.user}`);
|
||||
}
|
||||
if (User.roles.cache.has(supportbot.Roles.ModRoles.Muted)) {
|
||||
return interaction.reply({
|
||||
content: msgconfig.Ticket.Muted,
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
const TicketMessage = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: msgconfig.Ticket.TicketAuthorTitle.replace(/%ticketauthor%/g, interaction.user.id)
|
||||
.replace(/%ticketid%/g, ticketChannel.id)
|
||||
.replace(/%ticketusername%/g, interaction.user.username),
|
||||
iconURL: interaction.user.displayAvatarURL(),
|
||||
})
|
||||
.setTitle(
|
||||
msgconfig.Ticket.TicketTitle.replace(/%ticketauthor%/g, interaction.user.id)
|
||||
.replace(/%ticketid%/g, ticketChannel.id)
|
||||
.replace(/%ticketusername%/g, interaction.user.username)
|
||||
)
|
||||
.setDescription(
|
||||
msgconfig.Ticket.TicketMessage.replace(/%ticketauthor%/g, interaction.user.id)
|
||||
.replace(/%ticketid%/g, ticketChannel.id)
|
||||
.replace(/%ticketusername%/g, interaction.user.username)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
let ticketNumberID = await TicketNumberID.pad();
|
||||
const TicketSubject = interaction.options?.getString("reason") || msgconfig.Ticket.InvalidSubject;
|
||||
|
||||
const SelectMenus = new StringSelectMenuBuilder()
|
||||
.setCustomId("ticketcontrolpanel")
|
||||
.setPlaceholder("Ticket Control Panel")
|
||||
.addOptions(
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Add User")
|
||||
.setDescription("Add a user to the ticket.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.AddUserEmoji)
|
||||
.setValue("ticketadduser"),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Remove User")
|
||||
.setDescription("Remove a user from the ticket.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.RemoveUserEmoji)
|
||||
.setValue("ticketremoveuser"),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Close")
|
||||
.setDescription("Close the ticket.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.CloseEmoji)
|
||||
.setValue("ticketclose"),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Enable Invites")
|
||||
.setDescription("Enable invites.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.EnableInvitesEmoji)
|
||||
.setValue("enableinvites"),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Disable Invites")
|
||||
.setDescription("Disable invites.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.DisableInvitesEmoji)
|
||||
.setValue("disableinvites"),
|
||||
);
|
||||
const TicketExists = new EmbedBuilder()
|
||||
.setTitle("Ticket Exists!")
|
||||
.setDescription(msgconfig.Ticket.TicketExists);
|
||||
|
||||
const row2 = new ActionRowBuilder().addComponents(SelectMenus);
|
||||
if (
|
||||
await interaction.guild.channels.cache.find(
|
||||
(ticketChannel) => ticketChannel.name === `${supportbot.Ticket.Channel}${ticketNumberID}`
|
||||
)
|
||||
) {
|
||||
return await interaction.reply({
|
||||
embeds: [TicketExists],
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!department) {
|
||||
try {
|
||||
let buttons = supportbot.Departments.map((x) =>
|
||||
new ButtonBuilder()
|
||||
.setCustomId("Department" + supportbot.Departments.indexOf(x))
|
||||
.setLabel(x.title)
|
||||
.setStyle(x.color)
|
||||
.setEmoji(x.emoji)
|
||||
);
|
||||
|
||||
if (supportbot.Ticket.AllowMentions) {
|
||||
await ticketChannel.send("<@&" + supportbot.Ticket.RoleMention + ">");
|
||||
}
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(buttons);
|
||||
const m = await ticketChannel.send({
|
||||
embeds: [TicketMessage],
|
||||
components: [row2],
|
||||
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.reply({
|
||||
content: "Some roles seem to be missing!\nPlease check for errors when starting the bot.",
|
||||
ephemeral: true,
|
||||
});
|
||||
|
||||
try {
|
||||
const filter = (i) => i.user.id === interaction.user.id;
|
||||
await m.awaitMessageComponent({
|
||||
filter,
|
||||
max: 1,
|
||||
componentType: "BUTTON",
|
||||
time: supportbot.Ticket.Timeout * 60000,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.code === "COLLECTOR_ERROR") {
|
||||
try {
|
||||
let ticket = TicketData.tickets.findIndex((t) => t.id === ticketChannel.id);
|
||||
TicketData.tickets[ticket].open = false;
|
||||
fs.writeFileSync(
|
||||
"./Data/TicketData.json",
|
||||
JSON.stringify(TicketData, null, 4),
|
||||
(err) => {
|
||||
if (err) console.error(err);
|
||||
}
|
||||
);
|
||||
interaction.user.send(
|
||||
"Your ticket has timed out. Please open a new one, and select a department."
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("An error occurred while handling collector:", error);
|
||||
}
|
||||
return await ticketChannel.delete();
|
||||
}
|
||||
const TicketCategory = interaction.guild.channels.cache.find(
|
||||
(c) => c.name === supportbot.Ticket.TicketCategory || c.id === supportbot.Ticket.TicketCategory
|
||||
);
|
||||
|
||||
const channel = await getChannel(supportbot.Ticket.TicketCategory, interaction.guild);
|
||||
const ticketChannel = await channel.threads.create({
|
||||
name: `${supportbot.Ticket.Channel}${ticketNumberID}`,
|
||||
type: ChannelType.PrivateThread,
|
||||
parent: TicketCategory,
|
||||
autoArchiveDuration: 60,
|
||||
reason: 'support ticket',
|
||||
});
|
||||
|
||||
// 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) {
|
||||
const claimChannel = await interaction.guild.channels.cache.get(supportbot.Ticket.ClaimTickets.Channel);
|
||||
await claimChannel.send(`<@&${Staff.id}> <@&${Admin.id}>`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error sending message:", error);
|
||||
}
|
||||
} else {
|
||||
|
||||
// Add user to the ticket channel
|
||||
if (!department) {
|
||||
await ticketChannel.members.add(interaction.user.id);
|
||||
}
|
||||
|
||||
// Save ticket data
|
||||
TicketData.tickets.push({
|
||||
id: ticketChannel.id,
|
||||
name: ticketChannel.name,
|
||||
user: interaction.user.id,
|
||||
number: ticketNumberID,
|
||||
reason: TicketSubject,
|
||||
open: true,
|
||||
subUsers: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
claimedAt: null,
|
||||
});
|
||||
|
||||
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)
|
||||
.replace(/%ticketid%/g, ticketChannel.id)
|
||||
.replace(/%ticketusername%/g, interaction.user.username)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
await interaction.reply({ embeds: [CreatedTicket], ephemeral: true });
|
||||
|
||||
// Create the ticket message
|
||||
const TicketMessage = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: msgconfig.Ticket.TicketAuthorTitle.replace(/%ticketauthor%/g, interaction.user.id)
|
||||
.replace(/%ticketid%/g, ticketChannel.id)
|
||||
.replace(/%ticketusername%/g, interaction.user.username),
|
||||
iconURL: interaction.user.displayAvatarURL(),
|
||||
})
|
||||
.setTitle(
|
||||
msgconfig.Ticket.TicketTitle.replace(/%ticketauthor%/g, interaction.user.id)
|
||||
.replace(/%ticketid%/g, ticketChannel.id)
|
||||
.replace(/%ticketusername%/g, interaction.user.username)
|
||||
)
|
||||
.setDescription(
|
||||
msgconfig.Ticket.TicketMessage.replace(/%ticketauthor%/g, interaction.user.id)
|
||||
.replace(/%ticketid%/g, ticketChannel.id)
|
||||
.replace(/%ticketusername%/g, interaction.user.username)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
// Create the control panel
|
||||
const SelectMenus = new StringSelectMenuBuilder()
|
||||
.setCustomId("ticketcontrolpanel")
|
||||
.setPlaceholder("Ticket Control Panel")
|
||||
.addOptions(
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Add User")
|
||||
.setDescription("Add a user to the ticket.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.AddUserEmoji)
|
||||
.setValue("ticketadduser"),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Remove User")
|
||||
.setDescription("Remove a user from the ticket.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.RemoveUserEmoji)
|
||||
.setValue("ticketremoveuser"),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Close")
|
||||
.setDescription("Close the ticket.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.CloseEmoji)
|
||||
.setValue("ticketclose"),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Enable Invites")
|
||||
.setDescription("Enable invites.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.EnableInvitesEmoji)
|
||||
.setValue("enableinvites"),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Disable Invites")
|
||||
.setDescription("Disable invites.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.DisableInvitesEmoji)
|
||||
.setValue("disableinvites"),
|
||||
);
|
||||
|
||||
const row2 = new ActionRowBuilder().addComponents(SelectMenus);
|
||||
|
||||
// Send the ticket message and control panel
|
||||
await ticketChannel.send({
|
||||
embeds: [TicketMessage],
|
||||
components: [row2],
|
||||
});
|
||||
|
||||
if (!department) {
|
||||
try {
|
||||
let buttons = supportbot.Departments.map((x) =>
|
||||
new ButtonBuilder()
|
||||
.setCustomId("Department" + supportbot.Departments.indexOf(x))
|
||||
.setLabel(x.title)
|
||||
.setStyle(x.color)
|
||||
.setEmoji(x.emoji)
|
||||
);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(buttons);
|
||||
|
||||
try {
|
||||
const filter = (i) => i.user.id === interaction.user.id;
|
||||
await m.awaitMessageComponent({
|
||||
filter,
|
||||
max: 1,
|
||||
componentType: "BUTTON",
|
||||
time: supportbot.Ticket.Timeout * 60000,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.code === "COLLECTOR_ERROR") {
|
||||
try {
|
||||
let ticket = TicketData.tickets.findIndex((t) => t.id === ticketChannel.id);
|
||||
TicketData.tickets[ticket].open = false;
|
||||
fs.writeFileSync(
|
||||
"./Data/TicketData.json",
|
||||
JSON.stringify(TicketData, null, 4),
|
||||
(err) => {
|
||||
if (err) console.error(err);
|
||||
}
|
||||
);
|
||||
interaction.user.send(
|
||||
"Your ticket has timed out. Please open a new one, and select a department."
|
||||
);
|
||||
} catch (error) {return assignedUserId;
|
||||
console.error("An error occurred while handling collector:", error);
|
||||
}
|
||||
return await ticketChannel.delete();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error sending message:", error);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in run method:", error);
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
@@ -1,56 +0,0 @@
|
||||
const { EmbedBuilder, ApplicationCommandType } = 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");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.TicketStats.Command,
|
||||
description: cmdconfig.TicketStats.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
permissions: ["SendMessages"], // Adjust permissions as needed
|
||||
|
||||
async run(interaction) {
|
||||
|
||||
if (supportbot.Ticket.ClaimTickets.Enabled) {
|
||||
|
||||
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const userId = interaction.user.id;
|
||||
|
||||
const ticketsClaimed = TicketData.tickets.filter(t => t.claimedBy === userId);
|
||||
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); // Convert to minutes
|
||||
|
||||
const statsEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.TicketStats.Title)
|
||||
.setDescription(`> Ticket stats for <@${interaction.user.id}>`)
|
||||
.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 }
|
||||
);
|
||||
|
||||
await interaction.reply({ embeds: [statsEmbed], ephemeral: true });
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
@@ -98,6 +98,9 @@ Ticket:
|
||||
NoPermsToClaimTitle: "Error, no permissions!"
|
||||
NoPermsToClaim: "> :x: **Err!** You do not permissions to claim this ticket."
|
||||
|
||||
ReassigningTitle: "Reassigning!..."
|
||||
ReassigningMessage: "> <@%user%> did not claim the ticket. **Reassigning...**"
|
||||
|
||||
TicketStats:
|
||||
OpenTickets: "✅ Open Tickets"
|
||||
TotalTickets: "🎫 Total Tickets"
|
||||
|
||||
@@ -17,16 +17,16 @@ General:
|
||||
# Max tokens you can set is 400
|
||||
# For more information about tokens, go to https://openai.com/api/pricing/
|
||||
|
||||
OpenAI_Key: "OPEN_AI_API_KEY"
|
||||
OpenAI_Key: "OPEN AI KEY"
|
||||
# To create an OpenAI API key, go to https://platform.openai.com/api-keys
|
||||
|
||||
|
||||
PastebinAPI_Key: "PASTEBIN_API_KEY"
|
||||
PastebinAPI_Key: "PASTEBIN KEY"
|
||||
# You can get your Pastebin API Key from https://pastebin.com
|
||||
PastebinAPI_URL: "https://pastebin.com/api/api_post.php"
|
||||
|
||||
Channels:
|
||||
AIChannel: "0000000000000000000"
|
||||
AIChannel: "1250190338954498048"
|
||||
# This is the channel where AI will respond in without needing to mention the bot.
|
||||
|
||||
Embed:
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# Official Documentation: https://github.com/Emerald-Services/SupportBot/wiki
|
||||
# Third-Party Documentation: COMING SOON
|
||||
|
||||
SupportBot_Version: "v8.0"
|
||||
SupportBot_Version: "v8.0.1"
|
||||
# Do not change the version number
|
||||
|
||||
# -------------------------
|
||||
@@ -20,11 +20,11 @@ SupportBot_Version: "v8.0"
|
||||
|
||||
General:
|
||||
Name: "SupportBot"
|
||||
Token: "BOT_TOKEN_HERE"
|
||||
Token: "BOT TOKEN HERE"
|
||||
# To get your Discord Bot Token, Go to https://discord.com/developers/applications
|
||||
|
||||
Addons:
|
||||
Enabled: false
|
||||
Enabled: true
|
||||
|
||||
# -------------------------
|
||||
# Activity
|
||||
@@ -155,7 +155,11 @@ Ticket:
|
||||
Channel: "1249723473757077624"
|
||||
# Channel name or Channel ID
|
||||
|
||||
ClaimAcceptTime: 120000
|
||||
# How long the assigned user has to accept the ticket before it gets re-assigned to someone else.
|
||||
|
||||
ButtonTitle: "Claim Ticket"
|
||||
ButtonEmoji: "🎫"
|
||||
Button: "1"
|
||||
|
||||
# Available Button Styles
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"419821884431925260": {
|
||||
"bio": "Hi",
|
||||
"timezone": "",
|
||||
"isClockedIn": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"bio": "",
|
||||
"timezone": "",
|
||||
"clockedIn": false
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"clockedIn": true,
|
||||
"bio": "Hi",
|
||||
"timezone": "GMT"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"bio": "This is the most W bio",
|
||||
"timezone": "cst",
|
||||
"clockedIn": false
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"419821884431925260"
|
||||
]
|
||||
@@ -1 +1,16 @@
|
||||
{}
|
||||
{
|
||||
"1250864227460186240": {
|
||||
"suggestion": "hi",
|
||||
"author": "419821884431925260",
|
||||
"upvotes": [
|
||||
"419821884431925260"
|
||||
],
|
||||
"downvotes": []
|
||||
},
|
||||
"1250900161958248533": {
|
||||
"suggestion": "blob",
|
||||
"author": "419821884431925260",
|
||||
"upvotes": [],
|
||||
"downvotes": []
|
||||
}
|
||||
}
|
||||
+180
-1
@@ -1,5 +1,184 @@
|
||||
{
|
||||
"tickets": [
|
||||
{}
|
||||
{},
|
||||
{
|
||||
"id": "1253862843053510687",
|
||||
"name": "ticket-0021",
|
||||
"user": "419821884431925260",
|
||||
"number": "0021",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": true,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-06-22T00:03:18.227Z",
|
||||
"claimedAt": null
|
||||
},
|
||||
{
|
||||
"id": "1253863588012232727",
|
||||
"name": "ticket-0022",
|
||||
"user": "419821884431925260",
|
||||
"number": "0022",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": true,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-06-22T00:06:16.126Z",
|
||||
"claimedAt": "2024-06-22T00:09:12.693Z",
|
||||
"claimedBy": "419821884431925260"
|
||||
},
|
||||
{
|
||||
"id": "1255499438483574847",
|
||||
"name": "ticket-0023",
|
||||
"user": "419821884431925260",
|
||||
"number": "0023",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": true,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-06-26T12:26:35.729Z",
|
||||
"claimedAt": null
|
||||
},
|
||||
{
|
||||
"id": "1255500796355678229",
|
||||
"name": "ticket-0024",
|
||||
"user": "419821884431925260",
|
||||
"number": "0024",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": true,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-06-26T12:31:56.888Z",
|
||||
"claimedAt": "2024-06-26T12:32:41.240Z",
|
||||
"claimedBy": "419821884431925260"
|
||||
},
|
||||
{
|
||||
"id": "1255501873386094603",
|
||||
"name": "ticket-0025",
|
||||
"user": "419821884431925260",
|
||||
"number": "0025",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": false,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-06-26T12:36:13.810Z",
|
||||
"claimedAt": null
|
||||
},
|
||||
{
|
||||
"id": "1255508653906198709",
|
||||
"name": "ticket-0026",
|
||||
"user": "419821884431925260",
|
||||
"number": "0026",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": true,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-06-26T13:03:10.321Z",
|
||||
"claimedAt": null
|
||||
},
|
||||
{
|
||||
"id": "1256299016023572572",
|
||||
"name": "ticket-0027",
|
||||
"user": "419821884431925260",
|
||||
"number": "0027",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": false,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-06-28T17:23:50.113Z",
|
||||
"claimedAt": null
|
||||
},
|
||||
{
|
||||
"id": "1256300956824633426",
|
||||
"name": "ticket-0028",
|
||||
"user": "419821884431925260",
|
||||
"number": "0028",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": false,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-06-28T17:31:31.454Z",
|
||||
"claimedAt": null
|
||||
},
|
||||
{
|
||||
"id": "1256302271461986507",
|
||||
"name": "ticket-0029",
|
||||
"user": "419821884431925260",
|
||||
"number": "0029",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": false,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-06-28T17:36:44.673Z",
|
||||
"claimedAt": null
|
||||
},
|
||||
{
|
||||
"id": "1256303116505317457",
|
||||
"name": "ticket-0030",
|
||||
"user": "419821884431925260",
|
||||
"number": "0030",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": true,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-06-28T17:40:06.292Z",
|
||||
"claimedAt": null
|
||||
},
|
||||
{
|
||||
"id": "1257775687562166304",
|
||||
"name": "ticket-0031",
|
||||
"user": "419821884431925260",
|
||||
"number": "0031",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": false,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-07-02T19:11:33.760Z",
|
||||
"claimedAt": null
|
||||
},
|
||||
{
|
||||
"id": "1257776136843427841",
|
||||
"name": "ticket-0032",
|
||||
"user": "419821884431925260",
|
||||
"number": "0032",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": false,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-07-02T19:13:20.641Z",
|
||||
"claimedAt": null
|
||||
},
|
||||
{
|
||||
"id": "1257777774483996795",
|
||||
"name": "ticket-0033",
|
||||
"user": "419821884431925260",
|
||||
"number": "0033",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": false,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-07-02T19:19:51.116Z",
|
||||
"claimedAt": null
|
||||
},
|
||||
{
|
||||
"id": "1257778630600167535",
|
||||
"name": "ticket-0034",
|
||||
"user": "419821884431925260",
|
||||
"number": "0034",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": false,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-07-02T19:23:15.328Z",
|
||||
"claimedAt": null
|
||||
},
|
||||
{
|
||||
"id": "1257780259374366841",
|
||||
"name": "ticket-0035",
|
||||
"user": "419821884431925260",
|
||||
"number": "0035",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": false,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-07-02T19:29:43.607Z",
|
||||
"claimedAt": null
|
||||
},
|
||||
{
|
||||
"id": "1257782171389657180",
|
||||
"name": "ticket-0036",
|
||||
"user": "419821884431925260",
|
||||
"number": "0036",
|
||||
"reason": "> No Reason Provided!",
|
||||
"open": true,
|
||||
"subUsers": [],
|
||||
"createdAt": "2024-07-02T19:37:19.467Z",
|
||||
"claimedAt": "2024-07-02T19:43:12.873Z",
|
||||
"claimedBy": "419821884431925260"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"id": 0,
|
||||
"TicketPanelID": "0000000000000000000"
|
||||
"id": 37,
|
||||
"TicketPanelID": "1250863985335472312"
|
||||
}
|
||||
@@ -274,5 +274,52 @@ 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`;
|
||||
|
||||
let profileData;
|
||||
if (fs.existsSync(profilePath)) {
|
||||
profileData = JSON.parse(fs.readFileSync(profilePath, "utf8"));
|
||||
} else {
|
||||
profileData = {
|
||||
bio: "",
|
||||
timezone: "",
|
||||
clockedIn: false
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const newBio = interaction.fields.getTextInputValue('bio');
|
||||
const newTimezone = interaction.fields.getTextInputValue('timezone');
|
||||
|
||||
profileData.bio = newBio;
|
||||
profileData.timezone = newTimezone;
|
||||
|
||||
fs.writeFileSync(profilePath, JSON.stringify(profileData, null, 2));
|
||||
|
||||
const updatedProfileEmbed = new Discord.EmbedBuilder()
|
||||
.setTitle(`${interaction.user.username}'s Profile`)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.setThumbnail(interaction.user.displayAvatarURL())
|
||||
.addFields(
|
||||
{ name: 'Bio', value: newBio || 'No bio set.', inline: false },
|
||||
{ name: 'Timezone', value: newTimezone || 'No timezone set.', inline: true }
|
||||
);
|
||||
|
||||
if (profileData.clockedIn !== undefined) {
|
||||
updatedProfileEmbed.addFields({
|
||||
name: 'Clocked In Status',
|
||||
value: profileData.clockedIn ? '✅ Clocked In' : '❌ Clocked Out',
|
||||
inline: true
|
||||
});
|
||||
}
|
||||
|
||||
await interaction.reply({ embeds: [updatedProfileEmbed], ephemeral: true });
|
||||
} catch (error) {
|
||||
console.error("Failed to update profile data: ", error);
|
||||
await interaction.reply({ content: "There was an error while updating your profile. Please try again later.", ephemeral: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
+116
-64
@@ -18,6 +18,7 @@ const supportbotai = yaml.load(fs.readFileSync("./Configs/supportbot-ai.yml"));
|
||||
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"));
|
||||
|
||||
// Validate configuration values
|
||||
function validateConfig(config, keys) {
|
||||
keys.forEach(key => {
|
||||
@@ -68,99 +69,150 @@ module.exports = new Event("messageCreate", async (client, message) => {
|
||||
if (message.author.bot || !message.guild) return;
|
||||
if (!CHANNELS.includes(message.channelId) && !message.mentions.users.has(client.user.id)) return;
|
||||
|
||||
if (supportbotai.Enabled) {
|
||||
if (supportbotai.Enabled) {
|
||||
const sendTypingInterval = setInterval(() => {
|
||||
message.channel.sendTyping();
|
||||
}, 5000);
|
||||
|
||||
const sendTypingInterval = setInterval(() => {
|
||||
message.channel.sendTyping();
|
||||
}, 5000);
|
||||
let conversation = [{
|
||||
role: "system",
|
||||
content: `SupportBot is your friendly neighbourhood support bot. Current date and time: ${new Date().toLocaleString()}.
|
||||
The bot uses a modular command handler where commands are loaded dynamically from the './Commands' directory. Each command has a name, description, and execute method.
|
||||
|
||||
The main Client class structure:
|
||||
- Commands are loaded from the './Commands/' directory.
|
||||
- Configs are loaded from the './Configs/' directory if enabled.
|
||||
- Events are loaded from the './Events/' directory.
|
||||
- Addons are loaded from the './Addons/' directory if enabled.
|
||||
|
||||
Example of a command structure:
|
||||
{
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.Ping.Command,
|
||||
description: cmdconfig.Ping.Description,
|
||||
options: [],
|
||||
permissions: cmdconfig.Ping.Permission,
|
||||
)}
|
||||
}
|
||||
|
||||
let conversation = [{
|
||||
role: "system",
|
||||
content: "SupportBot is your friendly neighbourhood support bot."
|
||||
}];
|
||||
You can find more information about the commands and configuration files in the 'Configs' and 'Commands' directories.
|
||||
|
||||
let prevMessages = await message.channel.messages.fetch({ limit: 10 });
|
||||
prevMessages = prevMessages.reverse().filter(msg => !msg.author.bot || msg.author.id === client.user.id);
|
||||
All commands are loaded into the bot at startup from ./Events/ready.js and ./Client.js.
|
||||
|
||||
prevMessages.forEach((msg) => {
|
||||
const username = msg.author.username.replace(/\s+/g, '_').replace(/[^\w\s]/gi, '');
|
||||
If someone asks you to do something, you can use the 'assist' command to have the bot respond, For example: "Can I speak to a support agent?", You would respond by opening a ticket which is the /Commands/ticket.js file or you can tell them to create a ticket in the channel defined as supportbot.Ticket.
|
||||
|
||||
conversation.push({
|
||||
role: msg.author.id === client.user.id ? "assistant" : "user",
|
||||
name: username,
|
||||
content: msg.content,
|
||||
});
|
||||
});
|
||||
You can find the source code for the bot at https://github.com/EmeraldServices/SupportBot.
|
||||
|
||||
try {
|
||||
const response = await openai.chat.completions.create({
|
||||
model: supportbotai.General.Model,
|
||||
messages: conversation,
|
||||
max_tokens: supportbotai.General.Tokens,
|
||||
You then need to understand how the bot works. Interactions are handled by the ./Events/interactionCreate.js file.
|
||||
|
||||
Commands are filtered based on configuration settings and loaded into the bot at startup.
|
||||
|
||||
Xiled Gaming Network (XGN) is a gaming community that typically offers various services and activities for gamers.
|
||||
|
||||
Xiled Gaming Network (XGN) has 4 divisions, Team Xiled International (TXI), The XiledOnes (TXO), Dealers of Death (DOD), Psychotic Solutions (PS)
|
||||
Xiled Gaming Network (XGN) can be found at https://officialxgn.gg/handbook/, This is where you can the code of conduct, policies and other important information for members.
|
||||
You can join XGN with the discord - discord.gg/xgn
|
||||
You can apply to join the XGN Clan - join.officialxgn.gg
|
||||
|
||||
You can find the founders of XGN under the history section of the handbook. https://officialxgn.gg/handbook/.
|
||||
`
|
||||
}];
|
||||
|
||||
let prevMessages = await message.channel.messages.fetch({ limit: 10 });
|
||||
prevMessages = prevMessages.reverse().filter(msg => !msg.author.bot || msg.author.id === client.user.id);
|
||||
|
||||
prevMessages.forEach((msg) => {
|
||||
const username = msg.author.username.replace(/\s+/g, '_').replace(/[^\w\s]/gi, '');
|
||||
|
||||
conversation.push({
|
||||
role: msg.author.id === client.user.id ? "assistant" : "user",
|
||||
name: username,
|
||||
content: msg.content,
|
||||
});
|
||||
});
|
||||
|
||||
clearInterval(sendTypingInterval);
|
||||
try {
|
||||
const response = await openai.chat.completions.create({
|
||||
model: supportbotai.General.Model,
|
||||
messages: conversation,
|
||||
max_tokens: supportbotai.General.Tokens,
|
||||
});
|
||||
|
||||
if (response.choices && response.choices.length > 0) {
|
||||
const replyContent = response.choices[0].message.content;
|
||||
clearInterval(sendTypingInterval);
|
||||
|
||||
const SupportBotAI = new Discord.EmbedBuilder()
|
||||
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
|
||||
.setDescription(replyContent)
|
||||
.setColor(supportbotai.Embed.Color)
|
||||
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
if (response.choices && response.choices.length > 0) {
|
||||
const replyContent = response.choices[0].message.content;
|
||||
|
||||
if (replyContent.length > 2000) {
|
||||
const pasteLink = await createPaste(replyContent);
|
||||
if (replyContent.toLowerCase().startsWith("generate image:")) {
|
||||
const prompt = replyContent.substring(15).trim();
|
||||
const imageUrl = await generateImage(prompt);
|
||||
|
||||
const SupportBotAIPastebin = new Discord.EmbedBuilder()
|
||||
const SupportBotAIImage = new Discord.EmbedBuilder()
|
||||
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
|
||||
.setDescription(`Here is the image generated for your prompt: "${prompt}"`)
|
||||
.setImage(imageUrl)
|
||||
.setColor(supportbotai.Embed.Color)
|
||||
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
message.reply({ embeds: [SupportBotAIImage] });
|
||||
} else {
|
||||
const SupportBotAI = new Discord.EmbedBuilder()
|
||||
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
|
||||
.setDescription(replyContent)
|
||||
.setColor(supportbotai.Embed.Color)
|
||||
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
if (replyContent.length > 2000) {
|
||||
const pasteLink = await createPaste(replyContent);
|
||||
|
||||
const SupportBotAIPastebin = new Discord.EmbedBuilder()
|
||||
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
|
||||
.setDescription(supportbotai.Messages.MessageTooLong.replace("{pastebin}", pasteLink))
|
||||
.setColor(supportbotai.Embed.Color)
|
||||
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
message.reply({ embeds: [SupportBotAIPastebin] });
|
||||
} else {
|
||||
message.reply({ embeds: [SupportBotAI] });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const SupportBotAIErrorResponse = new Discord.EmbedBuilder()
|
||||
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
|
||||
.setDescription(supportbotai.Messages.MessageTooLong.replace("{pastebin}", pasteLink))
|
||||
.setDescription(supportbotai.Messages.ErrorResponse)
|
||||
.setColor(supportbotai.Embed.Color)
|
||||
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
message.reply({ embeds: [SupportBotAIPastebin] });
|
||||
} else {
|
||||
message.reply({ embeds: [SupportBotAI] });
|
||||
message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true });
|
||||
}
|
||||
} else {
|
||||
} catch (error) {
|
||||
clearInterval(sendTypingInterval);
|
||||
|
||||
const SupportBotAIErrorResponse = new Discord.EmbedBuilder()
|
||||
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
|
||||
.setDescription(supportbotai.Messages.ErrorResponse)
|
||||
.setColor(supportbotai.Embed.Color)
|
||||
.setDescription(supportbotai.Messages.OpenAIError)
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
console.error('OpenAI Error:', error);
|
||||
message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true });
|
||||
}
|
||||
} catch (error) {
|
||||
clearInterval(sendTypingInterval);
|
||||
}
|
||||
|
||||
const SupportBotAIErrorResponse = new Discord.EmbedBuilder()
|
||||
if (!supportbotai.Enabled) {
|
||||
const AIDisabled = new Discord.EmbedBuilder()
|
||||
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
|
||||
.setDescription(supportbotai.Messages.OpenAIError)
|
||||
.setDescription(supportbotai.Messages.AIDisabled)
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
console.error('OpenAI Error:', error);
|
||||
message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true });
|
||||
message.reply({ embeds: [AIDisabled], ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (supportbotai.Enabled === false) {
|
||||
const AIDisabled = new Discord.EmbedBuilder()
|
||||
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
|
||||
.setDescription(supportbotai.Messages.AIDisabled)
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
message.reply({ embeds: [AIDisabled], ephemeral: true })
|
||||
return;
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Emerald Services
|
||||
Copyright (c) 2024 Emerald Services
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+18
-19
@@ -1,12 +1,8 @@
|
||||
// C:\Users\ollie\Documents\GitHub\SupportBot\Structures\Client.js
|
||||
|
||||
// SupportBot | Emerald Services
|
||||
// Client Structure
|
||||
|
||||
const fs = require("fs");
|
||||
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 supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
@@ -89,18 +85,14 @@ class Client extends Discord.Client {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "Ping.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Ping.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "Ping.js");
|
||||
}
|
||||
|
||||
if (supportbot.Ticket.ClaimTickets.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "ticketstats.js");
|
||||
tempCommandFiles = tempCommandFiles.filter(item => item !== "ticketstats.js");
|
||||
}
|
||||
|
||||
const commandFiles = tempCommandFiles;
|
||||
const commands = commandFiles.map((file) => require(`../Commands/${file}`));
|
||||
|
||||
// Commands
|
||||
// Load main commands
|
||||
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬");
|
||||
|
||||
commands.forEach((cmd) => {
|
||||
@@ -108,13 +100,14 @@ class Client extends Discord.Client {
|
||||
this.commands.set(cmd.name, cmd);
|
||||
});
|
||||
|
||||
// Load addon commands and events
|
||||
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬");
|
||||
|
||||
if (supportbot.General.Addons.Enabled) {
|
||||
const addonFiles = fs.readdirSync("./Addons").filter((file) => file.endsWith(".js"));
|
||||
|
||||
const addons = addonFiles.map((file) => {
|
||||
let addon = require(`../Addons/${file}`);
|
||||
const addon = require(`../Addons/${file}`);
|
||||
addon.name = file.split(".")[0];
|
||||
return addon;
|
||||
});
|
||||
@@ -132,18 +125,24 @@ class Client extends Discord.Client {
|
||||
`\u001b[32;1m`,
|
||||
"Loaded"
|
||||
);
|
||||
addon.commands?.forEach((command) => {
|
||||
this.commands.set(command.name, command);
|
||||
});
|
||||
addon.events?.forEach((event) => {
|
||||
this.on(event.event, (...args) => event.run(this, ...args));
|
||||
});
|
||||
|
||||
if (addon instanceof Command) {
|
||||
this.commands.set(addon.name, addon);
|
||||
}
|
||||
|
||||
if (addon.events && Array.isArray(addon.events)) {
|
||||
addon.events.forEach((event) => {
|
||||
if (event instanceof Event) {
|
||||
this.on(event.event, (...args) => event.run(this, ...args));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
|
||||
}
|
||||
|
||||
// Slash Commands
|
||||
// Register Slash Commands
|
||||
|
||||
this.once("ready", async () => {
|
||||
await this.guilds.cache.first()?.commands.set(this.commands);
|
||||
|
||||
Reference in new issue
Block a user