Removed optional addons by default

This commit is contained in:
1tzEmerald committed 2026-07-26 21:23:16 +01:00
1 parent 004b638dbf
commit ea2a8c97ae
24 files changed
-2231

No files matched your search

-111
View File
@@ -1,111 +0,0 @@
Command:
Name: "applicationsetup"
Description: "Posts the application panel"
Permission:
- "ManageGuild"
Application:
ReviewChannelId: "1481380207305691287"
CooldownHours: 24
OneOpenApplicationPerUserPerType: true
Panel:
Title: "Applications"
Description: "Choose an application type from the dropdown below."
Placeholder: "Select an application type"
Review:
Title: "New Application"
AcceptLabel: "Accept"
RejectLabel: "Reject"
Applications:
staff:
Name: "Staff Team"
Description: "Apply for the staff team"
AcceptedRoleId: "" # Leave "" blank to disable
Questions:
Username:
Label: "What is your Discord username?"
Placeholder: "ExampleUser"
Required: true
Style: "Short"
MinLength: 2
MaxLength: 50
Age:
Label: "How old are you?"
Placeholder: "18"
Required: true
Style: "Short"
MinLength: 1
MaxLength: 3
Timezone:
Label: "What is your timezone?"
Placeholder: "GMT / BST / EST"
Required: true
Style: "Short"
MinLength: 2
MaxLength: 30
Experience:
Label: "What experience do you have?"
Placeholder: "Tell us about your experience"
Required: true
Style: "Paragraph"
MinLength: 10
MaxLength: 1000
Reason:
Label: "Why should we accept you?"
Placeholder: "Tell us why"
Required: true
Style: "Paragraph"
MinLength: 10
MaxLength: 1000
developer:
Name: "Developer"
Description: "Apply for the development team"
AcceptedRoleId: ""
Questions:
Username:
Label: "What is your Discord username?"
Placeholder: "ExampleUser"
Required: true
Style: "Short"
MinLength: 2
MaxLength: 50
Languages:
Label: "What languages do you know?"
Placeholder: "JavaScript, Python, Lua"
Required: true
Style: "Paragraph"
MinLength: 3
MaxLength: 500
Projects:
Label: "What projects have you worked on?"
Placeholder: "Describe your work"
Required: true
Style: "Paragraph"
MinLength: 10
MaxLength: 1000
Availability:
Label: "How active can you be?"
Placeholder: "How much time can you commit?"
Required: true
Style: "Paragraph"
MinLength: 5
MaxLength: 500
Reason:
Label: "Why do you want this role?"
Placeholder: "Tell us why"
Required: true
Style: "Paragraph"
MinLength: 10
MaxLength: 1000
-11
View File
@@ -1,11 +0,0 @@
# SupportBot | Emerald Services
# Discord Stats addon | Created by Xiled Emerald
Guild_ID: "GUILD_ID_HERE"
StatsCategory: "Server Statistics"
UserCount_Name: "Total Members:"
ChannelCount_Name: "Total Channels:"
RoleCount_Name: "Total Roles:"
-16
View File
@@ -1,16 +0,0 @@
# Minecraft Server Configuration
Java:
IP: "play.sapling.gg"
Port: 25565
DisplayName: "Java Edition"
Bedrock:
IP: "play.sapling.gg"
Port: 19132
DisplayName: "Bedrock Edition"
Embed:
Color: "#57F287"
OfflineColor: "#ED4245"
-38
View File
@@ -1,38 +0,0 @@
# SupportBot Temporary Voice Channels Configuration
# Addon Version 1.0
Enabled: true
# The ID of the voice channel that users join to create a new one
JoinToCreateChannelID: "CHANNEL_ID"
# The category ID where new channels should be created (Optional)
# If left blank, it will create it in the same category as the JoinToCreate channel
CategoryID: "CATEGORY_ID"
# The name pattern for new channels
# Placeholders:
# {user}: Username
ChannelNamePattern: "🔊 {user}'s Channel"
# Should the channel be deleted when the last person leaves?
AutoDelete: true
# Voice Channel Hub (Control Panel)
Hub:
Enabled: true
SendOnCreate: true
LayoutStyle: "classic" # classic | modern
# Classic Layout Settings (Standard Embed + Buttons)
Classic:
Title: "Voice Channel Hub"
Description: "Manage your voice channel using the buttons below."
Color: "#57F287"
# Modern Layout Settings (V2 Containers)
Modern:
Title: "Voice Channel Controller"
Body: "Control temporary voice channel using this control panel."
Color: "#57F287"
-28
View File
@@ -1,28 +0,0 @@
# +==================================================================================+
# | |
# | __ __ _ _ _ _ _ |
# | \ \ / /__ _ _ ___| |__ | | ___ _ _ / \ __| | __| | ___ _ __ |
# | \ \ / / _ \| | | |/ __| '_ \| |/ _ \ | | | / _ \ / _` |/ _` |/ _ \| '_ \ |
# | \ V / (_) | |_| | (__| | | | | __/ |_| | / ___ \ (_| | (_| | (_) | | | | |
# | \_/ \___/ \__,_|\___|_| |_|_|\___|\__, | /_/ \_\__,_|\__,_|\___/|_| |_| |
# | |___/ |
# | |
# | Vouchley Addon for SupportBot |
# | Created by Emerald Services |
# | |
# +==================================================================================+
Vouchley:
API_Key: "REQUEST_API_KEY_BELOW"
# Vouchley API Documentation: https://www.vouchley.com/docs/api/getting-started
# To use the API you will need an API key. To get this create a ticket in https://discord.gg/buzz
Embed:
Colour: 0x6063fc # 0x<hexcode> without the #
Ephermal: true # Make the profile searches hidden so only the interacting user can see them.
Command:
Name: "vouchley" # The name of the command (/vouchley)
Description: "Search a Vouchley Profile." # The description of the command (/vouchley)
Permissions: ["SendMessages"]
-3
View File
@@ -1,3 +0,0 @@
{
"applications": []
}
-78
View File
@@ -1,78 +0,0 @@
const Discord = require("discord.js");
const { Command } = require("../Structures/Addon.js");
const fs = require("fs");
const yaml = require("js-yaml");
const axios = require("axios");
// Load configs
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
const minecraftConfig = yaml.load(fs.readFileSync("./Addons/Configs/minecraft.yml", "utf8"));
module.exports = new Command({
name: "serverip",
description: "Display the Minecraft server IP and status",
options: [
{
name: "type",
description: "The edition of Minecraft (Java or Bedrock)",
type: Discord.ApplicationCommandOptionType.String,
required: true,
choices: [
{ name: "Java", value: "java" },
{ name: "Bedrock", value: "bedrock" },
],
},
],
permissions: ["SendMessages"],
async run(interaction) {
const type = interaction.options.getString("type");
const config = type === "java" ? minecraftConfig.Java : minecraftConfig.Bedrock;
await interaction.deferReply();
try {
const response = await axios.get(`https://api.mcstatus.io/v2/status/${type}/${config.IP}:${config.Port}`);
const data = response.data;
if (!data.online) {
throw new Error("Server is offline");
}
const embed = new Discord.EmbedBuilder()
.setTitle(`${config.DisplayName} Status`)
.setTimestamp();
embed.setColor(minecraftConfig.Embed.Color || supportbot.Embed.Colours.General)
.setDescription(`The server is currently **Online**!`)
.addFields(
{ name: "IP Address", value: `\`${config.IP}\``, inline: true },
{ name: "Port", value: `\`${config.Port}\``, inline: true },
{ name: "Players", value: `\`${data.players.online}/${data.players.max}\``, inline: true },
{ name: "Version", value: `\`${type === "java" ? data.version.name_clean : data.version.name || "Unknown"}\``, inline: true }
);
if (data.motd && data.motd.clean) {
embed.addFields({ name: "MOTD", value: `\`\`\`${data.motd.clean}\`\`\`` });
}
await interaction.editReply({ embeds: [embed] });
} catch (error) {
console.error("Error fetching Minecraft status:", error);
const config = interaction.options.getString("type") === "java" ? minecraftConfig.Java : minecraftConfig.Bedrock;
const embed = new Discord.EmbedBuilder()
.setTitle(`${config.DisplayName} Status`)
.setTimestamp()
.setColor(minecraftConfig.Embed.OfflineColor || "#FF0000")
.setDescription(`The server is currently **Offline**.`)
.addFields(
{ name: "IP Address", value: `\`${config.IP}\``, inline: true },
{ name: "Port", value: `\`${config.Port}\``, inline: true }
);
await interaction.editReply({ embeds: [embed] });
}
},
});
@@ -1,5 +0,0 @@
# SupportBot Sanction Addon
# Created by ItsNyoty
LogChannel: "1275426007980965908"
# The ID of the channel where the logs will be sent.
-157
View File
@@ -1,157 +0,0 @@
const Discord = require('discord.js');
const fs = require('fs');
const yaml = require('js-yaml');
const { Command } = require('../Structures/Addon.js');
const supportbot = yaml.load(fs.readFileSync('./Configs/supportbot.yml', 'utf8'));
const sactionaddon = yaml.load(fs.readFileSync('./Addons/Configs/sactionaddon.yml', 'utf8'));
module.exports = new Command({
name: 'sanction',
description: 'Sanction a user (kick, ban, or timeout).',
options: [
{
name: 'action',
type: 3,
description: 'The action to perform (kick, ban, timeout)',
required: true,
choices: [
{ name: 'Kick', value: 'kick' },
{ name: 'Ban', value: 'ban' },
{ name: 'Timeout', value: 'timeout' }
]
},
{
name: 'user',
type: 6,
description: 'The user to sanction',
required: true
},
{
name: 'reason',
type: 3,
description: 'Reason for the sanction',
required: false
},
{
name: 'duration',
type: 3,
description: 'Duration of the timeout (e.g., "10m", "2h", "1d")',
required: false
}
],
permissions: [],
async run(interaction) {
const action = interaction.options.getString('action');
const user = interaction.options.getUser('user');
const reason = interaction.options.getString('reason') || 'No reason provided';
const duration = interaction.options.getString('duration');
const staffRoleId = supportbot.Roles.StaffMember.Staff;
const member = interaction.guild.members.cache.get(interaction.user.id);
if (!member || !member.roles.cache.has(staffRoleId)) {
return interaction.reply({ content: 'You do not have permission to use this command.', ephemeral: true });
}
const targetMember = interaction.guild.members.cache.get(user.id);
if (!targetMember) {
return interaction.reply({ content: 'The user is not a member of this guild.', ephemeral: true });
}
try {
let actionMessage = '';
let logEmbed = new Discord.EmbedBuilder().setColor(supportbot.Embed.Colours.General);
switch (action) {
case 'kick':
if (!targetMember.kickable) return interaction.reply({ content: 'I cannot kick this user.', ephemeral: true });
await targetMember.kick(reason);
actionMessage = `Successfully kicked ${user.tag} for: ${reason}`;
logEmbed
.setTitle('User Kicked')
.setDescription(`**User:** ${user.tag}\n**Reason:** ${reason}`)
.addFields(
{ name: 'Action By', value: interaction.user.tag, inline: true },
{ name: 'Action', value: 'Kick', inline: true },
{ name: 'Timestamp', value: new Date().toISOString(), inline: true }
);
break;
case 'ban':
if (!targetMember.bannable) return interaction.reply({ content: 'I cannot ban this user.', ephemeral: true });
await targetMember.ban({ reason });
actionMessage = `Successfully banned ${user.tag} for: ${reason}`;
logEmbed
.setTitle('User Banned')
.setDescription(`**User:** ${user.tag}\n**Reason:** ${reason}`)
.addFields(
{ name: 'Action By', value: interaction.user.tag, inline: true },
{ name: 'Action', value: 'Ban', inline: true },
{ name: 'Timestamp', value: new Date().toISOString(), inline: true }
);
break;
case 'timeout':
// Ensure a valid duration is provided
if (!duration) return interaction.reply({ content: 'You must specify a duration for the timeout.', ephemeral: true });
// Convert duration to milliseconds
const parsedDuration = ms(duration);
if (!parsedDuration) return interaction.reply({ content: 'Invalid duration format. Use "1s", "5m", "2h", or "1d".', ephemeral: true });
// Apply the timeout
await targetMember.timeout(parsedDuration, reason);
actionMessage = `Successfully timed out ${user.tag} for ${duration} with reason: ${reason}`;
logEmbed
.setTitle('User Timed Out')
.setDescription(`**User:** ${user.tag}\n**Duration:** ${duration}\n**Reason:** ${reason}`)
.addFields(
{ name: 'Action By', value: interaction.user.tag, inline: true },
{ name: 'Action', value: 'Timeout', inline: true },
{ name: 'Timestamp', value: new Date().toISOString(), inline: true }
);
break;
default:
return interaction.reply({ content: 'Invalid action specified.', ephemeral: true });
}
// Send the action message
interaction.reply({ content: actionMessage, ephemeral: true });
// Log the action to the logging channel
const logChannel = interaction.guild.channels.cache.get(sactionaddon.LogChannel);
if (logChannel) {
logChannel.send({ embeds: [logEmbed] });
} else {
console.error('Logging channel not found.');
}
} catch (error) {
console.error('Error executing sanction command:', error);
interaction.reply({ content: 'An error occurred while executing the command.', ephemeral: true });
}
},
});
/**
* Converts a time duration string to milliseconds.
* @param {string} duration - The duration string (e.g., "10m", "2h", "1d").
* @returns {number} - The duration in milliseconds.
*/
function ms(duration) {
const regex = /^(\d+)([smhd])$/;
const match = regex.exec(duration);
if (!match) return null;
const value = parseInt(match[1], 10);
const unit = match[2];
switch (unit) {
case 's': return value * 1000; // seconds
case 'm': return value * 60 * 1000; // minutes
case 'h': return value * 60 * 60 * 1000; // hours
case 'd': return value * 24 * 60 * 60 * 1000; // days
default: return null;
}
}
@@ -1,2 +0,0 @@
{
}
-85
View File
@@ -1,85 +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: "tags",
description: "Retrieve a saved tag.",
options: [
{
name: 'name',
type: Discord.ApplicationCommandOptionType.String,
description: 'The name of the tag',
required: true,
autocomplete: true
},
],
permissions: ["SendMessages"],
async autocomplete(interaction) {
const tagsPath = './Addons/Data/tags.json';
const focusedValue = interaction.options.getFocused().toLowerCase();
if (!fs.existsSync(tagsPath)) {
return interaction.respond([]);
}
const tags = JSON.parse(fs.readFileSync(tagsPath, 'utf8'));
const choices = Object.keys(tags)
.filter(tag => tag.toLowerCase().includes(focusedValue))
.slice(0, 25) // Discord has a limit of 25 choices
.map(tag => ({ name: tag, value: tag }));
return interaction.respond(choices);
},
async run(interaction) {
const tagsPath = './Addons/Data/tags.json';
const tagName = interaction.options.getString('name').toLowerCase();
if (!fs.existsSync(tagsPath)) {
const embed = new Discord.EmbedBuilder()
.setDescription('No tags have been created yet.')
.setColor(supportbot.Embed.Colours.General);
return interaction.reply({ embeds: [embed], ephemeral: true });
}
const tags = JSON.parse(fs.readFileSync(tagsPath, 'utf8'));
if (tags[tagName]) {
const tag = tags[tagName];
if (tag.embed) {
// For embeds, Discord automatically handles line breaks correctly
const embed = new Discord.EmbedBuilder()
.setDescription(tag.content)
.setColor(supportbot.Embed.Colours.General);
if (tag.image) {
embed.setImage(tag.image);
}
return interaction.reply({ embeds: [embed], ephemeral: tag.ephemeral || false });
} else {
// For plain text, we need to keep the line breaks
const response = {
content: tag.content,
ephemeral: tag.ephemeral || false
};
if (tag.image) {
response.files = [tag.image];
}
return interaction.reply(response);
}
} else {
const embed = new Discord.EmbedBuilder()
.setDescription(`Tag "${tagName}" not found.`)
.setColor(supportbot.Embed.Colours.General);
return interaction.reply({ embeds: [embed], ephemeral: true });
}
}
});
-76
View File
@@ -1,76 +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: "tagscreate",
description: "Create a new tag.",
options: [
{
name: 'name',
type: Discord.ApplicationCommandOptionType.String,
description: 'The name of the tag',
required: true,
},
{
name: 'content',
type: Discord.ApplicationCommandOptionType.String,
description: 'The content of the tag (use ; or /n for new lines)',
required: true,
},
{
name: 'image',
type: Discord.ApplicationCommandOptionType.Attachment,
description: 'The image URL of the tag',
required: false,
},
{
name: 'embed',
type: Discord.ApplicationCommandOptionType.Boolean,
description: 'Do you want this tag to send as an embed',
required: false,
},
{
name: 'ephemeral',
type: Discord.ApplicationCommandOptionType.Boolean,
description: 'Do you want this tag to send as ephemeral',
required: false,
},
],
permissions: ["SendMessages"],
async run(interaction) {
const tagName = interaction.options.getString('name').toLowerCase();
let tagContent = interaction.options.getString('content');
const tagImage = interaction.options.getAttachment('image');
const isEmbed = interaction.options.getBoolean('embed');
const isEphemeral = interaction.options.getBoolean('ephemeral');
const tagsPath = './Addons/Data/tags.json';
// Process line breaks in content - support both ; and /n notations
tagContent = tagContent.replace(/;/g, '\n').replace(/\/n/g, '\n');
let tags = {};
if (fs.existsSync(tagsPath)) {
tags = JSON.parse(fs.readFileSync(tagsPath, 'utf8'));
}
tags[tagName] = {
content: tagContent,
image: tagImage ? tagImage.url : null,
embed: isEmbed || false,
ephemeral: isEphemeral || false,
};
fs.writeFileSync(tagsPath, JSON.stringify(tags, null, 4));
const embed = new Discord.EmbedBuilder()
.setDescription(`Tag "${tagName}" created successfully.`)
.setColor(supportbot.Embed.Colours.General);
return interaction.reply({ embeds: [embed], ephemeral: true });
},
});
-68
View File
@@ -1,68 +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: "tagsdelete",
description: "Delete an existing tag.",
options: [
{
name: 'name',
type: Discord.ApplicationCommandOptionType.String,
description: 'The name of the tag to delete',
required: true,
autocomplete: true
},
],
permissions: ["SendMessages"],
async autocomplete(interaction) {
const tagsPath = './Addons/Data/tags.json';
const focusedValue = interaction.options.getFocused().toLowerCase();
if (!fs.existsSync(tagsPath)) {
return interaction.respond([]);
}
const tags = JSON.parse(fs.readFileSync(tagsPath, 'utf8'));
const choices = Object.keys(tags)
.filter(tag => tag.toLowerCase().includes(focusedValue))
.slice(0, 25) // Discord has a limit of 25 choices
.map(tag => ({ name: tag, value: tag }));
return interaction.respond(choices);
},
async run(interaction) {
const tagsPath = './Addons/Data/tags.json';
const tagName = interaction.options.getString('name').toLowerCase();
if (!fs.existsSync(tagsPath)) {
const embed = new Discord.EmbedBuilder()
.setDescription('No tags have been created yet.')
.setColor(supportbot.Embed.Colours.General);
return interaction.reply({ embeds: [embed], ephemeral: true });
}
let tags = JSON.parse(fs.readFileSync(tagsPath, 'utf8'));
if (!tags[tagName]) {
const embed = new Discord.EmbedBuilder()
.setDescription(`Tag "${tagName}" not found.`)
.setColor(supportbot.Embed.Colours.General);
return interaction.reply({ embeds: [embed], ephemeral: true });
}
delete tags[tagName];
fs.writeFileSync(tagsPath, JSON.stringify(tags, null, 4));
const embed = new Discord.EmbedBuilder()
.setDescription(`Tag "${tagName}" deleted successfully.`)
.setColor(supportbot.Embed.Colours.General);
return interaction.reply({ embeds: [embed], ephemeral: true });
}
});
@@ -1,14 +0,0 @@
PayPal:
ClientID: "clientID_Here"
ClientSecret: "ClientSecret_Here"
Mode: "live" # or "live"
Email: "Email_Here" # Add your PayPal email here
Embed:
Colours:
General: "#0099ff"
Success: "#00ff00" # Add more colors if needed
Error: "#ff0000" # Add more colors if needed
Footer: "PayPal Invoice" # Add footer text here
Logo: "https://your-logo-url.com/logo.png" # Add logo URL if used in invoice
Note: "Thank you for your business!" # Add a note if used in invoice
TOS: "Please review our terms and conditions. https://domain.com/tos" # Add terms of service if used in invoice
-146
View File
@@ -1,146 +0,0 @@
const { EmbedBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ApplicationCommandOptionType } = require('discord.js');
const fs = require('fs');
const yaml = require('js-yaml');
const paypal = require('paypal-rest-sdk');
const { Command } = require('../Structures/Addon.js'); // Adjust the path as needed
// Load the PayPal configuration
const config = yaml.load(fs.readFileSync("./Addons/Configs/paypal.yml", "utf8"));
const supportbot = config;
// Configure PayPal SDK
paypal.configure({
mode: supportbot.PayPal.Mode, // sandbox or live
client_id: supportbot.PayPal.ClientID,
client_secret: supportbot.PayPal.ClientSecret
});
module.exports = new Command({
name: "invoice",
description: "Generate a PayPal invoice for a user.",
options: [
{
name: "user",
description: "The user to notify.",
type: ApplicationCommandOptionType.User,
required: true,
},
{
name: "price",
description: "The amount to invoice.",
type: ApplicationCommandOptionType.String,
required: true,
},
{
name: "service",
description: "The service provided.",
type: ApplicationCommandOptionType.String,
required: true,
},
{
name: "email",
description: "The email to invoice.",
type: ApplicationCommandOptionType.String,
required: true,
}
],
permissions: ["Administrator"],
async run(interaction) {
const user = interaction.options.getUser("user");
const price = interaction.options.getString("price");
const service = interaction.options.getString("service");
const email = interaction.options.getString("email");
// Validate the price format
if (isNaN(price) || parseFloat(price) <= 0) {
const errorEmbed = new EmbedBuilder()
.setDescription(`The price provided is invalid.`)
.setColor(supportbot.Embed.Colours.Error)
.setFooter({ text: supportbot.Embed.Footer });
return interaction.reply({ embeds: [errorEmbed], ephemeral: true });
}
const invoiceData = {
"merchant_info": {
"email": supportbot.PayPal.Email,
"business_name": interaction.guild.name
},
"billing_info": [{
"email": email
}],
"items": [{
"name": service,
"quantity": 1,
"unit_price": {
"currency": "USD",
"value": price
}
}],
"logo_url": supportbot.PayPal.Logo,
"note": supportbot.PayPal.Note,
"terms": supportbot.PayPal.TOS,
"payment_term": {
"term_type": "NET_45"
},
"tax_inclusive": false,
"total_amount": {
"currency": "USD",
"value": price
}
};
paypal.invoice.create(invoiceData, function (error, invoice) {
if (error) {
console.error(error);
const errorEmbed = new EmbedBuilder()
.setTitle(`PayPal Error`)
.setDescription(`There was an error creating the invoice. Please check your configuration and try again.`)
.setColor(supportbot.Embed.Colours.Error)
.setFooter({ text: supportbot.Embed.Footer });
return interaction.reply({ embeds: [errorEmbed], ephemeral: true });
}
const invoiceId = invoice.id;
paypal.invoice.send(invoiceId, function (error) {
if (error) {
console.error(error);
const errorEmbed = new EmbedBuilder()
.setTitle(`PayPal Error`)
.setDescription(`There was an error sending the invoice. Please check your configuration and try again.`)
.setColor(supportbot.Embed.Colours.Error)
.setFooter({ text: supportbot.Embed.Footer });
return interaction.reply({ embeds: [errorEmbed], ephemeral: true });
}
const invoiceEmbed = new EmbedBuilder()
.setTitle(`Payment System | ${interaction.guild.name}`)
.addFields(
{ name: 'Service', value: service, inline: true },
{ name: 'Amount Due', value: `$${price}`, inline: true },
{ name: 'Currency', value: 'USD', inline: true },
{ name: 'Invoice ID', value: `\`${invoiceId}\``, inline: false }
)
.setColor(supportbot.Embed.Colours.General)
.setFooter({ text: supportbot.Embed.Footer })
.setTimestamp();
// Create a button with a link to the invoice
const invoiceButton = new ButtonBuilder()
.setLabel('View Invoice')
.setStyle(ButtonStyle.Link)
.setURL(`https://www.paypal.com/invoice/payerView/details/${invoiceId}`);
// Create the action row
const actionRow = new ActionRowBuilder()
.addComponents(invoiceButton);
// Send the embed with the button
interaction.reply({
embeds: [invoiceEmbed],
components: [actionRow]
});
});
});
},
});
-227
View File
@@ -1,227 +0,0 @@
const Discord = require("discord.js");
const { Event } = require("../Structures/Addon.js");
const fs = require("fs");
const yaml = require("js-yaml");
const tempVCConfig = yaml.load(fs.readFileSync("./Addons/Configs/tempvc.yml", "utf8"));
const tempChannels = new Map();
const voiceStateUpdate = new Event("voiceStateUpdate", async (client, oldState, newState) => {
if (!tempVCConfig.Enabled) return;
const member = newState.member;
const guild = newState.guild;
if (newState.channelId === tempVCConfig.JoinToCreateChannelID) {
console.log(`[TempVC] ${member.user.tag} joined the Join-to-Create channel.`);
}
if (newState.channelId === tempVCConfig.JoinToCreateChannelID) {
try {
const parentCategory = tempVCConfig.CategoryID || newState.channel.parentId;
const channelName = tempVCConfig.ChannelNamePattern.replace("{user}", member.user.username);
const newChannel = await guild.channels.create({
name: channelName,
type: Discord.ChannelType.GuildVoice,
parent: parentCategory,
permissionOverwrites: [
{
id: member.id,
allow: [Discord.PermissionFlagsBits.ManageChannels, Discord.PermissionFlagsBits.MoveMembers],
},
],
});
tempChannels.set(newChannel.id, member.id);
await member.voice.setChannel(newChannel);
console.log(`[TempVC] Created channel "${channelName}" for ${member.user.tag}`);
if (tempVCConfig.Hub.Enabled && tempVCConfig.Hub.SendOnCreate) {
try {
const { ActionRowBuilder, ButtonBuilder, EmbedBuilder, MessageFlags, ButtonStyle } = Discord;
const layoutStyle = tempVCConfig.Hub.LayoutStyle || "classic";
if (layoutStyle === "classic") {
const config = tempVCConfig.Hub.Classic;
const embed = new EmbedBuilder()
.setTitle(config.Title)
.setDescription(config.Description)
.setColor(config.Color);
const buttons = new ActionRowBuilder().addComponents(
new ButtonBuilder().setCustomId("tempvc_lock").setLabel("Lock").setEmoji("🔒").setStyle(ButtonStyle.Secondary),
new ButtonBuilder().setCustomId("tempvc_unlock").setLabel("Unlock").setEmoji("🔓").setStyle(ButtonStyle.Secondary),
new ButtonBuilder().setCustomId("tempvc_rename").setLabel("Rename").setEmoji("✏️").setStyle(ButtonStyle.Secondary),
new ButtonBuilder().setCustomId("tempvc_limit").setLabel("Limit").setEmoji("👥").setStyle(ButtonStyle.Secondary),
new ButtonBuilder().setCustomId("tempvc_claim").setLabel("Claim Ownership").setEmoji("👑").setStyle(ButtonStyle.Primary)
);
await newChannel.send({ embeds: [embed], components: [buttons] });
} else {
const config = tempVCConfig.Hub.Modern;
const colorInt = parseInt(config.Color.replace("#", ""), 16);
const container = {
type: 11,
accent_color: colorInt,
children: [
{
type: 12,
children: [
{ type: 13, content: `## ${config.Title}` },
{ type: 13, content: config.Body }
]
},
{
type: 15,
divider: true
},
{
type: 1,
components: [
{ type: 2, custom_id: "tempvc_lock", label: "Lock", emoji: { name: "🔒" }, style: 2 },
{ type: 2, custom_id: "tempvc_unlock", label: "Unlock", emoji: { name: "🔓" }, style: 2 },
{ type: 2, custom_id: "tempvc_rename", label: "Rename", emoji: { name: "✏️" }, style: 2 },
{ type: 2, custom_id: "tempvc_limit", label: "Limit", emoji: { name: "👥" }, style: 2 }
]
},
{
type: 1,
components: [
{ type: 2, custom_id: "tempvc_claim", label: "Claim Ownership", emoji: { name: "👑" }, style: 1 }
]
}
]
};
await newChannel.send({
flags: MessageFlags.IsComponentsV2,
components: [container]
});
}
} catch (hubError) {
console.error("[TempVC] Error sending Hub:", hubError);
}
}
} catch (error) {
console.error("[TempVC] Error creating temporary channel:", error);
}
}
if (oldState.channelId && tempChannels.has(oldState.channelId)) {
const oldChannel = oldState.channel;
if (oldChannel && oldChannel.members.size === 0) {
try {
await oldChannel.delete();
tempChannels.delete(oldState.channelId);
console.log(`[TempVC] Deleted empty temporary channel "${oldChannel.name}"`);
} catch (error) {
if (error.code !== 10003) {
console.error("[TempVC] Error deleting temporary channel:", error);
}
tempChannels.delete(oldState.channelId);
}
}
}
});
const interactionEvent = new Event("interactionCreate", async (client, interaction) => {
if (!interaction.isButton() && !interaction.isModalSubmit()) return;
if (!interaction.customId.startsWith("tempvc_")) return;
const channel = interaction.channel;
if (!tempChannels.has(channel.id)) {
return interaction.reply({ content: "This channel is no longer a temporary voice channel.", flags: Discord.MessageFlags.Ephemeral });
}
const ownerId = tempChannels.get(channel.id);
const isOwner = interaction.user.id === ownerId || interaction.member.permissions.has(Discord.PermissionFlagsBits.Administrator);
if (interaction.customId === "tempvc_claim") {
const currentOwnerInVC = channel.members.has(ownerId);
if (currentOwnerInVC && interaction.user.id !== ownerId) {
return interaction.reply({ content: "The owner is still in the voice channel!", flags: Discord.MessageFlags.Ephemeral });
}
tempChannels.set(channel.id, interaction.user.id);
await channel.permissionOverwrites.edit(interaction.user.id, {
ManageChannels: true,
MoveMembers: true,
});
return interaction.reply({ content: "You are now the owner of this voice channel!", flags: Discord.MessageFlags.Ephemeral });
}
if (!isOwner) {
return interaction.reply({ content: "Only the channel owner can use these controls.", flags: Discord.MessageFlags.Ephemeral });
}
try {
switch (interaction.customId) {
case "tempvc_lock":
await channel.permissionOverwrites.edit(interaction.guild.id, { Connect: false });
await interaction.reply({ content: "Channel locked! 🔒", flags: Discord.MessageFlags.Ephemeral });
break;
case "tempvc_unlock":
await channel.permissionOverwrites.edit(interaction.guild.id, { Connect: null });
await interaction.reply({ content: "Channel unlocked! 🔓", flags: Discord.MessageFlags.Ephemeral });
break;
case "tempvc_rename":
const modal = new Discord.ModalBuilder()
.setCustomId("tempvc_modal_rename")
.setTitle("Rename Channel")
.addComponents(
new Discord.ActionRowBuilder().addComponents(
new Discord.TextInputBuilder()
.setCustomId("name")
.setLabel("New Name")
.setStyle(Discord.TextInputStyle.Short)
.setPlaceholder(channel.name)
.setRequired(true)
)
);
await interaction.showModal(modal);
break;
case "tempvc_limit":
const limitModal = new Discord.ModalBuilder()
.setCustomId("tempvc_modal_limit")
.setTitle("Set User Limit")
.addComponents(
new Discord.ActionRowBuilder().addComponents(
new Discord.TextInputBuilder()
.setCustomId("limit")
.setLabel("Limit (0-99)")
.setStyle(Discord.TextInputStyle.Short)
.setPlaceholder("0 for no limit")
.setRequired(true)
)
);
await interaction.showModal(limitModal);
break;
}
if (interaction.isModalSubmit()) {
if (interaction.customId === "tempvc_modal_rename") {
const newName = interaction.fields.getTextInputValue("name");
await channel.setName(newName);
await interaction.reply({ content: `Channel renamed to **${newName}**!`, flags: Discord.MessageFlags.Ephemeral });
} else if (interaction.customId === "tempvc_modal_limit") {
const limit = parseInt(interaction.fields.getTextInputValue("limit"));
if (isNaN(limit) || limit < 0 || limit > 99) {
return interaction.reply({ content: "Please enter a valid number between 0 and 99.", flags: Discord.MessageFlags.Ephemeral });
}
await channel.setUserLimit(limit);
await interaction.reply({ content: `User limit set to **${limit === 0 ? "Unlimited" : limit}**!`, flags: Discord.MessageFlags.Ephemeral });
}
}
} catch (error) {
console.error("[TempVC] Error handling interaction:", error);
if (!interaction.replied) await interaction.reply({ content: "An error occurred.", flags: Discord.MessageFlags.Ephemeral });
}
});
module.exports = [voiceStateUpdate, interactionEvent];
-652
View File
@@ -1,652 +0,0 @@
const Discord = require("discord.js");
const fs = require("fs");
const path = require("path");
const yaml = require("js-yaml");
const { Command } = require("../Structures/Addon.js");
// Main bot config
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
// Addon config
const addonConfig = yaml.load(
fs.readFileSync("./Addons/Configs/applicationsystem.yml", "utf8")
);
// Data file
const dataPath = path.join(__dirname, "Data/applicationsystem.json");
function ensureDataFile() {
const dir = path.dirname(dataPath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
if (!fs.existsSync(dataPath)) {
fs.writeFileSync(
dataPath,
JSON.stringify({ applications: [] }, null, 2),
"utf8"
);
}
}
function readData() {
ensureDataFile();
try {
const raw = fs.readFileSync(dataPath, "utf8");
const parsed = JSON.parse(raw);
if (!parsed || !Array.isArray(parsed.applications)) {
return { applications: [] };
}
return parsed;
} catch (error) {
console.error("[ApplicationSystem] Failed to read data file:", error);
return { applications: [] };
}
}
function writeData(data) {
ensureDataFile();
fs.writeFileSync(dataPath, JSON.stringify(data, null, 2), "utf8");
}
function now() {
return Date.now();
}
function createId() {
return `app_${Date.now()}_${Math.floor(Math.random() * 100000)}`;
}
function parseColor(value, fallback = 0x2b2d31) {
if (typeof value === "number") return value;
if (typeof value === "string") {
const cleaned = value.trim().replace("#", "");
if (/^[0-9A-Fa-f]{6}$/.test(cleaned)) {
return parseInt(cleaned, 16);
}
}
return fallback;
}
function getGeneralColour() {
return parseColor(supportbot?.Embed?.Colours?.General, 0x2b2d31);
}
function buildNoticeContainer(title, description, colour = 0x5865f2) {
return new Discord.ContainerBuilder()
.setAccentColor(colour)
.addTextDisplayComponents(
new Discord.TextDisplayBuilder().setContent(`## ${title}`)
)
.addSeparatorComponents(new Discord.SeparatorBuilder())
.addTextDisplayComponents(
new Discord.TextDisplayBuilder().setContent(description)
);
}
function buildPanelContainer() {
const options = Object.entries(addonConfig.Applications || {})
.slice(0, 25)
.map(([key, app]) =>
new Discord.StringSelectMenuOptionBuilder()
.setLabel(String(app.Name || key).slice(0, 100))
.setDescription(
String(app.Description || "Apply for this position").slice(0, 100)
)
.setValue(key)
);
return new Discord.ContainerBuilder()
.setAccentColor(getGeneralColour())
.addTextDisplayComponents(
new Discord.TextDisplayBuilder().setContent(
`## ${addonConfig.Panel?.Title || "Applications"}`
)
)
.addSeparatorComponents(new Discord.SeparatorBuilder())
.addTextDisplayComponents(
new Discord.TextDisplayBuilder().setContent(
addonConfig.Panel?.Description ||
"Choose an application from the dropdown below."
)
)
.addActionRowComponents(
new Discord.ActionRowBuilder().addComponents(
new Discord.StringSelectMenuBuilder()
.setCustomId("appsystem_select")
.setPlaceholder(
addonConfig.Panel?.Placeholder || "Select an application type"
)
.addOptions(options)
)
);
}
function textInputStyle(style) {
return style === "Paragraph"
? Discord.TextInputStyle.Paragraph
: Discord.TextInputStyle.Short;
}
function sanitizeQuestionKey(key) {
return String(key).toLowerCase().replace(/[^a-z0-9_]/g, "_");
}
function formatQuestionTitle(key) {
return String(key)
.replace(/_/g, " ")
.replace(/\b\w/g, (char) => char.toUpperCase());
}
function buildApplicationModal(typeKey) {
const applicationType = addonConfig.Applications?.[typeKey];
if (!applicationType) return null;
const modal = new Discord.ModalBuilder()
.setCustomId(`appsystem_modal_${typeKey}`)
.setTitle(String(applicationType.Name || typeKey).slice(0, 45));
const questionEntries = Object.entries(applicationType.Questions || {}).slice(0, 5);
for (const [key, question] of questionEntries) {
const input = new Discord.TextInputBuilder()
.setCustomId(sanitizeQuestionKey(key))
.setLabel(String(question.Label || key).slice(0, 45))
.setPlaceholder(String(question.Placeholder || "Enter your answer").slice(0, 100))
.setRequired(Boolean(question.Required))
.setStyle(textInputStyle(question.Style))
.setMinLength(Number(question.MinLength || 1))
.setMaxLength(Number(question.MaxLength || 1000));
modal.addComponents(
new Discord.ActionRowBuilder().addComponents(input)
);
}
return modal;
}
function buildReviewContainer(application, reviewerText = null, locked = false) {
const statusText =
application.status === "accepted"
? "Accepted"
: application.status === "rejected"
? "Rejected"
: "Pending";
const answerLines = Object.entries(application.answers || {})
.map(([key, value]) => `### ${formatQuestionTitle(key)}\n${value}`)
.join("\n\n");
const body = [
`**Applicant:** <@${application.userId}>`,
`**Tag:** ${application.userTag}`,
`**Application Type:** ${application.typeName}`,
`**Application ID:** \`${application.id}\``,
`**Status:** ${statusText}`,
"",
answerLines || "*No answers provided.*",
].join("\n");
const container = new Discord.ContainerBuilder()
.setAccentColor(getGeneralColour())
.addTextDisplayComponents(
new Discord.TextDisplayBuilder().setContent(
`## ${addonConfig.Review?.Title || "New Application"}`
)
)
.addSeparatorComponents(new Discord.SeparatorBuilder())
.addTextDisplayComponents(
new Discord.TextDisplayBuilder().setContent(body)
);
if (reviewerText) {
container
.addSeparatorComponents(new Discord.SeparatorBuilder())
.addTextDisplayComponents(
new Discord.TextDisplayBuilder().setContent(reviewerText)
);
}
container.addActionRowComponents(
new Discord.ActionRowBuilder().addComponents(
new Discord.ButtonBuilder()
.setCustomId(`appsystem_accept_${application.id}`)
.setLabel(addonConfig.Review?.AcceptLabel || "Accept")
.setStyle(Discord.ButtonStyle.Success)
.setDisabled(locked),
new Discord.ButtonBuilder()
.setCustomId(`appsystem_reject_${application.id}`)
.setLabel(addonConfig.Review?.RejectLabel || "Reject")
.setStyle(Discord.ButtonStyle.Danger)
.setDisabled(locked)
)
);
return container;
}
function getPendingApplication(data, guildId, userId, type) {
return data.applications.find(
(app) =>
app.guildId === guildId &&
app.userId === userId &&
app.type === type &&
app.status === "pending"
);
}
function getCooldownApplication(data, guildId, userId, type) {
const cooldownMs =
Number(addonConfig.Application?.CooldownHours || 24) * 60 * 60 * 1000;
return data.applications.find(
(app) =>
app.guildId === guildId &&
app.userId === userId &&
app.type === type &&
now() - Number(app.createdAt || 0) < cooldownMs
);
}
function memberCanReview(member) {
if (!member) return false;
if (member.permissions?.has(Discord.PermissionFlagsBits.ManageGuild)) {
return true;
}
return false;
}
function registerAddonInteractions(client) {
if (client.__applicationSystemRegistered) return;
client.__applicationSystemRegistered = true;
client.on("interactionCreate", async (interaction) => {
try {
if (!interaction.guild) return;
// Application dropdown select
if (
interaction.isStringSelectMenu() &&
interaction.customId === "appsystem_select"
) {
const typeKey = interaction.values[0];
const applicationType = addonConfig.Applications?.[typeKey];
if (!applicationType) {
return interaction.reply({
flags:
Discord.MessageFlags.Ephemeral |
Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
"Invalid Application",
"That application type does not exist.",
0xed4245
),
],
});
}
const data = readData();
if (addonConfig.Application?.OneOpenApplicationPerUserPerType) {
const existing = getPendingApplication(
data,
interaction.guild.id,
interaction.user.id,
typeKey
);
if (existing) {
return interaction.reply({
flags:
Discord.MessageFlags.Ephemeral |
Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
"Application Already Open",
`You already have a pending ${applicationType.Name} application.`,
0xed4245
),
],
});
}
}
const cooldownHit = getCooldownApplication(
data,
interaction.guild.id,
interaction.user.id,
typeKey
);
if (cooldownHit && cooldownHit.status !== "pending") {
return interaction.reply({
flags:
Discord.MessageFlags.Ephemeral |
Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
"Please Wait",
`You must wait ${addonConfig.Application?.CooldownHours || 24} hours before applying again for ${applicationType.Name}.`,
0xfee75c
),
],
});
}
const modal = buildApplicationModal(typeKey);
if (!modal) {
return interaction.reply({
flags:
Discord.MessageFlags.Ephemeral |
Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
"Modal Error",
"The application form could not be created.",
0xed4245
),
],
});
}
return interaction.showModal(modal);
}
// Application modal submit
if (
interaction.isModalSubmit() &&
interaction.customId.startsWith("appsystem_modal_")
) {
const typeKey = interaction.customId.replace("appsystem_modal_", "");
const applicationType = addonConfig.Applications?.[typeKey];
if (!applicationType) {
return interaction.reply({
flags:
Discord.MessageFlags.Ephemeral |
Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
"Invalid Application",
"That application type no longer exists.",
0xed4245
),
],
});
}
const reviewChannel = interaction.guild.channels.cache.get(
addonConfig.Application?.ReviewChannelId
);
if (!reviewChannel) {
return interaction.reply({
flags:
Discord.MessageFlags.Ephemeral |
Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
"Configuration Error",
"The review channel was not found. Please contact staff.",
0xed4245
),
],
});
}
const data = readData();
if (addonConfig.Application?.OneOpenApplicationPerUserPerType) {
const existing = getPendingApplication(
data,
interaction.guild.id,
interaction.user.id,
typeKey
);
if (existing) {
return interaction.reply({
flags:
Discord.MessageFlags.Ephemeral |
Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
"Application Already Open",
`You already have a pending ${applicationType.Name} application.`,
0xed4245
),
],
});
}
}
const answers = {};
const questionEntries = Object.entries(applicationType.Questions || {}).slice(0, 5);
for (const [key] of questionEntries) {
const inputKey = sanitizeQuestionKey(key);
answers[inputKey] = interaction.fields.getTextInputValue(inputKey);
}
const application = {
id: createId(),
type: typeKey,
typeName: applicationType.Name || typeKey,
guildId: interaction.guild.id,
userId: interaction.user.id,
userTag: interaction.user.tag || interaction.user.username,
status: "pending",
createdAt: now(),
reviewerId: null,
reviewedAt: null,
reviewMessageId: null,
answers,
};
const reviewMessage = await reviewChannel.send({
flags: Discord.MessageFlags.IsComponentsV2,
components: [buildReviewContainer(application, null, false)],
});
application.reviewMessageId = reviewMessage.id;
data.applications.push(application);
writeData(data);
return interaction.reply({
flags:
Discord.MessageFlags.Ephemeral |
Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
"Application Sent",
`Your ${applicationType.Name} application has been submitted successfully.`,
0x57f287
),
],
});
}
// Review buttons
if (
interaction.isButton() &&
(interaction.customId.startsWith("appsystem_accept_") ||
interaction.customId.startsWith("appsystem_reject_"))
) {
if (!memberCanReview(interaction.member)) {
return interaction.reply({
flags:
Discord.MessageFlags.Ephemeral |
Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
"No Permission",
"You do not have permission to review applications.",
0xed4245
),
],
});
}
const isAccept = interaction.customId.startsWith("appsystem_accept_");
const applicationId = interaction.customId.replace(
isAccept ? "appsystem_accept_" : "appsystem_reject_",
""
);
const data = readData();
const application = data.applications.find((app) => app.id === applicationId);
if (!application) {
return interaction.reply({
flags:
Discord.MessageFlags.Ephemeral |
Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
"Not Found",
"This application no longer exists in storage.",
0xed4245
),
],
});
}
if (application.status !== "pending") {
return interaction.reply({
flags:
Discord.MessageFlags.Ephemeral |
Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
"Already Reviewed",
`This application has already been ${application.status}.`,
0xfee75c
),
],
});
}
application.status = isAccept ? "accepted" : "rejected";
application.reviewerId = interaction.user.id;
application.reviewedAt = now();
writeData(data);
const reviewerText = [
`### Review Result`,
`**Decision:** ${isAccept ? "Accepted" : "Rejected"}`,
`**Reviewer:** <@${interaction.user.id}>`,
`**Reviewed At:** <t:${Math.floor(application.reviewedAt / 1000)}:F>`,
].join("\n");
await interaction.update({
flags: Discord.MessageFlags.IsComponentsV2,
components: [
buildReviewContainer(application, reviewerText, true),
],
});
const applicationType = addonConfig.Applications?.[application.type];
if (isAccept && applicationType?.AcceptedRoleId) {
const member = await interaction.guild.members
.fetch(application.userId)
.catch(() => null);
if (member) {
await member.roles.add(applicationType.AcceptedRoleId).catch(() => null);
}
}
const applicantUser = await interaction.client.users
.fetch(application.userId)
.catch(() => null);
if (applicantUser) {
await applicantUser
.send({
flags: Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
isAccept ? "Application Accepted" : "Application Rejected",
isAccept
? `Congratulations! Your ${application.typeName} application in **${interaction.guild.name}** has been accepted.`
: `Your ${application.typeName} application in **${interaction.guild.name}** has been rejected.`,
isAccept ? 0x57f287 : 0xed4245
),
],
})
.catch(() => null);
}
return;
}
} catch (error) {
console.error("[ApplicationSystem] Interaction error:", error);
if (interaction.deferred || interaction.replied) {
return;
}
return interaction
.reply({
flags:
Discord.MessageFlags.Ephemeral |
Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
"Error",
"Something went wrong while handling that application interaction.",
0xed4245
),
],
})
.catch(() => null);
}
});
}
module.exports = new Command({
name: addonConfig.Command?.Name || "applicationsetup",
description:
addonConfig.Command?.Description || "Posts the application panel",
options: [],
permissions: addonConfig.Command?.Permission || ["ManageGuild"],
async run(interaction) {
registerAddonInteractions(interaction.client);
await interaction.channel.send({
flags: Discord.MessageFlags.IsComponentsV2,
components: [buildPanelContainer()],
});
return interaction.reply({
flags:
Discord.MessageFlags.Ephemeral |
Discord.MessageFlags.IsComponentsV2,
components: [
buildNoticeContainer(
"Application Panel Posted",
"The application panel has been posted in this channel.",
0x57f287
),
],
});
},
});
-83
View File
@@ -1,83 +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: "calculator",
description: "Use a calculator to solve your math problems.",
options: [],
permissions: ["SendMessages"],
async run(interaction) {
let expression = "";
let history = "";
const createButton = (label, customId) => {
return new Discord.ButtonBuilder()
.setLabel(label)
.setCustomId(customId)
.setStyle(Discord.ButtonStyle.Secondary);
};
const createRow = (buttons) => {
return new Discord.ActionRowBuilder().addComponents(buttons);
};
const rows = [
createRow([createButton('7', '7'), createButton('8', '8'), createButton('9', '9'), createButton('/', '/')]),
createRow([createButton('4', '4'), createButton('5', '5'), createButton('6', '6'), createButton('*', '*')]),
createRow([createButton('1', '1'), createButton('2', '2'), createButton('3', '3'), createButton('-', '-')]),
createRow([createButton('0', '0'), createButton('.', '.'), createButton('=', '='), createButton('+', '+')]),
createRow([createButton('Clear', 'clear')])
];
const embed = new Discord.EmbedBuilder()
.setTitle('Calculator')
.setDescription('Calculator')
.setColor(supportbot.Embed.Colours.General);
const message = await interaction.reply({
embeds: [embed],
components: rows,
fetchReply: true
});
const filter = i => i.user.id === interaction.user.id;
const collector = message.createMessageComponentCollector({ filter, time: 60000 });
collector.on('collect', async i => {
if (i.customId === 'clear') {
expression = "";
history = "";
} else if (i.customId === '=') {
try {
const result = eval(expression).toString();
history += `${expression} = ${result}\n`;
expression = result;
} catch {
expression = "Error";
}
} else {
if (history.endsWith(`${expression} = ${expression}\n`)) {
history += `\n`;
expression = i.customId;
} else {
expression += i.customId;
}
}
await i.update({
embeds: [new Discord.EmbedBuilder().setTitle('Calculator').setDescription(`\`\`\`${history}${expression}\`\`\``).setColor(supportbot.Embed.Colours.General)],
components: rows
});
});
collector.on('end', collected => {
message.edit({
components: []
});
});
},
});
-139
View File
@@ -1,139 +0,0 @@
// SupportBot | Emerald Services
// Discord Stats addon | Created by Xiled Emerald
const fs = require("fs");
const Discord = require("discord.js");
const yaml = require("js-yaml");
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
// If you need to read something from the main config!
const cmdconfig = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
// This is for your Addon Configuration
const config = yaml.load(fs.readFileSync('./Addons/Configs/discord-stats.yml', 'utf8'));
const { Command, Event } = require("../Structures/Addon");
module.exports.commands = [
new Command({
name: "stats",
description: "Create the discord statistic channels",
options: [],
permissions: ["SEND_MESSAGES"],
async run(interaction) {
const guild = interaction.guild;
const statsCategory = guild.channels.cache.find(
(category) =>
category.name === config.StatsCategory ||
category.id === config.StatsCategory
);
if (!statsCategory) {
const errorEmbed = new Discord.MessageEmbed()
.setTitle("Invalid Category")
.setDescription(
`${supportbot.InvalidChannel}\n\n**Category Required:** \`${config.StatsCategory}\``
)
.setColor(supportbot.ErrorColour);
interaction.reply({
embeds: [errorEmbed],
ephemeral: true,
});
return;
}
const guildName = guild.name;
const userCount = guild.memberCount;
const roleCount = guild.roles.cache.size;
const channelCount = guild.channels.cache.size;
try {
const guildNameChannel = await guild.channels.create(guildName, {
type: "GUILD_VOICE",
});
const userCountChannel = await guild.channels.create(
`${config.UserCount_Name} ${userCount}`,
{
type: "GUILD_VOICE",
}
);
const channelCountChannel = await guild.channels.create(
`${config.ChannelCount_Name} ${channelCount}`,
{
type: "GUILD_VOICE",
}
);
const roleCountChannel = await guild.channels.create(
`${config.RoleCount_Name} ${roleCount}`,
{
type: "GUILD_VOICE",
}
);
guildNameChannel.permissionOverwrites.edit(guild.id, {
VIEW_CHANNEL: true,
CONNECT: false,
});
userCountChannel.permissionOverwrites.edit(guild.id, {
VIEW_CHANNEL: true,
CONNECT: false,
});
channelCountChannel.permissionOverwrites.edit(guild.id, {
VIEW_CHANNEL: true,
CONNECT: false,
});
roleCountChannel.permissionOverwrites.edit(guild.id, {
VIEW_CHANNEL: true,
CONNECT: false,
});
userCountChannel.setParent(statsCategory.id);
roleCountChannel.setParent(statsCategory.id);
guildNameChannel.setParent(statsCategory.id);
channelCountChannel.setParent(statsCategory.id);
const successEmbed = new Discord.MessageEmbed()
.setDescription(":white_check_mark: **[DISCORD STATS]** Channels created successfully!")
.setColor(supportbot.EmbedColour);
interaction.reply({
embeds: [successEmbed],
ephemeral: true,
});
setInterval(() => {
userCountChannel.setName(
`${config.UserCount_Name} ${userCount}`
);
channelCountChannel.setName(
`${config.ChannelCount_Name} ${channelCount}`
);
roleCountChannel.setName(
`${config.RoleCount_Name} ${roleCount}`
);
}, 30000);
} catch (error) {
console.error(error);
const errorEmbed = new Discord.MessageEmbed()
.setTitle("Error")
.setDescription("An error occurred while creating the stats channels.")
.setColor(supportbot.ErrorColour);
interaction.reply({
embeds: [errorEmbed],
ephemeral: true,
});
}
},
}),
];
-292
View File
@@ -1,292 +0,0 @@
const Discord = require("discord.js");
const fs = require("fs");
const yaml = require("js-yaml");
const fetch = require("node-fetch");
const { Command } = require('../Structures/Addon.js');
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
const vouchleyaddon = yaml.load(fs.readFileSync("./Addons/Configs/vouchley.yml", "utf8"));
function formatURL(url) {
if (!url) return 'https://www.vouchley.com';
if (!url.startsWith('http://') && !url.startsWith('https://')) {
return `https://${url}`;
}
return url;
}
function formatDate(timestamp) {
return `<t:${Math.floor(new Date(timestamp).getTime() / 1000)}:D>`;
}
function createReviewEmbed(review, username, currentPage, totalReviews) {
const embed = new Discord.EmbedBuilder()
.setTitle(`Review for ${username}`)
.setColor(vouchleyaddon.Embed.Colour);
const fields = [
{
name: "Rating",
value: "⭐".repeat(Math.min(Math.max(review.rating, 1), 5)) || "No rating",
inline: false
},
{
name: "Platform",
value: review.platform?.toString() || "Not specified",
inline: true
},
{
name: "Date",
value: formatDate(review.time_sent),
inline: true
}
];
if (review.message && review.message.trim()) {
fields.push({
name: "Review",
value: `\`\`\`${review.message.substring(0, 1024)}\`\`\``,
inline: false
});
}
embed.addFields(fields);
embed.setFooter({ text: `Review ${currentPage + 1} of ${totalReviews}` });
return embed;
}
function createReviewNavigation(currentPage, totalReviews) {
return new Discord.ActionRowBuilder()
.addComponents(
new Discord.ButtonBuilder()
.setCustomId('prev_review')
.setLabel('Previous')
.setStyle(Discord.ButtonStyle.Primary)
.setDisabled(currentPage === 0),
new Discord.ButtonBuilder()
.setCustomId('next_review')
.setLabel('Next')
.setStyle(Discord.ButtonStyle.Primary)
.setDisabled(currentPage === totalReviews - 1),
new Discord.ButtonBuilder()
.setCustomId('back_to_profile')
.setLabel('Back to Profile')
.setStyle(Discord.ButtonStyle.Secondary)
);
}
const activeReviews = new Map();
module.exports = new Command({
name: vouchleyaddon.Command.Name,
description: vouchleyaddon.Command.Description,
options: [
{
name: "search_type",
description: "How do you want to search for the user?",
type: 3,
required: true,
choices: [
{ name: "Vouchley ID", value: "id" },
{ name: "Vouchley Username", value: "username" },
]
},
{
name: "query",
description: "Enter the ID, username, or Discord ID to search",
type: 3,
required: true
}
],
permissions: vouchleyaddon.Command.Permissions,
async run(interaction) {
const searchType = interaction.options.getString("search_type");
const query = interaction.options.getString("query");
try {
let apiUrl;
switch (searchType) {
case "id":
apiUrl = `https://www.vouchley.com/api/v1/user?id=${query}`;
break;
case "username":
apiUrl = `https://www.vouchley.com/api/v1/user?username=${query}`;
break;
}
const response = await fetch(apiUrl, {
headers: {
'Authorization': `Bearer ${vouchleyaddon.Vouchley.API_Key}`,
'Content-Type': 'application/json',
}
});
const data = await response.json();
if (!response.ok || (data.error && data.error.code !== 200)) {
return interaction.reply({
content: `Error: ${data.error?.message || 'Failed to fetch user data'}`,
ephemeral: vouchleyaddon.Embed.Ephemeral
});
}
const user = data.user;
const profileEmbed = new Discord.EmbedBuilder()
.setTitle(`${user.username} - ${String(user.title || "N/A")}`)
.setColor(vouchleyaddon.Embed.Colour)
.setThumbnail(user.avatar_url)
.addFields([
{
name: "Discord",
value: `<@${String(user.discord_id)}> (\`${String(user.discord_id)}\`)`,
inline: false
},
{
name: "Average Rating",
value: `${String(user.average_rating)} (\`${String(user.reviews.length)} Reviews\`)`,
inline: false
}
]);
const profileButtons = [
new Discord.ButtonBuilder()
.setLabel(`Review ${user.username}`)
.setURL(`https://www.vouchley.com/review?user=${user.username}`)
.setStyle(Discord.ButtonStyle.Link)
];
if (user.website) {
profileButtons.push(
new Discord.ButtonBuilder()
.setLabel('Visit Website')
.setURL(formatURL(user.website))
.setStyle(Discord.ButtonStyle.Link)
);
}
if (user.reviews && user.reviews.length > 0) {
profileButtons.push(
new Discord.ButtonBuilder()
.setCustomId('seereviews')
.setLabel('See Reviews')
.setEmoji("⭐")
.setStyle(Discord.ButtonStyle.Secondary)
);
}
const profileRow = new Discord.ActionRowBuilder()
.addComponents(profileButtons);
const message = await interaction.reply({
embeds: [profileEmbed],
components: [profileRow],
fetchReply: true,
ephemeral: vouchleyaddon.Embed.Ephemeral
});
if (user.reviews && user.reviews.length > 0) {
activeReviews.set(message.id, {
reviews: user.reviews,
currentPage: 0,
username: user.username,
profileEmbed,
profileRow
});
const collector = message.createMessageComponentCollector({
time: 600000
});
collector.on('collect', async i => {
const reviewData = activeReviews.get(message.id);
if (!reviewData) return;
switch (i.customId) {
case 'seereviews': {
const review = reviewData.reviews[reviewData.currentPage];
const reviewEmbed = createReviewEmbed(
review,
reviewData.username,
reviewData.currentPage,
reviewData.reviews.length
);
const reviewNav = createReviewNavigation(
reviewData.currentPage,
reviewData.reviews.length
);
await i.update({
embeds: [reviewEmbed],
components: [reviewNav],
ephemeral: vouchleyaddon.Embed.Ephemeral
});
break;
}
case 'prev_review': {
if (reviewData.currentPage > 0) {
reviewData.currentPage--;
const review = reviewData.reviews[reviewData.currentPage];
const reviewEmbed = createReviewEmbed(
review,
reviewData.username,
reviewData.currentPage,
reviewData.reviews.length
);
const reviewNav = createReviewNavigation(
reviewData.currentPage,
reviewData.reviews.length
);
await i.update({
embeds: [reviewEmbed],
components: [reviewNav],
ephemeral: vouchleyaddon.Embed.Ephemeral
});
}
break;
}
case 'next_review': {
if (reviewData.currentPage < reviewData.reviews.length - 1) {
reviewData.currentPage++;
const review = reviewData.reviews[reviewData.currentPage];
const reviewEmbed = createReviewEmbed(
review,
reviewData.username,
reviewData.currentPage,
reviewData.reviews.length
);
const reviewNav = createReviewNavigation(
reviewData.currentPage,
reviewData.reviews.length
);
await i.update({ embeds: [reviewEmbed], components: [reviewNav] });
}
break;
}
case 'back_to_profile': {
await i.update({
embeds: [reviewData.profileEmbed],
components: [reviewData.profileRow]
});
break;
}
}
});
collector.on('end', () => {
activeReviews.delete(message.id);
});
}
} catch (error) {
console.error(error);
if (!interaction.replied) {
await interaction.reply({
content: "There was an error fetching Vouchley user data.",
ephemeral: vouchleyaddon.Embed.Ephemeral
});
}
}
}
});
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.