8.4-Dev 1

Added Suggestions mode switch
This commit is contained in:
1tzEmerald committed 2025-08-07 13:54:35 +01:00
1 parent 5c6a9f7306
commit c6db929d12
5 files changed
+214 -159

No files matched your search

+81 -17
View File
@@ -33,21 +33,90 @@ module.exports = new Command({
async run(interaction) {
const { getChannel } = interaction.client;
const mode = supportbot.Suggestions.Mode || "Standard";
const suggestChannel = await getChannel(
supportbot.Suggestions.Channel,
interaction.guild
);
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.InvalidChannel)
.setDescription(msgconfig.Error.MissingChannel)
.setColor(supportbot.Embed.Colours.Error);
if (!suggestChannel) return interaction.reply({ embeds: [NoChannel] });
let suggestion = interaction.options.getString("suggestion");
const SuggestEmbed = new Discord.EmbedBuilder()
.addFields(
{ name: "Suggestion", value: suggestion, inline: true },
@@ -75,7 +144,7 @@ module.exports = new Command({
const RemoveVoteButton = new Discord.ButtonBuilder()
.setCustomId("removevote")
.setLabel(supportbot.Suggestions.Buttons.RemoveVote_Title)
.setStyle(supportbot.Suggestions.Buttons.RemoveVote); // Red button
.setStyle(supportbot.Suggestions.Buttons.RemoveVote);
const row = new Discord.ActionRowBuilder().addComponents(UpvoteButton, DownvoteButton, RemoveVoteButton);
@@ -100,16 +169,11 @@ module.exports = new Command({
}
const Submitted = new Discord.EmbedBuilder()
.setTitle(`${msgconfig.Suggestions.Sent_Title}`)
.setDescription(`${msgconfig.Suggestions.Sent}`)
.addFields(
{ name: "Sent to:", value: `<#${suggestChannel.id}>` },
)
.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]
});
await interaction.reply({ ephemeral: true, embeds: [Submitted] });
},
});
+1
View File
@@ -268,6 +268,7 @@ Error:
EntryAlreadyExists: ":warning: Cannot complete the action, as the entry already exists."
EntryDoesNotExist: ":warning: Cannot complete the action, as the entry does not exist."
ActionFailed: ":x: **Error!** An error occurred while processing your request. Please try again later."
+25 -22
View File
@@ -13,7 +13,7 @@
#
# SupportBot Configuration (supportbot.yml)
SupportBot_Version: "v8.3"
SupportBot_Version: "v8.4-DEV-1"
# -------------------------
# General
@@ -21,7 +21,7 @@ SupportBot_Version: "v8.3"
General:
Name: "SupportBot"
Token: "BOT_TOKEN"
Token: "DISCORD_BOT_TOKEN"
Addons:
Enabled: true
@@ -66,12 +66,12 @@ Embed:
Roles:
AutoRole:
Enabled: true
Role: "ROLE_ID"
Role: "1202669457751801917"
StaffMember:
Staff: "ROLE_ID"
Admin: "ROLE_ID"
Moderator: "ROLE_ID"
Staff: "996420644373811371"
Admin: "996419960442208326"
Moderator: "996420644373811371"
Mod:
AllowSupportStaff: false
@@ -84,18 +84,21 @@ Roles:
Welcome:
Enabled: true
Channel: "JOIN_CHANNEL_ID"
Channel: "1295788797065166869"
Leave:
Enabled: true
Channel: "JOIN_CHANNEL_ID"
Channel: "1295788797065166869"
# ----------------------------
# Suggestions
# ----------------------------
Suggestions:
Channel: "SUGGESTIONS_CHANNEL_ID"
Mode: "Forum" # or Forum
ForumChannel: "1402655496477081671"
Channel: "1295788900094316706"
UpvoteEmoji: "✅"
DownvoteEmoji: "❌"
@@ -132,7 +135,7 @@ Suggestions:
VoiceTickets:
Name: "%username%'s VC"
Category: "VOICEE_CATEGORY_ID"
Category: "1297539636159315999"
Ticket:
TicketType: "channels"
@@ -141,16 +144,16 @@ Ticket:
# channels - Tickets will be created as channels in the TicketChannelsCategory
# threads - Tickets will be created as threads through the "TicketHome" channel.
TicketHome: "TICKETHOME_CHANNEL_ID"
TicketHome: "1295784943204700171"
# What is TicketHome?
# - Channel where the Ticket creation panel is sent.
# - Channel where ticket threads are made. (only if ticket threads is enabled above.)
# For TicketType: channels
TicketChannelsCategory: "TICKETCHANNEL_CATEGORY_ID"
TicketChannelsCategory2: "TICKETCHANNEL_BACKUP_CATEGORY_ID" # This will be used if the first category has reached the max channel limit.
TicketArchiveCategory: "ARCHIVE_CATEGORY_ID"
TicketChannelsCategory: "1295789095070728253"
TicketChannelsCategory2: "1295789210040537088" # This will be used if the first category has reached the max channel limit.
TicketArchiveCategory: "1295789286322343946"
# ---
Channel: "ticket-" # The prefix for ticket channel names
@@ -189,7 +192,7 @@ Ticket:
ClaimTickets:
Enabled: true
Channel: "CLAIM_TICKETS_CHANNEL_ID"
Channel: "1295789389619793961"
# Channel where the ticket assignments are sent for staff to claim.
ClaimAcceptTime: 120000
@@ -211,8 +214,8 @@ Ticket:
# 4 - Red
ReviewSystem:
Enabled: true
Channel: "REVIEW_CHANNEL_ID"
Enabled: false
Channel: "1304480728884445225"
UseModal: true
Questions:
@@ -223,9 +226,9 @@ Ticket:
- "Have you tried resolving the issue yourself?"
Log:
TicketDataLog: "TICKET_DATA_LOG_ID"
TicketDataLog: "1339271338761650290"
TickeDataTitle: "Ticket Data"
TicketBlacklistLog: "TICKET_BLACKLIST_LOG_ID"
TicketBlacklistLog: "1339271338761650290"
# -------------------------
# Buttons
@@ -278,12 +281,12 @@ SelectMenus:
# -------------------------
MessageDelete:
Channel: "MESSAGE_DELETE_CHANNEL_ID""
Channel: "1202671982932402246"
Colour: "#d92525"
MessageUpdate:
Channel: "MESSAGE_UPDATE_CHANNEL_ID"
Channel: "1202671982932402246"
Colour: "#f2a024"
Translate:
TranslateLog: "TRANSLATE_LOG_ID"
TranslateLog: "1202671982932402246"
+106 -119
View File
@@ -1,143 +1,130 @@
// SupportBot | Emerald Services
// Ready Event
const fs = require("fs");
const Discord = require("discord.js");
const { Client, GatewayIntentBits, ActivityType } = require('discord.js');
const client = new Discord.Client({intents: 32767})
const yaml = require("js-yaml");
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
const panelconfig = yaml.load(
fs.readFileSync("./Configs/ticket-panel.yml", "utf8")
);
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");
const gradient = require("gradient-string");
const figlet = require("figlet");
let chan1 = client.channels.cache.get(supportbot.Ticket.TicketHome);
// Load configs
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
const panel = yaml.load(fs.readFileSync("./Configs/ticket-panel.yml", "utf8"));
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
module.exports = new Event("ready", async (client, interaction) => {
const { getRole, getChannel, getCategory } = client;
const ACTIVITY_TYPES = {
PLAYING: Discord.ActivityType.Playing,
WATCHING: Discord.ActivityType.Watching,
LISTENING: Discord.ActivityType.Listening,
COMPETING: Discord.ActivityType.Competing
};
if (supportbot.Activity.Type === "Playing", "playing") {
const setActivity = (client, type, status) => {
const activityType = ACTIVITY_TYPES[type?.toUpperCase()];
if (activityType) {
client.user.setPresence({
activities: [{ name: supportbot.Activity.Status, type: Discord.ActivityType.Playing }],
status: supportbot.Activity.Type,
activities: [{ name: status || "Online", type: activityType }],
status: type?.toLowerCase() || "online"
});
}
};
if (supportbot.Activity.Type === "Watching", "watching") {
client.user.setPresence({
activities: [{ name: supportbot.Activity.Status, type: Discord.ActivityType.Watching }],
status: supportbot.Activity.Type,
});
}
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const logStartup = async (client) => {
console.clear();
if (supportbot.Activity.Type === "Listening", "listening") {
client.user.setPresence({
activities: [{ name: supportbot.Activity.Status, type: Discord.ActivityType.Listening }],
status: supportbot.Activity.Type,
});
// Animated loading
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
for (let i = 0; i < frames.length * 2; i++) {
process.stdout.write(`\r${frames[i % frames.length]} Initializing SupportBot...`);
await sleep(100);
}
console.clear();
if (supportbot.Activity.Type === "Competing", "competing") {
client.user.setPresence({
activities: [{ name: supportbot.Activity.Status, type: Discord.ActivityType.Competing }],
status: supportbot.Activity.Type,
});
}
// Main title
console.log("\n" + gradient.pastel.multiline(figlet.textSync("SupportBot", { font: "ANSI Shadow", horizontalLayout: "fitted" })));
console.log(`\u001b[33m`, `――――――――――――――――――――――――――――――――――――――――――――`);
console.log(` `);
console.log(`\u001b[31m`, `┏━━━┓╋╋╋╋╋╋╋╋╋╋╋╋╋┏┓┏━━┓╋╋╋┏┓`);
console.log(`\u001b[31m`, `┃┏━┓┃╋╋╋╋╋╋╋╋╋╋╋╋┏┛┗┫┏┓┃╋╋┏┛┗┓`);
console.log(`\u001b[31m`, `┃┗━━┳┓┏┳━━┳━━┳━━┳┻┓┏┫┗┛┗┳━┻┓┏┛`);
console.log(`\u001b[31m`, `┗━━┓┃┃┃┃┏┓┃┏┓┃┏┓┃┏┫┃┃┏━┓┃┏┓┃┃`);
console.log(`\u001b[31m`, `┃┗━┛┃┗┛┃┗┛┃┗┛┃┗┛┃┃┃┗┫┗━┛┃┗┛┃┗┓`);
console.log(`\u001b[31m`, `┗━━━┻━━┫┏━┫┏━┻━━┻┛┗━┻━━━┻━━┻━┛`);
console.log(`\u001b[31m`, `┗╋╋╋╋╋╋╋┃┃╋┃┃`);
console.log(`\u001b[31m`, `╋╋╋╋╋╋╋┗┛╋┗┛`);
console.log(` `);
console.log(`\u001b[33m`, `――――――――――――――――――――――――――――――――――――――――――――`);
console.log(` `);
console.log(`\u001b[33m`, `${supportbot.General.Name} | [${supportbot.SupportBot_Version}]`, `\u001b[32m`, `Connected to Discord`,);
console.log("\u001b[32m", "SupportBot created by Emerald Development ");
console.log(` `);
console.log(`\u001b[33m`, `――――――――――――――――― [Links] ――――――――――――――――――`);
console.log("\u001b[32m", "Discord: https://dsc.gg/emerald-dev");
console.log("\u001b[32m", "Website: https://emeraldsrv.com");
console.log("\u001b[32m", "Community: https://community.emeraldsrv.com");
console.log("\u001b[32m", "Documentation: https://docs.emeraldsrv.com");
console.log(` `);
console.log(`\u001b[33m`, `――――――――――――――――― [Invite] ――――――――――――――――――`);
console.log("\u001b[32m", `Invite URL: https://discord.com/api/oauth2/authorize?client_id=${client.user.id}&permissions=8&scope=bot%20applications.commands`);
console.log(` `);
console.log(`\u001b[33m`, `――――――――――――――――― [Config Check] ――――――――――――――――――`);
console.log("\u001b[32m", "Config initialization...");
// Extract bot details safely
const botName = supportbot?.General?.Name || "SupportBot";
const botVersion = supportbot.SupportBot_Version;
console.log("\n╔═══════════════════════════════════════════════════════════════╗");
console.log("║ System Information ║");
console.log("╠═══════════════════════════════════════════════════════════════╣");
console.log(`║ Bot Name : ${botName.padEnd(45)} ║`);
console.log(`║ Version : ${botVersion.padEnd(45)} ║`);
console.log(`║ Node.js : ${process.version.padEnd(45)} ║`);
console.log(`║ Discord.js : v${Discord.version.padEnd(44)} ║`);
console.log("╚═══════════════════════════════════════════════════════════════╝\n");
// 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("┃ ") + "🤝 Discord : " + gradient.cristal("https://dsc.gg/emerald-dev ┃"));
console.log(gradient.summer("┃ ┃"));
console.log(gradient.summer("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n"));
// Bot Invite
const inviteUrl = `https://discord.com/api/oauth2/authorize?client_id=${client.user.id}&permissions=8&scope=bot%20applications.commands`;
console.log(gradient.teen("╔════════════════════════ Bot Invite ════════════════════════╗"));
console.log(gradient.teen("║ ║"));
console.log(gradient.teen("║ ") + gradient.pastel(inviteUrl.padEnd(56)) + gradient.teen("║"));
console.log(gradient.teen("║ ║"));
console.log(gradient.teen("╚════════════════════════════════════════════════════════════╝\n"));
};
const checkConfigs = async (client) => {
const guild = client.guilds.cache.first();
if (!guild) return;
console.log(gradient.morning("┏━━━━━━━━━━━━━━━━━━━━ Config Check ━━━━━━━━━━━━━━━━━━━━━┓"));
const roles = [
supportbot.Roles.StaffMember.Admin,
supportbot.Roles.StaffMember.Staff
];
supportbot?.Roles?.StaffMember?.Admin,
supportbot?.Roles?.StaffMember?.Staff,
supportbot?.Roles?.AutoRole?.Role
].filter(Boolean);
if (supportbot.Roles.AutoRole.Role) roles.push(supportbot.Roles.AutoRole.Role);
const channels = [
supportbot.Suggestions.Channel,
supportbot.Ticket.Log.TicketLog,
supportbot.Ticket.Log.TranscriptLog,
supportbot.Ticket.TicketHome,
supportbot.Welcome.Channel,
supportbot.Leave.Channel,
supportbot.Translate.Log
];
//const categories = [supportbot.TicketCategory];
const channels = [
supportbot?.Suggestions?.Channel,
supportbot?.Ticket?.Log?.TicketLog,
supportbot?.Ticket?.Log?.TranscriptLog,
supportbot?.Ticket?.TicketHome,
supportbot?.Welcome?.Channel,
supportbot?.Leave?.Channel,
supportbot?.Translate?.Log
].filter(Boolean);
if (!channels) {
console.log("\u001b[31m", `[MISSING CHANNEL]`, `\u001b[37;1m`, `${channels}`, "\u001b[31m", `channel not found. Please check your config file.`);
return;
}
// else {
// console.log(`\u001b[32m`, `[CHANNEL LOCATED]`, `\u001b[37;1m`, `${channels}`, `\u001b[32;1m`, `channel has been found.`);
// }
const [missingRoles, missingChannels] = await Promise.all([
Promise.all(roles.map((role) => client.getRole(role, guild))),
Promise.all(channels.map((channel) => client.getChannel(channel, guild)))
]);
const missingC = [];
const missingR = [];
const missingCat = [];
for (let r of roles) {
const find = await getRole(r, client.guilds.cache.first());
if (!find) missingR.push(r);
}
// for (let cat of categories) {
// const find = await getCategory(cat, client.guilds.cache.first());
// if (!find) missingCat.push(cat);
// }
const missing = {
roles: roles.filter((_, i) => !missingRoles[i]),
channels: channels.filter((_, i) => !missingChannels[i])
};
const missingRoles = await Promise.all(roles.map(role => getRole(role, client.guilds.cache.first())));
const missingChannels = await Promise.all(channels.map(channel => getChannel(channel, client.guilds.cache.first())));
if (missingRoles.some(role => !role)) {
console.log("\u001b[31m", `Missing roles in your server configuration: ${missingRoles.filter(role => !role).join(', ')}`);
if (missing.roles.length || missing.channels.length) {
console.log(gradient.morning("┃ ⚠️ - Configuration Issues Found: ┃"));
if (missing.roles.length) {
console.log(gradient.morning("┃ • Missing Roles: ") + gradient.fruit(missing.roles.join(", ")));
}
if (missing.channels.length) {
console.log(gradient.morning("┃ • Missing Channels: ") + gradient.fruit(missing.channels.join(", ")));
}
} else {
console.log(gradient.morning("┃ ✅ - All configurations validated successfully ┃"));
}
if (missingChannels.some(channel => !channel)) {
console.log("\u001b[31m", `Missing channels in your server configuration: ${missingChannels.filter(channel => !channel).join(', ')}`);
}
console.log(gradient.morning("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n"));
};
console.log("\u001b[32m", "Configs initialized, No problems were detected.");
console.log(` `);
});
module.exports = new Event("ready", async (client) => {
setActivity(client, supportbot?.Activity?.Type, supportbot?.Activity?.Status);
await logStartup(client);
await checkConfigs(client);
});
+1 -1
View File
@@ -196,4 +196,4 @@ class Client extends Discord.Client {
}
}
module.exports = Client;
module.exports = Client;