From c6db929d128542daeb668f64644c8beaf57e66f9 Mon Sep 17 00:00:00 2001 From: 1tzEmerald Date: Thu, 7 Aug 2025 13:54:35 +0100 Subject: [PATCH] 8.4-Dev 1 Added Suggestions mode switch --- Commands/suggest.js | 98 ++++++++++++++---- Configs/messages.yml | 1 + Configs/supportbot.yml | 47 +++++---- Events/ready.js | 225 +++++++++++++++++++---------------------- Structures/Client.js | 2 +- 5 files changed, 214 insertions(+), 159 deletions(-) diff --git a/Commands/suggest.js b/Commands/suggest.js index 65f1c2d..c01b584 100644 --- a/Commands/suggest.js +++ b/Commands/suggest.js @@ -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] }); }, }); diff --git a/Configs/messages.yml b/Configs/messages.yml index 9733627..3253913 100644 --- a/Configs/messages.yml +++ b/Configs/messages.yml @@ -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." + diff --git a/Configs/supportbot.yml b/Configs/supportbot.yml index ea6b514..bbcc6e0 100644 --- a/Configs/supportbot.yml +++ b/Configs/supportbot.yml @@ -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" diff --git a/Events/ready.js b/Events/ready.js index 69a7908..169a74e 100644 --- a/Events/ready.js +++ b/Events/ready.js @@ -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(` `); - -}); \ No newline at end of file +module.exports = new Event("ready", async (client) => { + setActivity(client, supportbot?.Activity?.Type, supportbot?.Activity?.Status); + await logStartup(client); + await checkConfigs(client); +}); diff --git a/Structures/Client.js b/Structures/Client.js index 812b7e0..94ca573 100644 --- a/Structures/Client.js +++ b/Structures/Client.js @@ -196,4 +196,4 @@ class Client extends Discord.Client { } } -module.exports = Client; +module.exports = Client; \ No newline at end of file