🧑‍💻 commands and events are now plugins

This commit is contained in:
Axel Olausson Holtenäs 2022-06-10 22:28:07 +02:00
parent 3196e39c70
commit e93f604273
No known key found for this signature in database
GPG key ID: 7BF6826B76382CBA
103 changed files with 977 additions and 1786 deletions

View file

@ -1,43 +0,0 @@
// Dependencies
import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
// Modules
import modules from "./modules";
// Handlers
import logger from "../../logger";
export const builder = new SlashCommandBuilder()
.setName("config")
.setDescription("Manage guild configurations.")
.addSubcommand(modules.pterodactyl.builder)
.addSubcommand(modules.credits.builder)
.addSubcommand(modules.points.builder)
.addSubcommand(modules.welcome.builder)
.addSubcommand(modules.audits.builder)
.addSubcommand(modules.shop.builder)
.addSubcommand(modules.embeds.builder);
export const moduleData = modules;
// Function
export const execute = async (interaction: CommandInteraction) => {
switch (interaction.options?.getSubcommand()) {
case "pterodactyl":
return modules.pterodactyl.execute(interaction);
case "credits":
return modules.credits.execute(interaction);
case "points":
return modules.points.execute(interaction);
case "welcome":
return modules.welcome.execute(interaction);
case "audits":
return modules.audits.execute(interaction);
case "shop":
return modules.shop.execute(interaction);
case "embeds":
return modules.embeds.execute(interaction);
}
};

View file

@ -1,95 +0,0 @@
// Dependencies
import { CommandInteraction, Permissions } from "discord.js";
// Configurations
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
// Handlers
import logger from "../../../../logger";
// Models
import guildSchema from "../../../../models/guild";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { ChannelType } from "discord-api-types/v10";
// Function
export default {
metadata: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("audits")
.setDescription("Audits")
.addBooleanOption((option) =>
option.setName("status").setDescription("Should audits be enabled?")
)
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("Channel for audit messages.")
.addChannelTypes(ChannelType.GuildText)
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { guild, options } = interaction;
// Get options
const status = options?.getBoolean("status");
const channel = options?.getChannel("channel");
// Get guild object
const guildDB = await guildSchema?.findOne({
guildId: guild?.id,
});
if (guildDB === null) {
return logger?.silly(`Guild not found in database.`);
}
// Modify values
guildDB.audits.status = status !== null ? status : guildDB?.audits?.status;
guildDB.audits.channelId =
channel !== null ? channel.id : guildDB?.audits?.channelId;
// Save guild
await guildDB?.save()?.then(async () => {
logger?.silly(`Guild audits updated.`);
return interaction?.editReply({
embeds: [
{
title: ":hammer: Settings - Guild [Audits]",
description: `Audits settings updated.`,
color: successColor,
fields: [
{
name: "🤖 Status",
value: `${guildDB?.audits?.status}`,
inline: true,
},
{
name: "🌊 Channel",
value: `${guildDB?.audits?.channelId}`,
inline: true,
},
],
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
});
},
};

View file

@ -1,144 +0,0 @@
// Dependencies
import { CommandInteraction, Permissions } from "discord.js";
// Configurations
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
//Handlers
import logger from "../../../../logger";
// Models
import guildSchema from "../../../../models/guild";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
metadata: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("credits")
.setDescription(`Credits`)
.addBooleanOption((option) =>
option.setName("status").setDescription("Should credits be enabled?")
)
.addNumberOption((option) =>
option.setName("rate").setDescription("Amount of credits per message.")
)
.addNumberOption((option) =>
option
.setName("minimum-length")
.setDescription("Minimum length of message to earn credits.")
)
.addNumberOption((option) =>
option
.setName("work-rate")
.setDescription("Maximum amount of credits on work.")
)
.addNumberOption((option) =>
option
.setName("work-timeout")
.setDescription("Timeout between work schedules (seconds).")
)
.addNumberOption((option) =>
option
.setName("timeout")
.setDescription("Timeout between earning credits (seconds).")
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
); // Destructure member
const { guild, options } = interaction;
if (guild == null) return;
// Get options
const status = options?.getBoolean("status");
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");
// Get guild object
const guildDB = await guildSchema?.findOne({
guildId: guild?.id,
});
if (guildDB === null) {
return logger?.silly(`Guild is null`);
}
// Modify values
guildDB.credits.status =
status !== null ? status : guildDB?.credits?.status;
guildDB.credits.rate = rate !== null ? rate : guildDB?.credits?.rate;
guildDB.credits.timeout =
timeout !== null ? timeout : guildDB?.credits?.timeout;
guildDB.credits.workRate =
workRate !== null ? workRate : guildDB?.credits?.workRate;
guildDB.credits.workTimeout =
workTimeout !== null ? workTimeout : guildDB?.credits?.workTimeout;
guildDB.credits.minimumLength =
minimumLength !== null ? minimumLength : guildDB?.credits?.minimumLength;
// Save guild
await guildDB?.save()?.then(async () => {
logger?.silly(`Guild saved`);
return interaction?.editReply({
embeds: [
{
title: ":tools: Settings - Guild [Credits]",
description: `Credits settings updated.`,
color: successColor,
fields: [
{
name: "🤖 Status",
value: `${guildDB?.credits?.status}`,
inline: true,
},
{
name: "📈 Rate",
value: `${guildDB?.credits?.rate}`,
inline: true,
},
{
name: "📈 Work Rate",
value: `${guildDB?.credits?.workRate}`,
inline: true,
},
{
name: "🔨 Minimum Length",
value: `${guildDB?.credits?.minimumLength}`,
inline: true,
},
{
name: "⏰ Timeout",
value: `${guildDB?.credits?.timeout}`,
inline: true,
},
{
name: "⏰ Work Timeout",
value: `${guildDB?.credits?.workTimeout}`,
inline: true,
},
],
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
});
},
};

View file

@ -1,38 +0,0 @@
import { ColorResolvable, CommandInteraction } from "discord.js";
import guildSchema from "../../../../../../models/guild";
import getEmbedConfig from "../../../../../../helpers/getEmbedConfig";
export default async (interaction: CommandInteraction) => {
const { options, guild } = interaction;
if (!guild) throw new Error("Guild not found");
const embedConfig = await getEmbedConfig(guild);
if (!embedConfig) throw new Error("Embed config not found");
// Get new values
const newSuccessColor = options.getString("success-color") as ColorResolvable;
const newWaitColor = options.getString("wait-color") as ColorResolvable;
const newErrorColor = options.getString("error-color") as ColorResolvable;
const newFooterIcon = options.getString("footer-icon");
const newFooterText = options.getString("footer-text");
// Get guild values
const guildData = await guildSchema.findOne({
guildId: guild.id,
});
if (!guildData) throw new Error("Guild data not found");
if (!guildData?.embeds)
throw new Error("Guild embed configuration not found");
let { successColor, waitColor, errorColor, footerText, footerIcon } =
guildData.embeds;
// Set new values
successColor = newSuccessColor || successColor;
waitColor = newWaitColor || waitColor;
errorColor = newErrorColor || errorColor;
footerIcon = newFooterIcon || footerIcon;
footerText = newFooterText || footerText;
return { successColor, waitColor, errorColor, footerText, footerIcon };
};

View file

@ -1,104 +0,0 @@
// Dependencies
import {
ColorResolvable,
CommandInteraction,
MessageEmbed,
Permissions,
} from "discord.js";
//Handlers
import logger from "../../../../logger";
// Models
import guildSchema from "../../../../models/guild";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
import getValues from "./components/getValues";
// Function
export default {
metadata: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("embeds")
.setDescription(`Embeds`)
.addStringOption((option) =>
option
.setName("success-color")
.setDescription("No provided description")
)
.addStringOption((option) =>
option.setName("wait-color").setDescription("No provided description")
)
.addStringOption((option) =>
option.setName("error-color").setDescription("No provided description")
)
.addStringOption((option) =>
option.setName("footer-icon").setDescription("No provided description")
)
.addStringOption((option) =>
option.setName("footer-text").setDescription("No provided description")
);
},
execute: async (interaction: CommandInteraction) => {
const { guild } = interaction;
if (!guild) throw new Error("Guild not found");
const { successColor, waitColor, errorColor, footerText, footerIcon } =
await getValues(interaction);
// Initialize embed object
const embed = new MessageEmbed()
.setTitle("[:tools:] Embeds")
.setFooter({ text: footerText, iconURL: footerIcon })
.setTimestamp(new Date());
// Get guild values
const guildData = await guildSchema.findOne({
guildId: guild.id,
});
if (!guildData) throw new Error("Guild data not found");
await guildData.save().then(async () => {
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,
},
]);
return interaction.editReply({
embeds: [embed],
});
});
},
};

View file

@ -1,9 +0,0 @@
import audits from "./audits";
import credits from "./credits";
import points from "./points";
import pterodactyl from "./pterodactyl";
import shop from "./shop";
import welcome from "./welcome";
import embeds from "./embeds";
export default { audits, credits, points, pterodactyl, shop, welcome, embeds };

View file

@ -1,117 +0,0 @@
// Dependencies
import { CommandInteraction, Permissions } from "discord.js";
// Configurations
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
// Handlers
import logger from "../../../../logger";
// Models
import guildSchema from "../../../../models/guild";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
metadata: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("points")
.setDescription("Points")
.addBooleanOption((option) =>
option.setName("status").setDescription("Should credits be enabled?")
)
.addNumberOption((option) =>
option.setName("rate").setDescription("Amount of credits per message.")
)
.addNumberOption((option) =>
option
.setName("minimum-length")
.setDescription("Minimum length of message to earn credits.")
)
.addNumberOption((option) =>
option
.setName("timeout")
.setDescription("Timeout between earning credits (milliseconds).")
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
// Destructure member
const { options, guild } = interaction;
// Get options
const status = options?.getBoolean("status");
const rate = options?.getNumber("rate");
const timeout = options?.getNumber("timeout");
const minimumLength = options?.getNumber("minimum-length");
// Get guild object
const guildDB = await guildSchema?.findOne({
guildId: guild?.id,
});
if (guildDB === null) {
return logger?.silly(`Guild not found in database.`);
}
// Modify values
guildDB.points.status = status !== null ? status : guildDB?.points?.status;
guildDB.points.rate = rate !== null ? rate : guildDB?.points?.rate;
guildDB.points.timeout =
timeout !== null ? timeout : guildDB?.points?.timeout;
guildDB.points.minimumLength =
minimumLength !== null ? minimumLength : guildDB?.points?.minimumLength;
// Save guild
await guildDB?.save()?.then(async () => {
logger?.silly(`Guild points updated.`);
return interaction?.editReply({
embeds: [
{
title: ":hammer: Settings - Guild [Points]",
description: `Points settings updated.`,
color: successColor,
fields: [
{
name: "🤖 Status",
value: `${guildDB?.points?.status}`,
inline: true,
},
{
name: "📈 Rate",
value: `${guildDB?.points?.rate}`,
inline: true,
},
{
name: "🔨 Minimum Length",
value: `${guildDB?.points?.minimumLength}`,
inline: true,
},
{
name: "⏰ Timeout",
value: `${guildDB?.points?.timeout}`,
inline: true,
},
],
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
});
},
};

View file

@ -1,78 +0,0 @@
// Dependencies
import { CommandInteraction, Permissions } from "discord.js";
// Configurations
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
// Handlers
import logger from "../../../../logger";
// Models
import apiSchema from "../../../../models/api";
import encryption from "../../../../handlers/encryption";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
metadata: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("pterodactyl")
.setDescription("Controlpanel.gg")
.addStringOption((option) =>
option
.setName("url")
.setDescription(`Controlpanel.gg URL`)
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("token")
.setDescription(`Controlpanel.gg Token`)
.setRequired(true)
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
); // Destructure member
const { options, guild } = interaction;
// Get options
const tokenData = options.getString("token");
const url = options.getString("url");
const token = tokenData && encryption.encrypt(tokenData);
// Update API credentials
await apiSchema
?.findOneAndUpdate(
{ guildId: guild?.id },
{ url, token },
{ new: true, upsert: true }
)
.then(async () => {
logger?.silly(`Updated API credentials.`);
return interaction?.editReply({
embeds: [
{
title: ":hammer: Settings - Guild [Pterodactyl]",
color: successColor,
description: `Successfully updated API credentials.`,
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
});
},
};

View file

@ -1,97 +0,0 @@
// Dependencies
import { CommandInteraction, Permissions } from "discord.js";
// Configurations
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
// Handlers
import logger from "../../../../logger";
// Models
import guildSchema from "../../../../models/guild";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
metadata: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("shop")
.setDescription("Shop")
.addBooleanOption((option) =>
option
.setName("roles-status")
.setDescription("Should roles be enabled?")
)
.addNumberOption((option) =>
option
.setName("roles-price-per-hour")
.setDescription("Price per hour for roles.")
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
); // Destructure member
const { options, guild } = interaction;
// Get options
const rolesStatus = options?.getBoolean("roles-status");
const rolesPricePerHour = options?.getNumber("roles-price-per-hour");
// Get guild object
const guildDB = await guildSchema?.findOne({
guildId: guild?.id,
});
if (guildDB === null) {
return logger?.silly(`Guild not found in database.`);
}
// Modify values
guildDB.shop.roles.status =
rolesStatus !== null ? rolesStatus : guildDB?.shop?.roles?.status;
guildDB.shop.roles.pricePerHour =
rolesPricePerHour !== null
? rolesPricePerHour
: guildDB?.shop?.roles?.pricePerHour;
// Save guild
await guildDB?.save()?.then(async () => {
logger?.silly(`Guild shop updated.`);
return interaction?.editReply({
embeds: [
{
title: ":hammer: Settings - Guild [Shop]",
description: `Shop settings updated.`,
color: successColor,
fields: [
{
name: "🤖 Roles Status",
value: `${guildDB?.shop?.roles.status}`,
inline: true,
},
{
name: "🌊 Roles Price Per Hour",
value: `${guildDB?.shop?.roles.pricePerHour}`,
inline: true,
},
],
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
});
},
};

View file

@ -1,142 +0,0 @@
// Dependencies
import { CommandInteraction, Permissions } from "discord.js";
// Configurations
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
// Handlers
import logger from "../../../../logger";
// Models
import guildSchema from "../../../../models/guild";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { ChannelType } from "discord-api-types/v10";
// Function
export default {
metadata: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("welcome")
.setDescription("Welcome")
.addBooleanOption((option) =>
option.setName("status").setDescription("Should welcome be enabled?")
)
.addChannelOption((option) =>
option
.setName("join-channel")
.setDescription("Channel for join messages.")
.addChannelTypes(ChannelType.GuildText)
)
.addChannelOption((option) =>
option
.setName("leave-channel")
.setDescription("Channel for leave messages.")
.addChannelTypes(ChannelType.GuildText)
)
.addStringOption((option) =>
option
.setName("leave-message")
.setDescription("Message for leave messages.")
)
.addStringOption((option) =>
option
.setName("join-message")
.setDescription("Message for join messages.")
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
); // Destructure member
const { options, guild } = interaction;
// Get options
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");
// Get guild object
const guildDB = await guildSchema?.findOne({
guildId: guild?.id,
});
if (guildDB === null) {
return logger?.silly(`Guild not found in database.`);
}
// Modify values
guildDB.welcome.status =
status !== null ? status : guildDB?.welcome?.status;
guildDB.welcome.joinChannel =
joinChannel !== null ? joinChannel.id : guildDB?.welcome?.joinChannel;
guildDB.welcome.leaveChannel =
leaveChannel !== null ? leaveChannel.id : guildDB?.welcome?.leaveChannel;
guildDB.welcome.joinChannelMessage =
joinChannelMessage !== null
? joinChannelMessage
: guildDB?.welcome?.joinChannelMessage;
guildDB.welcome.leaveChannelMessage =
leaveChannelMessage !== null
? leaveChannelMessage
: guildDB?.welcome?.leaveChannelMessage;
// Save guild
await guildDB?.save()?.then(async () => {
logger?.silly(`Guild welcome updated.`);
if (!guildDB?.welcome?.status) {
return interaction?.editReply({
embeds: [
{
title: "[:tools:] Welcome",
description: `This module is currently disabled, please enable it to continue.`,
color: successColor,
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
}
return interaction?.editReply({
embeds: [
{
title: "[:tools:] Welcome",
description: `The following configuration will be used.
[👋] **Welcome**
**Channel**: <#${guildDB?.welcome?.joinChannel}>
**Message**: ${guildDB?.welcome?.joinChannelMessage}
[🚪] **Leave**
**Channel**: <#${guildDB?.welcome?.leaveChannel}>
**Message**: ${guildDB?.welcome?.leaveChannelMessage}`,
color: successColor,
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
});
},
};

View file

@ -1,165 +0,0 @@
// Dependencies
import { CommandInteraction } from "discord.js";
// Configurations
import getEmbedConfig from "../../../helpers/getEmbedConfig";
import { timeout } from "../../../config/reputation";
// Handlers
import logger from "../../../logger";
// Models
import timeoutSchema from "../../../models/timeout";
import fetchUser from "../../../helpers/fetchUser";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
metadata: { guildOnly: true, ephemeral: true },
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("give")
.setDescription("Give reputation to a user")
.addUserOption((option) =>
option
.setName("target")
.setDescription("The user you want to repute.")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("type")
.setDescription("What type of reputation you want to repute")
.setRequired(true)
.addChoices(
{ name: "Positive", value: "positive" },
{
name: "Negative",
value: "negative",
}
)
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild); // Destructure
const { options, user, guild } = interaction;
// Target option
const optionTarget = options?.getUser("target");
// Type information
const optionType = options?.getString("type");
if (guild === null) {
return logger?.silly(`Guild is null`);
}
// User information
const userObj = await fetchUser(user, guild);
if (userObj === null) {
return logger?.silly(`User is null`);
}
// Check if user has a timeout
const isTimeout = await timeoutSchema?.findOne({
guildId: guild?.id,
userId: user?.id,
timeoutId: "2022-04-10-16-42",
});
// If user is not on timeout
if (isTimeout) {
logger?.silly(`User is on timeout`);
return interaction?.editReply({
embeds: [
{
title: ":loudspeaker: Reputation [Give]",
description: `You cannot give reputation while on timeout, please wait ${timeout} seconds.`,
timestamp: new Date(),
color: errorColor,
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
}
// Do not allow self reputation
if (optionTarget?.id === user?.id) {
logger?.silly(`User is trying to give reputation to self`);
return interaction?.editReply({
embeds: [
{
title: ":loudspeaker: Reputation [Give]",
description: `You cannot give reputation to yourself.`,
timestamp: new Date(),
color: errorColor,
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
}
// If type is positive
if (optionType === "positive") {
logger?.silly(`User is giving positive reputation`);
userObj.reputation += 1;
}
// If type is negative
else if (optionType === "negative") {
logger?.silly(`User is giving negative reputation`);
userObj.reputation -= 1;
}
// Save user
await userObj?.save()?.then(async () => {
logger?.silly(`User reputation has been updated`);
await timeoutSchema?.create({
guildId: guild?.id,
userId: user?.id,
timeoutId: "2022-04-10-16-42",
});
return interaction?.editReply({
embeds: [
{
title: ":loudspeaker: Reputation [Give]",
description: `You have given reputation to ${optionTarget}`,
timestamp: new Date(),
color: successColor,
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
});
setTimeout(async () => {
logger?.silly(`Removing timeout`);
await timeoutSchema?.deleteOne({
guildId: guild?.id,
userId: user?.id,
timeoutId: "2022-04-10-16-42",
});
}, timeout);
},
};

View file

@ -1,4 +0,0 @@
import pterodactyl from "./pterodactyl";
import * as roles from "./roles";
export default { pterodactyl, roles };

View file

@ -1,38 +0,0 @@
// Dependencies
import { CommandInteraction } from "discord.js";
// Configurations
import getEmbedConfig from "../../../helpers/getEmbedConfig";
import { hosterName, hosterUrl } from "../../../config/other";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
metadata: { guildOnly: false, ephemeral: false },
builder: (command: SlashCommandSubcommandBuilder) => {
return command.setName("about").setDescription("About this bot!)");
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const interactionEmbed = {
title: ":hammer: Utilities [About]",
description: `This bot is hosted by ${
hosterUrl ? `[${hosterName}](${hosterUrl})` : `${hosterName}`
}, the bot is developed by [Zyner](https://github.com/ZynerOrg)!
If you are interested in contributing, then just [fork it](https://github.com/ZynerOrg/xyter) yourself, we :heart: Open Source.`,
color: successColor,
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
};
interaction?.editReply({ embeds: [interactionEmbed] });
},
};

View file

@ -8,7 +8,7 @@ export const guildId = "";
export const hosterName = "someone";
// Hoster Url
export const hosterUrl = "scheme://domain.tld";
export const hosterUrl = "https://xyter.zyner.org/customization/change-hoster";
// Winston log level
export const logLevel = "info";

View file

@ -1,178 +0,0 @@
// Dependencies
import { CommandInteraction, MessageEmbed } from "discord.js";
import logger from "../../../logger";
import deferReply from "../../../helpers/deferReply";
import getEmbedConfig from "../../../helpers/getEmbedConfig";
import getCommandMetadata from "../../../helpers/getCommandMetadata";
import capitalizeFirstLetter from "../../../helpers/capitalizeFirstLetter";
import timeoutSchema from "../../../models/timeout";
import addSeconds from "../../../helpers/addSeconds";
export default async (interaction: CommandInteraction) => {
if (!interaction.isCommand()) return;
if (interaction.guild == null) return;
const { errorColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { client, guild, commandName, user, memberPermissions } = interaction;
const currentCommand = client.commands.get(commandName);
if (currentCommand == null) {
logger.silly(`Command ${commandName} not found`);
}
const metadata = await getCommandMetadata(interaction, currentCommand);
await deferReply(interaction, metadata.ephemeral || false);
if (
metadata.permissions &&
metadata.guildOnly &&
!memberPermissions?.has(metadata.permissions)
) {
return interaction?.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:x:] Permission")
.setDescription(`You do not have the permission to manage the bot.`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
}
if (metadata.cooldown) {
console.log("cooldown");
// console.log(interaction);
// Check if user has a timeout
const isTimeout = await timeoutSchema.findOne({
guildId: guild.id,
userId: user.id,
cooldown: metadata.cooldown,
timeoutId: interaction.commandId,
});
// If user is not on timeout
if (isTimeout) {
logger?.silly(`User is on timeout`);
const { guildId, userId, timeoutId, cooldown, createdAt } = isTimeout;
const overDue = (await addSeconds(cooldown, createdAt)) < new Date();
if (overDue) {
timeoutSchema
.deleteOne({
guildId,
userId,
timeoutId,
cooldown,
})
.then(async () => {
logger.debug(
`Timeout document ${timeoutId} has been deleted from user ${userId}.`
);
});
} else {
const diff = Math.round(
((new Date(isTimeout.createdAt).getTime() - new Date().getTime()) *
-1) /
1000
);
return interaction?.editReply({
embeds: [
{
title: `[:x:] ${capitalizeFirstLetter(
interaction.options.getSubcommand()
)}`,
description: `
You are currently on timeout, please wait ${diff} seconds.
If it still doesn't work, please wait for a maximum of **1 hour** before contacting bot owner.
`,
timestamp: new Date(),
color: errorColor,
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
}
}
await timeoutSchema.create({
guildId: guild.id,
userId: user.id,
cooldown: metadata.cooldown,
timeoutId: interaction.commandId,
});
}
if (metadata.guildOnly) {
if (!guild) {
logger.debug(`Guild is null`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setDescription("This command is only available for guild")
.setColor(errorColor)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
}
}
if (metadata.dmOnly) {
if (guild) {
logger.silly(`Guild exist`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setDescription("This command is only available in DM.")
.setColor(errorColor)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
}
}
await currentCommand
.execute(interaction)
.then(async () => {
return logger?.silly(
`Command: ${commandName} executed in guild: ${guild?.name} (${guild?.id}) by user: ${user?.tag} (${user?.id})`
);
})
.catch(async (error: string) => {
logger?.error(`${error}`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle(
`[:x:] ${capitalizeFirstLetter(
interaction.options.getSubcommand()
)}`
)
.setDescription(`${"``"}${error}${"``"}`)
.setColor(errorColor)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
});
};

View file

@ -1,23 +0,0 @@
// 3rd party dependencies
import { CommandInteraction } from "discord.js";
// Dependencies
import isCommand from "../../events/interactionCreate/components/isCommand";
import logger from "../../logger";
import audits from "./audits";
import { IEventOptions } from "../../interfaces/EventOptions";
export const options: IEventOptions = {
type: "on",
};
export const execute = async (interaction: CommandInteraction) => {
const { guild, id } = interaction;
logger?.silly(
`New interaction: ${id} in guild: ${guild?.name} (${guild?.id})`
);
await audits.execute(interaction);
await isCommand(interaction);
};

View file

@ -1,85 +0,0 @@
import logger from "../../../../logger";
import timeouts from "../../../../models/timeout";
import { Message } from "discord.js";
import fetchUser from "../../../../helpers/fetchUser";
import fetchGuild from "../../../../helpers/fetchGuild";
export default {
execute: async (message: Message) => {
const { guild, author, content, channel } = message;
if (guild == null) return;
if (author.bot) return;
if (channel?.type !== "GUILD_TEXT") return;
const { id: guildId } = guild;
const { id: userId } = author;
const guildData = await fetchGuild(guild);
const userData = await fetchUser(author, guild);
if (content.length < guildData.credits.minimumLength) return;
const timeoutData = {
guildId,
userId,
timeoutId: "2022-04-14-13-51-00",
};
const timeout = await timeouts.findOne(timeoutData);
if (timeout) {
logger.silly(
`User ${userId} in guild ${guildId} is on timeout 2022-04-14-13-51-00`
);
return;
}
userData.credits += guildData.credits.rate;
await userData
.save()
.then(async () => {
logger.silly(
`User ${userId} in guild ${guildId} has ${userData.credits} credits`
);
})
.catch(async (err) => {
logger.error(
`Error saving credits for user ${userId} in guild ${guildId}`,
err
);
});
await timeouts
.create(timeoutData)
.then(async () => {
logger.silly(
`Timeout 2022-04-14-13-51-00 for user ${userId} in guild ${guildId} has been created`
);
})
.catch(async (err) => {
logger.error(
`Error creating timeout 2022-04-14-13-51-00 for user ${userId} in guild ${guildId}`,
err
);
});
setTimeout(async () => {
await timeouts
.deleteOne(timeoutData)
.then(async () => {
logger.silly(
`Timeout 2022-04-14-13-51-00 for user ${userId} in guild ${guildId} has been deleted`
);
})
.catch(async (err) => {
logger.error(
`Error deleting timeout 2022-04-14-13-51-00 for user ${userId} in guild ${guildId}`,
err
);
});
}, guildData.credits.timeout);
},
};

View file

@ -1,89 +0,0 @@
import logger from "../../../../logger";
import timeouts from "../../../../models/timeout";
import fetchUser from "../../../../helpers/fetchUser";
import fetchGuild from "../../../../helpers/fetchGuild";
import { Message } from "discord.js";
export default {
execute: async (message: Message) => {
const { guild, author, content, channel } = message;
if (guild == null) return;
if (author.bot) return;
if (channel?.type !== "GUILD_TEXT") return;
const { id: guildId } = guild;
const { id: userId } = author;
const guildData = await fetchGuild(guild);
const userData = await fetchUser(author, guild);
if (content.length < guildData.credits.minimumLength) return;
const timeoutData = {
guildId,
userId,
timeoutId: "2022-04-14-14-15-00",
};
const timeout = await timeouts.findOne(timeoutData);
if (timeout) {
logger.silly(
`User ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id} is on timeout 2022-04-14-14-15-00`
);
return;
}
userData.points += guildData.points.rate;
await userData
.save()
.then(async () => {
logger.silly(
`Successfully saved user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`
);
})
.catch(async (err) => {
logger.error(
`Error saving points for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`,
err
);
});
logger.silly(
`User ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id}) has ${userData.points} points`
);
await timeouts
.create(timeoutData)
.then(async () => {
logger.silly(
`Successfully created timeout for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`
);
})
.catch(async (err) => {
logger.error(
`Error creating timeout 2022-04-14-14-15-00 for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`,
err
);
});
setTimeout(async () => {
await timeouts
.deleteOne(timeoutData)
.then(async () => {
logger.silly(
`Successfully deleted timeout 2022-04-14-14-15-00 for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`
);
})
.catch(async (err) => {
logger.error(
`Error deleting timeout 2022-04-14-14-15-00 for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`,
err
);
});
}, guildData.points.timeout);
},
};

View file

@ -12,6 +12,10 @@ import { ICommand } from "../interfaces/Command";
export default async (client: Client) => {
const commandList: Array<RESTPostAPIApplicationCommandsJSONBody> = [];
if (!client.commands) {
throw new Error("client.commands is not defined");
}
logger.info("Gathering command list");
await Promise.all(
@ -25,7 +29,7 @@ export default async (client: Client) => {
logger.info(`Finished gathering command list.`);
})
.catch(async (error) => {
logger.error(`${error}`);
throw new Error(`Could not gather command list: ${error}`);
});
const rest = new REST({ version: "9" }).setToken(token);

View file

@ -1,4 +1,4 @@
export default async (numOfSeconds: number | undefined, date = new Date()) => {
export default async (numOfSeconds: number, date: Date) => {
if (!numOfSeconds) throw new Error("numOfSeconds is required");
date.setSeconds(date.getSeconds() + numOfSeconds);

View file

@ -0,0 +1,112 @@
// Dependencies
import { CommandInteraction, Message } from "discord.js";
import logger from "../../logger";
import getEmbedConfig from "../../helpers/getEmbedConfig";
import timeoutSchema from "../../models/timeout";
import addSeconds from "../../helpers/addSeconds";
export const interaction = async (i: CommandInteraction, cooldown: number) => {
const { guild, user, commandId } = i;
// Check if user has a timeout
const hasTimeout = await timeoutSchema.findOne({
guildId: guild?.id || "0",
userId: user.id,
cooldown: cooldown,
timeoutId: commandId,
});
// If user is not on timeout
if (hasTimeout) {
const { guildId, userId, timeoutId, createdAt } = hasTimeout;
const overDue = (await addSeconds(cooldown, createdAt)) < new Date();
if (!overDue) {
const diff = Math.round(
(new Date(hasTimeout.createdAt).getTime() - new Date().getTime()) / 1000
);
throw new Error(
`You must wait ${diff} seconds before using this command.`
);
}
// Delete timeout
await timeoutSchema
.deleteOne({
guildId,
userId,
timeoutId,
cooldown,
})
.then(async () => {
logger.debug(
`Timeout document ${timeoutId} has been deleted from user ${userId}.`
);
});
}
// Create timeout
await timeoutSchema.create({
guildId: guild?.id || "0",
userId: user.id,
cooldown: cooldown,
timeoutId: commandId,
});
};
export const message = async (
message: Message,
cooldown: number,
id: string
) => {
const { guild, member } = message;
if (!guild) throw new Error("Guild is undefined");
if (!member) throw new Error("Member is undefined");
// Check if user has a timeout
const hasTimeout = await timeoutSchema.findOne({
guildId: guild?.id || "0",
userId: member.id,
cooldown: cooldown,
timeoutId: id,
});
// If user is not on timeout
if (hasTimeout) {
const { guildId, userId, timeoutId, createdAt } = hasTimeout;
const overDue = (await addSeconds(cooldown, createdAt)) < new Date();
if (!overDue) {
const diff = Math.round(
(new Date(hasTimeout.createdAt).getTime() - new Date().getTime()) / 1000
);
throw new Error(
`User: ${userId} on timeout-id: ${id} with cooldown: ${cooldown} secs with remaining: ${diff} secs.`
);
}
// Delete timeout
await timeoutSchema
.deleteOne({
guildId,
userId: member.id,
timeoutId: id,
cooldown,
})
.then(async () => {
logger.debug(
`Timeout document ${timeoutId} has been deleted from user ${userId}.`
);
});
}
// Create timeout
await timeoutSchema.create({
guildId: guild?.id || "0",
userId: member.id,
cooldown: cooldown,
timeoutId: id,
});
};

View file

@ -2,8 +2,6 @@ import { CommandInteraction, MessageEmbed } from "discord.js";
import getEmbedConfig from "../../helpers/getEmbedConfig";
export default async (interaction: CommandInteraction, ephemeral: boolean) => {
if (interaction.guild == null) return;
await interaction.deferReply({
ephemeral,
});

View file

@ -2,9 +2,7 @@ import fs from "fs";
const fsPromises = fs.promises;
export default async (path: string) => {
try {
return await fsPromises.readdir(path);
} catch (err) {
console.error("Error occurred while reading directory!", err);
}
return fsPromises.readdir(path).catch(async (e) => {
throw new Error(`Could not list directory: ${path}`, e);
});
};

View file

@ -4,10 +4,7 @@ import { token, intents } from "./config/discord";
import { Client } from "discord.js"; // discord.js
import * as databaseManager from "./managers/database";
import * as scheduleManager from "./managers/schedule";
import * as eventManager from "./managers/event";
import * as commandManager from "./managers/command";
import * as managers from "./managers";
// Main process that starts all other sub processes
const main = async () => {
@ -16,11 +13,7 @@ const main = async () => {
intents,
});
// Start managers
await databaseManager.start();
await scheduleManager.start(client);
await commandManager.register(client);
await eventManager.register(client);
await managers.start(client);
// Authorize with Discord's API
await client.login(token);

View file

@ -3,7 +3,8 @@ import "winston-daily-rotate-file";
import { logLevel } from "../config/other";
const { combine, timestamp, printf, colorize, align, json } = winston.format;
const { combine, timestamp, printf, errors, colorize, align, json } =
winston.format;
export default winston.createLogger({
level: logLevel || "info",
@ -16,6 +17,7 @@ export default winston.createLogger({
}),
new winston.transports.Console({
format: combine(
errors({ stack: true, trace: true }), // <-- use errors format
colorize({ all: true }),
timestamp({
format: "YYYY-MM-DD HH:MM:ss",

View file

@ -1,20 +1,25 @@
import fs from "fs"; // fs
import { Collection, Client } from "discord.js"; // discord.js
import logger from "../../logger";
import { ICommand } from "../../interfaces/Command";
import { Collection, Client } from "discord.js";
import listDir from "../../helpers/listDir";
import logger from "../../logger";
import { ICommand } from "../../interfaces/Command";
export const register = async (client: Client) => {
client.commands = new Collection();
const commandNames = await listDir("commands");
if (!commandNames) return;
const commandNames = await listDir("plugins/commands");
if (!commandNames) throw new Error("Could not list commands");
logger.info(`Loading ${commandNames.length} commands`);
await Promise.all(
commandNames.map(async (commandName) => {
const command: ICommand = await import(`../../commands/${commandName}`);
const command: ICommand = await import(
`../../plugins/commands/${commandName}`
).catch(async (e) => {
throw new Error(`Could not load command: ${commandName}`, e);
});
client.commands.set(command.builder.name, command);
@ -25,6 +30,6 @@ export const register = async (client: Client) => {
logger.info(`Finished loading commands.`);
})
.catch(async (err) => {
logger.error(`${err}`);
throw new Error(`Could not load commands: ${err}`);
});
};

View file

@ -8,9 +8,14 @@ import logger from "../../logger";
import { url } from "../../config/database";
export const start = async () => {
await mongoose.connect(url).then(async (connection) => {
logger.info(`Connected to database: ${connection.connection.name}`);
});
await mongoose
.connect(url)
.then(async (connection) => {
logger.info(`Connected to database: ${connection.connection.name}`);
})
.catch(async (e) => {
logger.error("Could not connect to database", e);
});
mongoose.connection.on("error", async (error) => {
logger.error(`${error}`);

View file

@ -2,15 +2,18 @@
import { Client } from "discord.js";
import listDir from "../../helpers/listDir";
import { IEvent } from "../../interfaces/Event";
import logger from "../../logger";
export const register = async (client: Client) => {
const eventNames = await listDir("events");
const eventNames = await listDir("plugins/events");
if (!eventNames) return;
for await (const eventName of eventNames) {
const event: IEvent = await import(`../../events/${eventName}`);
const event: IEvent = await import(`../../plugins/events/${eventName}`);
const eventExecutor = async (...args: Promise<void>[]) =>
event.execute(...args);
event.execute(...args).catch(async (err) => {
logger.error(`${err}`);
});
if (!event.options?.type) return;
switch (event.options.type) {

13
src/managers/index.ts Normal file
View file

@ -0,0 +1,13 @@
import { Client } from "discord.js";
import * as database from "./database";
import * as schedule from "./schedule";
import * as event from "./event";
import * as command from "./command";
export const start = async (client: Client) => {
await database.start();
await schedule.start(client);
await command.register(client);
await event.register(client);
};

View file

@ -4,7 +4,7 @@ import { Schema, model } from "mongoose";
export interface ITimeout {
userId: Snowflake;
guildId: Snowflake;
cooldown?: number;
cooldown: number;
timeoutId: string;
createdAt: Date;
updatedAt: Date;
@ -26,7 +26,7 @@ const timeoutSchema = new Schema<ITimeout>(
},
cooldown: {
type: Number,
required: false,
required: true,
unique: false,
index: true,
},

View file

@ -0,0 +1,7 @@
import { CommandInteraction } from "discord.js";
export const metadata = { guildOnly: false, ephemeral: false };
export const execute = async (interaction: CommandInteraction) => {
console.log("primary button clicked!");
};

View file

@ -1,6 +1,6 @@
import { CommandInteraction } from "discord.js";
import { SlashCommandBuilder } from "@discordjs/builders";
import logger from "../../logger";
import logger from "../../../logger";
import modules from "../../commands/counters/modules";

View file

@ -1,10 +1,10 @@
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
import { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { ChannelType } from "discord-api-types/v10";
import counterSchema from "../../../../models/counter";
import counterSchema from "../../../../../models/counter";
export default {
metadata: { guildOnly: true, ephemeral: false },
@ -25,7 +25,6 @@ export default {
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const { options, guild } = interaction;

View file

@ -1,6 +1,6 @@
import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
import logger from "../../logger";
import logger from "../../../logger";
import modules from "./modules";

View file

@ -1,10 +1,10 @@
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
import { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import logger from "../../../../logger";
import logger from "../../../../../logger";
import fetchUser from "../../../../helpers/fetchUser";
import fetchUser from "../../../../../helpers/fetchUser";
export default {
metadata: { guildOnly: true, ephemeral: true },
@ -19,7 +19,6 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const { options, user, guild } = interaction;

View file

@ -2,15 +2,15 @@
import { CommandInteraction, MessageEmbed } from "discord.js";
// Configurations
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
// Handlers
import logger from "../../../../logger";
import logger from "../../../../../logger";
import mongoose from "mongoose";
// Models
import fetchUser from "../../../../helpers/fetchUser";
import fetchUser from "../../../../../helpers/fetchUser";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
@ -38,7 +38,6 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const { options, user, guild, client } = interaction;

View file

@ -1,10 +1,10 @@
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
import { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import logger from "../../../../logger";
import logger from "../../../../../logger";
import userSchema, { IUser } from "../../../../models/user";
import userSchema, { IUser } from "../../../../../models/user";
export default {
metadata: { guildOnly: true, ephemeral: false },
@ -13,7 +13,6 @@ export default {
return command.setName("top").setDescription(`View the top users`);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const { guild } = interaction;

View file

@ -4,17 +4,17 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import Chance from "chance";
// Configurations
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
// Handlers
import logger from "../../../../logger";
import logger from "../../../../../logger";
// Models
import timeoutSchema from "../../../../models/timeout";
import * as cooldown from "../../../../../helpers/cooldown";
// Helpers
import fetchUser from "../../../../helpers/fetchUser";
import fetchGuild from "../../../../helpers/fetchGuild";
import fetchUser from "../../../../../helpers/fetchUser";
import fetchGuild from "../../../../../helpers/fetchGuild";
export default {
metadata: { guildOnly: true, ephemeral: true },
@ -23,9 +23,9 @@ export default {
return command.setName("work").setDescription(`Work to earn credits`);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild); // Destructure member
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
); // Destructure member
const { guild, user } = interaction;
const embed = new MessageEmbed()
@ -39,33 +39,13 @@ export default {
// Chance module
const chance = new Chance();
// Check if user has a timeout
const isTimeout = await timeoutSchema?.findOne({
guildId: guild?.id,
userId: user?.id,
timeoutId: "2022-03-15-19-16",
});
if (guild === null) {
return logger?.silly(`Guild is null`);
}
const guildDB = await fetchGuild(guild);
// If user is not on timeout
if (isTimeout) {
logger?.silly(`User ${user?.id} is on timeout`);
return interaction.editReply({
embeds: [
embed
.setDescription(
`You are on timeout, please wait ${guildDB?.credits.workTimeout} seconds.`
)
.setColor(errorColor),
],
});
}
await cooldown.interaction(interaction, guildDB?.credits?.workTimeout);
const creditsEarned = chance.integer({
min: 0,
@ -93,23 +73,5 @@ export default {
],
});
});
// Create a timeout for the user
await timeoutSchema?.create({
guildId: guild?.id,
userId: user?.id,
timeoutId: "2022-03-15-19-16",
});
setTimeout(async () => {
logger?.silly(`Removing timeout for user ${user?.id}`);
// When timeout is out, remove it from the database
await timeoutSchema?.deleteOne({
guildId: guild?.id,
userId: user?.id,
timeoutId: "2022-03-15-19-16",
});
}, guildDB?.credits?.workTimeout);
},
};

View file

@ -1,6 +1,6 @@
import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
import logger from "../../logger";
import logger from "../../../logger";
import modules from "../../commands/fun/modules";

View file

@ -1,11 +1,11 @@
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
import axios from "axios";
import { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
export default {
metadata: { guildOnly: false, ephemeral: false, cooldown: 5 },
metadata: { guildOnly: false, ephemeral: false, cooldown: 15 },
builder: (command: SlashCommandSubcommandBuilder) => {
return command.setName("meme").setDescription("Get a meme from r/memes)");

View file

@ -4,7 +4,7 @@ import { CommandInteraction } from "discord.js";
// Groups
import modules from "../../commands/manage/modules";
import logger from "../../logger";
import logger from "../../../logger";
export const moduleData = modules;

View file

@ -2,7 +2,7 @@
import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
import logger from "../../../../logger";
import logger from "../../../../../logger";
// Modules
import modules from "./modules";

View file

@ -4,12 +4,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { ChannelType } from "discord-api-types/v10";
// Configurations
import getEmbedConfig from "../../../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig";
import logger from "../../../../../../logger";
import logger from "../../../../../../../logger";
// Models
import counterSchema from "../../../../../../models/counter";
import counterSchema from "../../../../../../../models/counter";
// Function
export default {
@ -43,7 +43,6 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const { options, guild } = interaction;

View file

@ -2,13 +2,13 @@
import { CommandInteraction, MessageEmbed, Permissions } from "discord.js";
// Configurations
import getEmbedConfig from "../../../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig";
// Handlers
import logger from "../../../../../../logger";
import logger from "../../../../../../../logger";
// Models
import counterSchema from "../../../../../../models/counter";
import counterSchema from "../../../../../../../models/counter";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { ChannelType } from "discord-api-types/v10";
@ -33,7 +33,6 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const { options, guild } = interaction;

View file

@ -1,6 +1,6 @@
import { CommandInteraction } from "discord.js";
import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders";
import logger from "../../../../logger";
import logger from "../../../../../logger";
import modules from "./modules";
@ -14,7 +14,7 @@ export const builder = (group: SlashCommandSubcommandGroupBuilder) => {
.addSubcommand(modules.set.builder)
.addSubcommand(modules.take.builder)
.addSubcommand(modules.transfer.builder)
.addSubcommand(modules.drop.builder);
.addSubcommand(modules.giveaway.builder);
};
export const execute = async (interaction: CommandInteraction) => {
@ -27,7 +27,7 @@ export const execute = async (interaction: CommandInteraction) => {
return modules.take.execute(interaction);
case "transfer":
return modules.transfer.execute(interaction);
case "drop":
return modules.drop.execute(interaction);
case "giveaway":
return modules.giveaway.execute(interaction);
}
};

View file

@ -3,16 +3,16 @@ import { CommandInteraction, MessageEmbed, Permissions } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Configurations
import getEmbedConfig from "../../../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig";
// Handlers
import logger from "../../../../../../logger";
import logger from "../../../../../../../logger";
// Helpers
import pluralize from "../../../../../../helpers/pluralize";
import pluralize from "../../../../../../../helpers/pluralize";
// Models
import fetchUser from "../../../../../../helpers/fetchUser";
import fetchUser from "../../../../../../../helpers/fetchUser";
// Function
export default {
@ -40,7 +40,6 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild); // Destructure
const { guild, options } = interaction;

View file

@ -1,22 +1,21 @@
// Dependencies
import { CommandInteraction, MessageEmbed, Permissions } from "discord.js";
import {
CommandInteraction,
MessageActionRow,
MessageButton,
MessageEmbed,
Permissions,
} from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { v4 as uuidv4 } from "uuid";
import axios from "axios";
import apiSchema from "../../../../../../models/api";
import encryption from "../../../../../../handlers/encryption";
import apiSchema from "../../../../../../../models/api";
import encryption from "../../../../../../../handlers/encryption";
// Configurations
import getEmbedConfig from "../../../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig";
// Handlers
import logger from "../../../../../../logger";
// Helpers
import pluralize from "../../../../../../helpers/pluralize";
// Models
import fetchUser from "../../../../../../helpers/fetchUser";
import { ChannelType } from "discord-api-types/v10";
// Function
export default {
@ -28,8 +27,8 @@ export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("drop")
.setDescription("Drop some credits for specified amount of users.")
.setName("giveaway")
.setDescription("Giveaway some credits for specified amount of users.")
.addIntegerOption((option) =>
option
.setName("uses")
@ -47,12 +46,13 @@ export default {
.setName("channel")
.setDescription("The channel to send the message to.")
.setRequired(true)
.addChannelTypes(ChannelType.GuildText)
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild); // Destructure
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
); // Destructure
const { guild, options } = interaction;
const uses = options?.getInteger("uses");
@ -76,31 +76,39 @@ export default {
if (!apiCredentials) return;
const api = axios?.create({
baseURL: apiCredentials.url,
baseURL: `${apiCredentials?.url}/api/`,
headers: {
Authorization: `Bearer ${encryption.decrypt(apiCredentials.token)}`,
},
});
const shopUrl = apiCredentials?.url?.replace("/api", "/store");
const shopUrl = `${apiCredentials?.url}/store`;
await api
.post("vouchers", {
uses,
code,
credits: creditAmount,
memo: `${interaction?.createdTimestamp} - ${interaction?.user?.id}`,
memo: `[GIVEAWAY] ${interaction?.createdTimestamp} - ${interaction?.user?.id}`,
})
.then(async () => {
await interaction.editReply({
embeds: [
embed
.setColor(successColor)
.setDescription(`Successfully crated code: ${code}`),
.setDescription(`Successfully created code: ${code}`),
],
});
const discordChannel = guild.channels.cache.get(channel.id);
const buttons = new MessageActionRow().addComponents(
new MessageButton()
.setLabel("Redeem it here")
.setStyle("LINK")
.setEmoji("🏦")
.setURL(`${shopUrl}?voucher=${code}`)
);
const discordChannel = guild?.channels.cache.get(channel.id);
if (!discordChannel) return;
@ -109,17 +117,20 @@ export default {
discordChannel.send({
embeds: [
new MessageEmbed()
.setTitle("[:parachute:] Code Drop!")
.setTitle("[:parachute:] Credits!")
.addFields([
{ name: "Code", value: `${code}`, inline: true },
{ name: "Amount", value: `${creditAmount}`, inline: true },
{ name: "Uses", value: `${uses}`, inline: true },
{
name: "💶 Credits",
value: `${creditAmount}`,
inline: true,
},
])
.setDescription(
`${interaction.user} dropped a voucher! You can use the code [here](${shopUrl})!`
`${interaction.user} dropped a voucher for a maximum **${uses}** members!`
)
.setColor(successColor),
],
components: [buttons],
});
});
},

View file

@ -2,6 +2,6 @@ import give from "./give";
import set from "./set";
import take from "./take";
import transfer from "./transfer";
import drop from "./drop";
import giveaway from "./giveaway";
export default { give, set, take, transfer, drop };
export default { give, set, take, transfer, giveaway };

View file

@ -2,15 +2,15 @@
import { CommandInteraction, MessageEmbed, Permissions } from "discord.js";
// Configurations
import getEmbedConfig from "../../../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig";
// Handlers
import logger from "../../../../../../logger";
import logger from "../../../../../../../logger";
// Helpers
// Models
import fetchUser from "../../../../../../helpers/fetchUser";
import fetchUser from "../../../../../../../helpers/fetchUser";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
@ -39,7 +39,6 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const { options, guild } = interaction;

View file

@ -2,16 +2,16 @@
import { CommandInteraction, MessageEmbed, Permissions } from "discord.js";
// Configurations
import getEmbedConfig from "../../../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig";
// Handlers
import logger from "../../../../../../logger";
import logger from "../../../../../../../logger";
// Helpers
import pluralize from "../../../../../../helpers/pluralize";
import pluralize from "../../../../../../../helpers/pluralize";
// Models
import fetchUser from "../../../../../../helpers/fetchUser";
import fetchUser from "../../../../../../../helpers/fetchUser";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
@ -40,7 +40,6 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild); // Destructure
const { guild, options } = interaction;

View file

@ -4,13 +4,13 @@ import { CommandInteraction, MessageEmbed, Permissions } from "discord.js";
import mongoose from "mongoose";
// Configurations
import getEmbedConfig from "../../../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig";
// Handlers
import logger from "../../../../../../logger";
import logger from "../../../../../../../logger";
// Models
import fetchUser from "../../../../../../helpers/fetchUser";
import fetchUser from "../../../../../../../helpers/fetchUser";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
@ -45,7 +45,6 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild); // Destructure member
const { guild, options } = interaction;

View file

@ -6,7 +6,7 @@ import { CommandInteraction } from "discord.js";
import modules from "../../commands/profile/modules";
// Handlers
import logger from "../../logger";
import logger from "../../../logger";
export const moduleData = modules;

View file

@ -2,12 +2,12 @@
import { CommandInteraction } from "discord.js";
// Configurations
import getEmbedConfig from "../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
// Models
import fetchUser from "../../../helpers/fetchUser";
import fetchUser from "../../../../../helpers/fetchUser";
import logger from "../../../logger";
import logger from "../../../../../logger";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
@ -24,7 +24,6 @@ export default {
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
); // Destructure

View file

@ -6,7 +6,7 @@ import { CommandInteraction } from "discord.js";
import modules from "./modules";
// Handlers
import logger from "../../logger";
import logger from "../../../logger";
export const moduleData = modules;
@ -17,13 +17,7 @@ export const builder = new SlashCommandBuilder()
.addSubcommand(modules.give.builder);
export const execute = async (interaction: CommandInteraction) => {
const { options } = interaction;
if (options?.getSubcommand() === "give") {
logger?.silly(`Executing give subcommand`);
if (interaction.options.getSubcommand() === "give") {
await modules.give.execute(interaction);
}
logger?.silly(`No subcommand found`);
};

View file

@ -0,0 +1,6 @@
import { User } from "discord.js";
export default async (to: User | null, from: User | null) => {
if (from?.id === to?.id) {
throw new Error("You cannot give reputation to yourself.");
}
};

View file

@ -0,0 +1,87 @@
import { CommandInteraction } from "discord.js";
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
import { timeout } from "../../../../../config/reputation";
import logger from "../../../../../logger";
import fetchUser from "../../../../../helpers/fetchUser";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import * as cooldown from "../../../../../helpers/cooldown";
import noSelfReputation from "./components/noSelfReputation";
export default {
metadata: { guildOnly: true, ephemeral: true },
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("give")
.setDescription("Give reputation to a user")
.addUserOption((option) =>
option
.setName("target")
.setDescription("The user you want to repute.")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("type")
.setDescription("What type of reputation you want to repute")
.setRequired(true)
.addChoices(
{ name: "Positive", value: "positive" },
{
name: "Negative",
value: "negative",
}
)
);
},
execute: async (interaction: CommandInteraction) => {
const { options, user, guild } = interaction;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(guild); // Destructure
const optionTarget = options?.getUser("target");
const optionType = options?.getString("type");
if (!guild) throw new Error("Guild is undefined");
const userObj = await fetchUser(user, guild);
if (!userObj) throw new Error("User is undefined");
// Pre-checks
await noSelfReputation(optionTarget, user);
// Check if user is on cooldown otherwise create one
await cooldown.interaction(interaction, timeout);
switch (optionType) {
case "positive":
userObj.reputation += 1;
break;
case "negative":
userObj.reputation += 1;
break;
default:
throw new Error("Invalid reputation type");
}
await userObj.save().then(async () => {
logger.silly(`User reputation has been updated`);
await interaction.editReply({
embeds: [
{
title: "[:loudspeaker:] Give",
description: `You have given a ${optionType} repute to ${optionTarget}`,
timestamp: new Date(),
color: successColor,
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
});
},
};

View file

@ -6,7 +6,7 @@ import { CommandInteraction } from "discord.js";
import modules from "./modules";
// Handlers
import logger from "../../logger";
import logger from "../../../logger";
export const moduleData = modules;
@ -14,16 +14,16 @@ export const moduleData = modules;
export const builder = new SlashCommandBuilder()
.setName("shop")
.setDescription("Shop for credits and custom roles.")
.addSubcommand(modules.pterodactyl.builder)
.addSubcommand(modules.cpgg.builder)
.addSubcommandGroup(modules.roles.builder);
export const execute = async (interaction: CommandInteraction) => {
const { options } = interaction;
if (options?.getSubcommand() === "pterodactyl") {
logger.silly(`Executing pterodactyl subcommand`);
if (options?.getSubcommand() === "cpgg") {
logger.silly(`Executing cpgg subcommand`);
return modules.pterodactyl.execute(interaction);
return modules.cpgg.execute(interaction);
}
if (options?.getSubcommandGroup() === "roles") {

View file

@ -1,25 +1,30 @@
import { CommandInteraction } from "discord.js";
import {
CommandInteraction,
MessageActionRow,
MessageButton,
} from "discord.js";
import { v4 as uuidv4 } from "uuid";
import axios from "axios";
import getEmbedConfig from "../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
import logger from "../../../logger";
import encryption from "../../../handlers/encryption";
import logger from "../../../../../logger";
import encryption from "../../../../../handlers/encryption";
import pluralize from "../../../helpers/pluralize";
import pluralize from "../../../../../helpers/pluralize";
import apiSchema from "../../../models/api";
import fetchUser from "../../../helpers/fetchUser";
import apiSchema from "../../../../../models/api";
import fetchUser from "../../../../../helpers/fetchUser";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { message } from "../../../../../helpers/cooldown/index";
export default {
metadata: { guildOnly: true, ephemeral: true },
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("pterodactyl")
.setDescription("Buy pterodactyl power.")
.setName("cpgg")
.setDescription("Buy cpgg power.")
.addIntegerOption((option) =>
option
.setName("amount")
@ -27,7 +32,6 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const { options, guild, user, client } = interaction;
@ -71,8 +75,8 @@ export default {
return interaction?.editReply({
embeds: [
{
title: ":shopping_cart: Shop [Pterodactyl]",
description: `You **can't** withdraw for __Pterodactyl__ below **100**.`,
title: "[:shopping_cart:] CPGG",
description: `You **can't** withdraw for __CPGG__ below **100**.`,
color: errorColor,
fields: [
{
@ -96,9 +100,9 @@ export default {
return interaction?.editReply({
embeds: [
{
title: ":shopping_cart: Shop [Pterodactyl]",
title: "[:shopping_cart:] CPGG",
description:
"You **can't** withdraw for __Pterodactyl__ above **1.000.000**.",
"You **can't** withdraw for __CPGG__ above **1.000.000**.",
color: errorColor,
fields: [
{
@ -122,7 +126,7 @@ export default {
return interaction?.editReply({
embeds: [
{
title: ":shopping_cart: Shop [Pterodactyl]",
title: "[:shopping_cart:] CPGG",
description: `You have **insufficient** credits.`,
color: errorColor,
fields: [
@ -150,13 +154,21 @@ export default {
if (!apiCredentials) return;
const api = axios?.create({
baseURL: apiCredentials.url,
baseURL: `${apiCredentials.url}/api/`,
headers: {
Authorization: `Bearer ${encryption.decrypt(apiCredentials.token)}`,
},
});
const shopUrl = apiCredentials?.url?.replace("/api", "/store");
const shopUrl = `${apiCredentials?.url}/store`;
const buttons = new MessageActionRow().addComponents(
new MessageButton()
.setLabel("Redeem it here")
.setStyle("LINK")
.setEmoji("🏦")
.setURL(`${shopUrl}?voucher=${code}`)
);
await api
@ -178,43 +190,47 @@ export default {
?.then(async () => {
logger?.silly(`Successfully saved new credits.`);
await dmUser?.send({
embeds: [
{
title: ":shopping_cart: Shop [Pterodactyl]",
description: `Redeem this voucher [here](${shopUrl})!`,
fields: [
{ name: "Code", value: `${code}`, inline: true },
if (!interaction.guild) throw new Error("Guild is undefined");
await dmUser
?.send({
embeds: [
{
title: "[:shopping_cart:] CPGG",
description: `This voucher comes from **${interaction.guild.name}**.`,
fields: [
{
name: "💶 Credits",
value: `${optionAmount || userDB?.credits}`,
inline: true,
},
],
color: successColor,
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
components: [buttons],
})
.then(async (msg) => {
return interaction?.editReply({
embeds: [
{
name: "Credits",
value: `${optionAmount || userDB?.credits}`,
inline: true,
title: "[:shopping_cart:] CPGG",
description: `I have sent you the code in [DM](${msg.url})!`,
color: successColor,
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
color: successColor,
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
return interaction?.editReply({
embeds: [
{
title: ":shopping_cart: Shop [Pterodactyl]",
description: "I have sent you the code in DM!",
color: successColor,
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
});
});
})
.catch(async (error) => {
@ -223,7 +239,7 @@ export default {
return interaction?.editReply({
embeds: [
{
title: ":shopping_cart: Shop [Pterodactyl]",
title: "[:shopping_cart:] CPGG",
description: "Something went wrong.",
color: errorColor,
timestamp: new Date(),
@ -243,7 +259,7 @@ export default {
return interaction?.editReply({
embeds: [
{
title: ":shopping_cart: Shop [Pterodactyl]",
title: "[:shopping_cart:] CPGG",
description: "Something went wrong.",
color: errorColor,
timestamp: new Date(),

View file

@ -0,0 +1,4 @@
import cpgg from "./cpgg";
import * as roles from "./roles";
export default { cpgg, roles };

View file

@ -3,14 +3,14 @@ import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
// Handlers
import logger from "../../../../logger";
import logger from "../../../../../logger";
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
// Modules
import modules from "./modules";
import guildSchema from "../../../../models/guild";
import guildSchema from "../../../../../models/guild";
export const moduleData = modules;

View file

@ -6,17 +6,17 @@ import {
} from "discord.js";
// Configurations
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig";
// Models
import shopRolesSchema from "../../../../../models/shopRole";
import guildSchema from "../../../../../models/guild";
import shopRolesSchema from "../../../../../../../models/shopRole";
import guildSchema from "../../../../../../../models/guild";
import logger from "../../../../../logger";
import logger from "../../../../../../../logger";
// Helpers
import pluralize from "../../../../../helpers/pluralize";
import fetchUser from "../../../../../helpers/fetchUser";
import pluralize from "../../../../../../../helpers/pluralize";
import fetchUser from "../../../../../../../helpers/fetchUser";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
@ -41,7 +41,6 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const { options, guild, user, member } = interaction;

View file

@ -2,16 +2,16 @@
import { CommandInteraction, GuildMemberRoleManager } from "discord.js";
// Configurations
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig";
// Models
import shopRolesSchema from "../../../../../models/shopRole";
import shopRolesSchema from "../../../../../../../models/shopRole";
import logger from "../../../../../logger";
import logger from "../../../../../../../logger";
// Helpers
import pluralize from "../../../../../helpers/pluralize";
import fetchUser from "../../../../../helpers/fetchUser";
import pluralize from "../../../../../../../helpers/pluralize";
import fetchUser from "../../../../../../../helpers/fetchUser";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
@ -30,7 +30,6 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const { options, guild, user, member } = interaction;

View file

@ -1,16 +1,9 @@
// Dependencies
import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
// Modules
import modules from "../../commands/utility/modules";
// Handlers
import logger from "../../logger";
import modules from "./modules";
export const moduleData = modules;
// Function
export const builder = new SlashCommandBuilder()
.setName("utility")
.setDescription("Common utility.")
@ -21,9 +14,7 @@ export const builder = new SlashCommandBuilder()
.addSubcommand(modules.avatar.builder);
export const execute = async (interaction: CommandInteraction) => {
const { options } = interaction;
switch (options.getSubcommand()) {
switch (interaction.options.getSubcommand()) {
case "lookup":
return modules.lookup.execute(interaction);
case "about":
@ -33,6 +24,8 @@ export const execute = async (interaction: CommandInteraction) => {
case "avatar":
return modules.avatar.execute(interaction);
default:
logger.error(`Unknown subcommand ${options.getSubcommand()}`);
throw new Error(
`Unknown subcommand: ${interaction.options.getSubcommand()}`
);
}
};

View file

@ -0,0 +1,71 @@
// Dependencies
import {
CommandInteraction,
MessageActionRow,
MessageButton,
} from "discord.js";
// Configurations
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
import { hosterName, hosterUrl } from "../../../../../config/other";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
metadata: { guildOnly: false, ephemeral: false },
builder: (command: SlashCommandSubcommandBuilder) => {
return command.setName("about").setDescription("About this bot!)");
},
execute: async (interaction: CommandInteraction) => {
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const buttons = new MessageActionRow().addComponents(
new MessageButton()
.setLabel("Source Code")
.setStyle("LINK")
.setEmoji("📄")
.setURL("https://github.com/ZynerOrg/xyter"),
new MessageButton()
.setLabel("Website")
.setStyle("LINK")
.setEmoji("🌐")
.setURL("https://zyner.org"),
new MessageButton()
.setLabel("Get Help")
.setStyle("LINK")
.setEmoji("💬")
.setURL("https://discord.zyner.org"),
new MessageButton()
.setLabel(`Hosted by ${hosterName}`)
.setStyle("LINK")
.setEmoji("⚒️")
.setURL(`${hosterUrl}`)
);
const interactionEmbed = {
title: "[:tools:] About",
description: `
**Xyter**'s goal is to provide a __privacy-friendly__ discord bot.
We created **Xyter** to **replace the mess** of having a dozen or so bots in __your__ community.
On top of this, you can also see our **source code** for **security** and **privacy** issues.
As well as making your own **fork** of the bot, you can also get **help** from our community.
Developed with by **Zyner**, a non-profit project by teens.
`,
color: successColor,
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
};
await interaction.editReply({
embeds: [interactionEmbed],
components: [buttons],
});
},
};

View file

@ -1,4 +1,4 @@
import getEmbedConfig from "../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
import { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
@ -17,7 +17,6 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);

View file

@ -1,11 +1,11 @@
import axios from "axios";
import { CommandInteraction } from "discord.js";
import getEmbedConfig from "../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import embedBuilder from "../../../helpers/embedBuilder";
import embedBuilder from "../../../../../helpers/embedBuilder";
export default {
metadata: { guildOnly: false, ephemeral: false },
@ -24,7 +24,6 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const embedTitle = "[:hammer:] Utility (Lookup)";

View file

@ -1,4 +1,4 @@
import getEmbedConfig from "../../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
@ -9,7 +9,6 @@ export default {
return command.setName("stats").setDescription("Check bot statistics!)");
},
execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);

View file

@ -1,8 +1,8 @@
import { Guild } from "discord.js";
import updatePresence from "../../helpers/updatePresence";
import fetchGuild from "../../helpers/fetchGuild";
import logger from "../../logger";
import { IEventOptions } from "../../interfaces/EventOptions";
import updatePresence from "../../../helpers/updatePresence";
import fetchGuild from "../../../helpers/fetchGuild";
import logger from "../../../logger";
import { IEventOptions } from "../../../interfaces/EventOptions";
export const options: IEventOptions = {
type: "on",

View file

@ -2,10 +2,10 @@
import { Guild } from "discord.js";
// Dependencies
import updatePresence from "../../helpers/updatePresence";
import dropGuild from "../../helpers/dropGuild";
import logger from "../../logger";
import { IEventOptions } from "../../interfaces/EventOptions";
import updatePresence from "../../../helpers/updatePresence";
import dropGuild from "../../../helpers/dropGuild";
import logger from "../../../logger";
import { IEventOptions } from "../../../interfaces/EventOptions";
export const options: IEventOptions = {
type: "on",

View file

@ -1,9 +1,9 @@
import logger from "../../logger";
import logger from "../../../logger";
import { GuildMember, MessageEmbed, TextChannel } from "discord.js";
import guildSchema from "../../models/guild";
import guildSchema from "../../../models/guild";
import getEmbedConfig from "../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../helpers/getEmbedConfig";
export default {
execute: async (member: GuildMember) => {

View file

@ -2,14 +2,14 @@
import { GuildMember } from "discord.js";
// Dependencies
import updatePresence from "../../helpers/updatePresence";
import fetchUser from "../../helpers/fetchUser";
import logger from "../../logger";
import updatePresence from "../../../helpers/updatePresence";
import fetchUser from "../../../helpers/fetchUser";
import logger from "../../../logger";
import joinMessage from "./joinMessage";
import audits from "./audits";
import { IEventOptions } from "../../interfaces/EventOptions";
import { IEventOptions } from "../../../interfaces/EventOptions";
export const options: IEventOptions = {
type: "on",

View file

@ -1,8 +1,8 @@
import { GuildMember, MessageEmbed, TextChannel } from "discord.js";
import guildSchema from "../../models/guild";
import guildSchema from "../../../models/guild";
import getEmbedConfig from "../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../helpers/getEmbedConfig";
export default {
execute: async (member: GuildMember) => {

View file

@ -1,9 +1,9 @@
import logger from "../../logger";
import logger from "../../../logger";
import { GuildMember, MessageEmbed, TextChannel } from "discord.js";
import guildSchema from "../../models/guild";
import guildSchema from "../../../models/guild";
import getEmbedConfig from "../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../helpers/getEmbedConfig";
export default {
execute: async (member: GuildMember) => {

View file

@ -2,12 +2,12 @@
import { GuildMember } from "discord.js";
// Dependencies
import updatePresence from "../../helpers/updatePresence";
import dropUser from "../../helpers/dropUser";
import logger from "../../logger";
import updatePresence from "../../../helpers/updatePresence";
import dropUser from "../../../helpers/dropUser";
import logger from "../../../logger";
import leaveMessage from "./leaveMessage";
import audits from "./audits";
import { IEventOptions } from "../../interfaces/EventOptions";
import { IEventOptions } from "../../../interfaces/EventOptions";
export const options: IEventOptions = {
type: "on",

View file

@ -1,8 +1,8 @@
import { GuildMember, MessageEmbed, TextChannel } from "discord.js";
import guildSchema from "../../models/guild";
import guildSchema from "../../../models/guild";
import getEmbedConfig from "../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../helpers/getEmbedConfig";
export default {
execute: async (member: GuildMember) => {

View file

@ -1,9 +1,9 @@
import logger from "../../logger";
import logger from "../../../logger";
import { Interaction, MessageEmbed, TextChannel } from "discord.js";
import guildSchema from "../../models/guild";
import guildSchema from "../../../models/guild";
import getEmbedConfig from "../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../helpers/getEmbedConfig";
export default {
execute: async (interaction: Interaction) => {
@ -38,10 +38,12 @@ export default {
.setDescription(
`
**Interaction created by** ${interaction.user.username} **in** ${interaction.channel}
**Interaction ID**: ${interaction.id}
**Type**: ${interaction.type}
**User ID**: ${interaction.user.id}
`
)
.setThumbnail(interaction.user.displayAvatarURL())
.addFields([{ name: "Event", value: "interactionCreate" }])
.setTimestamp()
.setFooter({
text: footerText,

View file

@ -0,0 +1,51 @@
import { CommandInteraction, MessageEmbed } from "discord.js";
import * as cooldown from "../../../../helpers/cooldown";
import logger from "../../../../logger";
export default async (
interaction: CommandInteraction,
metadata: any,
embedConfig: any
) => {
if (
metadata.permissions &&
metadata.guildOnly &&
!interaction.memberPermissions?.has(metadata.permissions)
) {
return interaction?.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:x:] Permission")
.setDescription(`You do not have the permission to manage the bot.`)
.setTimestamp(new Date())
.setColor(embedConfig.errorColor)
.setFooter({
text: embedConfig.footerText,
iconURL: embedConfig.footerIcon,
}),
],
});
}
logger.info(metadata);
if (metadata.cooldown) {
await cooldown
.interaction(interaction, metadata.cooldown)
.catch(async (error) => {
throw new Error("Cooldown error: " + error);
});
}
if (metadata.guildOnly) {
if (!interaction.guild) {
throw new Error("This command is guild only.");
}
}
if (metadata.dmOnly) {
if (interaction.guild) {
throw new Error("This command is DM only.");
}
}
};

View file

@ -0,0 +1,123 @@
// Dependencies
import { CommandInteraction, MessageEmbed } from "discord.js";
import logger from "../../../../logger";
import deferReply from "../../../../helpers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
import capitalizeFirstLetter from "../../../../helpers/capitalizeFirstLetter";
import * as cooldown from "../../../../helpers/cooldown";
export default async (interaction: CommandInteraction) => {
if (!interaction.isButton()) return;
const { errorColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { guild, customId, user, memberPermissions } = interaction;
const currentButton = await import(`../../../buttons/${customId}`);
if (currentButton == null) {
logger.silly(`Button ${customId} not found`);
}
const metadata = currentButton.metadata;
await deferReply(interaction, metadata.ephemeral || false);
if (metadata.guildOnly) {
if (!guild) {
logger.debug(`Guild is null`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:x:] Permission")
.setDescription("This command is only available for guild")
.setColor(errorColor)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
}
}
if (
metadata.permissions &&
metadata.guildOnly &&
!memberPermissions?.has(metadata.permissions)
) {
return interaction?.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:x:] Permission")
.setDescription(`You do not have the permission to manage the bot.`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
}
if (metadata.dmOnly) {
if (guild) {
logger.silly(`Guild exist`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:x:] Permission")
.setDescription("This command is only available in DM.")
.setColor(errorColor)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
}
}
if (metadata.cooldown) {
await cooldown
.interaction(interaction, metadata.cooldown)
.catch(async (error) => {
return interaction?.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:x:] Permission")
.setDescription(`${error}`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
});
}
await currentButton
.execute(interaction)
.then(async () => {
return logger?.silly(
`Button: ${customId} executed in guild: ${guild?.name} (${guild?.id}) by user: ${user?.tag} (${user?.id})`
);
})
.catch(async (error: string) => {
logger?.debug(`INTERACTION BUTTON CATCH: ${error}`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle(
`[:x:] ${capitalizeFirstLetter(
interaction.options.getSubcommand()
)}`
)
.setDescription(`${"``"}${error}${"``"}`)
.setColor(errorColor)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
});
};

View file

@ -0,0 +1,48 @@
// Dependencies
import { CommandInteraction, MessageEmbed } from "discord.js";
import logger from "../../../../logger";
import deferReply from "../../../../helpers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedConfig";
import getCommandMetadata from "../../../../helpers/getCommandMetadata";
import capitalizeFirstLetter from "../../../../helpers/capitalizeFirstLetter";
import * as cooldown from "../../../../helpers/cooldown";
export default async (interaction: CommandInteraction) => {
if (!interaction.isCommand()) return;
const { errorColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { client, guild, commandName, user, memberPermissions } = interaction;
const currentCommand = client.commands.get(commandName);
if (currentCommand == null) {
logger.silly(`Command ${commandName} not found`);
}
const metadata = await getCommandMetadata(interaction, currentCommand);
await deferReply(interaction, metadata.ephemeral || false);
if (metadata.guildOnly && !guild)
throw new Error("This command is guild only.");
if (
metadata.permissions &&
metadata.guildOnly &&
!memberPermissions?.has(metadata.permissions)
)
throw new Error("You don't have the required permissions");
if (metadata.dmOnly && guild)
throw new Error("This command is only available in DM");
if (metadata.cooldown)
await cooldown.interaction(interaction, metadata.cooldown);
await currentCommand.execute(interaction);
};

View file

@ -0,0 +1,51 @@
// 3rd party dependencies
import { CommandInteraction, MessageEmbed } from "discord.js";
// Dependencies
import isCommand from "../../events/interactionCreate/components/isCommand";
import isButton from "../../events/interactionCreate/components/isButton";
import logger from "../../../logger";
import audits from "./audits";
import { IEventOptions } from "../../../interfaces/EventOptions";
import capitalizeFirstLetter from "../../../helpers/capitalizeFirstLetter";
import getEmbedConfig from "../../../helpers/getEmbedConfig";
export const options: IEventOptions = {
type: "on",
};
export const execute = async (interaction: CommandInteraction) => {
const { guild, id } = interaction;
logger?.silly(
`New interaction: ${id} in guild: ${guild?.name} (${guild?.id})`
);
const { errorColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
await audits.execute(interaction);
try {
await isCommand(interaction);
await isButton(interaction);
} catch (error) {
logger.debug(`${error}`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle(
`[:x:] ${capitalizeFirstLetter(
interaction.options.getSubcommand()
)}`
)
.setDescription(`${"``"}${error}${"``"}`)
.setColor(errorColor)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
}
};

View file

@ -1,7 +1,7 @@
import { Message } from "discord.js";
import modules from "../../events/messageCreate/modules";
import { IEventOptions } from "../../interfaces/EventOptions";
import { IEventOptions } from "../../../interfaces/EventOptions";
export const options: IEventOptions = {
type: "on",

View file

@ -1,7 +1,7 @@
import { Message } from "discord.js";
import logger from "../../../../logger";
import counterSchema from "../../../../models/counter";
import logger from "../../../../../logger";
import counterSchema from "../../../../../models/counter";
export default {
execute: async (message: Message) => {

View file

@ -0,0 +1,46 @@
import logger from "../../../../../logger";
import { Message } from "discord.js";
import fetchUser from "../../../../../helpers/fetchUser";
import fetchGuild from "../../../../../helpers/fetchGuild";
import * as cooldown from "../../../../../helpers/cooldown";
export default {
execute: async (message: Message) => {
const { guild, author, content, channel } = message;
if (guild == null) return;
if (author.bot) return;
if (channel?.type !== "GUILD_TEXT") return;
const { id: guildId } = guild;
const { id: userId } = author;
const guildData = await fetchGuild(guild);
const userData = await fetchUser(author, guild);
if (content.length < guildData.credits.minimumLength) return;
await cooldown.message(
message,
guildData.credits.timeout,
"messageCreate-credits"
);
userData.credits += guildData.credits.rate;
await userData
.save()
.then(async () => {
logger.silly(
`User ${userId} in guild ${guildId} has ${userData.credits} credits`
);
})
.catch(async (err) => {
logger.error(
`Error saving credits for user ${userId} in guild ${guildId} - ${err}`
);
});
},
};

View file

@ -0,0 +1,51 @@
import logger from "../../../../../logger";
import * as cooldown from "../../../../../helpers/cooldown";
import fetchUser from "../../../../../helpers/fetchUser";
import fetchGuild from "../../../../../helpers/fetchGuild";
import { Message } from "discord.js";
export default {
execute: async (message: Message) => {
const { guild, author, content, channel } = message;
if (guild == null) return;
if (author.bot) return;
if (channel?.type !== "GUILD_TEXT") return;
const { id: guildId } = guild;
const { id: userId } = author;
const guildData = await fetchGuild(guild);
const userData = await fetchUser(author, guild);
if (content.length < guildData.credits.minimumLength) return;
await cooldown.message(
message,
guildData.credits.timeout,
"messageCreate-points"
);
userData.points += guildData.points.rate;
await userData
.save()
.then(async () => {
logger.silly(
`Successfully saved user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`
);
})
.catch(async (err) => {
logger.error(
`Error saving points for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`,
err
);
});
logger.silly(
`User ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id}) has ${userData.points} points`
);
},
};

View file

@ -1,9 +1,9 @@
import logger from "../../logger";
import logger from "../../../logger";
import { Message, MessageEmbed, TextChannel } from "discord.js";
import guildSchema from "../../models/guild";
import guildSchema from "../../../models/guild";
import getEmbedConfig from "../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../helpers/getEmbedConfig";
export default {
execute: async (message: Message) => {

View file

@ -1,7 +1,7 @@
import { Message } from "discord.js";
import audits from "../../events/messageDelete/audits";
import counter from "./modules/counter";
import { IEventOptions } from "../../interfaces/EventOptions";
import { IEventOptions } from "../../../interfaces/EventOptions";
export const options: IEventOptions = {
type: "on",

View file

@ -2,8 +2,8 @@
import { Message } from "discord.js";
// Models
import counterSchema from "../../../models/counter";
import logger from "../../../logger";
import counterSchema from "../../../../models/counter";
import logger from "../../../../logger";
export default async (message: Message) => {
const { guild, channel, author, content } = message;

View file

@ -1,10 +1,10 @@
/* eslint-disable no-loops/no-loops */
import logger from "../../logger";
import logger from "../../../logger";
import { Message, MessageEmbed, TextChannel } from "discord.js";
import guildSchema from "../../models/guild";
import guildSchema from "../../../models/guild";
import getEmbedConfig from "../../helpers/getEmbedConfig";
import getEmbedConfig from "../../../helpers/getEmbedConfig";
export default {
execute: async (oldMessage: Message, newMessage: Message) => {

View file

@ -1,12 +1,12 @@
// Dependencies
import { Message } from "discord.js";
import logger from "../../logger";
import logger from "../../../logger";
// Modules
import counter from "./modules/counter";
import audits from "./audits";
import { IEventOptions } from "../../interfaces/EventOptions";
import { IEventOptions } from "../../../interfaces/EventOptions";
export const options: IEventOptions = {
type: "on",
@ -15,7 +15,7 @@ export const options: IEventOptions = {
export const execute = async (oldMessage: Message, newMessage: Message) => {
const { author, guild } = newMessage;
await audits.execute(oldMessage, newMessage);
// await audits.execute(oldMessage, newMessage);
logger?.silly(
`Message update event fired by ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`

View file

@ -2,8 +2,8 @@
import { Message } from "discord.js";
// Models
import counterSchema from "../../../models/counter";
import logger from "../../../logger";
import counterSchema from "../../../../models/counter";
import logger from "../../../../logger";
export default async (message: Message) => {
const { guild, channel, author, content } = message;

Some files were not shown because too many files have changed in this diff Show more