From 7383bd2c486a7615c8b82c5902a50590da3a6429 Mon Sep 17 00:00:00 2001 From: 1tzEmerald Date: Mon, 22 Sep 2025 18:36:04 +0100 Subject: [PATCH] 8.4-DEV-2 Added Buttons to WelcomeMessage Added Components v2 Support for Welcome Message Added improved logging system --- Addons/addon.js | 26 ---- Commands/Tickets/close.js | 261 ++++++++++++++++++++---------------- Configs/messages.yml | 65 +++++++-- Configs/supportbot.yml | 10 +- Configs/ticket-panel.yml | 2 + Data/supportbot.db | Bin 57344 -> 0 bytes Events/guildMemberAdd.js | 179 +++++++++++++++++++------ Events/guildMemberRemove.js | 74 +++++----- Events/interactionCreate.js | 31 ++--- Events/messageDelete.js | 29 ++-- Events/ready.js | 2 +- LICENSE.md | 2 +- Structures/Client.js | 3 +- Structures/Database.js | 6 - index.js | 55 ++++++-- package.json | 4 +- 16 files changed, 459 insertions(+), 290 deletions(-) delete mode 100644 Addons/addon.js delete mode 100644 Data/supportbot.db diff --git a/Addons/addon.js b/Addons/addon.js deleted file mode 100644 index 7609fd7..0000000 --- a/Addons/addon.js +++ /dev/null @@ -1,26 +0,0 @@ -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], - }); - }, - }); \ No newline at end of file diff --git a/Commands/Tickets/close.js b/Commands/Tickets/close.js index b56ef40..8ebf8e3 100644 --- a/Commands/Tickets/close.js +++ b/Commands/Tickets/close.js @@ -288,119 +288,154 @@ function parseMarkdown(content) { function createTranscriptHTML(ticket, reason) { return ` - - - - - - - - - -
-
-

Ticket Transcript

-

Channel: ${ticket.name}

-

Ticket ID: ${ticket.id}

-

Message Count: ${ticket.messages.length}

-

Close Reason: ${reason}

-
- -
- ${ticket.messages.map(msg => ` -
-
- Avatar - ${msg.username} - ${new Date(msg.timestamp).toLocaleString()} -
-
- ${parseMarkdown(msg.content)} - - ${msg.embeds.map(embed => ` -
- ${embed.title ? `
${embed.title}
` : ''} - ${embed.description ? `
${embed.description}
` : ''} - ${embed.fields.map(field => ` -
- ${field.name}: -
${field.value}
-
- `).join('')} -
- `).join('')} - - ${msg.attachments.map(att => ` - - šŸ“Ž ${att.name} - - `).join('')} -
-
- `).join('')} -
+ + + + + + + + + +
+

Ticket Transcript

+
+

Channel: ${ticket.name}

+

Ticket ID: ${ticket.id}

+

Message Count: ${ticket.messages.length}

+

Close Reason: ${reason}

- - + +
+ ${ticket.messages.map(msg => ` +
+
+ Avatar + ${msg.username} + ${new Date(msg.timestamp).toLocaleString()} +
+
+ ${parseMarkdown(msg.content)} + + ${msg.embeds.map(embed => ` +
+ ${embed.title ? `
${embed.title}
` : ''} + ${embed.description ? `
${embed.description}
` : ''} + ${embed.fields.map(field => ` +
+ ${field.name}: +
${field.value}
+
+ `).join('')} +
+ `).join('')} + + ${msg.attachments.map(att => ` + + šŸ“Ž ${att.name} + + `).join('')} +
+
+ `).join('')} +
+
+ + `; } diff --git a/Configs/messages.yml b/Configs/messages.yml index 3253913..a728c11 100644 --- a/Configs/messages.yml +++ b/Configs/messages.yml @@ -22,27 +22,74 @@ Info: Description: "SupportBot is an open-sourced support system for discord. It's completely free to use with high amounts of customization to match your own brand." Button: "Visit Site" URL: "https://docs.emeraldsrv.com" - Colour: "#f74343" + Colour: "#45b463" # ---------------------------- # Welcome/Leave Messages # ---------------------------- Welcome: - Embed: - Colour: "#f74343" - Title: "Welcome to ServerName" - Message: ":smile: %joined_user% has just landed!" - Thumbnail: "BOT" + Enabled: true + LayoutStyle: "modern" + # Available Layout Styles + # "modern" - For the new Discord v2 components + # "classic" - Use the classic Discord look. - ImageEnabled: false - ImageURL: "https://animagelink.png/" + Embed: + Colour: "#45b463" + Title: "Welcome to ServerName" + Message: ":smile: %joined_user% has just landed!\n\nšŸ”Ø **Server Rules:** <#1202667199853232129>" + Icon: + Mode: "USER" # USER | BOT | CUSTOM + CustomURL: "https://example.com/custom.png" + Image: + Enabled: false + URL: "https://example.com/banner.png" + Buttons: + Enabled: true + List: + - label: "BuiltByBit" + style: "Link" + url: "https://your-website.com/how-to-play-java" + emoji: "āŒØļø" + - label: "GitHub" + style: "Link" + url: "https://your-website.com/how-to-play-bedrock" + emoji: "šŸ‘¾" + - label: "Tebex" + style: "Link" + url: "https://your-store.tebex.io/" + emoji: "šŸ›’" + + Modern: + Colour: "#45b463" + Title: "**Welcome to ServerName**" + Body: ":smile: %joined_user% has just landed!\n\nšŸ”Ø **Server Rules:** <#1202667199853232129>" + Icon: + Mode: "BOT" # USER | BOT | CUSTOM + CustomURL: "https://example.com/custom-icon.png" + Image: + Enabled: false + URL: "https://example.com/banner.png" + Buttons: + - label: "BuiltByBit" + style: "Link" + url: "https://your-website.com/how-to-play-java" + emoji: "āŒØļø" + - label: "GitHub" + style: "Link" + url: "https://your-website.com/how-to-play-bedrock" + emoji: "šŸ‘¾" + - label: "Tebex" + style: "Link" + url: "https://your-store.tebex.io/" + emoji: "šŸ›’" Leave: Embed: Colour: "#f74343" Title: "Goodbye!" - Message: ":slight_frown: %joined_user% has recently dropped out!" + Message: ":slight_frown: %left_user% has recently dropped out!" Thumbnail: "BOT" ImageEnabled: false diff --git a/Configs/supportbot.yml b/Configs/supportbot.yml index d19a3e1..3498630 100644 --- a/Configs/supportbot.yml +++ b/Configs/supportbot.yml @@ -8,12 +8,12 @@ # Installed with MIT License # # Discord Support: https://discord.gg/emeraldsrv -# Official Documentation: https://docs.emeraldsrv.com -# Addons Marketplace: https://community.emeraldsrv.com +# Official Documentation: https://docs.emeraldsrv.dev +# Addons Marketplace: https://community.emeraldsrv.dev # # SupportBot Configuration (supportbot.yml) -SupportBot_Version: "v8.4-DEV-1" +SupportBot_Version: "v8.4-DEV-2" # ------------------------- # General @@ -21,7 +21,7 @@ SupportBot_Version: "v8.4-DEV-1" General: Name: "SupportBot" - Token: "BOT_TOKEN" + Token: "" Addons: Enabled: true @@ -144,7 +144,7 @@ VoiceTickets: Category: "1419062819688550460" Ticket: - TicketType: "channels" + TicketType: "threads" # Available TicketTypes: # channels - Tickets will be created as channels in the TicketChannelsCategory diff --git a/Configs/ticket-panel.yml b/Configs/ticket-panel.yml index 5cb7f3d..c8e6b5b 100644 --- a/Configs/ticket-panel.yml +++ b/Configs/ticket-panel.yml @@ -19,6 +19,8 @@ Panel: true +LayoutStyle: modern + # ---------------------------------------- # Ticket Panel Message Configuration #2b2d31 # ---------------------------------------- diff --git a/Data/supportbot.db b/Data/supportbot.db deleted file mode 100644 index 672dfb3ac04f0ed8dce1369084747577974d1679..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57344 zcmeI*Z)_W99S88cI8I_acjva$UKdq(DxtC3wfFxI3<LSFbee;@ycOrGaPA0Ex*9?QL!{Z_7;y>Fw{ z5r+T-AOHafKmY>&e}RkT^l))(jQv8nS3fi7^xBQHPR|V=h7O&mR%WWDnacggs--Yk zn#h)D3SQOPw)dhmQ+;Tr^uUS9TxT&_B3LGfyS;jE$@N!^);(N3 zQaN>OrnI-a;B@u|i!~OVde5OC({bj!!g^H+!-s(*zTwO;GD&#hfK2=+InFgJzE z!cCU(HvZ2)7%TCev+S}PcL8VGU2nsrslP?`+Vjq*x*aDTYs__N4{c9&qMl!h_MD>Eq6Y^z+YF*I z)uiZIdgTB+T->vVy|^dX5SC_V9qIwyj_dZH4kT(8n-Ny7*P`xaN8NCJiWM4 zVwH0q4foOgSpWBe7h9{@-a|p}-Rsp*8yG51?%Al#Vr8pIu@@=&O*-QJHs!p#ymyBQ z0uX=z1Rwwb2tWV=5P$##AOL}_DvU$-3yB|4#`oF~V^!a#I7F$NEK5Gdz%q}ft@KSv;O#><7!MkyTSGTecQRvTBOEe6_R7}~_Ek!c*^?$K?{}7SA)ZX>|{RW;Ex3{~AQ?amwDrnH6Zj#+!wc4ytge2}NOmPfM`I)O&n6vZ#_ zEhp5%;%)!qQ3-W2_6Nf$AkkD)rW1w4Ol}LgAp3i{pkAV( z=(n9Pt3S=KFGSE*-ilI%uJp=58orFjf> zTlgs>d%gQVtRi)uGue!fvX2Wk2i-IjNwOqaluVV{>edsuYbrOt{~WbK_VKsp=iA*! zT3*#JSf&}mQzx+u$rDx$`J=-@<@Q8Unyl)gYDto=ZC1J0&$FJC^wyD2oU9z7Tjk`D zsdPeJqv9&2AyPwXlD0{4U!8uKa$R}lpUq{D-ClN^ZgXldnM^YjOQF84SdzF&|8A|e z|3S%qoqoC1YSLEQq95CxOa;mA32I5j(KSt^Ml>{SGsvHO;CGa4_0YAZvrL8aZim?* zRWV4V(9Vf^G3}_Rben;F+4%^idS>iv{`NaP5Mj!+qR;v+LaT{4pw(cm6-m)A9eGg;#_Zh0DU1gmc1ap(2!p zZT#E(ReqI!p1(v3V1fVyAOHafKmY;|fB*y_009X6F9PA|>RZSC^8I*X;7;hr69aL4 zEFK?P<6`mnVA*Ito*3rJt;LfAFWG)PF%*>P#}mU>BmKA(A6*%a#pB~sLu>Km2uivi zPYi|(_T!0hjp+10eJmg`yf9EmDH(s5Ejf7Meg2>4zrxU`|9>nzEqqa!7fuNu62=9N ze}k?Ec$t5WW@3T>1Rwwb2tWV=5P$##AOHafyu$)TKF#djnfAXK@?J^p(AWLZRSmno z{<9(%pP_^^c3!`~I2xZJhcmWcJyFcXXGqbE%JpJ4K0^#=U~Ie&h6ug`rqn598N9eYKE|#kDa0`oa$igRyv6=hl_$-zcPF z@vzFRS0{f|7>LEg8n;&M?@DZ%QIxcQm`@k&Xmr^KU7(;@bm@d_X@;rNMK^T8fgy^L zA<-oTmsfx29sje!Um3b#f&c^{009U<00Izz00bZa0SG`~O9H+}>KmY;|fB*y_009U< z00I!W69gvM6n$v~eWiey{BMG)Dl4+0shVWyl1g90A$sTkar}QL^a<1v0uX=z1Rwwb z2tWV=5P$##ww^$FP?3WF_W$pW|D*H&Th9PBf&c^{009U<00Izz00bZa0SG`K68IN6 Cw3&YZ diff --git a/Events/guildMemberAdd.js b/Events/guildMemberAdd.js index f0b97fd..58692a7 100644 --- a/Events/guildMemberAdd.js +++ b/Events/guildMemberAdd.js @@ -1,56 +1,151 @@ - -const Discord = require('discord.js'); +const { + EmbedBuilder, + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + MessageFlags, + ContainerBuilder, + TextDisplayBuilder, + SectionBuilder, + ThumbnailBuilder, + AttachmentBuilder +} = require("discord.js"); const fs = require("fs"); -const Event = require("../Structures/Event.js"); const yaml = require("js-yaml"); +const Event = require("../Structures/Event.js"); const supportbot = yaml.load( fs.readFileSync("./Configs/supportbot.yml", "utf8") ); - -const cmdconfig = yaml.load( - fs.readFileSync("./Configs/commands.yml", "utf8") -); - const msgconfig = yaml.load( fs.readFileSync("./Configs/messages.yml", "utf8") ); -module.exports = new Event("guildMemberAdd", async (client, member, interaction, guild) => { +function getWelcomeThumbnail(client, member, iconCfg) { + switch ((iconCfg?.Mode || "").toUpperCase()) { + case "USER": + return member.user.displayAvatarURL(); + case "BOT": + return client.user.displayAvatarURL(); + case "CUSTOM": + return iconCfg.CustomURL; + default: + return null; + } +} - if (supportbot.Welcome.Enabled) { - const WelcomeChannel = member.guild.channels.cache.get(supportbot.Welcome.Channel) || - member.guild.channels.cache.find(channel => channel.name === supportbot.Welcome.Channel); +function buildModernComponents(client, member) { + const container = new ContainerBuilder().setAccentColor( + parseInt(msgconfig.Welcome.Modern.Colour.replace("#", ""), 16) + ); - const WelcomeEmbed = new Discord.EmbedBuilder() - .setColor(msgconfig.Welcome.Embed.Colour) - .setTitle(msgconfig.Welcome.Embed.Title) - .setDescription(msgconfig.Welcome.Embed.Message.replace(/%joined_user%/g, member.user)) - .setTimestamp(); - - if (msgconfig.Welcome.Embed.Thumbnail === "BOT") { - WelcomeEmbed.setThumbnail(client.user.displayAvatarURL()) - } else if (msgconfig.Welcome.Embed.Thumbnail === "USER") { - WelcomeEmbed.setThumbnail(member.user.displayAvatarURL()) - } - - if (msgconfig.Welcome.Embed.ImageEnabled) { - WelcomeEmbed.setImage(msgconfig.Welcome.Embed.ImageURL) - } - - if (WelcomeChannel) { - WelcomeChannel.send({ embeds: [WelcomeEmbed] }); - } - - if (supportbot.Roles.AutoRole.Enabled) { - const role = member.guild.roles.cache.get(supportbot.Roles.AutoRole.Role) || - member.guild.roles.cache.find(role => role.name === supportbot.Roles.AutoRole.Role); - if (role) { - member.roles.add(role); - } - } - - console.log(`\u001b[32m`, `[+]`, `\u001b[33m`, `${member.user.username} joined the server!`); + const section = new SectionBuilder().addTextDisplayComponents( + new TextDisplayBuilder().setContent( + msgconfig.Welcome.Modern.Title.replace(/%joined_user%/g, member.user) + ), + new TextDisplayBuilder().setContent( + msgconfig.Welcome.Modern.Body.replace(/%joined_user%/g, member.user) + ) + ); + + const thumbUrl = getWelcomeThumbnail(client, member, msgconfig.Welcome.Modern.Icon); + if (thumbUrl) { + section.setThumbnailAccessory( + new ThumbnailBuilder() + .setURL(thumbUrl) + ); } -}); \ No newline at end of file + container.addSectionComponents(section); + + if (msgconfig.Welcome.Modern.Image?.Enabled && msgconfig.Welcome.Modern.Image.URL) { + container.addTextDisplayComponents( + new TextDisplayBuilder().setContent( + `![image](${msgconfig.Welcome.Modern.Image.URL})` + ) + ); + } + + if (Array.isArray(msgconfig.Welcome.Modern.Buttons)) { + const row = new ActionRowBuilder(); + msgconfig.Welcome.Modern.Buttons.slice(0, 5).forEach(b => { + const btn = new ButtonBuilder() + .setLabel(b.label) + .setStyle(ButtonStyle[b.style]) + .setEmoji(b.emoji || null); + if (b.style === "Link") btn.setURL(b.url); + else if (b.customId) btn.setCustomId(b.customId); + row.addComponents(btn); + }); + + container.addActionRowComponents(row); + } + + return [container]; +} + +module.exports = new Event("guildMemberAdd", async (client, member) => { + if (!supportbot.Welcome.Enabled) return; + + const WelcomeChannel = + member.guild.channels.cache.get(supportbot.Welcome.Channel) || + member.guild.channels.cache.find(c => c.name === supportbot.Welcome.Channel); + if (!WelcomeChannel) return; + + const style = (msgconfig.Welcome.LayoutStyle || "classic").toLowerCase(); + + if (style === "classic") { + const WelcomeEmbed = new EmbedBuilder() + .setColor(msgconfig.Welcome.Embed.Colour) + .setTitle(msgconfig.Welcome.Embed.Title || null) + .setDescription( + msgconfig.Welcome.Embed.Message.replace(/%joined_user%/g, member.user) + ) + .setTimestamp(); + + const thumb = getWelcomeThumbnail(client, member, msgconfig.Welcome.Embed.Icon); + if (thumb) WelcomeEmbed.setThumbnail(thumb); + + if (msgconfig.Welcome.Embed.Image?.Enabled && msgconfig.Welcome.Embed.Image.URL) { + WelcomeEmbed.setImage(msgconfig.Welcome.Embed.Image.URL); + } + + const components = []; + if ( + msgconfig.Welcome.Embed.Buttons?.Enabled && + Array.isArray(msgconfig.Welcome.Embed.Buttons.List) + ) { + const row = new ActionRowBuilder(); + msgconfig.Welcome.Embed.Buttons.List.slice(0, 5).forEach(b => { + const btn = new ButtonBuilder() + .setLabel(b.label) + .setStyle(ButtonStyle[b.style]) + .setEmoji(b.emoji || null); + if (b.style === "Link") btn.setURL(b.url); + else if (b.customId) btn.setCustomId(b.customId); + row.addComponents(btn); + }); + components.push(row); + } + + await WelcomeChannel.send({ embeds: [WelcomeEmbed], components }); + } else if (style === "modern") { + const components = buildModernComponents(client, member); + if (components.length > 0) { + await WelcomeChannel.send({ + components, + flags: MessageFlags.IsComponentsV2 + }); + } + } + + if (supportbot.Roles.AutoRole.Enabled) { + const role = + member.guild.roles.cache.get(supportbot.Roles.AutoRole.Role) || + member.guild.roles.cache.find(r => r.name === supportbot.Roles.AutoRole.Role); + if (role) await member.roles.add(role); + } + + console.log("\u001b[32m", "[+]", "\u001b[33m", `${member.user.username} joined the server!`); + +}); diff --git a/Events/guildMemberRemove.js b/Events/guildMemberRemove.js index 3004af7..f4d1bb2 100644 --- a/Events/guildMemberRemove.js +++ b/Events/guildMemberRemove.js @@ -1,50 +1,40 @@ - -const Discord = require('discord.js'); +const { EmbedBuilder } = require("discord.js"); const fs = require("fs"); -const Event = require("../Structures/Event.js"); const yaml = require("js-yaml"); +const Event = require("../Structures/Event.js"); -const supportbot = yaml.load( - fs.readFileSync("./Configs/supportbot.yml", "utf8") -); - -const cmdconfig = yaml.load( - fs.readFileSync("./Configs/commands.yml", "utf8") -); - -const msgconfig = yaml.load( - fs.readFileSync("./Configs/messages.yml", "utf8") -); +const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); +const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8")); -module.exports = new Event("guildMemberRemove", async (client, member, interaction, guild) => { +module.exports = new Event("guildMemberRemove", async (client, member) => { - if (supportbot.Leave.Enabled) { - const LeaveChannel = member.guild.channels.cache.get(supportbot.Leave.Channel) || - member.guild.channels.cache.find(channel => channel.name === supportbot.Leave.Channel); + if (!supportbot.Leave || !supportbot.Leave.Enabled) return; - const LeaveEmbed = new Discord.EmbedBuilder() - .setColor(msgconfig.Leave.Embed.Colour) - .setTitle(msgconfig.Leave.Embed.Title) - .setDescription(msgconfig.Leave.Embed.Message.replace(/%left_user%/g, member.user)) - .setTimestamp(); - - if (msgconfig.Leave.Embed.Thumbnail === "BOT") { - LeaveEmbed.setThumbnail(client.user.displayAvatarURL()); - } else if (msgconfig.Leave.Embed.Thumbnail === "USER") { - LeaveEmbed.setThumbnail(member.user.displayAvatarURL()); - } - - if (msgconfig.Leave.Embed.ImageEnabled) { - LeaveEmbed.setImage(msgconfig.Leave.Embed.ImageURL); - } - - if (LeaveChannel) { - LeaveChannel.send({ - embeds: [LeaveEmbed] - }); - } - - console.log(`\u001b[31m`, `[-]`, `\u001b[33m`, `${member.user.username} left the server!`); + const leaveChannel = + member.guild.channels.cache.get(supportbot.Leave.Channel) || + member.guild.channels.cache.find(c => c.name === supportbot.Leave.Channel); + + if (!leaveChannel) return; + + const leaveEmbed = new EmbedBuilder() + .setColor(msgconfig.Leave.Embed.Colour) + .setTitle(msgconfig.Leave.Embed.Title) + .setDescription( + msgconfig.Leave.Embed.Message.replace(/%left_user%/g, member.user.toString()) + ) + .setTimestamp(); + + if (msgconfig.Leave.Embed.Thumbnail === "BOT") { + leaveEmbed.setThumbnail(client.user.displayAvatarURL()); + } else if (msgconfig.Leave.Embed.Thumbnail === "USER") { + leaveEmbed.setThumbnail(member.user.displayAvatarURL()); } -}); \ No newline at end of file + if (msgconfig.Leave.Embed.ImageEnabled) { + leaveEmbed.setImage(msgconfig.Leave.Embed.ImageURL); + } + + await leaveChannel.send({ embeds: [leaveEmbed] }); + + console.log(`[-] ${member.user.tag} left the server`); +}); diff --git a/Events/interactionCreate.js b/Events/interactionCreate.js index 2346eeb..a52dd32 100644 --- a/Events/interactionCreate.js +++ b/Events/interactionCreate.js @@ -25,7 +25,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { if (!command) return interaction.reply({ embeds: [NotValid], - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); const permission = interaction.member.permissions.has(command.permissions); @@ -39,7 +39,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { if (!permission) return interaction.reply({ embeds: [ValidPerms], - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); try { @@ -66,7 +66,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { console.error("Error executing command:", error); await interaction.reply({ content: "An error occurred while executing the command.", - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); } } @@ -97,7 +97,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { if (!ticketChannel) { return interaction.reply({ content: "Ticket channel not found.", - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); } try { @@ -107,16 +107,17 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { .setColor(supportbot.Embed.Colours.Success); return interaction.reply({ embeds: [renamedsuccess], - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); } catch (error) { console.error("Error renaming the ticket:", error); return interaction.reply({ content: "There was an error renaming the ticket.", - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); } } + if (interaction.isStringSelectMenu()) { if (interaction.customId === "ticketcontrolpanel") { const selectedOption = interaction.values[0]; @@ -151,7 +152,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { if (!VCcategory) { return interaction.reply({ content: "āš ļø The Support VC category does not exist!", - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); } @@ -343,7 +344,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { .setColor(supportbot.Embed.Colours.Success); await interaction.reply({ embeds: [successEmbed], - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); } else { const errorEmbed = new Discord.EmbedBuilder() @@ -351,7 +352,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { .setColor(supportbot.Embed.Colours.Error); await interaction.reply({ embeds: [errorEmbed], - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); } } catch (error) { @@ -361,7 +362,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { .setColor(supportbot.Embed.Colours.Error); await interaction.reply({ embeds: [errorEmbed], - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); } } else { @@ -370,7 +371,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { .setColor(supportbot.Embed.Colours.Warn); await interaction.reply({ embeds: [errorEmbed], - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); } } @@ -462,7 +463,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { console.error("Error executing the ticket command:", error); await interaction.reply({ content: "An error occurred while creating the ticket.", - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); } } @@ -500,7 +501,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { if (!ticketChannel) { return interaction.reply({ content: "Ticket channel not found.", - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); } @@ -675,7 +676,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { await interaction.reply({ embeds: [embed], - ephemeral: true, + flags: Discord.MessageFlags.Ephemeral, }); } @@ -725,7 +726,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { 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, + flags: Discord.MessageFlags.Ephemeral, }); } } diff --git a/Events/messageDelete.js b/Events/messageDelete.js index 49807f7..5490f9c 100644 --- a/Events/messageDelete.js +++ b/Events/messageDelete.js @@ -3,41 +3,34 @@ const fs = require("fs"); const yaml = require("js-yaml"); const Event = require("../Structures/Event.js"); -// Load the configuration files const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8")); module.exports = new Event("messageDelete", async (client, message) => { + if (!message.guild) return; - // Check if the message is from a guild and not from a bot - if (!message.guild || message.author.bot) return; + const author = message.author || (message.partial ? await message.fetch().then(m => m.author).catch(() => null) : null); + if (!author || author.bot) return; + + const content = message.content || (message.attachments.size > 0 ? "Contains attachment(s)" : "*No content*"); - // Get the delete log channel either by ID or by name const deleteLogChannel = message.guild.channels.cache.get(supportbot.MessageDelete.Channel) || - message.guild.channels.cache.find(channel => channel.name === supportbot.MessageDelete.Channel); + message.guild.channels.cache.find(c => c.name === supportbot.MessageDelete.Channel); - // Create the embed for the deleted message const deletedMessageEmbed = new EmbedBuilder() .setColor(supportbot.MessageDelete.Colour) .setTitle("Message Deleted") - .setDescription(`> **Channel:** <#${message.channel.id}>\n> **Message ID** [${message.id}](https://discord.com/channels/${message.guild.id}/${message.channel.id}/${message.id})\n> **Message author:** ${message.author.tag} (${message.author.id})\n> **Message Crated:** `) - .addFields( - { name: 'Message', value: message.content || '*No content*', inline: false } - ) - .setThumbnail(message.author.displayAvatarURL()) + .setDescription(`> **Channel:** <#${message.channel.id}>\n> **Message ID:** ${message.id}\n> **Message author:** ${author.tag} (${author.id})\n> **Message Created:** `) + .addFields({ name: "Deleted Content", value: content, inline: false }) + .setThumbnail(author.displayAvatarURL()) .setTimestamp(); - // If the message contains an attachment, add it to the embed if (message.attachments.size > 0) { const attachment = message.attachments.first(); - deletedMessageEmbed.addFields({ name: 'Attachment', value: `[${attachment.name}](${attachment.proxyURL})` }); + deletedMessageEmbed.addFields({ name: "Attachment", value: `[${attachment.name}](${attachment.proxyURL})` }); } - // Send the embed to the delete log channel if the channel is found if (deleteLogChannel) { - deleteLogChannel.send({ - embeds: [deletedMessageEmbed] - }); + deleteLogChannel.send({ embeds: [deletedMessageEmbed] }); } }); - diff --git a/Events/ready.js b/Events/ready.js index b4d575d..6921a17 100644 --- a/Events/ready.js +++ b/Events/ready.js @@ -125,4 +125,4 @@ module.exports = new Event("clientReady", async (client) => { setActivity(client, supportbot?.Activity?.Type, supportbot?.Activity?.Status); await logStartup(client); await checkConfigs(client); -}); +}); \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index 0899fec..801ae42 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Emerald Services +Copyright (c) 2025 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 diff --git a/Structures/Client.js b/Structures/Client.js index a1388fe..e3ac602 100644 --- a/Structures/Client.js +++ b/Structures/Client.js @@ -8,6 +8,7 @@ const { Command, Event } = require('./Addon.js'); const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8")); + function walk(dir, ext = ".js", fileList = []) { const files = fs.readdirSync(dir); for (const file of files) { @@ -179,7 +180,7 @@ class Client extends Discord.Client { console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬"); } - this.once("ready", async () => { + this.once("clientReady", async () => { await this.guilds.cache.first()?.commands.set(this.commands); console.log( "\u001b[32m", diff --git a/Structures/Database.js b/Structures/Database.js index 7134056..9b10848 100644 --- a/Structures/Database.js +++ b/Structures/Database.js @@ -6,7 +6,6 @@ const path = require("path"); const db = new Database(path.join(__dirname, "../Data/supportbot.db")); -// ----- Core tables ----- db.prepare(` CREATE TABLE IF NOT EXISTS suggestions ( thread_id TEXT PRIMARY KEY, @@ -62,7 +61,6 @@ db.prepare(` ) `).run(); -/* ---- NEW table for global bot settings ---- */ db.prepare(` CREATE TABLE IF NOT EXISTS settings ( id INTEGER PRIMARY KEY, @@ -71,7 +69,6 @@ db.prepare(` `).run(); module.exports = { - // ----- Suggestions ----- addSuggestion(threadId, authorId, text) { db.prepare(` INSERT INTO suggestions (thread_id, author_id, text) @@ -122,7 +119,6 @@ module.exports = { .run(value ? 1 : 0, userId); }, - // ----- Tickets ----- addTicket(ticket) { const now = Date.now(); db.prepare(` @@ -184,7 +180,6 @@ module.exports = { `).get(); }, - // ----- Ticket participants ----- addUserToTicket(ticketId, userId) { db.prepare(` INSERT INTO ticket_users (ticket_id, user_id) @@ -205,7 +200,6 @@ module.exports = { `).all(ticketId).map(r => r.user_id); }, - // ----- Global Settings ----- getSettings() { const row = db.prepare(`SELECT data FROM settings WHERE id=1`).get(); if (!row) return {}; diff --git a/index.js b/index.js index 7343fe9..7690bda 100644 --- a/index.js +++ b/index.js @@ -8,9 +8,10 @@ // Installed with MIT License // // Discord Support: https://emeraldsrv.dev/discord -// Community Resources: https://emeraldsrv.dev/resources +// Community Resources: https://community.emeraldsrv.dev const fs = require("fs"); +const path = require("path"); const yaml = require("js-yaml"); const supportbot = yaml.load( @@ -24,13 +25,51 @@ const client = new Client({ client.start(supportbot.General.Token); - - - - - - - +// SupportBot - New Logging System + +const logTypes = ["Output", "Warn", "Error"]; + +if (!fs.existsSync("./Logs")) { + fs.mkdirSync("./Logs"); +} +logTypes.forEach((type) => { + const dir = `./Logs/${type}`; + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } +}); + +function logToFile(type, data) { + const date = new Date().toISOString().split("T")[0]; + const file = path.join(`./logs/${type}`, `${type}-${date}.log`); + fs.appendFileSync(file, `[${new Date().toISOString()}] ${data}\n`); +} + +const origLog = console.log; +console.log = (...args) => { + origLog(...args); + logToFile("output", args.join(" ")); +}; + +const origWarn = console.warn; +console.warn = (...args) => { + origWarn(...args); + logToFile("warn", args.join(" ")); +}; + +const origError = console.error; +console.error = (...args) => { + origError(...args); + logToFile("error", args.join(" ")); +}; + +process.on("unhandledRejection", (reason) => { + console.error("Unhandled Rejection:", reason); +}); + +process.on("uncaughtException", (err) => { + console.error("Uncaught Exception:", err); +}); diff --git a/package.json b/package.json index af2d866..eea17b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supportbot", - "version": "8.3", + "version": "8.4", "description": "SupportBot is an open-sourced support system for discord. It's completely free to use with high amounts of customization to match your own brand.", "main": "index.js", "scripts": { @@ -18,8 +18,6 @@ }, "homepage": "https://community.emeraldsrv.dev", "dependencies": { - "@paypal/checkout-server-sdk": "^1.0.3", - "@vitalets/google-translate-api": "^8.0.0", "axios": "^1.7.2", "better-sqlite3": "^12.2.0", "discord.js": "^14.13.0",