Compare commits

...
19 Commits
Author SHA1 Message Date
1tzEmerald c1b76a2b84 Merge pull request #194 from Emerald-Services/dev
8.4 from Dev to Release
2025-09-23 23:26:30 +01:00
1tzEmerald a1429aa723 Merge branch 'release' into dev 2025-09-23 23:26:13 +01:00
1tzEmerald 2de7c7b19c 8.4 2025-09-23 23:23:19 +01:00
1tzEmerald 7383bd2c48 8.4-DEV-2
Added Buttons to WelcomeMessage
Added Components v2 Support for Welcome Message
Added improved logging system
2025-09-22 18:36:04 +01:00
1tzEmerald dc88f6c51a Merge pull request #186 from chorimethod/release
Added "InvalidChannel" message (required by suggest.js), fixed a double quote that was preventing the bot to start correctly.
2025-07-14 16:37:45 +01:00
Nyo d69fb0e26d Update supportbot.yml 2025-05-22 20:30:06 +02:00
chorisancio3d 4b2d53d3f4 Added "InvalidChannel" message (required by suggest.js)
Fixed a double quote that was preventing the bot to start correctly.
2025-02-03 21:43:03 -03:00
1tzEmerald e3fa8b6d72 Merge pull request #185 from Emerald-Services/dev
Update close.js
2024-11-10 13:31:17 +00:00
1tzEmerald 71e33dbf66 Update close.js 2024-11-10 13:30:27 +00:00
1tzEmerald d468ab1575 Delete Data/Profiles directory 2024-11-10 13:18:50 +00:00
1tzEmerald 74482af471 Merge pull request #184 from Emerald-Services/dev
Dev
2024-11-09 13:04:16 +00:00
1tzEmerald 3cdfaef942 Update profile.js 2024-11-09 13:03:41 +00:00
1tzEmerald fcf441a190 Update close.js 2024-11-09 02:46:35 +00:00
1tzEmerald 58eb37124e Update close.js 2024-11-09 02:44:39 +00:00
1tzEmerald afeeda3a8d Merge pull request #183 from Emerald-Services/dev
Dev
2024-11-09 02:38:20 +00:00
1tzEmerald 357a85fa3d Update close.js 2024-11-09 02:37:14 +00:00
1tzEmerald 1bb343e49a Update ticket.js 2024-11-09 02:32:28 +00:00
1tzEmerald dffa5d4161 Update close.js 2024-11-09 02:32:06 +00:00
1tzEmerald 6e14ce209d Merge pull request #182 from Emerald-Services/dev
8.3 Dev -> Release
2024-11-09 00:45:40 +00:00
19 changed files with 740 additions and 376 deletions

No files matched your search

-26
View File
@@ -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],
});
},
});
+45
View File
@@ -0,0 +1,45 @@
// SupportBot | Emerald Services
// Info Command
const fs = require("fs");
const { ContainerBuilder, UserSelectMenuBuilder, ButtonStyle, MessageFlags } = require('discord.js');
const yaml = require("js-yaml");
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
const cmdconfig = yaml.load(
fs.readFileSync("./Configs/commands.yml", "utf8")
);
const msgconfig = yaml.load(
fs.readFileSync("./Configs/messages.yml", "utf8")
);
const db = require("../../Structures/Database.js");
const Command = require("../../Structures/Command.js");
module.exports = new Command({
name: "test",
description: cmdconfig.Info.Description,
options: [],
permissions: cmdconfig.Info.Permission,
async run(interaction, client) {
const ticketPanelembed = new ContainerBuilder()
.addTextDisplayComponents(
textDisplay => textDisplay
.setContent("SupportBot"),
)
await interaction.reply({
components: [ticketPanelembed],
flags: MessageFlags.IsComponentsV2,
});
},
});
+175 -113
View File
@@ -288,119 +288,181 @@ function parseMarkdown(content) {
function createTranscriptHTML(ticket, reason) { function createTranscriptHTML(ticket, reason) {
return ` return `
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet"> <title>Ticket Transcript</title>
<style> <link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
body { <style>
background-color: #1a1a1a; body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
color: white; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0; color: #e5e7eb;
padding: 20px; margin: 0;
} padding: 40px;
.container { }
max-width: 1000px;
margin: 0 auto; .container {
background-color: rgba(0, 0, 0, 0.3); max-width: 1000px;
border-radius: 15px; margin: auto;
padding: 20px; padding: 25px;
} border-radius: 20px;
.message { backdrop-filter: blur(18px) saturate(180%);
background-color: rgba(255, 255, 255, 0.1); background: rgba(17, 25, 40, 0.75);
border-radius: 10px; box-shadow: 0 8px 32px rgba(0,0,0,0.6);
padding: 15px; border: 1px solid rgba(255, 255, 255, 0.1);
margin-bottom: 15px; }
}
.message-header { h1 {
display: flex; font-size: 2rem;
align-items: center; font-weight: 700;
margin-bottom: 10px; text-align: center;
} color: #22c55e; /* green accent */
.avatar { margin-bottom: 20px;
width: 40px; text-shadow: 0 0 4px rgba(34,197,94,0.4); /* subtle glow */
height: 40px; }
border-radius: 50%;
margin-right: 15px; .ticket-info {
} margin-bottom: 25px;
.username { padding: 15px;
font-weight: bold; border-radius: 12px;
color: #4CAF50; background: rgba(255, 255, 255, 0.05);
} border: 1px solid rgba(255,255,255,0.1);
.timestamp { }
margin-left: auto;
color: #888; .ticket-info p {
font-size: 0.9em; margin: 6px 0;
} font-size: 0.95rem;
.content { }
word-break: break-word;
} .message {
.embed { background: rgba(255, 255, 255, 0.05);
border-left: 4px solid #4CAF50; border: 1px solid rgba(255, 255, 255, 0.1);
padding-left: 10px; border-radius: 15px;
margin: 10px 0; padding: 16px;
background-color: rgba(0, 0, 0, 0.2); margin-bottom: 15px;
} transition: all 0.2s ease-in-out;
.attachment { }
display: inline-block;
background-color: rgba(76, 175, 80, 0.1); .message:hover {
border: 1px solid #4CAF50; background: rgba(255, 255, 255, 0.08);
border-radius: 5px; transform: translateY(-2px);
padding: 5px 10px; box-shadow: 0 6px 20px rgba(0,0,0,0.4);
margin: 5px 0; }
color: #4CAF50;
text-decoration: none; .message-header {
} display: flex;
</style> align-items: center;
</head> margin-bottom: 12px;
<body> }
<div class="container">
<div class="mb-6"> .avatar {
<h1 class="text-3xl font-bold mb-4">Ticket Transcript</h1> width: 45px;
<p><strong>Channel:</strong> ${ticket.name}</p> height: 45px;
<p><strong>Ticket ID:</strong> ${ticket.id}</p> border-radius: 50%;
<p><strong>Message Count:</strong> ${ticket.messages.length}</p> margin-right: 12px;
<p><strong>Close Reason:</strong> ${reason}</p> border: 2px solid rgba(34,197,94,0.6);
</div> }
<div class="messages"> .username {
${ticket.messages.map(msg => ` font-weight: 600;
<div class="message"> color: #22c55e;
<div class="message-header"> }
<img src="${msg.avatar}" alt="Avatar" class="avatar">
<span class="username">${msg.username}</span> .timestamp {
<span class="timestamp">${new Date(msg.timestamp).toLocaleString()}</span> margin-left: auto;
</div> font-size: 0.85rem;
<div class="content"> color: #9ca3af;
${parseMarkdown(msg.content)} }
${msg.embeds.map(embed => ` .content {
<div class="embed"> word-break: break-word;
${embed.title ? `<div class="font-bold">${embed.title}</div>` : ''} font-size: 0.95rem;
${embed.description ? `<div>${embed.description}</div>` : ''} line-height: 1.4rem;
${embed.fields.map(field => ` }
<div class="mt-2">
<strong>${field.name}:</strong> .embed {
<div>${field.value}</div> margin-top: 12px;
</div> border-left: 4px solid #22c55e;
`).join('')} padding-left: 12px;
</div> border-radius: 6px;
`).join('')} background: rgba(34,197,94,0.1);
}
${msg.attachments.map(att => `
<a href="${att.url}" class="attachment" target="_blank"> .embed strong {
📎 ${att.name} color: #f9fafb;
</a> }
`).join('')}
</div> .attachment {
</div> display: inline-block;
`).join('')} background: rgba(34,197,94,0.1);
</div> border: 1px solid #22c55e;
border-radius: 6px;
padding: 6px 12px;
margin: 6px 0;
color: #22c55e;
text-decoration: none;
font-size: 0.9rem;
}
.attachment:hover {
background: rgba(34,197,94,0.2);
}
code, pre {
background: rgba(0,0,0,0.4);
color: #f8fafc;
padding: 4px 8px;
border-radius: 6px;
font-family: 'Consolas', 'Monaco', monospace;
}
</style>
</head>
<body>
<div class="container">
<h1>Ticket Transcript</h1>
<div class="ticket-info">
<p><strong>Channel:</strong> ${ticket.name}</p>
<p><strong>Ticket ID:</strong> ${ticket.id}</p>
<p><strong>Message Count:</strong> ${ticket.messages.length}</p>
<p><strong>Close Reason:</strong> ${reason}</p>
</div> </div>
</body>
</html> <div class="messages">
${ticket.messages.map(msg => `
<div class="message">
<div class="message-header">
<img src="${msg.avatar}" alt="Avatar" class="avatar">
<span class="username">${msg.username}</span>
<span class="timestamp">${new Date(msg.timestamp).toLocaleString()}</span>
</div>
<div class="content">
${parseMarkdown(msg.content)}
${msg.embeds.map(embed => `
<div class="embed">
${embed.title ? `<div class="font-bold">${embed.title}</div>` : ''}
${embed.description ? `<div>${embed.description}</div>` : ''}
${embed.fields.map(field => `
<div class="mt-2">
<strong>${field.name}:</strong>
<div>${field.value}</div>
</div>
`).join('')}
</div>
`).join('')}
${msg.attachments.map(att => `
<a href="${att.url}" class="attachment" target="_blank">📎 ${att.name}</a>
`).join('')}
</div>
</div>
`).join('')}
</div>
</div>
</body>
</html>
`; `;
} }
+60 -26
View File
@@ -11,6 +11,9 @@ const {
ButtonStyle, ButtonStyle,
MessageCollector, MessageCollector,
MessageFlags, MessageFlags,
SeparatorBuilder,
ContainerBuilder,
TextDisplayBuilder
} = require("discord.js"); } = require("discord.js");
const yaml = require("js-yaml"); const yaml = require("js-yaml");
@@ -455,32 +458,57 @@ module.exports = new Command({
.setColor(supportbot.Embed.Colours.General); .setColor(supportbot.Embed.Colours.General);
await interaction.reply({ embeds: [CreatedTicket], flags: MessageFlags.Ephemeral }); await interaction.reply({ embeds: [CreatedTicket], flags: MessageFlags.Ephemeral });
const TicketMessage = new EmbedBuilder() // TICKET MESSAGE - UPDATED WITH COMPONENTS V2
.setAuthor({
name: msgconfig.Ticket.TicketAuthorTitle.replace(/%ticketauthor%/g, interaction.user.id) const ticketMsgContainer = new ContainerBuilder()
.replace(/%ticketid%/g, ticketChannel.id)
.replace(/%ticketusername%/g, interaction.user.username), if (supportbot.Embed.Colours.General) {
iconURL: interaction.user.displayAvatarURL(), ticketMsgContainer.setAccentColor(
}) parseInt(supportbot.Embed.Colours.General.replace("#", ""), 16)
.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)
.replace(/%ticketreason%/g, TicketSubject)
)
.setColor(supportbot.Embed.Colours.General);
if (supportbot.Ticket.TicketReason && TicketReason) {
TicketMessage.addFields(
{ name: "Reason", value: TicketReason, inline: false }
);
} }
const ticketAuthor = new TextDisplayBuilder().setContent(
msgconfig.Ticket.TicketAuthorTitle.replace(/%ticketauthor%/g, interaction.user.id)
.replace(/%ticketid%/g, ticketChannel.id)
.replace(/%ticketusername%/g, interaction.user.username),
);
ticketMsgContainer.addTextDisplayComponents(ticketAuthor)
const ticketTitle = new TextDisplayBuilder().setContent(
msgconfig.Ticket.TicketTitle.replace(/%ticketauthor%/g, interaction.user.id)
.replace(/%ticketid%/g, ticketChannel.id)
.replace(/%ticketusername%/g, interaction.user.username)
);
ticketMsgContainer.addTextDisplayComponents(ticketTitle)
const seperator2 = new SeparatorBuilder()
.setDivider(true)
ticketMsgContainer.addSeparatorComponents(seperator2)
const ticketDescription = new TextDisplayBuilder().setContent(
msgconfig.Ticket.TicketMessage.replace(/%ticketauthor%/g, interaction.user.id)
.replace(/%ticketid%/g, ticketChannel.id)
.replace(/%ticketusername%/g, interaction.user.username)
.replace(/%ticketreason%/g, TicketSubject)
);
ticketMsgContainer.addTextDisplayComponents(ticketDescription)
const seperator3 = new SeparatorBuilder()
.setDivider(true)
ticketMsgContainer.addSeparatorComponents(seperator3)
const ticketReason = new TextDisplayBuilder().setContent(
`**Reason:**\n ${TicketReason}`
);
const selectMenuRow = new ActionRowBuilder();
const SelectMenus = new StringSelectMenuBuilder() const SelectMenus = new StringSelectMenuBuilder()
.setCustomId("ticketcontrolpanel") .setCustomId("ticketcontrolpanel")
.setPlaceholder("Ticket Control Panel") .setPlaceholder("Ticket Control Panel")
@@ -529,11 +557,17 @@ module.exports = new Command({
); );
} }
const row2 = new ActionRowBuilder().addComponents(SelectMenus); selectMenuRow.addComponents(SelectMenus)
ticketMsgContainer.addActionRowComponents(selectMenuRow)
if (supportbot.Ticket.TicketReason && TicketReason) {
ticketMsgContainer.addTextDisplayComponents(ticketReason)
}
await ticketChannel.send({ await ticketChannel.send({
embeds: [TicketMessage], flags: MessageFlags.IsComponentsV2,
components: [row2], components: [ticketMsgContainer],
}); });
if (supportbot.Ticket.Questions.Enabled) { if (supportbot.Ticket.Questions.Enabled) {
+84 -16
View File
@@ -5,6 +5,12 @@ const {
ActionRowBuilder, ActionRowBuilder,
ButtonBuilder, ButtonBuilder,
MessageFlags, MessageFlags,
ContainerBuilder,
TextDisplayBuilder,
MediaGalleryBuilder,
SeparatorBuilder,
ThumbnailBuilder,
SectionBuilder
} = require("discord.js"); } = require("discord.js");
const yaml = require("js-yaml"); const yaml = require("js-yaml");
@@ -52,6 +58,7 @@ module.exports = new Command({
} }
const panelRow = db.getTicketPanel(); const panelRow = db.getTicketPanel();
if (panelRow) { if (panelRow) {
try { try {
await channel.messages.fetch(panelRow.message_id); await channel.messages.fetch(panelRow.message_id);
@@ -62,24 +69,35 @@ module.exports = new Command({
} catch (err) {} } catch (err) {}
} }
let embed = new EmbedBuilder() const panelContainer = new ContainerBuilder()
.setTitle(panelconfig.PanelTitle)
.setColor(panelconfig.PanelColour)
.setFooter({
text: supportbot.Embed.Footer,
iconURL: interaction.user.displayAvatarURL(),
});
if (panelconfig.TicketPanel_Description) { if (panelconfig.Style.Color) {
embed.setDescription(panelconfig.PanelMessage); panelContainer.setAccentColor(
parseInt(panelconfig.Style.Color.replace("#", ""), 16)
)
} }
if (panelconfig.TicketPanel_Thumbnail) { const panelTitle = new TextDisplayBuilder().setContent(
embed.setThumbnail(panelconfig.PanelThumbnail); panelconfig.Style.Title,
);
const topSeperator = new SeparatorBuilder()
.setDivider(true)
if (panelconfig.Settings.TopTitle) {
panelContainer.addTextDisplayComponents(panelTitle)
} }
if (panelconfig.TicketPanel_Image) { if (panelconfig.Settings.TopDivider) {
embed.setImage(panelconfig.PanelImage); panelContainer.addSeparatorComponents(topSeperator)
}
const panelDesc = new TextDisplayBuilder().setContent(
panelconfig.Style.Description,
);
if (panelconfig.Style.Layout === "3") {
panelContainer.addTextDisplayComponents(panelDesc)
} }
const createTicketButton = new ButtonBuilder() const createTicketButton = new ButtonBuilder()
@@ -88,12 +106,62 @@ module.exports = new Command({
.setEmoji(panelconfig.Button.Emoji) .setEmoji(panelconfig.Button.Emoji)
.setStyle(panelconfig.Button.Color); .setStyle(panelconfig.Button.Color);
let row = new ActionRowBuilder().addComponents(createTicketButton); const middleSection = new SectionBuilder()
.addTextDisplayComponents(panelDesc)
.setButtonAccessory(createTicketButton)
if (panelconfig.Style.Layout === "1") {
panelContainer.addSectionComponents(middleSection)
}
const panelImage = new MediaGalleryBuilder()
.addItems([
{
media: {
url: panelconfig.Style.Image,
},
}
])
if (panelconfig.Settings.Image) {
panelContainer.addMediaGalleryComponents(panelImage)
}
// START OF STYLE 2 - TEXT UNDER THE IMAGE
const bottomSeperator = new SeparatorBuilder()
.setDivider(true)
if (panelconfig.Settings.BottomTitle) {
panelContainer.addTextDisplayComponents(panelTitle)
}
if (panelconfig.Settings.BottomDivider) {
panelContainer.addSeparatorComponents(bottomSeperator)
}
if (panelconfig.Style.Layout === "2") {
panelContainer.addSectionComponents(middleSection)
}
// START OF STYLE 3
const buttonRow = new ActionRowBuilder();
const ticketButtonRow = new ButtonBuilder()
.setCustomId("createticket")
.setLabel(panelconfig.Button.Text)
.setEmoji(panelconfig.Button.Emoji)
.setStyle(panelconfig.Button.Color);
buttonRow.addComponents(ticketButtonRow)
if (panelconfig.Style.Layout === "3") {
panelContainer.addActionRowComponents(buttonRow)
}
try { try {
const message = await channel.send({ const message = await channel.send({
embeds: [embed], flags: MessageFlags.IsComponentsV2,
components: [row], components: [panelContainer],
}); });
db.saveTicketPanel(message.id, channel.id); db.saveTicketPanel(message.id, channel.id);
+69 -17
View File
@@ -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." 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" Button: "Visit Site"
URL: "https://docs.emeraldsrv.com" URL: "https://docs.emeraldsrv.com"
Colour: "#f74343" Colour: "#45b463"
# ---------------------------- # ----------------------------
# Welcome/Leave Messages # Welcome/Leave Messages
# ---------------------------- # ----------------------------
Welcome: Welcome:
Embed: Enabled: true
Colour: "#f74343" LayoutStyle: "modern"
Title: "Welcome to ServerName" # Available Layout Styles
Message: ":smile: %joined_user% has just landed!" # "modern" - For the new Discord v2 components
Thumbnail: "BOT" # "classic" - Use the classic Discord look.
ImageEnabled: false Embed:
ImageURL: "https://animagelink.png/" 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: Leave:
Embed: Embed:
Colour: "#f74343" Colour: "#f74343"
Title: "Goodbye!" Title: "Goodbye!"
Message: ":slight_frown: %joined_user% has recently dropped out!" Message: ":slight_frown: %left_user% has recently dropped out!"
Thumbnail: "BOT" Thumbnail: "BOT"
ImageEnabled: false ImageEnabled: false
@@ -70,8 +117,18 @@ Ticket:
# <@%ticketauthor%> - Tags the user who created the ticket. # <@%ticketauthor%> - Tags the user who created the ticket.
# <#%ticketid%> - Mentions the ticket channel. # <#%ticketid%> - Mentions the ticket channel.
TicketAuthorTitle: "%ticketusername%'s Ticket" # You can use Markdown formatting in this message:
TicketTitle: "Support Ticket" # # Heading 1
# ## Heading 2
# ### Heading 3
# **Bold** text
# *Italic* text
# -# Small text
# - Bullet points
# Links: [text](https://example.com)
TicketAuthorTitle: "-# %ticketusername%'s Ticket"
TicketTitle: "## Support Ticket"
TicketMessage: "Hello <@%ticketauthor%>, Thank you for creating a support ticket\nPlease wait patiently whilst a member of our team reaches out to you." TicketMessage: "Hello <@%ticketauthor%>, Thank you for creating a support ticket\nPlease wait patiently whilst a member of our team reaches out to you."
TicketThumbnail: "https://images.emojiterra.com/google/android-pie/512px/1f39f.png" TicketThumbnail: "https://images.emojiterra.com/google/android-pie/512px/1f39f.png"
TicketCreatedAlert: ":ticket: Ticked Created: <#%ticketid%>" TicketCreatedAlert: ":ticket: Ticked Created: <#%ticketid%>"
@@ -268,9 +325,4 @@ Error:
EntryAlreadyExists: ":warning: Cannot complete the action, as the entry already exists." EntryAlreadyExists: ":warning: Cannot complete the action, as the entry already exists."
EntryDoesNotExist: ":warning: Cannot complete the action, as the entry does not exist." 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." ActionFailed: ":x: **Error!** An error occurred while processing your request. Please try again later."
InvalidChannel: ":x: **Error!** The suggestions channel has not been set up correctly."
+32 -32
View File
@@ -8,12 +8,12 @@
# Installed with MIT License # Installed with MIT License
# #
# Discord Support: https://discord.gg/emeraldsrv # Discord Support: https://discord.gg/emeraldsrv
# Official Documentation: https://docs.emeraldsrv.com # Official Documentation: https://docs.emeraldsrv.dev
# Addons Marketplace: https://community.emeraldsrv.com # Addons Marketplace: https://community.emeraldsrv.dev
# #
# SupportBot Configuration (supportbot.yml) # SupportBot Configuration (supportbot.yml)
SupportBot_Version: "v8.4-DEV-1" SupportBot_Version: "v8.4"
# ------------------------- # -------------------------
# General # General
@@ -50,10 +50,10 @@ Activity:
Embed: Embed:
Colours: Colours:
General: 0x0cc559 General: "#0cc559"
Success: 0x04b545 Success: "#04b545"
Error: 0xd13030 Error: "#d13030"
Warn: 0xfca117 Warn: "#fca117"
Footer: "SupportBot | Developed by Emerald Development" Footer: "SupportBot | Developed by Emerald Development"
@@ -66,12 +66,12 @@ Embed:
Roles: Roles:
AutoRole: AutoRole:
Enabled: true Enabled: true
Role: "1202669457751801917" Role: "1111111111111111"
StaffMember: StaffMember:
Staff: "996420644373811371" Staff: "1111111111111111"
Admin: "996419960442208326" Admin: "1111111111111111"
Moderator: "996420644373811371" Moderator: "1111111111111111"
Mod: Mod:
AllowSupportStaff: false AllowSupportStaff: false
@@ -84,11 +84,11 @@ Roles:
Welcome: Welcome:
Enabled: true Enabled: true
Channel: "1295788797065166869" Channel: "1111111111111111"
Leave: Leave:
Enabled: true Enabled: true
Channel: "1295788797065166869" Channel: "1111111111111111"
# ---------------------------- # ----------------------------
# Suggestions # Suggestions
@@ -96,8 +96,8 @@ Leave:
Suggestions: Suggestions:
Mode: "Channel" # Channel or Forum Mode: "Channel" # Channel or Forum
ForumChannel: "1402655496477081671" ForumChannel: "1111111111111111"
Channel: "1295788900094316706" Channel: "1111111111111111"
UpvoteEmoji: "✅" UpvoteEmoji: "✅"
DownvoteEmoji: "❌" DownvoteEmoji: "❌"
@@ -130,10 +130,10 @@ Suggestions:
# 4 - Red # 4 - Red
Tags: Tags:
Created: "1418937323118137435" Created: "1111111111111111"
Considering: "1418937237156139130" Considering: "1111111111111111"
Solved: "1418937168029679697" Solved: "1111111111111111"
Denied: "1418937185469464647" Denied: "1111111111111111"
# ------------------------- # -------------------------
# Tickets # Tickets
@@ -141,25 +141,25 @@ Suggestions:
VoiceTickets: VoiceTickets:
Name: "%username%'s VC" Name: "%username%'s VC"
Category: "1419062819688550460" Category: "1111111111111111"
Ticket: Ticket:
TicketType: "channels" TicketType: "threads"
# Available TicketTypes: # Available TicketTypes:
# channels - Tickets will be created as channels in the TicketChannelsCategory # channels - Tickets will be created as channels in the TicketChannelsCategory
# threads - Tickets will be created as threads through the "TicketHome" channel. # threads - Tickets will be created as threads through the "TicketHome" channel.
TicketHome: "1295784943204700171" TicketHome: "1111111111111111"
# What is TicketHome? # What is TicketHome?
# - Channel where the Ticket creation panel is sent. # - Channel where the Ticket creation panel is sent.
# - Channel where ticket threads are made. (only if ticket threads is enabled above.) # - Channel where ticket threads are made. (only if ticket threads is enabled above.)
# For TicketType: channels # For TicketType: channels
TicketChannelsCategory: "1418988408193155112" TicketChannelsCategory: "1111111111111111"
TicketChannelsCategory2: "1418988456167870615" # This will be used if the first category has reached the max channel limit. TicketChannelsCategory2: "1111111111111111" # This will be used if the first category has reached the max channel limit.
TicketArchiveCategory: "1418988434600759348" TicketArchiveCategory: "1111111111111111"
# --- # ---
Channel: "ticket-" # The prefix for ticket channel names Channel: "ticket-" # The prefix for ticket channel names
@@ -198,7 +198,7 @@ Ticket:
ClaimTickets: ClaimTickets:
Enabled: true Enabled: true
Channel: "1295789389619793961" Channel: "1111111111111111"
# Channel where the ticket assignments are sent for staff to claim. # Channel where the ticket assignments are sent for staff to claim.
ClaimAcceptTime: 120000 ClaimAcceptTime: 120000
@@ -221,7 +221,7 @@ Ticket:
ReviewSystem: ReviewSystem:
Enabled: false Enabled: false
Channel: "1304480728884445225" Channel: "1111111111111111"
UseModal: true UseModal: true
Questions: Questions:
@@ -232,9 +232,9 @@ Ticket:
- "Have you tried resolving the issue yourself?" - "Have you tried resolving the issue yourself?"
Log: Log:
TicketDataLog: "1339271338761650290" TicketDataLog: "1111111111111111"
TickeDataTitle: "Ticket Data" TickeDataTitle: "Ticket Data"
TicketBlacklistLog: "1339271338761650290" TicketBlacklistLog: "1111111111111111"
# ------------------------- # -------------------------
# Buttons # Buttons
@@ -287,12 +287,12 @@ SelectMenus:
# ------------------------- # -------------------------
MessageDelete: MessageDelete:
Channel: "1202671982932402246" Channel: "1111111111111111"
Colour: "#d92525" Colour: "#d92525"
MessageUpdate: MessageUpdate:
Channel: "1202671982932402246" Channel: "1111111111111111"
Colour: "#f2a024" Colour: "#f2a024"
Translate: Translate:
TranslateLog: "1202671982932402246" TranslateLog: "1111111111111111"
+25 -9
View File
@@ -23,13 +23,24 @@ Panel: true
# Ticket Panel Message Configuration #2b2d31 # Ticket Panel Message Configuration #2b2d31
# ---------------------------------------- # ----------------------------------------
PanelTitle: "SupportBot" Style:
PanelColour: "#0cc559" Title: "## SupportBot Ticket Creation"
Description: "Click on the button to file a `Ticket` and our team will try their best to assist you in the best way we can!"
Image: "https://i.imgur.com/dejQvkg.png"
Color: "#45b463"
# Leave Color empty to remove the color from the embed/container.
Layout: "3"
# Available Layout Styles
# "1" - Style 1 - Title & Text, Button at the top -- Image at the bottom
# "2" - Style 2 - Title & Text, Button at the bottom -- Image at the top
# "3" - Style 3 - The classic look - Title & Text at the top -- Image in the middle -- Button at the bottom
Button: Button:
Text: "Create Ticket" Text: "Create Ticket"
Emoji: "🎫" Emoji: "🎫"
Color: "2" Color: "3"
# Available Button Styles # Available Button Styles
# 1 - Blurple # 1 - Blurple
@@ -37,10 +48,15 @@ Button:
# 3 - Red # 3 - Red
# 4 - Green # 4 - Green
TicketPanel_Description: true Settings:
TicketPanel_Thumbnail: false Image: true
TicketPanel_Image: true
PanelMessage: "Click on the button to file a `Ticket` and our team will try their best to assist you in the best way we can!" # Text and divider will appear above the image.
PanelThumbnail: "https://i.imgur.com/ieeZejl.png" TopTitle: true
PanelImage: "https://i.imgur.com/dejQvkg.png" TopDivider: true
# Text and divider will appear below the image.
BottomTitle: false
BottomDivider: false
# Note: If Image is set to false, the bottom and top settings will look the samme.
Binary file not shown.
+139 -42
View File
@@ -1,56 +1,153 @@
const {
const Discord = require('discord.js'); EmbedBuilder,
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
MessageFlags,
ContainerBuilder,
TextDisplayBuilder,
SectionBuilder,
ThumbnailBuilder,
AttachmentBuilder
} = require("discord.js");
const fs = require("fs"); const fs = require("fs");
const Event = require("../Structures/Event.js");
const yaml = require("js-yaml"); const yaml = require("js-yaml");
const Event = require("../Structures/Event.js");
const supportbot = yaml.load( const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8") fs.readFileSync("./Configs/supportbot.yml", "utf8")
); );
const cmdconfig = yaml.load(
fs.readFileSync("./Configs/commands.yml", "utf8")
);
const msgconfig = yaml.load( const msgconfig = yaml.load(
fs.readFileSync("./Configs/messages.yml", "utf8") 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) { function buildModernComponents(client, member) {
const WelcomeChannel = member.guild.channels.cache.get(supportbot.Welcome.Channel) || const container = new ContainerBuilder()
member.guild.channels.cache.find(channel => channel.name === supportbot.Welcome.Channel); .setAccentColor(
parseInt(msgconfig.Welcome.Modern.Colour.replace("#", ""), 16)
);
const WelcomeEmbed = new Discord.EmbedBuilder() const section = new SectionBuilder().addTextDisplayComponents(
.setColor(msgconfig.Welcome.Embed.Colour) new TextDisplayBuilder().setContent(
.setTitle(msgconfig.Welcome.Embed.Title) msgconfig.Welcome.Modern.Title.replace(/%joined_user%/g, member.user)
.setDescription(msgconfig.Welcome.Embed.Message.replace(/%joined_user%/g, member.user)) ),
.setTimestamp(); new TextDisplayBuilder().setContent(
msgconfig.Welcome.Modern.Body.replace(/%joined_user%/g, member.user)
if (msgconfig.Welcome.Embed.Thumbnail === "BOT") { )
WelcomeEmbed.setThumbnail(client.user.displayAvatarURL()) );
} else if (msgconfig.Welcome.Embed.Thumbnail === "USER") {
WelcomeEmbed.setThumbnail(member.user.displayAvatarURL()) const thumbUrl = getWelcomeThumbnail(client, member, msgconfig.Welcome.Modern.Icon);
} if (thumbUrl) {
section.setThumbnailAccessory(
if (msgconfig.Welcome.Embed.ImageEnabled) { new ThumbnailBuilder()
WelcomeEmbed.setImage(msgconfig.Welcome.Embed.ImageURL) .setURL(thumbUrl)
} );
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!`);
} }
}); 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!`);
});
+32 -42
View File
@@ -1,50 +1,40 @@
const { EmbedBuilder } = require("discord.js");
const Discord = require('discord.js');
const fs = require("fs"); const fs = require("fs");
const Event = require("../Structures/Event.js");
const yaml = require("js-yaml"); const yaml = require("js-yaml");
const Event = require("../Structures/Event.js");
const supportbot = yaml.load( const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
fs.readFileSync("./Configs/supportbot.yml", "utf8") const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
);
const cmdconfig = yaml.load(
fs.readFileSync("./Configs/commands.yml", "utf8")
);
const 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) { if (!supportbot.Leave || !supportbot.Leave.Enabled) return;
const LeaveChannel = member.guild.channels.cache.get(supportbot.Leave.Channel) ||
member.guild.channels.cache.find(channel => channel.name === supportbot.Leave.Channel);
const LeaveEmbed = new Discord.EmbedBuilder() const leaveChannel =
.setColor(msgconfig.Leave.Embed.Colour) member.guild.channels.cache.get(supportbot.Leave.Channel) ||
.setTitle(msgconfig.Leave.Embed.Title) member.guild.channels.cache.find(c => c.name === supportbot.Leave.Channel);
.setDescription(msgconfig.Leave.Embed.Message.replace(/%left_user%/g, member.user))
.setTimestamp(); if (!leaveChannel) return;
if (msgconfig.Leave.Embed.Thumbnail === "BOT") { const leaveEmbed = new EmbedBuilder()
LeaveEmbed.setThumbnail(client.user.displayAvatarURL()); .setColor(msgconfig.Leave.Embed.Colour)
} else if (msgconfig.Leave.Embed.Thumbnail === "USER") { .setTitle(msgconfig.Leave.Embed.Title)
LeaveEmbed.setThumbnail(member.user.displayAvatarURL()); .setDescription(
} msgconfig.Leave.Embed.Message.replace(/%left_user%/g, member.user.toString())
)
if (msgconfig.Leave.Embed.ImageEnabled) { .setTimestamp();
LeaveEmbed.setImage(msgconfig.Leave.Embed.ImageURL);
} if (msgconfig.Leave.Embed.Thumbnail === "BOT") {
leaveEmbed.setThumbnail(client.user.displayAvatarURL());
if (LeaveChannel) { } else if (msgconfig.Leave.Embed.Thumbnail === "USER") {
LeaveChannel.send({ leaveEmbed.setThumbnail(member.user.displayAvatarURL());
embeds: [LeaveEmbed]
});
}
console.log(`\u001b[31m`, `[-]`, `\u001b[33m`, `${member.user.username} left the server!`);
} }
}); if (msgconfig.Leave.Embed.ImageEnabled) {
leaveEmbed.setImage(msgconfig.Leave.Embed.ImageURL);
}
await leaveChannel.send({ embeds: [leaveEmbed] });
console.log(`[-] ${member.user.tag} left the server`);
});
+16 -15
View File
@@ -25,7 +25,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
if (!command) if (!command)
return interaction.reply({ return interaction.reply({
embeds: [NotValid], embeds: [NotValid],
ephemeral: true, flags: Discord.MessageFlags.Ephemeral,
}); });
const permission = interaction.member.permissions.has(command.permissions); const permission = interaction.member.permissions.has(command.permissions);
@@ -39,7 +39,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
if (!permission) if (!permission)
return interaction.reply({ return interaction.reply({
embeds: [ValidPerms], embeds: [ValidPerms],
ephemeral: true, flags: Discord.MessageFlags.Ephemeral,
}); });
try { try {
@@ -66,7 +66,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
console.error("Error executing command:", error); console.error("Error executing command:", error);
await interaction.reply({ await interaction.reply({
content: "An error occurred while executing the command.", 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) { if (!ticketChannel) {
return interaction.reply({ return interaction.reply({
content: "Ticket channel not found.", content: "Ticket channel not found.",
ephemeral: true, flags: Discord.MessageFlags.Ephemeral,
}); });
} }
try { try {
@@ -107,16 +107,17 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
.setColor(supportbot.Embed.Colours.Success); .setColor(supportbot.Embed.Colours.Success);
return interaction.reply({ return interaction.reply({
embeds: [renamedsuccess], embeds: [renamedsuccess],
ephemeral: true, flags: Discord.MessageFlags.Ephemeral,
}); });
} catch (error) { } catch (error) {
console.error("Error renaming the ticket:", error); console.error("Error renaming the ticket:", error);
return interaction.reply({ return interaction.reply({
content: "There was an error renaming the ticket.", content: "There was an error renaming the ticket.",
ephemeral: true, flags: Discord.MessageFlags.Ephemeral,
}); });
} }
} }
if (interaction.isStringSelectMenu()) { if (interaction.isStringSelectMenu()) {
if (interaction.customId === "ticketcontrolpanel") { if (interaction.customId === "ticketcontrolpanel") {
const selectedOption = interaction.values[0]; const selectedOption = interaction.values[0];
@@ -151,7 +152,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
if (!VCcategory) { if (!VCcategory) {
return interaction.reply({ return interaction.reply({
content: "⚠️ The Support VC category does not exist!", 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); .setColor(supportbot.Embed.Colours.Success);
await interaction.reply({ await interaction.reply({
embeds: [successEmbed], embeds: [successEmbed],
ephemeral: true, flags: Discord.MessageFlags.Ephemeral,
}); });
} else { } else {
const errorEmbed = new Discord.EmbedBuilder() const errorEmbed = new Discord.EmbedBuilder()
@@ -351,7 +352,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
.setColor(supportbot.Embed.Colours.Error); .setColor(supportbot.Embed.Colours.Error);
await interaction.reply({ await interaction.reply({
embeds: [errorEmbed], embeds: [errorEmbed],
ephemeral: true, flags: Discord.MessageFlags.Ephemeral,
}); });
} }
} catch (error) { } catch (error) {
@@ -361,7 +362,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
.setColor(supportbot.Embed.Colours.Error); .setColor(supportbot.Embed.Colours.Error);
await interaction.reply({ await interaction.reply({
embeds: [errorEmbed], embeds: [errorEmbed],
ephemeral: true, flags: Discord.MessageFlags.Ephemeral,
}); });
} }
} else { } else {
@@ -370,7 +371,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
.setColor(supportbot.Embed.Colours.Warn); .setColor(supportbot.Embed.Colours.Warn);
await interaction.reply({ await interaction.reply({
embeds: [errorEmbed], 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); console.error("Error executing the ticket command:", error);
await interaction.reply({ await interaction.reply({
content: "An error occurred while creating the ticket.", 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) { if (!ticketChannel) {
return interaction.reply({ return interaction.reply({
content: "Ticket channel not found.", 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({ await interaction.reply({
embeds: [embed], 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); console.error("Failed to update profile data: ", error);
await interaction.reply({ await interaction.reply({
content: "There was an error while updating your profile. Please try again later.", content: "There was an error while updating your profile. Please try again later.",
ephemeral: true, flags: Discord.MessageFlags.Ephemeral,
}); });
} }
} }
+11 -18
View File
@@ -3,41 +3,34 @@ const fs = require("fs");
const yaml = require("js-yaml"); const yaml = require("js-yaml");
const Event = require("../Structures/Event.js"); const Event = require("../Structures/Event.js");
// Load the configuration files
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8")); const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
module.exports = new Event("messageDelete", async (client, message) => { 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 const author = message.author || (message.partial ? await message.fetch().then(m => m.author).catch(() => null) : null);
if (!message.guild || message.author.bot) return; 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) || 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() const deletedMessageEmbed = new EmbedBuilder()
.setColor(supportbot.MessageDelete.Colour) .setColor(supportbot.MessageDelete.Colour)
.setTitle("Message Deleted") .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:** <t:${Math.floor(message.createdTimestamp / 1000)}:F>`) .setDescription(`> **Channel:** <#${message.channel.id}>\n> **Message ID:** ${message.id}\n> **Message author:** ${author.tag} (${author.id})\n> **Message Created:** <t:${Math.floor(message.createdTimestamp / 1000)}:F>`)
.addFields( .addFields({ name: "Deleted Content", value: content, inline: false })
{ name: 'Message', value: message.content || '*No content*', inline: false } .setThumbnail(author.displayAvatarURL())
)
.setThumbnail(message.author.displayAvatarURL())
.setTimestamp(); .setTimestamp();
// If the message contains an attachment, add it to the embed
if (message.attachments.size > 0) { if (message.attachments.size > 0) {
const attachment = message.attachments.first(); 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) { if (deleteLogChannel) {
deleteLogChannel.send({ deleteLogChannel.send({ embeds: [deletedMessageEmbed] });
embeds: [deletedMessageEmbed]
});
} }
}); });
+1 -1
View File
@@ -125,4 +125,4 @@ module.exports = new Event("clientReady", async (client) => {
setActivity(client, supportbot?.Activity?.Type, supportbot?.Activity?.Status); setActivity(client, supportbot?.Activity?.Type, supportbot?.Activity?.Status);
await logStartup(client); await logStartup(client);
await checkConfigs(client); await checkConfigs(client);
}); });
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+2 -1
View File
@@ -8,6 +8,7 @@ const { Command, Event } = require('./Addon.js');
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8")); const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
function walk(dir, ext = ".js", fileList = []) { function walk(dir, ext = ".js", fileList = []) {
const files = fs.readdirSync(dir); const files = fs.readdirSync(dir);
for (const file of files) { for (const file of files) {
@@ -179,7 +180,7 @@ class Client extends Discord.Client {
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬"); console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
} }
this.once("ready", async () => { this.once("clientReady", async () => {
await this.guilds.cache.first()?.commands.set(this.commands); await this.guilds.cache.first()?.commands.set(this.commands);
console.log( console.log(
"\u001b[32m", "\u001b[32m",
-6
View File
@@ -6,7 +6,6 @@ const path = require("path");
const db = new Database(path.join(__dirname, "../Data/supportbot.db")); const db = new Database(path.join(__dirname, "../Data/supportbot.db"));
// ----- Core tables -----
db.prepare(` db.prepare(`
CREATE TABLE IF NOT EXISTS suggestions ( CREATE TABLE IF NOT EXISTS suggestions (
thread_id TEXT PRIMARY KEY, thread_id TEXT PRIMARY KEY,
@@ -62,7 +61,6 @@ db.prepare(`
) )
`).run(); `).run();
/* ---- NEW table for global bot settings ---- */
db.prepare(` db.prepare(`
CREATE TABLE IF NOT EXISTS settings ( CREATE TABLE IF NOT EXISTS settings (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
@@ -71,7 +69,6 @@ db.prepare(`
`).run(); `).run();
module.exports = { module.exports = {
// ----- Suggestions -----
addSuggestion(threadId, authorId, text) { addSuggestion(threadId, authorId, text) {
db.prepare(` db.prepare(`
INSERT INTO suggestions (thread_id, author_id, text) INSERT INTO suggestions (thread_id, author_id, text)
@@ -122,7 +119,6 @@ module.exports = {
.run(value ? 1 : 0, userId); .run(value ? 1 : 0, userId);
}, },
// ----- Tickets -----
addTicket(ticket) { addTicket(ticket) {
const now = Date.now(); const now = Date.now();
db.prepare(` db.prepare(`
@@ -184,7 +180,6 @@ module.exports = {
`).get(); `).get();
}, },
// ----- Ticket participants -----
addUserToTicket(ticketId, userId) { addUserToTicket(ticketId, userId) {
db.prepare(` db.prepare(`
INSERT INTO ticket_users (ticket_id, user_id) INSERT INTO ticket_users (ticket_id, user_id)
@@ -205,7 +200,6 @@ module.exports = {
`).all(ticketId).map(r => r.user_id); `).all(ticketId).map(r => r.user_id);
}, },
// ----- Global Settings -----
getSettings() { getSettings() {
const row = db.prepare(`SELECT data FROM settings WHERE id=1`).get(); const row = db.prepare(`SELECT data FROM settings WHERE id=1`).get();
if (!row) return {}; if (!row) return {};
+47 -8
View File
@@ -8,9 +8,10 @@
// Installed with MIT License // Installed with MIT License
// //
// Discord Support: https://emeraldsrv.dev/discord // Discord Support: https://emeraldsrv.dev/discord
// Community Resources: https://emeraldsrv.dev/resources // Community Resources: https://community.emeraldsrv.dev
const fs = require("fs"); const fs = require("fs");
const path = require("path");
const yaml = require("js-yaml"); const yaml = require("js-yaml");
const supportbot = yaml.load( const supportbot = yaml.load(
@@ -24,13 +25,51 @@ const client = new Client({
client.start(supportbot.General.Token); 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);
});
+1 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "supportbot", "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.", "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", "main": "index.js",
"scripts": { "scripts": {
@@ -18,8 +18,6 @@
}, },
"homepage": "https://community.emeraldsrv.dev", "homepage": "https://community.emeraldsrv.dev",
"dependencies": { "dependencies": {
"@paypal/checkout-server-sdk": "^1.0.3",
"@vitalets/google-translate-api": "^8.0.0",
"axios": "^1.7.2", "axios": "^1.7.2",
"better-sqlite3": "^12.2.0", "better-sqlite3": "^12.2.0",
"discord.js": "^14.13.0", "discord.js": "^14.13.0",