refactor: 🧑‍💻 modules are called subcommands and groups

It makes more sense to use subcommands and groups as names instead of modules
This commit is contained in:
Axel Olausson Holtenäs 2022-12-15 13:00:51 +01:00
parent 9687f617b6
commit 3c079943b0
70 changed files with 2902 additions and 2843 deletions

View file

@ -1,51 +1,72 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
// Modules
import moduleAudits from "./modules/audits";
import moduleCpgg from "./modules/cpgg";
import moduleCredits from "./modules/credits";
import moduleEmbeds from "./modules/embeds";
import modulePoints from "./modules/points";
import moduleShop from "./modules/shop";
import moduleWelcome from "./modules/welcome";
// Subcommands
import {
builder as AuditsBuilder,
execute as AuditsExecute,
} from "./subcommands/audits";
import {
builder as CpggBuilder,
execute as CpggExecute,
} from "./subcommands/cpgg";
import {
builder as CreditsBuilder,
execute as CreditsExecute,
} from "./subcommands/credits";
import {
builder as EmbedsBuilder,
execute as EmbedsExecute,
} from "./subcommands/embeds";
import {
builder as PointsBuilder,
execute as PointsExecute,
} from "./subcommands/points";
import {
builder as ShopBuilder,
execute as ShopExecute,
} from "./subcommands/shop";
import {
builder as WelcomeBuilder,
execute as WelcomeExecute,
} from "./subcommands/welcome";
export const builder = new SlashCommandBuilder()
.setName("config")
.setDescription("Manage guild configurations.")
.setDMPermission(false)
// Modules
.addSubcommand(moduleAudits.builder)
.addSubcommand(moduleCpgg.builder)
.addSubcommand(moduleCredits.builder)
.addSubcommand(moduleEmbeds.builder)
.addSubcommand(modulePoints.builder)
.addSubcommand(moduleShop.builder)
.addSubcommand(moduleWelcome.builder);
// Subcommands
.addSubcommand(AuditsBuilder)
.addSubcommand(CpggBuilder)
.addSubcommand(CreditsBuilder)
.addSubcommand(EmbedsBuilder)
.addSubcommand(PointsBuilder)
.addSubcommand(ShopBuilder)
.addSubcommand(WelcomeBuilder);
// Execute function
export const execute = async (interaction: ChatInputCommandInteraction) => {
switch (interaction.options.getSubcommand()) {
case "audits":
await moduleAudits.execute(interaction);
await AuditsExecute(interaction);
break;
case "cpgg":
await moduleCpgg.execute(interaction);
await CpggExecute(interaction);
break;
case "credits":
await moduleCredits.execute(interaction);
await CreditsExecute(interaction);
break;
case "embeds":
await moduleEmbeds.execute(interaction);
await EmbedsExecute(interaction);
break;
case "points":
await modulePoints.execute(interaction);
await PointsExecute(interaction);
break;
case "shop":
await moduleShop.execute(interaction);
await ShopExecute(interaction);
break;
case "welcome":
await moduleWelcome.execute(interaction);
await WelcomeExecute(interaction);
break;
default:
throw new Error("No module found for that specific command.");

View file

@ -1,97 +0,0 @@
import {
ChannelType,
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("audits")
.setDescription("Audits")
.addBooleanOption((option) =>
option
.setName("status")
.setDescription("Should audits be enabled?")
.setRequired(true)
)
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("Channel for audit messages.")
.addChannelTypes(ChannelType.GuildText)
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { guild, options } = interaction;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
guild
);
const status = options.getBoolean("status");
const channel = options.getChannel("channel");
if (!guild) throw new Error("Guild not found.");
if (!channel) throw new Error("Channel not found.");
if (status === null) throw new Error("Status not found.");
const createGuild = await prisma.guild.upsert({
where: {
id: guild.id,
},
update: {
auditsEnabled: status,
auditsChannelId: channel.id,
},
create: {
id: guild.id,
auditsEnabled: status,
auditsChannelId: channel.id,
},
});
logger.silly(createGuild);
const embedSuccess = new EmbedBuilder()
.setTitle("[:hammer:] Audits")
.setDescription("Guild configuration updated successfully.")
.setColor(successColor)
.addFields(
{
name: "🤖 Status",
value: `${
createGuild.auditsEnabled
? ":white_check_mark: Enabled"
: ":x: Disabled"
}`,
inline: true,
},
{
name: "🌊 Channel",
value: `<#${createGuild.auditsChannelId}>`,
inline: true,
}
)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
await interaction.editReply({
embeds: [embedSuccess],
});
return;
},
};

View file

@ -1,106 +0,0 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import encryption from "../../../../helpers/encryption";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("cpgg")
.setDescription("Controlpanel.gg")
.addStringOption((option) =>
option
.setName("scheme")
.setDescription(`Controlpanel.gg Scheme`)
.setRequired(true)
.setChoices(
{ name: "HTTPS (secure)", value: "https" },
{ name: "HTTP (insecure)", value: "http" }
)
)
.addStringOption((option) =>
option
.setName("domain")
.setDescription(`Controlpanel.gg Domain`)
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("token")
.setDescription(`Controlpanel.gg Application API`)
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { options, guild } = interaction;
const tokenData = options.getString("token");
const scheme = options.getString("scheme");
const domain = options.getString("domain");
const token = tokenData && encryption.encrypt(tokenData);
const url = scheme && domain && encryption.encrypt(`${scheme}://${domain}`);
if (!guild) throw new Error("No guild found");
if (!token) throw new Error("Token not found");
if (!url) throw new Error("URL not found");
const createGuild = await prisma.guild.upsert({
where: {
id: guild.id,
},
update: {
apiCpggTokenIv: token.iv,
apiCpggTokenContent: token.content,
apiCpggUrlIv: url.iv,
apiCpggUrlContent: url.content,
},
create: {
id: guild.id,
apiCpggTokenIv: token.iv,
apiCpggTokenContent: token.content,
apiCpggUrlIv: url.iv,
apiCpggUrlContent: url.content,
},
});
logger.silly(createGuild);
logger?.silly(`Updated API credentials.`);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:tools:] CPGG")
.setDescription(
`The following configuration will be used.
**Scheme**: ${scheme}
**Domain**: ${domain}
**Token**: ends with ${tokenData?.slice(-4)}`
)
.setColor(successColor)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
await interaction?.editReply({
embeds: [interactionEmbed],
});
return;
},
};

View file

@ -1,165 +0,0 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("credits")
.setDescription(`Configure this guild's credits module.`)
.addBooleanOption((option) =>
option
.setName("enabled")
.setDescription("Do you want to activate the credit module?")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("rate")
.setDescription("Credit rate per message.")
.setRequired(true)
.setMinValue(1)
)
.addNumberOption((option) =>
option
.setName("minimum-length")
.setDescription("Minimum message length to receive credit.")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("work-rate")
.setDescription(
"The maximum amount of credit that can be obtained within a working day."
)
.setRequired(true)
.setMinValue(1)
)
.addNumberOption((option) =>
option
.setName("work-timeout")
.setDescription(
"How long you need to wait before you can work again provided in seconds."
)
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("timeout")
.setDescription(
"How long you need to wait before you can earn more credits."
)
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { guild, options } = interaction;
const enabled = options.getBoolean("enabled");
const rate = options.getNumber("rate");
const timeout = options.getNumber("timeout");
const minimumLength = options.getNumber("minimum-length");
const workRate = options.getNumber("work-rate");
const workTimeout = options.getNumber("work-timeout");
if (!guild) throw new Error("Guild not found.");
if (typeof enabled !== "boolean")
throw new Error("Enabled option is not a boolean.");
if (typeof rate !== "number") throw new Error("Rate is not a number.");
if (typeof workRate !== "number")
throw new Error("Work rate is not a number.");
if (typeof workTimeout !== "number")
throw new Error("Work timeout is not a number.");
if (typeof timeout !== "number")
throw new Error("Timeout is not a number.");
if (typeof minimumLength !== "number")
throw new Error("Minimum length is not a number.");
const createGuild = await prisma.guild.upsert({
where: {
id: guild.id,
},
update: {
creditsEnabled: enabled,
creditsRate: rate,
creditsTimeout: timeout,
creditsWorkRate: workRate,
creditsWorkTimeout: workTimeout,
creditsMinimumLength: minimumLength,
},
create: {
id: guild.id,
creditsEnabled: enabled,
creditsRate: rate,
creditsTimeout: timeout,
creditsWorkRate: workRate,
creditsWorkTimeout: workTimeout,
creditsMinimumLength: minimumLength,
},
});
logger.silly(createGuild);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:tools:] Credits")
.setDescription("Credits settings updated")
.setColor(successColor)
.addFields(
{
name: "🤖 Enabled?",
value: `${createGuild.creditsEnabled}`,
inline: true,
},
{
name: "📈 Rate",
value: `${createGuild.creditsRate}`,
inline: true,
},
{
name: "📈 Work Rate",
value: `${createGuild.creditsWorkRate}`,
inline: true,
},
{
name: "🔨 Minimum Length",
value: `${createGuild.creditsMinimumLength}`,
inline: true,
},
{
name: "⏰ Timeout",
value: `${createGuild.creditsTimeout}`,
inline: true,
},
{
name: "⏰ Work Timeout",
value: `${createGuild.creditsWorkTimeout}`,
inline: true,
}
)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
await interaction.editReply({
embeds: [interactionEmbed],
});
return;
},
};

View file

@ -1,100 +0,0 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import getValues from "./components/getValues";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("embeds")
.setDescription(`Embeds`)
.addStringOption((option) =>
option
.setName("success-color")
.setDescription("No provided description")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("wait-color")
.setDescription("No provided description")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("error-color")
.setDescription("No provided description")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("footer-icon")
.setDescription("No provided description")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("footer-text")
.setDescription("No provided description")
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { guild } = interaction;
if (!guild) throw new Error("Guild not found");
const { successColor, waitColor, errorColor, footerText, footerIcon } =
await getValues(interaction);
const embed = new EmbedBuilder()
.setTitle("[:tools:] Embeds")
.setFooter({ text: footerText, iconURL: footerIcon })
.setTimestamp(new Date());
embed
.setDescription("Following embed configuration will be used.")
.setColor(successColor)
.addFields([
{
name: "🟢 Success Color",
value: `${successColor}`,
inline: true,
},
{
name: "🟡 Wait Color",
value: `${waitColor}`,
inline: true,
},
{
name: "🔴 Error Color",
value: `${errorColor}`,
inline: true,
},
{
name: "🖼️ Footer Icon",
value: `${footerIcon}`,
inline: true,
},
{
name: "📄 Footer Text",
value: `${footerText}`,
inline: true,
},
]);
await interaction.editReply({
embeds: [embed],
});
return;
},
};

View file

@ -1,123 +0,0 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("points")
.setDescription("Points")
.addBooleanOption((option) =>
option
.setName("status")
.setDescription("Should credits be enabled?")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("rate")
.setDescription("Amount of credits per message.")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("minimum-length")
.setDescription("Minimum length of message to earn credits.")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("timeout")
.setDescription("Timeout between earning credits (milliseconds).")
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { options, guild } = interaction;
const status = options?.getBoolean("status");
const rate = options?.getNumber("rate");
const timeout = options?.getNumber("timeout");
const minimumLength = options?.getNumber("minimum-length");
if (!guild) throw new Error("Guild is required");
if (status === null) throw new Error("Status must be specified");
if (!rate) throw new Error("Rate must be specified");
if (!timeout) throw new Error("Timeout must be specified");
if (!minimumLength) throw new Error("Minimum length must be specified");
const createGuild = await prisma.guild.upsert({
where: {
id: guild.id,
},
update: {
pointsEnabled: status,
pointsRate: rate,
pointsTimeout: timeout,
pointsMinimumLength: minimumLength,
},
create: {
id: guild.id,
pointsEnabled: status,
pointsRate: rate,
pointsTimeout: timeout,
pointsMinimumLength: minimumLength,
},
});
logger.silly(createGuild);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:tools:] Points")
.setDescription("Points settings updated")
.setColor(successColor)
.addFields(
{
name: "🤖 Status",
value: `${createGuild.pointsEnabled}`,
inline: true,
},
{
name: "📈 Rate",
value: `${createGuild.pointsRate}`,
inline: true,
},
{
name: "🔨 Minimum Length",
value: `${createGuild.pointsMinimumLength}`,
inline: true,
},
{
name: "⏰ Timeout",
value: `${createGuild.pointsTimeout}`,
inline: true,
}
)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
await interaction?.editReply({
embeds: [interactionEmbed],
});
return;
},
};

View file

@ -1,93 +0,0 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("shop")
.setDescription("Shop")
.addBooleanOption((option) =>
option
.setName("roles-status")
.setDescription("Should roles be enabled?")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("roles-price-per-hour")
.setDescription("Price per hour for roles.")
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { options, guild } = interaction;
const rolesStatus = options?.getBoolean("roles-status");
const rolesPricePerHour = options?.getNumber("roles-price-per-hour");
if (!guild) throw new Error("Guild not found");
if (rolesStatus === null) throw new Error("Status must be provided");
if (!rolesPricePerHour)
throw new Error("Roles price per hour must be provided");
const createGuild = await prisma.guild.upsert({
where: {
id: guild.id,
},
update: {
shopRolesEnabled: rolesStatus,
shopRolesPricePerHour: rolesPricePerHour,
},
create: {
id: guild.id,
shopRolesEnabled: rolesStatus,
shopRolesPricePerHour: rolesPricePerHour,
},
});
logger.silly(createGuild);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:tools:] Shop")
.setDescription("Shop settings updated")
.setColor(successColor)
.addFields(
{
name: "🤖 Roles Status",
value: `${createGuild.shopRolesEnabled}`,
inline: true,
},
{
name: "🌊 Roles Price Per Hour",
value: `${createGuild.shopRolesPricePerHour}`,
inline: true,
}
)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
await interaction?.editReply({
embeds: [interactionEmbed],
});
return;
},
};

View file

@ -1,147 +0,0 @@
import {
ChannelType,
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("welcome")
.setDescription("Welcome")
.addBooleanOption((option) =>
option
.setName("status")
.setDescription("Should welcome be enabled?")
.setRequired(true)
)
.addChannelOption((option) =>
option
.setName("join-channel")
.setDescription("Channel for join messages.")
.addChannelTypes(ChannelType.GuildText)
.setRequired(true)
)
.addChannelOption((option) =>
option
.setName("leave-channel")
.setDescription("Channel for leave messages.")
.addChannelTypes(ChannelType.GuildText)
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("leave-message")
.setDescription("Message for leave messages.")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("join-message")
.setDescription("Message for join messages.")
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { options, guild } = interaction;
const status = options?.getBoolean("status");
const joinChannel = options?.getChannel("join-channel");
const leaveChannel = options?.getChannel("leave-channel");
const joinChannelMessage = options?.getString("join-message");
const leaveChannelMessage = options?.getString("leave-message");
if (!guild) throw new Error("Guild not found");
if (status === null) throw new Error("Status not specified");
if (!joinChannel) throw new Error("Join channel not specified");
if (!joinChannelMessage)
throw new Error("Join channel message not specified");
if (!leaveChannel) throw new Error("Leave channel not specified");
if (!leaveChannelMessage)
throw new Error("Leave channel message not specified");
const createGuild = await prisma.guild.upsert({
where: {
id: guild.id,
},
update: {
welcomeEnabled: status,
welcomeJoinChannelId: joinChannel.id,
welcomeJoinChannelMessage: joinChannelMessage,
welcomeLeaveChannelId: leaveChannel.id,
welcomeLeaveChannelMessage: leaveChannelMessage,
},
create: {
id: guild.id,
welcomeEnabled: status,
welcomeJoinChannelId: joinChannel.id,
welcomeJoinChannelMessage: joinChannelMessage,
welcomeLeaveChannelId: leaveChannel.id,
welcomeLeaveChannelMessage: leaveChannelMessage,
},
});
logger.silly(createGuild);
const interactionEmbedDisabled = new EmbedBuilder()
.setTitle("[:tools:] Welcome")
.setDescription(
"This module is currently disabled, please enable it to continue."
)
.setColor(successColor)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
if (!createGuild.welcomeEnabled) {
return interaction?.editReply({
embeds: [interactionEmbedDisabled],
});
}
const interactionEmbed = new EmbedBuilder()
.setTitle("[:tools:] Welcome")
.setDescription(
`The following configuration will be used.
[👋] **Welcome**
**Channel**: <#${createGuild.welcomeJoinChannelId}>
**Message**: ${createGuild.welcomeJoinChannelMessage}
[🚪] **Leave**
**Channel**: <#${createGuild.welcomeLeaveChannelId}>
**Message**: ${createGuild.welcomeLeaveChannelMessage}`
)
.setColor(successColor)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
await interaction?.editReply({
embeds: [interactionEmbed],
});
return true;
},
};

View file

@ -0,0 +1,94 @@
import {
ChannelType,
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("audits")
.setDescription("Audits")
.addBooleanOption((option) =>
option
.setName("status")
.setDescription("Should audits be enabled?")
.setRequired(true)
)
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("Channel for audit messages.")
.addChannelTypes(ChannelType.GuildText)
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { guild, options } = interaction;
const { successColor, footerText, footerIcon } = await getEmbedConfig(guild);
const status = options.getBoolean("status");
const channel = options.getChannel("channel");
if (!guild) throw new Error("Guild not found.");
if (!channel) throw new Error("Channel not found.");
if (status === null) throw new Error("Status not found.");
const createGuild = await prisma.guild.upsert({
where: {
id: guild.id,
},
update: {
auditsEnabled: status,
auditsChannelId: channel.id,
},
create: {
id: guild.id,
auditsEnabled: status,
auditsChannelId: channel.id,
},
});
logger.silly(createGuild);
const embedSuccess = new EmbedBuilder()
.setTitle("[:hammer:] Audits")
.setDescription("Guild configuration updated successfully.")
.setColor(successColor)
.addFields(
{
name: "🤖 Status",
value: `${
createGuild.auditsEnabled
? ":white_check_mark: Enabled"
: ":x: Disabled"
}`,
inline: true,
},
{
name: "🌊 Channel",
value: `<#${createGuild.auditsChannelId}>`,
inline: true,
}
)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
await interaction.editReply({
embeds: [embedSuccess],
});
return;
};

View file

@ -0,0 +1,105 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import encryption from "../../../../helpers/encryption";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("cpgg")
.setDescription("Controlpanel.gg")
.addStringOption((option) =>
option
.setName("scheme")
.setDescription(`Controlpanel.gg Scheme`)
.setRequired(true)
.setChoices(
{ name: "HTTPS (secure)", value: "https" },
{ name: "HTTP (insecure)", value: "http" }
)
)
.addStringOption((option) =>
option
.setName("domain")
.setDescription(`Controlpanel.gg Domain`)
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("token")
.setDescription(`Controlpanel.gg Application API`)
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { options, guild } = interaction;
const tokenData = options.getString("token");
const scheme = options.getString("scheme");
const domain = options.getString("domain");
const token = tokenData && encryption.encrypt(tokenData);
const url = scheme && domain && encryption.encrypt(`${scheme}://${domain}`);
if (!guild) throw new Error("No guild found");
if (!token) throw new Error("Token not found");
if (!url) throw new Error("URL not found");
const createGuild = await prisma.guild.upsert({
where: {
id: guild.id,
},
update: {
apiCpggTokenIv: token.iv,
apiCpggTokenContent: token.content,
apiCpggUrlIv: url.iv,
apiCpggUrlContent: url.content,
},
create: {
id: guild.id,
apiCpggTokenIv: token.iv,
apiCpggTokenContent: token.content,
apiCpggUrlIv: url.iv,
apiCpggUrlContent: url.content,
},
});
logger.silly(createGuild);
logger?.silly(`Updated API credentials.`);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:tools:] CPGG")
.setDescription(
`The following configuration will be used.
**Scheme**: ${scheme}
**Domain**: ${domain}
**Token**: ends with ${tokenData?.slice(-4)}`
)
.setColor(successColor)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
await interaction?.editReply({
embeds: [interactionEmbed],
});
return;
};

View file

@ -0,0 +1,163 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("credits")
.setDescription(`Configure this guild's credits module.`)
.addBooleanOption((option) =>
option
.setName("enabled")
.setDescription("Do you want to activate the credit module?")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("rate")
.setDescription("Credit rate per message.")
.setRequired(true)
.setMinValue(1)
)
.addNumberOption((option) =>
option
.setName("minimum-length")
.setDescription("Minimum message length to receive credit.")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("work-rate")
.setDescription(
"The maximum amount of credit that can be obtained within a working day."
)
.setRequired(true)
.setMinValue(1)
)
.addNumberOption((option) =>
option
.setName("work-timeout")
.setDescription(
"How long you need to wait before you can work again provided in seconds."
)
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("timeout")
.setDescription(
"How long you need to wait before you can earn more credits."
)
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { guild, options } = interaction;
const enabled = options.getBoolean("enabled");
const rate = options.getNumber("rate");
const timeout = options.getNumber("timeout");
const minimumLength = options.getNumber("minimum-length");
const workRate = options.getNumber("work-rate");
const workTimeout = options.getNumber("work-timeout");
if (!guild) throw new Error("Guild not found.");
if (typeof enabled !== "boolean")
throw new Error("Enabled option is not a boolean.");
if (typeof rate !== "number") throw new Error("Rate is not a number.");
if (typeof workRate !== "number")
throw new Error("Work rate is not a number.");
if (typeof workTimeout !== "number")
throw new Error("Work timeout is not a number.");
if (typeof timeout !== "number") throw new Error("Timeout is not a number.");
if (typeof minimumLength !== "number")
throw new Error("Minimum length is not a number.");
const createGuild = await prisma.guild.upsert({
where: {
id: guild.id,
},
update: {
creditsEnabled: enabled,
creditsRate: rate,
creditsTimeout: timeout,
creditsWorkRate: workRate,
creditsWorkTimeout: workTimeout,
creditsMinimumLength: minimumLength,
},
create: {
id: guild.id,
creditsEnabled: enabled,
creditsRate: rate,
creditsTimeout: timeout,
creditsWorkRate: workRate,
creditsWorkTimeout: workTimeout,
creditsMinimumLength: minimumLength,
},
});
logger.silly(createGuild);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:tools:] Credits")
.setDescription("Credits settings updated")
.setColor(successColor)
.addFields(
{
name: "🤖 Enabled?",
value: `${createGuild.creditsEnabled}`,
inline: true,
},
{
name: "📈 Rate",
value: `${createGuild.creditsRate}`,
inline: true,
},
{
name: "📈 Work Rate",
value: `${createGuild.creditsWorkRate}`,
inline: true,
},
{
name: "🔨 Minimum Length",
value: `${createGuild.creditsMinimumLength}`,
inline: true,
},
{
name: "⏰ Timeout",
value: `${createGuild.creditsTimeout}`,
inline: true,
},
{
name: "⏰ Work Timeout",
value: `${createGuild.creditsWorkTimeout}`,
inline: true,
}
)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
await interaction.editReply({
embeds: [interactionEmbed],
});
return;
};

View file

@ -0,0 +1,99 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import getValues from "./components/getValues";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("embeds")
.setDescription(`Embeds`)
.addStringOption((option) =>
option
.setName("success-color")
.setDescription("No provided description")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("wait-color")
.setDescription("No provided description")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("error-color")
.setDescription("No provided description")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("footer-icon")
.setDescription("No provided description")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("footer-text")
.setDescription("No provided description")
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { guild } = interaction;
if (!guild) throw new Error("Guild not found");
const { successColor, waitColor, errorColor, footerText, footerIcon } =
await getValues(interaction);
const embed = new EmbedBuilder()
.setTitle("[:tools:] Embeds")
.setFooter({ text: footerText, iconURL: footerIcon })
.setTimestamp(new Date());
embed
.setDescription("Following embed configuration will be used.")
.setColor(successColor)
.addFields([
{
name: "🟢 Success Color",
value: `${successColor}`,
inline: true,
},
{
name: "🟡 Wait Color",
value: `${waitColor}`,
inline: true,
},
{
name: "🔴 Error Color",
value: `${errorColor}`,
inline: true,
},
{
name: "🖼️ Footer Icon",
value: `${footerIcon}`,
inline: true,
},
{
name: "📄 Footer Text",
value: `${footerText}`,
inline: true,
},
]);
await interaction.editReply({
embeds: [embed],
});
return;
};

View file

@ -0,0 +1,122 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("points")
.setDescription("Points")
.addBooleanOption((option) =>
option
.setName("status")
.setDescription("Should credits be enabled?")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("rate")
.setDescription("Amount of credits per message.")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("minimum-length")
.setDescription("Minimum length of message to earn credits.")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("timeout")
.setDescription("Timeout between earning credits (milliseconds).")
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { options, guild } = interaction;
const status = options?.getBoolean("status");
const rate = options?.getNumber("rate");
const timeout = options?.getNumber("timeout");
const minimumLength = options?.getNumber("minimum-length");
if (!guild) throw new Error("Guild is required");
if (status === null) throw new Error("Status must be specified");
if (!rate) throw new Error("Rate must be specified");
if (!timeout) throw new Error("Timeout must be specified");
if (!minimumLength) throw new Error("Minimum length must be specified");
const createGuild = await prisma.guild.upsert({
where: {
id: guild.id,
},
update: {
pointsEnabled: status,
pointsRate: rate,
pointsTimeout: timeout,
pointsMinimumLength: minimumLength,
},
create: {
id: guild.id,
pointsEnabled: status,
pointsRate: rate,
pointsTimeout: timeout,
pointsMinimumLength: minimumLength,
},
});
logger.silly(createGuild);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:tools:] Points")
.setDescription("Points settings updated")
.setColor(successColor)
.addFields(
{
name: "🤖 Status",
value: `${createGuild.pointsEnabled}`,
inline: true,
},
{
name: "📈 Rate",
value: `${createGuild.pointsRate}`,
inline: true,
},
{
name: "🔨 Minimum Length",
value: `${createGuild.pointsMinimumLength}`,
inline: true,
},
{
name: "⏰ Timeout",
value: `${createGuild.pointsTimeout}`,
inline: true,
}
)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
await interaction?.editReply({
embeds: [interactionEmbed],
});
return;
};

View file

@ -0,0 +1,92 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("shop")
.setDescription("Shop")
.addBooleanOption((option) =>
option
.setName("roles-status")
.setDescription("Should roles be enabled?")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("roles-price-per-hour")
.setDescription("Price per hour for roles.")
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { options, guild } = interaction;
const rolesStatus = options?.getBoolean("roles-status");
const rolesPricePerHour = options?.getNumber("roles-price-per-hour");
if (!guild) throw new Error("Guild not found");
if (rolesStatus === null) throw new Error("Status must be provided");
if (!rolesPricePerHour)
throw new Error("Roles price per hour must be provided");
const createGuild = await prisma.guild.upsert({
where: {
id: guild.id,
},
update: {
shopRolesEnabled: rolesStatus,
shopRolesPricePerHour: rolesPricePerHour,
},
create: {
id: guild.id,
shopRolesEnabled: rolesStatus,
shopRolesPricePerHour: rolesPricePerHour,
},
});
logger.silly(createGuild);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:tools:] Shop")
.setDescription("Shop settings updated")
.setColor(successColor)
.addFields(
{
name: "🤖 Roles Status",
value: `${createGuild.shopRolesEnabled}`,
inline: true,
},
{
name: "🌊 Roles Price Per Hour",
value: `${createGuild.shopRolesPricePerHour}`,
inline: true,
}
)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
await interaction?.editReply({
embeds: [interactionEmbed],
});
return;
};

View file

@ -0,0 +1,146 @@
import {
ChannelType,
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("welcome")
.setDescription("Welcome")
.addBooleanOption((option) =>
option
.setName("status")
.setDescription("Should welcome be enabled?")
.setRequired(true)
)
.addChannelOption((option) =>
option
.setName("join-channel")
.setDescription("Channel for join messages.")
.addChannelTypes(ChannelType.GuildText)
.setRequired(true)
)
.addChannelOption((option) =>
option
.setName("leave-channel")
.setDescription("Channel for leave messages.")
.addChannelTypes(ChannelType.GuildText)
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("leave-message")
.setDescription("Message for leave messages.")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("join-message")
.setDescription("Message for join messages.")
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { options, guild } = interaction;
const status = options?.getBoolean("status");
const joinChannel = options?.getChannel("join-channel");
const leaveChannel = options?.getChannel("leave-channel");
const joinChannelMessage = options?.getString("join-message");
const leaveChannelMessage = options?.getString("leave-message");
if (!guild) throw new Error("Guild not found");
if (status === null) throw new Error("Status not specified");
if (!joinChannel) throw new Error("Join channel not specified");
if (!joinChannelMessage)
throw new Error("Join channel message not specified");
if (!leaveChannel) throw new Error("Leave channel not specified");
if (!leaveChannelMessage)
throw new Error("Leave channel message not specified");
const createGuild = await prisma.guild.upsert({
where: {
id: guild.id,
},
update: {
welcomeEnabled: status,
welcomeJoinChannelId: joinChannel.id,
welcomeJoinChannelMessage: joinChannelMessage,
welcomeLeaveChannelId: leaveChannel.id,
welcomeLeaveChannelMessage: leaveChannelMessage,
},
create: {
id: guild.id,
welcomeEnabled: status,
welcomeJoinChannelId: joinChannel.id,
welcomeJoinChannelMessage: joinChannelMessage,
welcomeLeaveChannelId: leaveChannel.id,
welcomeLeaveChannelMessage: leaveChannelMessage,
},
});
logger.silly(createGuild);
const interactionEmbedDisabled = new EmbedBuilder()
.setTitle("[:tools:] Welcome")
.setDescription(
"This module is currently disabled, please enable it to continue."
)
.setColor(successColor)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
if (!createGuild.welcomeEnabled) {
return interaction?.editReply({
embeds: [interactionEmbedDisabled],
});
}
const interactionEmbed = new EmbedBuilder()
.setTitle("[:tools:] Welcome")
.setDescription(
`The following configuration will be used.
[👋] **Welcome**
**Channel**: <#${createGuild.welcomeJoinChannelId}>
**Message**: ${createGuild.welcomeJoinChannelMessage}
[🚪] **Leave**
**Channel**: <#${createGuild.welcomeLeaveChannelId}>
**Message**: ${createGuild.welcomeLeaveChannelMessage}`
)
.setColor(successColor)
.setTimestamp()
.setFooter({
iconURL: footerIcon,
text: footerText,
});
await interaction?.editReply({
embeds: [interactionEmbed],
});
return true;
};

View file

@ -1,7 +1,10 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
// Modules
import { builder as ViewBuilder, execute as ViewExecute } from "./modules/view";
// Subcommands
import {
builder as ViewBuilder,
execute as ViewExecute,
} from "./subcommands/view";
//
export const builder = new SlashCommandBuilder()
@ -9,7 +12,7 @@ export const builder = new SlashCommandBuilder()
.setDescription("View guild counters")
.setDMPermission(false)
// Modules
// Subcommands
.addSubcommand(ViewBuilder);
// Execute function

View file

@ -1,13 +1,22 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
// Modules
// Subcommands
import {
builder as BalanceBuilder,
execute as BalanceExecute,
} from "./modules/balance";
import { builder as GiftBuilder, execute as GiftExecute } from "./modules/gift";
import { builder as TopBuilder, execute as TopExecute } from "./modules/top";
import { builder as WorkBuilder, execute as WorkExecute } from "./modules/work";
} from "./subcommands/balance";
import {
builder as GiftBuilder,
execute as GiftExecute,
} from "./subcommands/gift";
import {
builder as TopBuilder,
execute as TopExecute,
} from "./subcommands/top";
import {
builder as WorkBuilder,
execute as WorkExecute,
} from "./subcommands/work";
// 1. Export a builder function.
export const builder = new SlashCommandBuilder()

View file

@ -1,20 +1,23 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
// Modules
import moduleLookup from "./modules/lookup";
// Subcommands
import {
builder as LookupBuilder,
execute as LookupExecute,
} from "./subcommands/lookup";
export const builder = new SlashCommandBuilder()
.setName("dns")
.setDescription("DNS commands.")
// Modules
.addSubcommand(moduleLookup.builder);
// Subcommands
.addSubcommand(LookupBuilder);
// Execute the command
export const execute = async (interaction: ChatInputCommandInteraction) => {
switch (interaction.options.getSubcommand()) {
case "lookup":
await moduleLookup.execute(interaction);
await LookupExecute(interaction);
break;
default:
throw new Error(

View file

@ -1,133 +0,0 @@
import axios from "axios";
import {
ChatInputCommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("lookup")
.setDescription(
"Lookup a domain or ip. (Request sent over HTTP, proceed with caution!)"
)
.addStringOption((option) =>
option
.setName("query")
.setDescription("The query you want to look up.")
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, false);
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const embedTitle = "[:hammer:] Utility (Lookup)";
const { options } = interaction;
const query = options.getString("query");
await axios
.get(`http://ip-api.com/json/${query}`)
.then(async (response) => {
if (response.data.status !== "success") {
await interaction.editReply({
embeds: [
new EmbedBuilder()
.setTitle(embedTitle)
.setFooter({
text: footerText,
iconURL: footerIcon,
})
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon })
.setDescription(
`${response?.data?.message}: ${response?.data?.query}`
),
],
});
return;
}
await interaction.editReply({
embeds: [
new EmbedBuilder()
.setTitle(embedTitle)
.setFooter({
text: footerText,
iconURL: footerIcon,
})
.setTimestamp(new Date())
.setColor(successColor)
.setFields([
{
name: ":classical_building: AS",
value: `${response.data.as || "Unknown"}`,
inline: true,
},
{
name: ":classical_building: ISP",
value: `${response.data.isp || "Unknown"}`,
inline: true,
},
{
name: ":classical_building: Organization",
value: `${response.data.org || "Unknown"}`,
inline: true,
},
{
name: ":compass: Latitude",
value: `${response.data.lat || "Unknown"}`,
inline: true,
},
{
name: ":compass: Longitude",
value: `${response.data.lon || "Unknown"}`,
inline: true,
},
{
name: ":clock4: Timezone",
value: `${response.data.timezone || "Unknown"}`,
inline: true,
},
{
name: ":globe_with_meridians: Country",
value: `${response.data.country || "Unknown"}`,
inline: true,
},
{
name: ":globe_with_meridians: Region",
value: `${response.data.regionName || "Unknown"}`,
inline: true,
},
{
name: ":globe_with_meridians: City",
value: `${response.data.city || "Unknown"}`,
inline: true,
},
{
name: ":globe_with_meridians: Country Code",
value: `${response.data.countryCode || "Unknown"}`,
inline: true,
},
{
name: ":globe_with_meridians: Region Code",
value: `${response.data.region || "Unknown"}`,
inline: true,
},
{
name: ":globe_with_meridians: ZIP",
value: `${response.data.zip || "Unknown"}`,
inline: true,
},
]),
],
});
});
},
};

View file

@ -0,0 +1,130 @@
import axios from "axios";
import {
ChatInputCommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("lookup")
.setDescription(
"Lookup a domain or ip. (Request sent over HTTP, proceed with caution!)"
)
.addStringOption((option) =>
option
.setName("query")
.setDescription("The query you want to look up.")
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, false);
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const embedTitle = "[:hammer:] Utility (Lookup)";
const { options } = interaction;
const query = options.getString("query");
await axios.get(`http://ip-api.com/json/${query}`).then(async (response) => {
if (response.data.status !== "success") {
await interaction.editReply({
embeds: [
new EmbedBuilder()
.setTitle(embedTitle)
.setFooter({
text: footerText,
iconURL: footerIcon,
})
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon })
.setDescription(
`${response?.data?.message}: ${response?.data?.query}`
),
],
});
return;
}
await interaction.editReply({
embeds: [
new EmbedBuilder()
.setTitle(embedTitle)
.setFooter({
text: footerText,
iconURL: footerIcon,
})
.setTimestamp(new Date())
.setColor(successColor)
.setFields([
{
name: ":classical_building: AS",
value: `${response.data.as || "Unknown"}`,
inline: true,
},
{
name: ":classical_building: ISP",
value: `${response.data.isp || "Unknown"}`,
inline: true,
},
{
name: ":classical_building: Organization",
value: `${response.data.org || "Unknown"}`,
inline: true,
},
{
name: ":compass: Latitude",
value: `${response.data.lat || "Unknown"}`,
inline: true,
},
{
name: ":compass: Longitude",
value: `${response.data.lon || "Unknown"}`,
inline: true,
},
{
name: ":clock4: Timezone",
value: `${response.data.timezone || "Unknown"}`,
inline: true,
},
{
name: ":globe_with_meridians: Country",
value: `${response.data.country || "Unknown"}`,
inline: true,
},
{
name: ":globe_with_meridians: Region",
value: `${response.data.regionName || "Unknown"}`,
inline: true,
},
{
name: ":globe_with_meridians: City",
value: `${response.data.city || "Unknown"}`,
inline: true,
},
{
name: ":globe_with_meridians: Country Code",
value: `${response.data.countryCode || "Unknown"}`,
inline: true,
},
{
name: ":globe_with_meridians: Region Code",
value: `${response.data.region || "Unknown"}`,
inline: true,
},
{
name: ":globe_with_meridians: ZIP",
value: `${response.data.zip || "Unknown"}`,
inline: true,
},
]),
],
});
});
};

View file

@ -1,21 +1,24 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
import logger from "../../middlewares/logger";
// Modules
import moduleMeme from "./modules/meme";
// Subcommands
import {
builder as MemeBuilder,
execute as MemeExecute,
} from "./subcommands/meme";
export const builder = new SlashCommandBuilder()
.setName("fun")
.setDescription("Fun commands.")
.addSubcommand(moduleMeme.builder);
.addSubcommand(MemeBuilder);
// Execute function
export const execute = async (interaction: ChatInputCommandInteraction) => {
const { options } = interaction;
if (options.getSubcommand() === "meme") {
await moduleMeme.execute(interaction);
await MemeExecute(interaction);
} else {
logger.silly(`Unknown subcommand ${options.getSubcommand()}`);
}

View file

@ -1,61 +0,0 @@
import axios from "axios";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
CommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import cooldown from "../../../../middlewares/cooldown";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command.setName("meme").setDescription("Random memes from r/memes");
},
execute: async (interaction: CommandInteraction) => {
await deferReply(interaction, false);
const { guild, user, commandId } = interaction;
if (!guild) throw new Error("Server unavailable");
if (!user) throw new Error("User unavailable");
await cooldown(guild, user, commandId, 15);
const embedConfig = await getEmbedConfig(guild);
await axios
.get("https://www.reddit.com/r/memes/random/.json")
.then(async (res) => {
const response = res.data[0].data.children;
const content = response[0].data;
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("View post")
.setStyle(ButtonStyle.Link)
.setEmoji("🔗")
.setURL(`https://reddit.com${content.permalink}`)
);
const embed = new EmbedBuilder()
.setTitle(`😆︱Meme`)
.setDescription(`**${content.title}**`)
.setTimestamp(new Date())
.setImage(content.url)
.setFooter({
text: `👍 ${content.ups}︱👎 ${content.downs}`,
})
.setColor(embedConfig.successColor);
await interaction.editReply({ embeds: [embed], components: [buttons] });
return;
})
.catch((error) => {
throw new Error(error.message);
});
},
};

View file

@ -0,0 +1,59 @@
import axios from "axios";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
CommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import cooldown from "../../../../middlewares/cooldown";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command.setName("meme").setDescription("Random memes from r/memes");
};
export const execute = async (interaction: CommandInteraction) => {
await deferReply(interaction, false);
const { guild, user, commandId } = interaction;
if (!guild) throw new Error("Server unavailable");
if (!user) throw new Error("User unavailable");
await cooldown(guild, user, commandId, 15);
const embedConfig = await getEmbedConfig(guild);
await axios
.get("https://www.reddit.com/r/memes/random/.json")
.then(async (res) => {
const response = res.data[0].data.children;
const content = response[0].data;
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("View post")
.setStyle(ButtonStyle.Link)
.setEmoji("🔗")
.setURL(`https://reddit.com${content.permalink}`)
);
const embed = new EmbedBuilder()
.setTitle(`😆︱Meme`)
.setDescription(`**${content.title}**`)
.setTimestamp(new Date())
.setImage(content.url)
.setFooter({
text: `👍 ${content.ups}︱👎 ${content.downs}`,
})
.setColor(embedConfig.successColor);
await interaction.editReply({ embeds: [embed], components: [buttons] });
return;
})
.catch((error) => {
throw new Error(error.message);
});
};

View file

@ -0,0 +1,41 @@
// Dependencies
import {
ChatInputCommandInteraction,
SlashCommandSubcommandGroupBuilder,
} from "discord.js";
// Modules
import {
builder as AddBuilder,
execute as AddExecute,
} from "./subcommands/add";
import {
builder as RemoveBuilder,
execute as RemoveExecute,
} from "./subcommands/remove";
export const builder = (group: SlashCommandSubcommandGroupBuilder) => {
return group
.setName("counters")
.setDescription("Manage guild counters.")
.addSubcommand(AddBuilder)
.addSubcommand(RemoveBuilder);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
const { options } = interaction;
switch (options.getSubcommand()) {
case "add": {
await AddExecute(interaction);
break;
}
case "remove": {
await RemoveExecute(interaction);
break;
}
default: {
throw new Error("Could not found a module for that command.");
}
}
};

View file

@ -0,0 +1,112 @@
// Dependencies
import {
ChannelType,
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../../../handlers/deferReply";
import checkPermission from "../../../../../../helpers/checkPermission";
// Configurations
import prisma from "../../../../../../handlers/database";
import getEmbedConfig from "../../../../../../helpers/getEmbedData";
import logger from "../../../../../../middlewares/logger";
// Function
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("add")
.setDescription("Add a counter to your guild.")
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("The channel to send the counter to.")
.setRequired(true)
.addChannelTypes(ChannelType.GuildText)
)
.addStringOption((option) =>
option
.setName("word")
.setDescription("The word to use for the counter.")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("start")
.setDescription("The starting value of the counter.")
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { options, guild } = interaction;
const discordChannel = options?.getChannel("channel");
const triggerWord = options?.getString("word");
const startValue = options?.getNumber("start");
if (!guild) throw new Error("We could not find a guild");
if (!discordChannel) throw new Error("We could not find a channel");
if (!triggerWord) throw new Error("We could not find a word");
const channelCounter = await prisma.guildCounter.findUnique({
where: {
guildId_channelId: {
guildId: guild.id,
channelId: discordChannel.id,
},
},
});
if (channelCounter)
throw new Error("A counter already exists for this channel.");
const createGuildCounter = await prisma.guildCounter.upsert({
where: {
guildId_channelId: {
guildId: guild.id,
channelId: discordChannel.id,
},
},
update: {},
create: {
channelId: discordChannel.id,
triggerWord,
count: startValue || 0,
guild: {
connectOrCreate: {
create: {
id: guild.id,
},
where: {
id: guild.id,
},
},
},
},
});
logger.silly(createGuildCounter);
if (createGuildCounter) {
const embed = new EmbedBuilder()
.setTitle("[:toolbox:] Counters - Add")
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction?.editReply({
embeds: [
embed
.setDescription(":white_check_mark: Counter created successfully.")
.setColor(successColor),
],
});
}
};

View file

@ -0,0 +1,81 @@
// Dependencies
// Models
import {
ChannelType,
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../../../handlers/deferReply";
import checkPermission from "../../../../../../helpers/checkPermission";
// Configurations
import prisma from "../../../../../../handlers/database";
import getEmbedConfig from "../../../../../../helpers/getEmbedData";
// Function
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("remove")
.setDescription(`Delete a counter from your guild.`)
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("The channel to delete the counter from.")
.setRequired(true)
.addChannelTypes(ChannelType.GuildText)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { options, guild } = interaction;
const discordChannel = options?.getChannel("channel");
if (!guild) throw new Error("We could not find a guild");
if (!discordChannel) throw new Error("We could not find a channel");
const embed = new EmbedBuilder()
.setTitle("[:toolbox:] Counters - Remove")
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
const channelCounter = await prisma.guildCounter.findUnique({
where: {
guildId_channelId: {
guildId: guild.id,
channelId: discordChannel.id,
},
},
});
if (!channelCounter)
throw new Error(
"There is no counter sin this channel, please add one first."
);
const deleteGuildCounter = await prisma.guildCounter.deleteMany({
where: {
guildId: guild.id,
channelId: discordChannel.id,
},
});
if (!deleteGuildCounter)
throw new Error("We could not find a counter for this guild");
await interaction?.editReply({
embeds: [
embed
.setDescription(":white_check_mark: Counter deleted successfully.")
.setColor(successColor),
],
});
};

View file

@ -0,0 +1,58 @@
import {
ChatInputCommandInteraction,
SlashCommandSubcommandGroupBuilder,
} from "discord.js";
// Modules
import {
builder as GiveBuilder,
execute as GiveExecute,
} from "./subcommands/give";
import {
builder as GiveawayBuilder,
execute as GiveawayExecute,
} from "./subcommands/giveaway";
import {
builder as SetBuilder,
execute as SetExecute,
} from "./subcommands/set";
import {
builder as TakeBuilder,
execute as TakeExecute,
} from "./subcommands/take";
import {
builder as TransferBuilder,
execute as TransferExecute,
} from "./subcommands/transfer";
export const builder = (group: SlashCommandSubcommandGroupBuilder) => {
return group
.setName("credits")
.setDescription("Manage the credits of a user.")
.addSubcommand(GiveBuilder)
.addSubcommand(SetBuilder)
.addSubcommand(TakeBuilder)
.addSubcommand(TransferBuilder)
.addSubcommand(GiveawayBuilder);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
switch (interaction.options.getSubcommand()) {
case "give":
await GiveExecute(interaction);
break;
case "set":
await SetExecute(interaction);
break;
case "take":
await TakeExecute(interaction);
break;
case "transfer":
await TransferExecute(interaction);
break;
case "giveaway":
await GiveawayExecute(interaction);
break;
default:
throw new Error("No module found for that specific command");
}
};

View file

@ -0,0 +1,70 @@
// Dependencies
import {
ChatInputCommandInteraction,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
// Configurations
// Helpers../../../../../../../helpers/userData
import pluralize from "../../../../../../helpers/pluralize";
// Models
// Handlers
import deferReply from "../../../../../../handlers/deferReply";
import { success as baseEmbedSuccess } from "../../../../../../helpers/baseEmbeds";
import checkPermission from "../../../../../../helpers/checkPermission";
import creditsGive from "../../../../../../helpers/credits/give";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("give")
.setDescription("Give credits to a user.")
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user to give credits to.")
.setRequired(true)
)
.addIntegerOption((option) =>
option
.setName("amount")
.setDescription(`The amount of credits to give.`)
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
// 1. Defer reply as ephemeral.
await deferReply(interaction, true);
// 2. Check if the user has the MANAGE_GUILD permission.
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
// 3. Destructure interaction object.
const { guild, options } = interaction;
if (!guild)
throw new Error("We could not get the current guild from discord.");
if (!options) throw new Error("We could not get the options from discord.");
// 4. Get the user and amount from the options.
const discordReceiver = options.getUser("user");
const creditsAmount = options.getInteger("amount");
if (typeof creditsAmount !== "number")
throw new Error("You need to provide a credit amount.");
if (!discordReceiver)
throw new Error("We could not get the receiving user from Discord");
// 5. Create base embeds.
const embedSuccess = await baseEmbedSuccess(guild, "[:toolbox:] Give");
// 6. Give the credits.
await creditsGive(guild, discordReceiver, creditsAmount);
// 7. Send embed.
return await interaction.editReply({
embeds: [
embedSuccess.setDescription(
`Successfully gave ${pluralize(creditsAmount, "credit")}`
),
],
});
};

View file

@ -0,0 +1,188 @@
// Dependencies
import axios from "axios";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
ChannelType,
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import { v4 as uuidv4 } from "uuid";
import encryption from "../../../../../../helpers/encryption";
// Configurations
import prisma from "../../../../../../handlers/database";
import deferReply from "../../../../../../handlers/deferReply";
import checkPermission from "../../../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../../../helpers/getEmbedData";
import logger from "../../../../../../middlewares/logger";
// Function
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("giveaway")
.setDescription("Giveaway some credits for specified amount of users.")
.addIntegerOption((option) =>
option
.setName("uses")
.setDescription("How many users should be able to use this.")
.setRequired(true)
)
.addIntegerOption((option) =>
option
.setName("credit")
.setDescription(`How much credits provided per use.`)
.setRequired(true)
)
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("The channel to send the message to.")
.setRequired(true)
.addChannelTypes(ChannelType.GuildText)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
); // Destructure
const { guild, user, options } = interaction;
const uses = options?.getInteger("uses");
const creditAmount = options?.getInteger("credit");
const channel = options?.getChannel("channel");
if (!uses) throw new Error("Amount of uses is required.");
if (!creditAmount) throw new Error("Amount of credits is required.");
if (!channel) throw new Error("Channel is required.");
if (!guild) throw new Error("Guild is required.");
const embed = new EmbedBuilder()
.setTitle("[:toolbox:] Giveaway")
.setFooter({ text: footerText, iconURL: footerIcon });
const code = uuidv4();
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId: user.id,
guildId: guild.id,
},
},
update: {},
create: {
user: {
connectOrCreate: {
create: {
id: user.id,
},
where: {
id: user.id,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guild.id,
},
where: {
id: guild.id,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createGuildMember);
if (
!createGuildMember.guild.apiCpggUrlIv ||
!createGuildMember.guild.apiCpggUrlContent
)
throw new Error("No API url available");
if (
!createGuildMember.guild.apiCpggTokenIv ||
!createGuildMember.guild.apiCpggTokenContent
)
throw new Error("No API token available");
const url = encryption.decrypt({
iv: createGuildMember.guild.apiCpggUrlIv,
content: createGuildMember.guild.apiCpggUrlContent,
});
const api = axios?.create({
baseURL: `${url}/api/`,
headers: {
Authorization: `Bearer ${encryption.decrypt({
iv: createGuildMember.guild.apiCpggTokenIv,
content: createGuildMember.guild.apiCpggTokenContent,
})}`,
},
});
const shopUrl = `${url}/store`;
await api
.post("vouchers", {
uses,
code,
credits: creditAmount,
memo: `[GIVEAWAY] ${interaction?.createdTimestamp} - ${interaction?.user?.id}`,
})
.then(async () => {
await interaction.editReply({
embeds: [
embed
.setColor(successColor)
.setDescription(`Successfully created code: ${code}`),
],
});
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("Redeem it here")
.setStyle(ButtonStyle.Link)
.setEmoji("🏦")
.setURL(`${shopUrl}?voucher=${code}`)
);
const discordChannel = guild?.channels.cache.get(channel.id);
if (!discordChannel) return;
if (discordChannel.type !== ChannelType.GuildText) return;
discordChannel.send({
embeds: [
new EmbedBuilder()
.setTitle("[:parachute:] Credits!")
.addFields([
{
name: "💶 Credits",
value: `${creditAmount}`,
inline: true,
},
])
.setDescription(
`${interaction.user} dropped a voucher for a maximum **${uses}** members!`
)
.setColor(successColor),
],
components: [buttons],
});
});
};

View file

@ -0,0 +1,65 @@
// Dependencies
// Helpers
// Models
import {
ChatInputCommandInteraction,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../../../handlers/deferReply";
import { success as baseEmbedSuccess } from "../../../../../../helpers/baseEmbeds";
import checkPermission from "../../../../../../helpers/checkPermission";
import creditsSet from "../../../../../../helpers/credits/set";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("set")
.setDescription("Set the amount of credits a user has.")
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user to set the amount of credits for.")
.setRequired(true)
)
.addIntegerOption((option) =>
option
.setName("amount")
.setDescription(`The amount of credits to set.`)
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
// 1. Defer reply as ephemeral.
await deferReply(interaction, true);
// 2. Check if the user has the permission to manage the guild.
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
// 3. Destructure interaction object.
const { options, guild } = interaction;
if (!guild) throw new Error(`We could not find this guild.`);
if (!options) throw new Error(`We could not find the options.`);
// 4. Get the user and amount from the options.
const discordUser = options.getUser("user");
const creditAmount = options.getInteger("amount");
if (typeof creditAmount !== "number") throw new Error("Amount is not set.");
if (!discordUser) throw new Error("User is not specified");
// 5. Set the credits.
await creditsSet(guild, discordUser, creditAmount);
// 6. Create base embeds.
const embedSuccess = await baseEmbedSuccess(guild, "[:toolbox:] Set");
// 7. Send embed.
return await interaction.editReply({
embeds: [
embedSuccess.setDescription(
`Set **${discordUser}**'s credits to **${creditAmount}**.`
),
],
});
};

View file

@ -0,0 +1,65 @@
// Dependencies
// Models
import {
ChatInputCommandInteraction,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../../../handlers/deferReply";
import { success as baseEmbedSuccess } from "../../../../../../helpers/baseEmbeds";
import checkPermission from "../../../../../../helpers/checkPermission";
import creditsTake from "../../../../../../helpers/credits/take";
import pluralize from "../../../../../../helpers/pluralize";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("take")
.setDescription("Take credits from a user.")
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user to take credits from.")
.setRequired(true)
)
.addIntegerOption((option) =>
option
.setName("amount")
.setDescription(`The amount of credits to take.`)
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
// 1. Defer reply as ephemeral.
await deferReply(interaction, true);
// 2. Check if the user has the MANAGE_GUILD permission.
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
// 3. Destructure interaction object.
const { guild, options } = interaction;
if (!guild) throw new Error("Invalid guild.");
if (!options) throw new Error("Invalid options.");
// 4. Get the user and amount from the options.
const discordReceiver = options.getUser("user");
const optionAmount = options.getInteger("amount");
if (typeof optionAmount !== "number") throw new Error("Invalid amount.");
if (!discordReceiver) throw new Error("Invalid user.");
// 5. Create base embeds.
const embedSuccess = await baseEmbedSuccess(guild, "[:toolbox:] Take");
// 6. Take the credits.
await creditsTake(guild, discordReceiver, optionAmount);
// 7. Send embed.
return await interaction.editReply({
embeds: [
embedSuccess.setDescription(
`Took ${pluralize(optionAmount, "credit")} from ${discordReceiver}.`
),
],
});
};

View file

@ -0,0 +1,79 @@
// Dependencies
// Models
import {
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import creditsTransfer from "../../../../../../helpers/credits/transfer";
// Configurations
import deferReply from "../../../../../../handlers/deferReply";
import checkPermission from "../../../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../../../helpers/getEmbedData";
// Function
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("transfer")
.setDescription("Transfer credits from one user to another.")
.addUserOption((option) =>
option
.setName("from")
.setDescription("The user to transfer credits from.")
.setRequired(true)
)
.addUserOption((option) =>
option
.setName("to")
.setDescription("The user to transfer credits to.")
.setRequired(true)
)
.addIntegerOption((option) =>
option
.setName("amount")
.setDescription(`The amount of credits to transfer.`)
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
); // Destructure member
const { guild, options } = interaction;
// Get options
const optionFromUser = options?.getUser("from");
const optionToUser = options?.getUser("to");
const optionAmount = options?.getInteger("amount");
if (optionAmount === null) throw new Error("Amount is not specified");
if (optionAmount <= 0)
throw new Error("You need to set amount above zero to transfer.");
if (!guild) throw new Error(`We could not find this guild.`);
if (!optionFromUser)
throw new Error("You must provide a user to transfer from.");
if (!optionToUser) throw new Error("You must provide a user to transfer to.");
await creditsTransfer(guild, optionFromUser, optionToUser, optionAmount);
return interaction?.editReply({
embeds: [
new EmbedBuilder()
.setTitle("[:toolbox:] Manage - Credits (Transfer)")
.setDescription(`Transferred ${optionAmount} credits.`)
.setTimestamp(new Date())
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
};

View file

@ -2,8 +2,14 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
// Modules
import moduleCounters from "./modules/counters";
import moduleCredits from "./modules/credits";
import {
builder as CountersBuilder,
execute as CountersExecute,
} from "./groups/counters";
import {
builder as CreditsBuilder,
execute as CreditsExecute,
} from "./groups/credits";
// Function
export const builder = new SlashCommandBuilder()
@ -12,8 +18,8 @@ export const builder = new SlashCommandBuilder()
.setDMPermission(false)
// Modules
.addSubcommandGroup(moduleCounters.builder)
.addSubcommandGroup(moduleCredits.builder);
.addSubcommandGroup(CountersBuilder)
.addSubcommandGroup(CreditsBuilder);
export const execute = async (interaction: ChatInputCommandInteraction) => {
// Destructure
@ -21,11 +27,11 @@ export const execute = async (interaction: ChatInputCommandInteraction) => {
switch (options.getSubcommandGroup()) {
case "credits": {
await moduleCredits.execute(interaction);
await CreditsExecute(interaction);
break;
}
case "counters": {
await moduleCounters.execute(interaction);
await CountersExecute(interaction);
break;
}
default: {

View file

@ -1,36 +0,0 @@
// Dependencies
import {
ChatInputCommandInteraction,
SlashCommandSubcommandGroupBuilder,
} from "discord.js";
// Modules
import moduleAdd from "./modules/add";
import moduleRemove from "./modules/remove";
export default {
builder: (group: SlashCommandSubcommandGroupBuilder) => {
return group
.setName("counters")
.setDescription("Manage guild counters.")
.addSubcommand(moduleAdd.builder)
.addSubcommand(moduleRemove.builder);
},
execute: async (interaction: ChatInputCommandInteraction) => {
const { options } = interaction;
switch (options.getSubcommand()) {
case "add": {
await moduleAdd.execute(interaction);
break;
}
case "remove": {
await moduleRemove.execute(interaction);
break;
}
default: {
throw new Error("Could not found a module for that command.");
}
}
},
};

View file

@ -1,113 +0,0 @@
// Dependencies
import {
ChannelType,
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../../../handlers/deferReply";
import checkPermission from "../../../../../../helpers/checkPermission";
// Configurations
import prisma from "../../../../../../handlers/database";
import getEmbedConfig from "../../../../../../helpers/getEmbedData";
import logger from "../../../../../../middlewares/logger";
// Function
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("add")
.setDescription("Add a counter to your guild.")
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("The channel to send the counter to.")
.setRequired(true)
.addChannelTypes(ChannelType.GuildText)
)
.addStringOption((option) =>
option
.setName("word")
.setDescription("The word to use for the counter.")
.setRequired(true)
)
.addNumberOption((option) =>
option
.setName("start")
.setDescription("The starting value of the counter.")
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { options, guild } = interaction;
const discordChannel = options?.getChannel("channel");
const triggerWord = options?.getString("word");
const startValue = options?.getNumber("start");
if (!guild) throw new Error("We could not find a guild");
if (!discordChannel) throw new Error("We could not find a channel");
if (!triggerWord) throw new Error("We could not find a word");
const channelCounter = await prisma.guildCounter.findUnique({
where: {
guildId_channelId: {
guildId: guild.id,
channelId: discordChannel.id,
},
},
});
if (channelCounter)
throw new Error("A counter already exists for this channel.");
const createGuildCounter = await prisma.guildCounter.upsert({
where: {
guildId_channelId: {
guildId: guild.id,
channelId: discordChannel.id,
},
},
update: {},
create: {
channelId: discordChannel.id,
triggerWord,
count: startValue || 0,
guild: {
connectOrCreate: {
create: {
id: guild.id,
},
where: {
id: guild.id,
},
},
},
},
});
logger.silly(createGuildCounter);
if (createGuildCounter) {
const embed = new EmbedBuilder()
.setTitle("[:toolbox:] Counters - Add")
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction?.editReply({
embeds: [
embed
.setDescription(":white_check_mark: Counter created successfully.")
.setColor(successColor),
],
});
}
},
};

View file

@ -1,82 +0,0 @@
// Dependencies
// Models
import {
ChannelType,
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../../../handlers/deferReply";
import checkPermission from "../../../../../../helpers/checkPermission";
// Configurations
import prisma from "../../../../../../handlers/database";
import getEmbedConfig from "../../../../../../helpers/getEmbedData";
// Function
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("remove")
.setDescription(`Delete a counter from your guild.`)
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("The channel to delete the counter from.")
.setRequired(true)
.addChannelTypes(ChannelType.GuildText)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { options, guild } = interaction;
const discordChannel = options?.getChannel("channel");
if (!guild) throw new Error("We could not find a guild");
if (!discordChannel) throw new Error("We could not find a channel");
const embed = new EmbedBuilder()
.setTitle("[:toolbox:] Counters - Remove")
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
const channelCounter = await prisma.guildCounter.findUnique({
where: {
guildId_channelId: {
guildId: guild.id,
channelId: discordChannel.id,
},
},
});
if (!channelCounter)
throw new Error(
"There is no counter sin this channel, please add one first."
);
const deleteGuildCounter = await prisma.guildCounter.deleteMany({
where: {
guildId: guild.id,
channelId: discordChannel.id,
},
});
if (!deleteGuildCounter)
throw new Error("We could not find a counter for this guild");
await interaction?.editReply({
embeds: [
embed
.setDescription(":white_check_mark: Counter deleted successfully.")
.setColor(successColor),
],
});
},
};

View file

@ -1,45 +0,0 @@
import {
ChatInputCommandInteraction,
SlashCommandSubcommandGroupBuilder,
} from "discord.js";
// Modules
import moduleGive from "./modules/give";
import moduleGiveaway from "./modules/giveaway";
import moduleSet from "./modules/set";
import moduleTake from "./modules/take";
import moduleTransfer from "./modules/transfer";
export default {
builder: (group: SlashCommandSubcommandGroupBuilder) => {
return group
.setName("credits")
.setDescription("Manage the credits of a user.")
.addSubcommand(moduleGive.builder)
.addSubcommand(moduleSet.builder)
.addSubcommand(moduleTake.builder)
.addSubcommand(moduleTransfer.builder)
.addSubcommand(moduleGiveaway.builder);
},
execute: async (interaction: ChatInputCommandInteraction) => {
switch (interaction.options.getSubcommand()) {
case "give":
await moduleGive.execute(interaction);
break;
case "set":
await moduleSet.execute(interaction);
break;
case "take":
await moduleTake.execute(interaction);
break;
case "transfer":
await moduleTransfer.execute(interaction);
break;
case "giveaway":
await moduleGiveaway.execute(interaction);
break;
default:
throw new Error("No module found for that specific command");
}
},
};

View file

@ -1,72 +0,0 @@
// Dependencies
import {
ChatInputCommandInteraction,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
// Configurations
// Helpers../../../../../../../helpers/userData
import pluralize from "../../../../../../helpers/pluralize";
// Models
// Handlers
import deferReply from "../../../../../../handlers/deferReply";
import { success as baseEmbedSuccess } from "../../../../../../helpers/baseEmbeds";
import checkPermission from "../../../../../../helpers/checkPermission";
import creditsGive from "../../../../../../helpers/credits/give";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("give")
.setDescription("Give credits to a user.")
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user to give credits to.")
.setRequired(true)
)
.addIntegerOption((option) =>
option
.setName("amount")
.setDescription(`The amount of credits to give.`)
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
// 1. Defer reply as ephemeral.
await deferReply(interaction, true);
// 2. Check if the user has the MANAGE_GUILD permission.
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
// 3. Destructure interaction object.
const { guild, options } = interaction;
if (!guild)
throw new Error("We could not get the current guild from discord.");
if (!options) throw new Error("We could not get the options from discord.");
// 4. Get the user and amount from the options.
const discordReceiver = options.getUser("user");
const creditsAmount = options.getInteger("amount");
if (typeof creditsAmount !== "number")
throw new Error("You need to provide a credit amount.");
if (!discordReceiver)
throw new Error("We could not get the receiving user from Discord");
// 5. Create base embeds.
const embedSuccess = await baseEmbedSuccess(guild, "[:toolbox:] Give");
// 6. Give the credits.
await creditsGive(guild, discordReceiver, creditsAmount);
// 7. Send embed.
return await interaction.editReply({
embeds: [
embedSuccess.setDescription(
`Successfully gave ${pluralize(creditsAmount, "credit")}`
),
],
});
},
};

View file

@ -1,189 +0,0 @@
// Dependencies
import axios from "axios";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
ChannelType,
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import { v4 as uuidv4 } from "uuid";
import encryption from "../../../../../../helpers/encryption";
// Configurations
import prisma from "../../../../../../handlers/database";
import deferReply from "../../../../../../handlers/deferReply";
import checkPermission from "../../../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../../../helpers/getEmbedData";
import logger from "../../../../../../middlewares/logger";
// Function
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("giveaway")
.setDescription("Giveaway some credits for specified amount of users.")
.addIntegerOption((option) =>
option
.setName("uses")
.setDescription("How many users should be able to use this.")
.setRequired(true)
)
.addIntegerOption((option) =>
option
.setName("credit")
.setDescription(`How much credits provided per use.`)
.setRequired(true)
)
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("The channel to send the message to.")
.setRequired(true)
.addChannelTypes(ChannelType.GuildText)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
); // Destructure
const { guild, user, options } = interaction;
const uses = options?.getInteger("uses");
const creditAmount = options?.getInteger("credit");
const channel = options?.getChannel("channel");
if (!uses) throw new Error("Amount of uses is required.");
if (!creditAmount) throw new Error("Amount of credits is required.");
if (!channel) throw new Error("Channel is required.");
if (!guild) throw new Error("Guild is required.");
const embed = new EmbedBuilder()
.setTitle("[:toolbox:] Giveaway")
.setFooter({ text: footerText, iconURL: footerIcon });
const code = uuidv4();
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId: user.id,
guildId: guild.id,
},
},
update: {},
create: {
user: {
connectOrCreate: {
create: {
id: user.id,
},
where: {
id: user.id,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guild.id,
},
where: {
id: guild.id,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createGuildMember);
if (
!createGuildMember.guild.apiCpggUrlIv ||
!createGuildMember.guild.apiCpggUrlContent
)
throw new Error("No API url available");
if (
!createGuildMember.guild.apiCpggTokenIv ||
!createGuildMember.guild.apiCpggTokenContent
)
throw new Error("No API token available");
const url = encryption.decrypt({
iv: createGuildMember.guild.apiCpggUrlIv,
content: createGuildMember.guild.apiCpggUrlContent,
});
const api = axios?.create({
baseURL: `${url}/api/`,
headers: {
Authorization: `Bearer ${encryption.decrypt({
iv: createGuildMember.guild.apiCpggTokenIv,
content: createGuildMember.guild.apiCpggTokenContent,
})}`,
},
});
const shopUrl = `${url}/store`;
await api
.post("vouchers", {
uses,
code,
credits: creditAmount,
memo: `[GIVEAWAY] ${interaction?.createdTimestamp} - ${interaction?.user?.id}`,
})
.then(async () => {
await interaction.editReply({
embeds: [
embed
.setColor(successColor)
.setDescription(`Successfully created code: ${code}`),
],
});
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("Redeem it here")
.setStyle(ButtonStyle.Link)
.setEmoji("🏦")
.setURL(`${shopUrl}?voucher=${code}`)
);
const discordChannel = guild?.channels.cache.get(channel.id);
if (!discordChannel) return;
if (discordChannel.type !== ChannelType.GuildText) return;
discordChannel.send({
embeds: [
new EmbedBuilder()
.setTitle("[:parachute:] Credits!")
.addFields([
{
name: "💶 Credits",
value: `${creditAmount}`,
inline: true,
},
])
.setDescription(
`${interaction.user} dropped a voucher for a maximum **${uses}** members!`
)
.setColor(successColor),
],
components: [buttons],
});
});
},
};

View file

@ -1,66 +0,0 @@
// Dependencies
// Helpers
// Models
import {
ChatInputCommandInteraction,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../../../handlers/deferReply";
import { success as baseEmbedSuccess } from "../../../../../../helpers/baseEmbeds";
import checkPermission from "../../../../../../helpers/checkPermission";
import creditsSet from "../../../../../../helpers/credits/set";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("set")
.setDescription("Set the amount of credits a user has.")
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user to set the amount of credits for.")
.setRequired(true)
)
.addIntegerOption((option) =>
option
.setName("amount")
.setDescription(`The amount of credits to set.`)
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
// 1. Defer reply as ephemeral.
await deferReply(interaction, true);
// 2. Check if the user has the permission to manage the guild.
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
// 3. Destructure interaction object.
const { options, guild } = interaction;
if (!guild) throw new Error(`We could not find this guild.`);
if (!options) throw new Error(`We could not find the options.`);
// 4. Get the user and amount from the options.
const discordUser = options.getUser("user");
const creditAmount = options.getInteger("amount");
if (typeof creditAmount !== "number") throw new Error("Amount is not set.");
if (!discordUser) throw new Error("User is not specified");
// 5. Set the credits.
await creditsSet(guild, discordUser, creditAmount);
// 6. Create base embeds.
const embedSuccess = await baseEmbedSuccess(guild, "[:toolbox:] Set");
// 7. Send embed.
return await interaction.editReply({
embeds: [
embedSuccess.setDescription(
`Set **${discordUser}**'s credits to **${creditAmount}**.`
),
],
});
},
};

View file

@ -1,66 +0,0 @@
// Dependencies
// Models
import {
ChatInputCommandInteraction,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../../../handlers/deferReply";
import { success as baseEmbedSuccess } from "../../../../../../helpers/baseEmbeds";
import checkPermission from "../../../../../../helpers/checkPermission";
import creditsTake from "../../../../../../helpers/credits/take";
import pluralize from "../../../../../../helpers/pluralize";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("take")
.setDescription("Take credits from a user.")
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user to take credits from.")
.setRequired(true)
)
.addIntegerOption((option) =>
option
.setName("amount")
.setDescription(`The amount of credits to take.`)
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
// 1. Defer reply as ephemeral.
await deferReply(interaction, true);
// 2. Check if the user has the MANAGE_GUILD permission.
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
// 3. Destructure interaction object.
const { guild, options } = interaction;
if (!guild) throw new Error("Invalid guild.");
if (!options) throw new Error("Invalid options.");
// 4. Get the user and amount from the options.
const discordReceiver = options.getUser("user");
const optionAmount = options.getInteger("amount");
if (typeof optionAmount !== "number") throw new Error("Invalid amount.");
if (!discordReceiver) throw new Error("Invalid user.");
// 5. Create base embeds.
const embedSuccess = await baseEmbedSuccess(guild, "[:toolbox:] Take");
// 6. Take the credits.
await creditsTake(guild, discordReceiver, optionAmount);
// 7. Send embed.
return await interaction.editReply({
embeds: [
embedSuccess.setDescription(
`Took ${pluralize(optionAmount, "credit")} from ${discordReceiver}.`
),
],
});
},
};

View file

@ -1,81 +0,0 @@
// Dependencies
// Models
import {
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import creditsTransfer from "../../../../../../helpers/credits/transfer";
// Configurations
import deferReply from "../../../../../../handlers/deferReply";
import checkPermission from "../../../../../../helpers/checkPermission";
import getEmbedConfig from "../../../../../../helpers/getEmbedData";
// Function
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("transfer")
.setDescription("Transfer credits from one user to another.")
.addUserOption((option) =>
option
.setName("from")
.setDescription("The user to transfer credits from.")
.setRequired(true)
)
.addUserOption((option) =>
option
.setName("to")
.setDescription("The user to transfer credits to.")
.setRequired(true)
)
.addIntegerOption((option) =>
option
.setName("amount")
.setDescription(`The amount of credits to transfer.`)
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
); // Destructure member
const { guild, options } = interaction;
// Get options
const optionFromUser = options?.getUser("from");
const optionToUser = options?.getUser("to");
const optionAmount = options?.getInteger("amount");
if (optionAmount === null) throw new Error("Amount is not specified");
if (optionAmount <= 0)
throw new Error("You need to set amount above zero to transfer.");
if (!guild) throw new Error(`We could not find this guild.`);
if (!optionFromUser)
throw new Error("You must provide a user to transfer from.");
if (!optionToUser)
throw new Error("You must provide a user to transfer to.");
await creditsTransfer(guild, optionFromUser, optionToUser, optionAmount);
return interaction?.editReply({
embeds: [
new EmbedBuilder()
.setTitle("[:toolbox:] Manage - Credits (Transfer)")
.setDescription(`Transferred ${optionAmount} credits.`)
.setTimestamp(new Date())
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
},
};

View file

@ -1,20 +1,23 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
// Modules
import modulePrune from "./modules/prune";
import {
builder as PruneBuilder,
execute as PruneExecute,
} from "./subcommands/prune";
export const builder = new SlashCommandBuilder()
.setName("moderation")
.setDescription("Moderation.")
.setDMPermission(false)
.addSubcommand(modulePrune.builder);
.addSubcommand(PruneBuilder);
// Execute the command
export const execute = async (interaction: ChatInputCommandInteraction) => {
switch (interaction.options.getSubcommand()) {
case "prune": {
await modulePrune.execute(interaction);
await PruneExecute(interaction);
break;
}
default: {

View file

@ -1,85 +0,0 @@
// Dependencies
import {
ChannelType,
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
// Configurations
import getEmbedConfig from "../../../../helpers/getEmbedData";
// Function
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("prune")
.setDescription("Prune messages!")
.addIntegerOption((option) =>
option
.setName("count")
.setDescription("How many messages you want to prune.")
.setRequired(true)
)
.addBooleanOption((option) =>
option.setName("bots").setDescription("Include bots.")
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, false);
checkPermission(interaction, PermissionsBitField.Flags.ManageMessages);
const { errorColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const count = interaction.options.getInteger("count");
if (count === null) return;
const bots = interaction.options.getBoolean("bots");
if (count < 1 || count > 100) {
const interactionEmbed = new EmbedBuilder()
.setTitle("[:police_car:] Prune")
.setDescription(`You can only prune between 1 and 100 messages.`)
.setTimestamp()
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction.editReply({
embeds: [interactionEmbed],
});
return;
}
if (interaction?.channel?.type !== ChannelType.GuildText) return;
await interaction.channel.messages.fetch().then(async (messages) => {
const messagesToDelete = (
bots
? messages.filter((m) => m?.interaction?.id !== interaction.id)
: messages.filter(
(m) =>
m?.interaction?.id !== interaction.id && m?.author?.bot !== true
)
).first(count);
if (interaction?.channel?.type !== ChannelType.GuildText) return;
await interaction.channel
.bulkDelete(messagesToDelete, true)
.then(async () => {
const interactionEmbed = new EmbedBuilder()
.setTitle("[:police_car:] Prune")
.setDescription(`Successfully pruned \`${count}\` messages.`)
.setTimestamp()
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction.editReply({
embeds: [interactionEmbed],
});
});
});
},
};

View file

@ -0,0 +1,84 @@
// Dependencies
import {
ChannelType,
ChatInputCommandInteraction,
EmbedBuilder,
PermissionsBitField,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../handlers/deferReply";
import checkPermission from "../../../../helpers/checkPermission";
// Configurations
import getEmbedConfig from "../../../../helpers/getEmbedData";
// Function
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("prune")
.setDescription("Prune messages!")
.addIntegerOption((option) =>
option
.setName("count")
.setDescription("How many messages you want to prune.")
.setRequired(true)
)
.addBooleanOption((option) =>
option.setName("bots").setDescription("Include bots.")
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, false);
checkPermission(interaction, PermissionsBitField.Flags.ManageMessages);
const { errorColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const count = interaction.options.getInteger("count");
if (count === null) return;
const bots = interaction.options.getBoolean("bots");
if (count < 1 || count > 100) {
const interactionEmbed = new EmbedBuilder()
.setTitle("[:police_car:] Prune")
.setDescription(`You can only prune between 1 and 100 messages.`)
.setTimestamp()
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction.editReply({
embeds: [interactionEmbed],
});
return;
}
if (interaction?.channel?.type !== ChannelType.GuildText) return;
await interaction.channel.messages.fetch().then(async (messages) => {
const messagesToDelete = (
bots
? messages.filter((m) => m?.interaction?.id !== interaction.id)
: messages.filter(
(m) =>
m?.interaction?.id !== interaction.id && m?.author?.bot !== true
)
).first(count);
if (interaction?.channel?.type !== ChannelType.GuildText) return;
await interaction.channel
.bulkDelete(messagesToDelete, true)
.then(async () => {
const interactionEmbed = new EmbedBuilder()
.setTitle("[:police_car:] Prune")
.setDescription(`Successfully pruned \`${count}\` messages.`)
.setTimestamp()
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction.editReply({
embeds: [interactionEmbed],
});
});
});
};

View file

@ -2,8 +2,14 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
// Modules
import moduleCheck from "./modules/check";
import moduleRepute from "./modules/repute";
import {
builder as CheckBuilder,
execute as CheckExecute,
} from "./subcommands/check";
import {
builder as ReputeBuilder,
execute as ReputeExecute,
} from "./subcommands/repute";
// Function
export const builder = new SlashCommandBuilder()
@ -12,17 +18,17 @@ export const builder = new SlashCommandBuilder()
.setDMPermission(false)
// Modules
.addSubcommand(moduleRepute.builder)
.addSubcommand(moduleCheck.builder);
.addSubcommand(ReputeBuilder)
.addSubcommand(CheckBuilder);
// Execute function
export const execute = async (interaction: ChatInputCommandInteraction) => {
if (interaction.options.getSubcommand() === "repute") {
await moduleRepute.execute(interaction);
await ReputeExecute(interaction);
return;
}
if (interaction.options.getSubcommand() === "check") {
await moduleCheck.execute(interaction);
await CheckExecute(interaction);
return;
}
};

View file

@ -1,104 +0,0 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("check")
.setDescription("Check reputation")
.addUserOption((option) =>
option
.setName("account")
.setDescription("The account you checking")
.setRequired(false)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
const { options, guild, user } = interaction;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
guild
);
const optionAccount = options?.getUser("account");
if (!guild) throw new Error("Server unavailable");
if (!user) throw new Error("User unavailable");
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId: (optionAccount || user).id,
guildId: guild.id,
},
},
update: {},
create: {
user: {
connectOrCreate: {
create: {
id: (optionAccount || user).id,
},
where: {
id: (optionAccount || user).id,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guild.id,
},
where: {
id: guild.id,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createGuildMember);
const reputationType = (reputation: number) => {
if (reputation < 0) return `negative reputation of ${reputation}`;
if (reputation > 0) return `positive reputation of ${reputation}`;
return "neutral reputation";
};
const interactionEmbed = new EmbedBuilder()
.setTitle(
optionAccount
? `:loudspeaker:︱Showing ${optionAccount.username}'s reputation`
: ":loudspeaker:︱Showing your reputation"
)
.setDescription(
optionAccount
? `${optionAccount} have a ${reputationType(
createGuildMember.user.reputationsEarned
)}`
: `You have a ${reputationType(
createGuildMember.user.reputationsEarned
)}`
)
.setTimestamp()
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction.editReply({
embeds: [interactionEmbed],
});
},
};

View file

@ -1,122 +0,0 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
} from "discord.js";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
import noSelfReputation from "./components/noSelfReputation";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import cooldown from "../../../../middlewares/cooldown";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("repute")
.setDescription("Repute an account")
.addUserOption((option) =>
option
.setName("account")
.setDescription("The account you repute")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("type")
.setDescription("Type of reputation")
.setRequired(true)
.addChoices(
{ name: "Positive", value: "positive" },
{
name: "Negative",
value: "negative",
}
)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
const { options, user, guild, commandId } = interaction;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
guild
);
const optionAccount = options?.getUser("account");
const optionType = options?.getString("type");
if (!guild) throw new Error("Server unavailable");
if (!optionAccount) throw new Error("User unavailable");
// Pre-checks
noSelfReputation(optionAccount, user);
// Check if user is on cooldown otherwise create one
await cooldown(
guild,
user,
commandId,
parseInt(process.env.REPUTATION_TIMEOUT)
);
switch (optionType) {
case "positive": {
const createUser = await prisma.user.upsert({
where: {
id: optionAccount.id,
},
update: {
reputationsEarned: {
increment: 1,
},
},
create: {
id: optionAccount.id,
reputationsEarned: 1,
},
});
logger.silly(createUser);
break;
}
case "negative": {
const createUser = await prisma.user.upsert({
where: {
id: optionAccount.id,
},
update: {
reputationsEarned: {
decrement: 1,
},
},
create: {
id: optionAccount.id,
reputationsEarned: -1,
},
});
logger.silly(createUser);
break;
}
default: {
throw new Error("Invalid reputation type");
}
}
const interactionEmbed = new EmbedBuilder()
.setTitle(`:loudspeaker:︱Reputing ${optionAccount.username}`)
.setDescription(
`You have given a ${optionType} repute to ${optionAccount}!`
)
.setTimestamp()
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction.editReply({
embeds: [interactionEmbed],
});
},
};

View file

@ -0,0 +1,101 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("check")
.setDescription("Check reputation")
.addUserOption((option) =>
option
.setName("account")
.setDescription("The account you checking")
.setRequired(false)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
const { options, guild, user } = interaction;
const { successColor, footerText, footerIcon } = await getEmbedConfig(guild);
const optionAccount = options?.getUser("account");
if (!guild) throw new Error("Server unavailable");
if (!user) throw new Error("User unavailable");
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId: (optionAccount || user).id,
guildId: guild.id,
},
},
update: {},
create: {
user: {
connectOrCreate: {
create: {
id: (optionAccount || user).id,
},
where: {
id: (optionAccount || user).id,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guild.id,
},
where: {
id: guild.id,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createGuildMember);
const reputationType = (reputation: number) => {
if (reputation < 0) return `negative reputation of ${reputation}`;
if (reputation > 0) return `positive reputation of ${reputation}`;
return "neutral reputation";
};
const interactionEmbed = new EmbedBuilder()
.setTitle(
optionAccount
? `:loudspeaker:︱Showing ${optionAccount.username}'s reputation`
: ":loudspeaker:︱Showing your reputation"
)
.setDescription(
optionAccount
? `${optionAccount} have a ${reputationType(
createGuildMember.user.reputationsEarned
)}`
: `You have a ${reputationType(
createGuildMember.user.reputationsEarned
)}`
)
.setTimestamp()
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction.editReply({
embeds: [interactionEmbed],
});
};

View file

@ -0,0 +1,119 @@
import {
ChatInputCommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
} from "discord.js";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
import noSelfReputation from "./components/noSelfReputation";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import cooldown from "../../../../middlewares/cooldown";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("repute")
.setDescription("Repute an account")
.addUserOption((option) =>
option
.setName("account")
.setDescription("The account you repute")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("type")
.setDescription("Type of reputation")
.setRequired(true)
.addChoices(
{ name: "Positive", value: "positive" },
{
name: "Negative",
value: "negative",
}
)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
const { options, user, guild, commandId } = interaction;
const { successColor, footerText, footerIcon } = await getEmbedConfig(guild);
const optionAccount = options?.getUser("account");
const optionType = options?.getString("type");
if (!guild) throw new Error("Server unavailable");
if (!optionAccount) throw new Error("User unavailable");
// Pre-checks
noSelfReputation(optionAccount, user);
// Check if user is on cooldown otherwise create one
await cooldown(
guild,
user,
commandId,
parseInt(process.env.REPUTATION_TIMEOUT)
);
switch (optionType) {
case "positive": {
const createUser = await prisma.user.upsert({
where: {
id: optionAccount.id,
},
update: {
reputationsEarned: {
increment: 1,
},
},
create: {
id: optionAccount.id,
reputationsEarned: 1,
},
});
logger.silly(createUser);
break;
}
case "negative": {
const createUser = await prisma.user.upsert({
where: {
id: optionAccount.id,
},
update: {
reputationsEarned: {
decrement: 1,
},
},
create: {
id: optionAccount.id,
reputationsEarned: -1,
},
});
logger.silly(createUser);
break;
}
default: {
throw new Error("Invalid reputation type");
}
}
const interactionEmbed = new EmbedBuilder()
.setTitle(`:loudspeaker:︱Reputing ${optionAccount.username}`)
.setDescription(
`You have given a ${optionType} repute to ${optionAccount}!`
)
.setTimestamp()
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction.editReply({
embeds: [interactionEmbed],
});
};

View file

@ -0,0 +1,52 @@
// Dependencies
import {
ChatInputCommandInteraction,
SlashCommandSubcommandGroupBuilder,
} from "discord.js";
// Handlers
// Modules
import {
builder as BuyBuilder,
execute as BuyExecute,
} from "./subcommands/buy";
import {
builder as CancelBuilder,
execute as CancelExecute,
} from "./subcommands/cancel";
import prisma from "../../../../handlers/database";
export const builder = (group: SlashCommandSubcommandGroupBuilder) => {
return (
group
.setName("roles")
.setDescription("Shop for custom roles.")
// Modules
.addSubcommand(BuyBuilder)
.addSubcommand(CancelBuilder)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
if (!interaction.guild) return;
const { options, guild } = interaction;
const getGuild = await prisma.guild.findUnique({
where: { id: guild.id },
});
if (!getGuild) throw new Error("Guild not found");
if (!getGuild.shopRolesEnabled)
throw new Error("This server has disabled shop roles.");
if (options?.getSubcommand() === "buy") {
await BuyExecute(interaction);
}
if (options?.getSubcommand() === "cancel") {
await CancelExecute(interaction);
}
};

View file

@ -0,0 +1,177 @@
// Dependencies
// Helpers
import {
ChatInputCommandInteraction,
ColorResolvable,
EmbedBuilder,
GuildMemberRoleManager,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../../../handlers/deferReply";
import getEmbedData from "../../../../../../helpers/getEmbedData";
import logger from "../../../../../../middlewares/logger";
// Configurations
// import fetchUser from "../../../../../../helpers/userData";
// Models
import prisma from "../../../../../../handlers/database";
import pluralize from "../../../../../../helpers/pluralize";
// Function
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("buy")
.setDescription("Buy a custom role.")
.addStringOption((option) =>
option
.setName("name")
.setDescription("Name of the role you wish to buy.")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("color")
.setDescription("Color of the role you wish to buy.")
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
const { successColor, footerText, footerIcon } = await getEmbedData(
interaction.guild
);
const { options, guild, user, member } = interaction;
const optionName = options?.getString("name");
const optionColor = options?.getString("color");
// If amount is null
if (optionName === null)
throw new Error("We could not read your requested name");
await guild?.roles
.create({
name: optionName,
color: optionColor as ColorResolvable,
reason: `${user?.id} bought from shop`,
})
.then(async (role) => {
const userId = "SNOWFLKAE";
const guildId = "SNOWFLAKE";
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId,
guildId,
},
},
update: {},
create: {
user: {
connectOrCreate: {
create: {
id: userId,
},
where: {
id: userId,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guildId,
},
where: {
id: guildId,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createGuildMember);
// Get guild object
const pricePerHour = createGuildMember.guild.shopRolesPricePerHour;
const updateGuildMember = await prisma.guildMember.update({
where: {
userId_guildId: {
userId,
guildId,
},
},
data: {
creditsEarned: { decrement: pricePerHour },
},
});
logger.silly(updateGuildMember);
const createShopRole = await prisma.guildShopRoles.upsert({
where: {
guildId_userId_roleId: {
guildId: guild.id,
userId: user.id,
roleId: role.id,
},
},
update: {},
create: {
roleId: role.id,
lastPayed: new Date(),
user: {
connectOrCreate: {
create: {
id: user.id,
},
where: {
id: user.id,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guild.id,
},
where: {
id: guild.id,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createShopRole);
await (member?.roles as GuildMemberRoleManager)?.add(role?.id);
logger?.silly(`Role ${role?.name} was bought by ${user?.tag}`);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:shopping_cart:] Buy")
.setDescription(
`You bought **${optionName}** for **${pluralize(
pricePerHour,
"credit"
)}**.`
)
.setTimestamp()
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon });
return interaction?.editReply({
embeds: [interactionEmbed],
});
})
.catch(() => {
throw new Error("Failed creating role.");
});
};

View file

@ -0,0 +1,126 @@
// Dependencies
// Helpers
import {
ChatInputCommandInteraction,
EmbedBuilder,
GuildMemberRoleManager,
SlashCommandSubcommandBuilder,
} from "discord.js";
// Configurations
// Models
import deferReply from "../../../../../../handlers/deferReply";
import logger from "../../../../../../middlewares/logger";
// Configurations
// Models
import prisma from "../../../../../../handlers/database";
import getEmbedData from "../../../../../../helpers/getEmbedData";
import pluralize from "../../../../../../helpers/pluralize";
// Function
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("cancel")
.setDescription("Cancel a purchase.")
.addRoleOption((option) =>
option
.setName("role")
.setDescription("Role you wish to cancel.")
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
const { successColor, footerText, footerIcon } = await getEmbedData(
interaction.guild
);
const { options, guild, user, member } = interaction;
const optionRole = options.getRole("role");
if (optionRole === null)
throw new Error("We could not read your requested role.");
if (!guild) throw new Error("No guild specified");
if (!user) throw new Error("No user specified");
const roleExist = await prisma.guildShopRoles.findUnique({
where: {
guildId_userId_roleId: {
guildId: guild.id,
userId: user.id,
roleId: optionRole.id,
},
},
});
if (roleExist === null) return;
await (member?.roles as GuildMemberRoleManager)?.remove(optionRole?.id);
await guild?.roles
.delete(optionRole?.id, `${user?.id} canceled from shop`)
.then(async () => {
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId: user.id,
guildId: guild.id,
},
},
update: {},
create: {
user: {
connectOrCreate: {
create: {
id: user.id,
},
where: {
id: user.id,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guild.id,
},
where: {
id: guild.id,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createGuildMember);
if (!createGuildMember) throw new Error("Guild member not created");
const deleteShopRole = await prisma.guildShopRoles.delete({
where: {
guildId_userId_roleId: {
guildId: guild?.id,
userId: user?.id,
roleId: optionRole?.id,
},
},
});
logger.silly(deleteShopRole);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:shopping_cart:] Cancel")
.setDescription(`You have canceled ${optionRole.name}.`)
.setTimestamp()
.setColor(successColor)
.addFields({
name: "Your balance",
value: `${pluralize(createGuildMember.creditsEarned, "credit")}`,
})
.setFooter({ text: footerText, iconURL: footerIcon });
return interaction?.editReply({
embeds: [interactionEmbed],
});
});
};

View file

@ -2,8 +2,14 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
// Modules
import moduleCpgg from "./modules/cpgg";
import moduleRoles from "./modules/roles";
import {
builder as RolesBuilder,
execute as RolesExecute,
} from "./groups/roles";
import {
builder as CpggBuilder,
execute as CpggExecute,
} from "./subcommands/cpgg";
// Function
export const builder = new SlashCommandBuilder()
@ -12,8 +18,8 @@ export const builder = new SlashCommandBuilder()
.setDMPermission(false)
// Modules
.addSubcommand(moduleCpgg.builder)
.addSubcommandGroup(moduleRoles.builder);
.addSubcommand(CpggBuilder)
.addSubcommandGroup(RolesBuilder);
// Execute the command
export const execute = async (interaction: ChatInputCommandInteraction) => {
@ -21,7 +27,7 @@ export const execute = async (interaction: ChatInputCommandInteraction) => {
switch (options.getSubcommand()) {
case "cpgg": {
await moduleCpgg.execute(interaction);
await CpggExecute(interaction);
break;
}
default: {
@ -31,7 +37,7 @@ export const execute = async (interaction: ChatInputCommandInteraction) => {
switch (options.getSubcommandGroup()) {
case "roles": {
await moduleRoles.execute(interaction);
await RolesExecute(interaction);
break;
}
default: {

View file

@ -1,195 +0,0 @@
import axios from "axios";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
ChatInputCommandInteraction,
EmbedBuilder,
Message,
SlashCommandSubcommandBuilder,
} from "discord.js";
import { v4 as uuidv4 } from "uuid";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import encryption from "../../../../helpers/encryption";
import getEmbedData from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("cpgg")
.setDescription("Buy cpgg power.")
.addIntegerOption((option) =>
option
.setName("amount")
.setDescription("How much credits you want to withdraw.")
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedData(interaction.guild);
const { options, guild, user, client } = interaction;
const optionAmount = options?.getInteger("amount");
if (optionAmount === null) {
logger?.silly(`Amount is null.`);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:dollar:] Gift")
.setDescription("We could not read your requested amount.")
.setTimestamp()
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon });
return interaction?.editReply({
embeds: [interactionEmbed],
});
}
if (!guild) throw new Error("Guild not found");
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId: user.id,
guildId: guild.id,
},
},
update: {},
create: {
user: {
connectOrCreate: {
create: {
id: user.id,
},
where: {
id: user.id,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guild.id,
},
where: {
id: guild.id,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createGuildMember);
const dmUser = client?.users?.cache?.get(user?.id);
if ((optionAmount || createGuildMember.creditsEarned) < 100)
throw new Error("You can't withdraw to CPGG below 100 credits.");
if ((optionAmount || createGuildMember.creditsEarned) > 1000000)
throw new Error("Amount or user credits is above 1.000.000.");
if (createGuildMember.creditsEarned < optionAmount)
throw new Error("You can't withdraw more than you have on your account.");
if (
!createGuildMember.guild.apiCpggUrlIv ||
!createGuildMember.guild.apiCpggUrlContent
)
throw new Error("No API url available");
if (
!createGuildMember.guild.apiCpggTokenIv ||
!createGuildMember.guild.apiCpggTokenContent
)
throw new Error("No API token available");
const code = uuidv4();
const url = encryption.decrypt({
iv: createGuildMember.guild.apiCpggUrlIv,
content: createGuildMember.guild.apiCpggUrlContent,
});
const api = axios?.create({
baseURL: `${url}/api/`,
headers: {
Authorization: `Bearer ${encryption.decrypt({
iv: createGuildMember.guild.apiCpggTokenIv,
content: createGuildMember.guild.apiCpggTokenContent,
})}`,
},
});
const shopUrl = `${url}/store`;
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("Redeem it here")
.setStyle(ButtonStyle.Link)
.setEmoji("🏦")
.setURL(`${shopUrl}?voucher=${code}`)
);
await api
?.post("vouchers", {
uses: 1,
code,
credits: optionAmount || createGuildMember.creditsEarned,
memo: `${interaction?.createdTimestamp} - ${interaction?.user?.id}`,
})
?.then(async () => {
logger?.silly(`Successfully created voucher.`);
createGuildMember.creditsEarned -=
optionAmount || createGuildMember.creditsEarned;
const updateGuildMember = await prisma.guildMember.update({
where: {
userId_guildId: {
userId: user.id,
guildId: guild.id,
},
},
data: {
creditsEarned: {
decrement: optionAmount || createGuildMember.creditsEarned,
},
},
});
logger.silly(updateGuildMember);
if (!interaction.guild) throw new Error("Guild is undefined");
const dmEmbed = new EmbedBuilder()
.setTitle("[:shopping_cart:] CPGG")
.setDescription(
`This voucher comes from **${interaction.guild.name}**.`
)
.setTimestamp()
.addFields({
name: "💶 Credits",
value: `${optionAmount || createGuildMember.creditsEarned}`,
inline: true,
})
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon });
await dmUser
?.send({
embeds: [dmEmbed],
components: [buttons],
})
.then(async (msg: Message) => {
const interactionEmbed = new EmbedBuilder()
.setTitle("[:shopping_cart:] CPGG")
.setDescription(`I have sent you the code in [DM](${msg.url})!`)
.setTimestamp()
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction?.editReply({
embeds: [interactionEmbed],
});
});
});
return true;
},
};

View file

@ -1,47 +0,0 @@
// Dependencies
import {
ChatInputCommandInteraction,
SlashCommandSubcommandGroupBuilder,
} from "discord.js";
// Handlers
// Modules
import moduleBuy from "./modules/buy";
import moduleCancel from "./modules/cancel";
import prisma from "../../../../handlers/database";
export default {
builder: (group: SlashCommandSubcommandGroupBuilder) => {
return (
group
.setName("roles")
.setDescription("Shop for custom roles.")
// Modules
.addSubcommand(moduleBuy.builder)
.addSubcommand(moduleCancel.builder)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
if (!interaction.guild) return;
const { options, guild } = interaction;
const getGuild = await prisma.guild.findUnique({
where: { id: guild.id },
});
if (!getGuild) throw new Error("Guild not found");
if (!getGuild.shopRolesEnabled)
throw new Error("This server has disabled shop roles.");
if (options?.getSubcommand() === "buy") {
await moduleBuy.execute(interaction);
}
if (options?.getSubcommand() === "cancel") {
await moduleCancel.execute(interaction);
}
},
};

View file

@ -1,178 +0,0 @@
// Dependencies
// Helpers
import {
ChatInputCommandInteraction,
ColorResolvable,
EmbedBuilder,
GuildMemberRoleManager,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../../../handlers/deferReply";
import getEmbedData from "../../../../../../helpers/getEmbedData";
import logger from "../../../../../../middlewares/logger";
// Configurations
// import fetchUser from "../../../../../../helpers/userData";
// Models
import prisma from "../../../../../../handlers/database";
import pluralize from "../../../../../../helpers/pluralize";
// Function
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("buy")
.setDescription("Buy a custom role.")
.addStringOption((option) =>
option
.setName("name")
.setDescription("Name of the role you wish to buy.")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("color")
.setDescription("Color of the role you wish to buy.")
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
const { successColor, footerText, footerIcon } = await getEmbedData(
interaction.guild
);
const { options, guild, user, member } = interaction;
const optionName = options?.getString("name");
const optionColor = options?.getString("color");
// If amount is null
if (optionName === null)
throw new Error("We could not read your requested name");
await guild?.roles
.create({
name: optionName,
color: optionColor as ColorResolvable,
reason: `${user?.id} bought from shop`,
})
.then(async (role) => {
const userId = "SNOWFLKAE";
const guildId = "SNOWFLAKE";
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId,
guildId,
},
},
update: {},
create: {
user: {
connectOrCreate: {
create: {
id: userId,
},
where: {
id: userId,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guildId,
},
where: {
id: guildId,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createGuildMember);
// Get guild object
const pricePerHour = createGuildMember.guild.shopRolesPricePerHour;
const updateGuildMember = await prisma.guildMember.update({
where: {
userId_guildId: {
userId,
guildId,
},
},
data: {
creditsEarned: { decrement: pricePerHour },
},
});
logger.silly(updateGuildMember);
const createShopRole = await prisma.guildShopRoles.upsert({
where: {
guildId_userId_roleId: {
guildId: guild.id,
userId: user.id,
roleId: role.id,
},
},
update: {},
create: {
roleId: role.id,
lastPayed: new Date(),
user: {
connectOrCreate: {
create: {
id: user.id,
},
where: {
id: user.id,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guild.id,
},
where: {
id: guild.id,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createShopRole);
await (member?.roles as GuildMemberRoleManager)?.add(role?.id);
logger?.silly(`Role ${role?.name} was bought by ${user?.tag}`);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:shopping_cart:] Buy")
.setDescription(
`You bought **${optionName}** for **${pluralize(
pricePerHour,
"credit"
)}**.`
)
.setTimestamp()
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon });
return interaction?.editReply({
embeds: [interactionEmbed],
});
})
.catch(() => {
throw new Error("Failed creating role.");
});
},
};

View file

@ -1,127 +0,0 @@
// Dependencies
// Helpers
import {
ChatInputCommandInteraction,
EmbedBuilder,
GuildMemberRoleManager,
SlashCommandSubcommandBuilder,
} from "discord.js";
// Configurations
// Models
import deferReply from "../../../../../../handlers/deferReply";
import logger from "../../../../../../middlewares/logger";
// Configurations
// Models
import prisma from "../../../../../../handlers/database";
import getEmbedData from "../../../../../../helpers/getEmbedData";
import pluralize from "../../../../../../helpers/pluralize";
// Function
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("cancel")
.setDescription("Cancel a purchase.")
.addRoleOption((option) =>
option
.setName("role")
.setDescription("Role you wish to cancel.")
.setRequired(true)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
const { successColor, footerText, footerIcon } = await getEmbedData(
interaction.guild
);
const { options, guild, user, member } = interaction;
const optionRole = options.getRole("role");
if (optionRole === null)
throw new Error("We could not read your requested role.");
if (!guild) throw new Error("No guild specified");
if (!user) throw new Error("No user specified");
const roleExist = await prisma.guildShopRoles.findUnique({
where: {
guildId_userId_roleId: {
guildId: guild.id,
userId: user.id,
roleId: optionRole.id,
},
},
});
if (roleExist === null) return;
await (member?.roles as GuildMemberRoleManager)?.remove(optionRole?.id);
await guild?.roles
.delete(optionRole?.id, `${user?.id} canceled from shop`)
.then(async () => {
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId: user.id,
guildId: guild.id,
},
},
update: {},
create: {
user: {
connectOrCreate: {
create: {
id: user.id,
},
where: {
id: user.id,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guild.id,
},
where: {
id: guild.id,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createGuildMember);
if (!createGuildMember) throw new Error("Guild member not created");
const deleteShopRole = await prisma.guildShopRoles.delete({
where: {
guildId_userId_roleId: {
guildId: guild?.id,
userId: user?.id,
roleId: optionRole?.id,
},
},
});
logger.silly(deleteShopRole);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:shopping_cart:] Cancel")
.setDescription(`You have canceled ${optionRole.name}.`)
.setTimestamp()
.setColor(successColor)
.addFields({
name: "Your balance",
value: `${pluralize(createGuildMember.creditsEarned, "credit")}`,
})
.setFooter({ text: footerText, iconURL: footerIcon });
return interaction?.editReply({
embeds: [interactionEmbed],
});
});
},
};

View file

@ -0,0 +1,194 @@
import axios from "axios";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
ChatInputCommandInteraction,
EmbedBuilder,
Message,
SlashCommandSubcommandBuilder,
} from "discord.js";
import { v4 as uuidv4 } from "uuid";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import encryption from "../../../../helpers/encryption";
import getEmbedData from "../../../../helpers/getEmbedData";
import logger from "../../../../middlewares/logger";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("cpgg")
.setDescription("Buy cpgg power.")
.addIntegerOption((option) =>
option
.setName("amount")
.setDescription("How much credits you want to withdraw.")
.setRequired(true)
);
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedData(interaction.guild);
const { options, guild, user, client } = interaction;
const optionAmount = options?.getInteger("amount");
if (optionAmount === null) {
logger?.silly(`Amount is null.`);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:dollar:] Gift")
.setDescription("We could not read your requested amount.")
.setTimestamp()
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon });
return interaction?.editReply({
embeds: [interactionEmbed],
});
}
if (!guild) throw new Error("Guild not found");
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId: user.id,
guildId: guild.id,
},
},
update: {},
create: {
user: {
connectOrCreate: {
create: {
id: user.id,
},
where: {
id: user.id,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guild.id,
},
where: {
id: guild.id,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createGuildMember);
const dmUser = client?.users?.cache?.get(user?.id);
if ((optionAmount || createGuildMember.creditsEarned) < 100)
throw new Error("You can't withdraw to CPGG below 100 credits.");
if ((optionAmount || createGuildMember.creditsEarned) > 1000000)
throw new Error("Amount or user credits is above 1.000.000.");
if (createGuildMember.creditsEarned < optionAmount)
throw new Error("You can't withdraw more than you have on your account.");
if (
!createGuildMember.guild.apiCpggUrlIv ||
!createGuildMember.guild.apiCpggUrlContent
)
throw new Error("No API url available");
if (
!createGuildMember.guild.apiCpggTokenIv ||
!createGuildMember.guild.apiCpggTokenContent
)
throw new Error("No API token available");
const code = uuidv4();
const url = encryption.decrypt({
iv: createGuildMember.guild.apiCpggUrlIv,
content: createGuildMember.guild.apiCpggUrlContent,
});
const api = axios?.create({
baseURL: `${url}/api/`,
headers: {
Authorization: `Bearer ${encryption.decrypt({
iv: createGuildMember.guild.apiCpggTokenIv,
content: createGuildMember.guild.apiCpggTokenContent,
})}`,
},
});
const shopUrl = `${url}/store`;
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("Redeem it here")
.setStyle(ButtonStyle.Link)
.setEmoji("🏦")
.setURL(`${shopUrl}?voucher=${code}`)
);
await api
?.post("vouchers", {
uses: 1,
code,
credits: optionAmount || createGuildMember.creditsEarned,
memo: `${interaction?.createdTimestamp} - ${interaction?.user?.id}`,
})
?.then(async () => {
logger?.silly(`Successfully created voucher.`);
createGuildMember.creditsEarned -=
optionAmount || createGuildMember.creditsEarned;
const updateGuildMember = await prisma.guildMember.update({
where: {
userId_guildId: {
userId: user.id,
guildId: guild.id,
},
},
data: {
creditsEarned: {
decrement: optionAmount || createGuildMember.creditsEarned,
},
},
});
logger.silly(updateGuildMember);
if (!interaction.guild) throw new Error("Guild is undefined");
const dmEmbed = new EmbedBuilder()
.setTitle("[:shopping_cart:] CPGG")
.setDescription(
`This voucher comes from **${interaction.guild.name}**.`
)
.setTimestamp()
.addFields({
name: "💶 Credits",
value: `${optionAmount || createGuildMember.creditsEarned}`,
inline: true,
})
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon });
await dmUser
?.send({
embeds: [dmEmbed],
components: [buttons],
})
.then(async (msg: Message) => {
const interactionEmbed = new EmbedBuilder()
.setTitle("[:shopping_cart:] CPGG")
.setDescription(`I have sent you the code in [DM](${msg.url})!`)
.setTimestamp()
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction?.editReply({
embeds: [interactionEmbed],
});
});
});
return true;
};

View file

@ -1,25 +1,31 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
// Modules
import moduleAbout from "./modules/about";
import moduleAvatar from "./modules/avatar";
import {
builder as AboutBuilder,
execute as AboutExecute,
} from "./modules/about";
import {
builder as AvatarBuilder,
execute as AvatarExecute,
} from "./modules/avatar";
export const builder = new SlashCommandBuilder()
.setName("utils")
.setDescription("Common utility.")
// Modules
.addSubcommand(moduleAbout.builder)
.addSubcommand(moduleAvatar.builder);
.addSubcommand(AboutBuilder)
.addSubcommand(AvatarBuilder);
// Execute the command
export const execute = async (interaction: ChatInputCommandInteraction) => {
switch (interaction.options.getSubcommand()) {
case "about":
await moduleAbout.execute(interaction);
await AboutExecute(interaction);
break;
case "avatar":
await moduleAvatar.execute(interaction);
await AvatarExecute(interaction);
break;
default:
throw new Error(

View file

@ -14,107 +14,103 @@ import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData";
// Function
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("about")
.setDescription("Check information about this instance");
},
execute: async (interaction: CommandInteraction) => {
await deferReply(interaction, false);
if (!interaction.guild) throw new Error("You need to be in a guild");
const { client } = interaction;
// await cooldown(
// interaction.guild,
// interaction.user,
// interaction.commandId,
// 3600
// );
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
// // Initialize a storage for the user ids
// const userIds = new Set();
// // Iterate over all guilds (always cached)
// for await (const guild of client.guilds.cache.values()) {
// // Fetch all guild members and iterate over them
// for await (const member of (await guild.members.fetch()).values()) {
// // Fetch the user, if user already cached, returns value from cache
// // Will probably always return from cache
// const user = await client.users.fetch(member.id);
// // Check if user id is not already in set and user is not a bot
// if (!userIds.has(user.id) && !user.bot) {
// // Add unique user id to our set
// userIds.add(user.id);
// }
// }
// }
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("Support")
.setStyle(ButtonStyle.Link)
.setEmoji("💬")
.setURL("https://discord.zyner.org"),
new ButtonBuilder()
.setLabel("Documentation")
.setStyle(ButtonStyle.Link)
.setEmoji("📚")
.setURL("https://xyter.zyner.org")
);
const interactionEmbed = new EmbedBuilder()
.setColor(successColor)
.setTitle(":toolbox:︱About this instance")
.setDescription(
`This bot instance is hosted by [${process.env.BOT_HOSTER_NAME}](${process.env.BOT_HOSTER_URL}) who might have modified the [source code](https://github.com/ZynerOrg/xyter).`
)
.setFields(
{
name: "Latency",
value: `${Math.round(client.ws.ping)} ms`,
inline: true,
},
{
name: "Servers (cached)",
value: `${client.guilds.cache.size}`,
inline: true,
},
{
name: "Users (cached)",
value: `${client.guilds.cache.reduce(
(a, g) => a + g.memberCount,
0
)}`,
inline: true,
},
{
name: "Version",
value: `[${process.env.npm_package_version}](https://github.com/ZynerOrg/xyter/releases/tag/${process.env.npm_package_version})`,
inline: true,
},
{
name: "Since last restart",
value: `${formatDuration(
intervalToDuration({
start: subMilliseconds(new Date(), client.uptime),
end: new Date(),
})
)}`,
inline: true,
}
)
.setTimestamp()
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction.editReply({
embeds: [interactionEmbed],
components: [buttons],
});
},
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("about")
.setDescription("Check information about this instance");
};
export const execute = async (interaction: CommandInteraction) => {
await deferReply(interaction, false);
if (!interaction.guild) throw new Error("You need to be in a guild");
const { client } = interaction;
// await cooldown(
// interaction.guild,
// interaction.user,
// interaction.commandId,
// 3600
// );
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
// // Initialize a storage for the user ids
// const userIds = new Set();
// // Iterate over all guilds (always cached)
// for await (const guild of client.guilds.cache.values()) {
// // Fetch all guild members and iterate over them
// for await (const member of (await guild.members.fetch()).values()) {
// // Fetch the user, if user already cached, returns value from cache
// // Will probably always return from cache
// const user = await client.users.fetch(member.id);
// // Check if user id is not already in set and user is not a bot
// if (!userIds.has(user.id) && !user.bot) {
// // Add unique user id to our set
// userIds.add(user.id);
// }
// }
// }
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("Support")
.setStyle(ButtonStyle.Link)
.setEmoji("💬")
.setURL("https://discord.zyner.org"),
new ButtonBuilder()
.setLabel("Documentation")
.setStyle(ButtonStyle.Link)
.setEmoji("📚")
.setURL("https://xyter.zyner.org")
);
const interactionEmbed = new EmbedBuilder()
.setColor(successColor)
.setTitle(":toolbox:︱About this instance")
.setDescription(
`This bot instance is hosted by [${process.env.BOT_HOSTER_NAME}](${process.env.BOT_HOSTER_URL}) who might have modified the [source code](https://github.com/ZynerOrg/xyter).`
)
.setFields(
{
name: "Latency",
value: `${Math.round(client.ws.ping)} ms`,
inline: true,
},
{
name: "Servers (cached)",
value: `${client.guilds.cache.size}`,
inline: true,
},
{
name: "Users (cached)",
value: `${client.guilds.cache.reduce((a, g) => a + g.memberCount, 0)}`,
inline: true,
},
{
name: "Version",
value: `[${process.env.npm_package_version}](https://github.com/ZynerOrg/xyter/releases/tag/${process.env.npm_package_version})`,
inline: true,
},
{
name: "Since last restart",
value: `${formatDuration(
intervalToDuration({
start: subMilliseconds(new Date(), client.uptime),
end: new Date(),
})
)}`,
inline: true,
}
)
.setTimestamp()
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction.editReply({
embeds: [interactionEmbed],
components: [buttons],
});
};

View file

@ -6,45 +6,44 @@ import {
import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("avatar")
.setDescription("Check someones avatar!)")
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user whose avatar you want to check")
);
},
execute: async (interaction: CommandInteraction) => {
await deferReply(interaction, false);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("avatar")
.setDescription("Check someones avatar!)")
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user whose avatar you want to check")
);
const userOption = interaction.options.getUser("user");
const targetUser = userOption || interaction.user;
const embed = new EmbedBuilder()
.setTitle(":toolbox:︱Avatar")
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
const avatarUrl = targetUser.displayAvatarURL();
return interaction.editReply({
embeds: [
embed
.setDescription(
userOption
? `You can also [download it here](${avatarUrl})!`
: `Your avatar is available to [download here](${avatarUrl}).`
)
.setThumbnail(avatarUrl)
.setColor(successColor),
],
});
},
};
export const execute = async (interaction: CommandInteraction) => {
await deferReply(interaction, false);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const userOption = interaction.options.getUser("user");
const targetUser = userOption || interaction.user;
const embed = new EmbedBuilder()
.setTitle(":toolbox:︱Avatar")
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
const avatarUrl = targetUser.displayAvatarURL();
return interaction.editReply({
embeds: [
embed
.setDescription(
userOption
? `You can also [download it here](${avatarUrl})!`
: `Your avatar is available to [download here](${avatarUrl}).`
)
.setThumbnail(avatarUrl)
.setColor(successColor),
],
});
};

View file

@ -11,4 +11,13 @@ export const execute = async (message: Message) => {
await modules.credits.execute(message);
await modules.points.execute(message);
await modules.counters.execute(message);
const { client } = message;
if (!message.member) return;
if (message.author.bot) return;
client.emit("guildMemberAdd", message.member);
client.emit("guildMemberRemove", message.member);
client.emit("messageDelete", message);
client.emit("messageUpdate", message, message);
};