Merge pull request #347 from VermiumSifell/dev

New Features and Breaking Changes
This commit is contained in:
Axel Olausson Holtenäs 2022-06-10 22:53:17 +02:00 committed by GitHub
commit 9793cc294c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
128 changed files with 1160 additions and 1837 deletions

View file

@ -1,5 +1,6 @@
# Custom Dictionary Words # Custom Dictionary Words
Controlpanel Controlpanel
cooldown
cpgg cpgg
dagen dagen
discordjs discordjs

4
.gitignore vendored
View file

@ -5,9 +5,7 @@ config.json
package-lock.json package-lock.json
**/config/*.ts config/
!**/config/index.ts
!**/config/example.*.ts
# Build # Build
build/ build/

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 "../../../../database/schemas/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 "../../../../database/schemas/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 "../../../../../../database/schemas/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 "../../../../database/schemas/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 "../../../../database/schemas/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 "../../../../database/schemas/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 "../../../../database/schemas/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 "../../../../database/schemas/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 "../../../database/schemas/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"; export const hosterName = "someone";
// Hoster Url // Hoster Url
export const hosterUrl = "scheme://domain.tld"; export const hosterUrl = "https://xyter.zyner.org/customization/change-hoster";
// Winston log level // Winston log level
export const logLevel = "info"; export const logLevel = "info";

View file

@ -1,22 +0,0 @@
// 3rd party dependencies
import mongoose from "mongoose";
// Dependencies
import logger from "../logger";
// Configuration
import { url } from "../config/database";
export default async () => {
await mongoose.connect(url).then(async (connection) => {
logger.info(`Connected to database: ${connection.connection.name}`);
});
mongoose.connection.on("error", async (error) => {
logger.error(`${error}`);
});
mongoose.connection.on("warn", async (warning) => {
logger.warn(warning);
});
};

View file

@ -1,58 +0,0 @@
import logger from "../../logger";
import { GuildMember, MessageEmbed, TextChannel } from "discord.js";
import guildSchema from "../../database/schemas/guild";
import getEmbedConfig from "../../helpers/getEmbedConfig";
export default {
execute: async (member: GuildMember) => {
const { footerText, footerIcon, successColor } = await getEmbedConfig(
member.guild
);
const guildData = await guildSchema.findOne({ guildId: member.guild.id });
const { client } = member;
if (guildData === null) return;
if (guildData.audits.status !== true) return;
if (!guildData.audits.channelId) return;
const channel = client.channels.cache.get(`${guildData.audits.channelId}`);
if (channel === null) return;
(channel as TextChannel)
.send({
embeds: [
new MessageEmbed()
.setColor(successColor)
.setAuthor({
name: "Member Joined",
iconURL: member.user.displayAvatarURL(),
})
.setDescription(`${member.user} ${member.user.tag}`)
.addFields([
{ name: "Account Age", value: `${member.user.createdAt}` },
])
.setTimestamp()
.setFooter({
text: footerText,
iconURL: footerIcon,
}),
],
})
.then(async () => {
logger.info(
`Audit log sent for event guildMemberAdd in guild ${member.guild.name} (${member.guild.id})`
);
})
.catch(async () => {
logger.error(
`Audit log failed to send for event guildMemberAdd in guild ${member.guild.name} (${member.guild.id})`
);
});
},
};

View file

@ -1,55 +0,0 @@
import logger from "../../logger";
import { GuildMember, MessageEmbed, TextChannel } from "discord.js";
import guildSchema from "../../database/schemas/guild";
import getEmbedConfig from "../../helpers/getEmbedConfig";
export default {
execute: async (member: GuildMember) => {
const { footerText, footerIcon, errorColor } = await getEmbedConfig(
member.guild
);
const guildData = await guildSchema.findOne({ guildId: member.guild.id });
const { client } = member;
if (guildData === null) return;
if (guildData.audits.status !== true) return;
if (!guildData.audits.channelId) return;
const channel = client.channels.cache.get(`${guildData.audits.channelId}`);
if (channel === null) return;
(channel as TextChannel)
.send({
embeds: [
new MessageEmbed()
.setColor(errorColor)
.setAuthor({
name: "Member Left",
iconURL: member.user.displayAvatarURL(),
})
.setDescription(`${member.user} ${member.user.tag}`)
.setTimestamp()
.setFooter({
text: footerText,
iconURL: footerIcon,
}),
],
})
.then(async () => {
logger.info(
`Audit log sent for event guildMemberRemove in guild ${member.guild.name} (${member.guild.id})`
);
})
.catch(async () => {
logger.error(
`Audit log failed to send for event guildMemberRemove in guild ${member.guild.name} (${member.guild.id})`
);
});
},
};

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

View file

@ -1,24 +0,0 @@
// Dependencies
import { Client } from "discord.js";
import schedule from "node-schedule";
import logger from "../../logger";
// Jobs
import shopRoles from "../../jobs/shopRoles";
export default async (client: Client) => {
const expression = "*/5 * * * *";
schedule.scheduleJob(expression, async () => {
logger.info("Running jobs.");
await shopRoles(client)
.then(() => {
logger.info("Shop roles job finished.");
})
.catch((err) => {
logger.error(`Shop roles job failed: ${err}`);
});
});
};

View file

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

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"; import getEmbedConfig from "../../helpers/getEmbedConfig";
export default async (interaction: CommandInteraction, ephemeral: boolean) => { export default async (interaction: CommandInteraction, ephemeral: boolean) => {
if (interaction.guild == null) return;
await interaction.deferReply({ await interaction.deferReply({
ephemeral, ephemeral,
}); });

View file

@ -1,9 +1,9 @@
import guildSchema from "../../database/schemas/guild"; import guildSchema from "../../models/guild";
import userSchema from "../../database/schemas/user"; import userSchema from "../../models/user";
import apiSchema from "../../database/schemas/api"; import apiSchema from "../../models/api";
import counterSchema from "../../database/schemas/counter"; import counterSchema from "../../models/counter";
import shopRoleSchema from "../../database/schemas/shopRole"; import shopRoleSchema from "../../models/shopRole";
import timeoutSchema from "../../database/schemas/timeout"; import timeoutSchema from "../../models/timeout";
import logger from "../../logger"; import logger from "../../logger";

View file

@ -1,4 +1,4 @@
import userSchema from "../../database/schemas/user"; import userSchema from "../../models/user";
import logger from "../../logger"; import logger from "../../logger";

View file

@ -2,7 +2,7 @@
import { Guild } from "discord.js"; import { Guild } from "discord.js";
// Models // Models
import guildSchema from "../../database/schemas/guild"; import guildSchema from "../../models/guild";
// Handlers // Handlers
import logger from "../../logger"; import logger from "../../logger";

View file

@ -2,7 +2,7 @@
import { Guild, User } from "discord.js"; import { Guild, User } from "discord.js";
// Models // Models
import userSchema from "../../database/schemas/user"; import userSchema from "../../models/user";
// Handlers // Handlers
import logger from "../../logger"; import logger from "../../logger";

View file

@ -1,4 +1,4 @@
import guildSchema from "../../database/schemas/guild"; import guildSchema from "../../models/guild";
import * as embedConfig from "../../config/embed"; import * as embedConfig from "../../config/embed";
import { Guild } from "discord.js"; import { Guild } from "discord.js";

View file

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

View file

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

6
src/interfaces/Event.ts Normal file
View file

@ -0,0 +1,6 @@
import { IEventOptions } from "./EventOptions";
export interface IEvent {
options: IEventOptions;
execute: (...args: Promise<void>[]) => Promise<void>;
}

8
src/interfaces/Job.ts Normal file
View file

@ -0,0 +1,8 @@
import { Client } from "discord.js";
export interface IJob {
options: {
schedule: string;
};
execute: (client: Client) => Promise<void>;
}

View file

@ -4,57 +4,47 @@ import { Client } from "discord.js";
import logger from "../logger"; import logger from "../logger";
// Schemas // Schemas
import userSchema from "../database/schemas/user"; import userSchema from "../models/user";
import shopRoleSchema from "../database/schemas/shopRole"; import shopRoleSchema from "../models/shopRole";
import guildSchema from "../database/schemas/guild"; import guildSchema from "../models/guild";
export default async (client: Client) => { export const options = {
schedule: "*/5 * * * *", // https://crontab.guru/
};
export const execute = async (client: Client) => {
const roles = await shopRoleSchema.find(); const roles = await shopRoleSchema.find();
await Promise.all( await Promise.all(
roles.map(async (role) => { roles.map(async (role) => {
const { guildId, userId, roleId } = role; const { guildId, userId, roleId } = role;
const lastPayment = new Date(role.lastPayed); const lastPayment = new Date(role.lastPayed);
const nextPayment = new Date( const nextPayment = new Date(
lastPayment.setHours(lastPayment.getHours() + 1) lastPayment.setHours(lastPayment.getHours() + 1)
); );
if (new Date() < nextPayment) { if (new Date() < nextPayment) {
logger.silly(`Shop role ${roleId} is not due for payment.`); logger.silly(`Shop role ${roleId} is not due for payment.`);
} }
const guildData = await guildSchema.findOne({ guildId }); const guildData = await guildSchema.findOne({ guildId });
if (!guildData) { if (!guildData) {
logger.error(`Guild ${guildId} not found.`); logger.error(`Guild ${guildId} not found.`);
return; return;
} }
if (!userId) { if (!userId) {
logger.error(`User ID not found for shop role ${roleId}.`); logger.error(`User ID not found for shop role ${roleId}.`);
return; return;
} }
const userData = await userSchema.findOne({ guildId, userId }); const userData = await userSchema.findOne({ guildId, userId });
if (!userData) { if (!userData) {
logger.error(`User ${userId} not found for shop role ${roleId}.`); logger.error(`User ${userId} not found for shop role ${roleId}.`);
return; return;
} }
const rGuild = client?.guilds?.cache?.get(guildId); const rGuild = client?.guilds?.cache?.get(guildId);
const rMember = await rGuild?.members?.fetch(userId); const rMember = await rGuild?.members?.fetch(userId);
if (!rMember) { if (!rMember) {
logger.error(`Member ${userId} not found for shop role ${roleId}.`); logger.error(`Member ${userId} not found for shop role ${roleId}.`);
return; return;
} }
const rRole = rMember.roles.cache.get(roleId); const rRole = rMember.roles.cache.get(roleId);
if (!rMember || !rRole) { if (!rMember || !rRole) {
logger.error(`Member ${userId} not found for shop role ${roleId}.`); logger.error(`Member ${userId} not found for shop role ${roleId}.`);
await shopRoleSchema await shopRoleSchema
@ -76,36 +66,27 @@ export default async (client: Client) => {
}); });
return; return;
} }
if (new Date() > nextPayment) { if (new Date() > nextPayment) {
logger.silly( logger.silly(
`Shop role ${roleId} is due for payment. Withdrawing credits from user ${userId}.` `Shop role ${roleId} is due for payment. Withdrawing credits from user ${userId}.`
); );
const { pricePerHour } = guildData.shop.roles; const { pricePerHour } = guildData.shop.roles;
if (userData.credits < pricePerHour) { if (userData.credits < pricePerHour) {
logger.error( logger.error(
`User ${userId} does not have enough credits to pay for shop role ${roleId}.` `User ${userId} does not have enough credits to pay for shop role ${roleId}.`
); );
if (!rMember) { if (!rMember) {
logger.error(`Member ${userId} not found for shop role ${roleId}.`); logger.error(`Member ${userId} not found for shop role ${roleId}.`);
return; return;
} }
rMember.roles.remove(roleId); rMember.roles.remove(roleId);
return; return;
} }
userData.credits -= pricePerHour; userData.credits -= pricePerHour;
await userData await userData
.save() .save()
.then(async () => { .then(async () => {
role.lastPayed = new Date(); role.lastPayed = new Date();
await role await role
.save() .save()
.then(async () => { .then(async () => {
@ -117,7 +98,6 @@ export default async (client: Client) => {
err err
); );
}); });
logger.silly( logger.silly(
`Shop role ${roleId} has been paid for. Keeping role ${roleId} for user ${userId}.` `Shop role ${roleId} has been paid for. Keeping role ${roleId} for user ${userId}.`
); );

35
src/jobs/timeouts.ts Normal file
View file

@ -0,0 +1,35 @@
import logger from "../logger";
import timeoutSchema from "../models/timeout";
import addSeconds from "../helpers/addSeconds";
export const options = {
schedule: "*/30 * * * *", // https://crontab.guru/
};
export const execute = async () => {
const timeouts = await timeoutSchema.find();
await Promise.all(
timeouts.map(async (timeout) => {
const { guildId, userId, timeoutId, cooldown, createdAt } = timeout;
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}.`
);
});
}
})
);
};

View file

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

View file

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

View file

@ -0,0 +1,27 @@
// 3rd party dependencies
import mongoose from "mongoose";
// Dependencies
import logger from "../../logger";
// Configuration
import { url } from "../../config/database";
export const start = async () => {
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}`);
});
mongoose.connection.on("warn", async (warning) => {
logger.warn(warning);
});
};

View file

@ -1,14 +1,19 @@
/* eslint-disable no-loops/no-loops */ /* eslint-disable no-loops/no-loops */
import { Client } from "discord.js"; import { Client } from "discord.js";
import listDir from "../../helpers/listDir"; import listDir from "../../helpers/listDir";
import { IEvent } from "../../interfaces/Event";
import logger from "../../logger";
export const register = async (client: Client) => { export const register = async (client: Client) => {
const eventNames = await listDir("events"); const eventNames = await listDir("plugins/events");
if (!eventNames) return; if (!eventNames) return;
for await (const eventName of eventNames) { for await (const eventName of eventNames) {
const event = await import(`../../events/${eventName}`); const event: IEvent = await import(`../../plugins/events/${eventName}`);
const eventExecutor = async (...args: any[]) => event.execute(...args); const eventExecutor = async (...args: Promise<void>[]) =>
event.execute(...args).catch(async (err) => {
logger.error(`${err}`);
});
if (!event.options?.type) return; if (!event.options?.type) return;
switch (event.options.type) { 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

@ -0,0 +1,29 @@
import logger from "../../logger";
import { Client } from "discord.js";
import { IJob } from "../../interfaces/Job";
import listDir from "../../helpers/listDir";
import schedule from "node-schedule";
export const start = async (client: Client) => {
logger.info("Starting schedule manager...");
const jobNames = await listDir("jobs");
if (!jobNames) return logger.info("No jobs found");
await Promise.all(
jobNames.map(async (jobName) => {
const job: IJob = await import(`../../jobs/${jobName}`);
schedule.scheduleJob(job.options.schedule, async () => {
logger.info(`Executed job ${jobName}!`);
await job.execute(client);
});
})
).then(async () => {
const list = schedule.scheduledJobs;
logger.silly(list);
});
};

View file

@ -1,6 +1,6 @@
import { Snowflake } from "discord.js"; import { Snowflake } from "discord.js";
import { model, Schema } from "mongoose"; import { model, Schema } from "mongoose";
import { IEncryptionData } from "../../interfaces/EncryptionData"; import { IEncryptionData } from "../interfaces/EncryptionData";
export interface IApi { export interface IApi {
guildId: Snowflake; guildId: Snowflake;

View file

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

View file

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

View file

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

View file

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

View file

@ -2,15 +2,15 @@
import { CommandInteraction, MessageEmbed } from "discord.js"; import { CommandInteraction, MessageEmbed } from "discord.js";
// Configurations // Configurations
import getEmbedConfig from "../../../../helpers/getEmbedConfig"; import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
// Handlers // Handlers
import logger from "../../../../logger"; import logger from "../../../../../logger";
import mongoose from "mongoose"; import mongoose from "mongoose";
// Models // Models
import fetchUser from "../../../../helpers/fetchUser"; import fetchUser from "../../../../../helpers/fetchUser";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function // Function
@ -38,7 +38,6 @@ export default {
); );
}, },
execute: async (interaction: CommandInteraction) => { execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } = const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild); await getEmbedConfig(interaction.guild);
const { options, user, guild, client } = interaction; 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 { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import logger from "../../../../logger"; import logger from "../../../../../logger";
import userSchema, { IUser } from "../../../../database/schemas/user"; import userSchema, { IUser } from "../../../../../models/user";
export default { export default {
metadata: { guildOnly: true, ephemeral: false }, metadata: { guildOnly: true, ephemeral: false },
@ -13,7 +13,6 @@ export default {
return command.setName("top").setDescription(`View the top users`); return command.setName("top").setDescription(`View the top users`);
}, },
execute: async (interaction: CommandInteraction) => { execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } = const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild); await getEmbedConfig(interaction.guild);
const { guild } = interaction; const { guild } = interaction;

View file

@ -4,17 +4,17 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import Chance from "chance"; import Chance from "chance";
// Configurations // Configurations
import getEmbedConfig from "../../../../helpers/getEmbedConfig"; import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
// Handlers // Handlers
import logger from "../../../../logger"; import logger from "../../../../../logger";
// Models // Models
import timeoutSchema from "../../../../database/schemas/timeout"; import * as cooldown from "../../../../../helpers/cooldown";
// Helpers // Helpers
import fetchUser from "../../../../helpers/fetchUser"; import fetchUser from "../../../../../helpers/fetchUser";
import fetchGuild from "../../../../helpers/fetchGuild"; import fetchGuild from "../../../../../helpers/fetchGuild";
export default { export default {
metadata: { guildOnly: true, ephemeral: true }, metadata: { guildOnly: true, ephemeral: true },
@ -23,9 +23,9 @@ export default {
return command.setName("work").setDescription(`Work to earn credits`); return command.setName("work").setDescription(`Work to earn credits`);
}, },
execute: async (interaction: CommandInteraction) => { execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return; const { successColor, footerText, footerIcon } = await getEmbedConfig(
const { errorColor, successColor, footerText, footerIcon } = interaction.guild
await getEmbedConfig(interaction.guild); // Destructure member ); // Destructure member
const { guild, user } = interaction; const { guild, user } = interaction;
const embed = new MessageEmbed() const embed = new MessageEmbed()
@ -39,33 +39,13 @@ export default {
// Chance module // Chance module
const chance = new Chance(); 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) { if (guild === null) {
return logger?.silly(`Guild is null`); return logger?.silly(`Guild is null`);
} }
const guildDB = await fetchGuild(guild); const guildDB = await fetchGuild(guild);
// If user is not on timeout await cooldown.interaction(interaction, guildDB?.credits?.workTimeout);
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),
],
});
}
const creditsEarned = chance.integer({ const creditsEarned = chance.integer({
min: 0, 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 { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js"; import { CommandInteraction } from "discord.js";
import logger from "../../logger"; import logger from "../../../logger";
import modules from "../../commands/fun/modules"; 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 axios from "axios";
import { CommandInteraction, MessageEmbed } from "discord.js"; import { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
export default { export default {
metadata: { guildOnly: false, ephemeral: false }, metadata: { guildOnly: false, ephemeral: false, cooldown: 15 },
builder: (command: SlashCommandSubcommandBuilder) => { builder: (command: SlashCommandSubcommandBuilder) => {
return command.setName("meme").setDescription("Get a meme from r/memes)"); return command.setName("meme").setDescription("Get a meme from r/memes)");

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,7 +6,7 @@ import { CommandInteraction } from "discord.js";
import modules from "./modules"; import modules from "./modules";
// Handlers // Handlers
import logger from "../../logger"; import logger from "../../../logger";
export const moduleData = modules; export const moduleData = modules;
@ -17,13 +17,7 @@ export const builder = new SlashCommandBuilder()
.addSubcommand(modules.give.builder); .addSubcommand(modules.give.builder);
export const execute = async (interaction: CommandInteraction) => { export const execute = async (interaction: CommandInteraction) => {
const { options } = interaction; if (interaction.options.getSubcommand() === "give") {
if (options?.getSubcommand() === "give") {
logger?.silly(`Executing give subcommand`);
await modules.give.execute(interaction); 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"; import modules from "./modules";
// Handlers // Handlers
import logger from "../../logger"; import logger from "../../../logger";
export const moduleData = modules; export const moduleData = modules;
@ -14,16 +14,16 @@ export const moduleData = modules;
export const builder = new SlashCommandBuilder() export const builder = new SlashCommandBuilder()
.setName("shop") .setName("shop")
.setDescription("Shop for credits and custom roles.") .setDescription("Shop for credits and custom roles.")
.addSubcommand(modules.pterodactyl.builder) .addSubcommand(modules.cpgg.builder)
.addSubcommandGroup(modules.roles.builder); .addSubcommandGroup(modules.roles.builder);
export const execute = async (interaction: CommandInteraction) => { export const execute = async (interaction: CommandInteraction) => {
const { options } = interaction; const { options } = interaction;
if (options?.getSubcommand() === "pterodactyl") { if (options?.getSubcommand() === "cpgg") {
logger.silly(`Executing pterodactyl subcommand`); logger.silly(`Executing cpgg subcommand`);
return modules.pterodactyl.execute(interaction); return modules.cpgg.execute(interaction);
} }
if (options?.getSubcommandGroup() === "roles") { 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 { v4 as uuidv4 } from "uuid";
import axios from "axios"; import axios from "axios";
import getEmbedConfig from "../../../helpers/getEmbedConfig"; import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
import logger from "../../../logger"; import logger from "../../../../../logger";
import encryption from "../../../handlers/encryption"; import encryption from "../../../../../handlers/encryption";
import pluralize from "../../../helpers/pluralize"; import pluralize from "../../../../../helpers/pluralize";
import apiSchema from "../../../database/schemas/api"; import apiSchema from "../../../../../models/api";
import fetchUser from "../../../helpers/fetchUser"; import fetchUser from "../../../../../helpers/fetchUser";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { message } from "../../../../../helpers/cooldown/index";
export default { export default {
metadata: { guildOnly: true, ephemeral: true }, metadata: { guildOnly: true, ephemeral: true },
builder: (command: SlashCommandSubcommandBuilder) => { builder: (command: SlashCommandSubcommandBuilder) => {
return command return command
.setName("pterodactyl") .setName("cpgg")
.setDescription("Buy pterodactyl power.") .setDescription("Buy cpgg power.")
.addIntegerOption((option) => .addIntegerOption((option) =>
option option
.setName("amount") .setName("amount")
@ -27,7 +32,6 @@ export default {
); );
}, },
execute: async (interaction: CommandInteraction) => { execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { errorColor, successColor, footerText, footerIcon } = const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild); await getEmbedConfig(interaction.guild);
const { options, guild, user, client } = interaction; const { options, guild, user, client } = interaction;
@ -71,8 +75,8 @@ export default {
return interaction?.editReply({ return interaction?.editReply({
embeds: [ embeds: [
{ {
title: ":shopping_cart: Shop [Pterodactyl]", title: "[:shopping_cart:] CPGG",
description: `You **can't** withdraw for __Pterodactyl__ below **100**.`, description: `You **can't** withdraw for __CPGG__ below **100**.`,
color: errorColor, color: errorColor,
fields: [ fields: [
{ {
@ -96,9 +100,9 @@ export default {
return interaction?.editReply({ return interaction?.editReply({
embeds: [ embeds: [
{ {
title: ":shopping_cart: Shop [Pterodactyl]", title: "[:shopping_cart:] CPGG",
description: description:
"You **can't** withdraw for __Pterodactyl__ above **1.000.000**.", "You **can't** withdraw for __CPGG__ above **1.000.000**.",
color: errorColor, color: errorColor,
fields: [ fields: [
{ {
@ -122,7 +126,7 @@ export default {
return interaction?.editReply({ return interaction?.editReply({
embeds: [ embeds: [
{ {
title: ":shopping_cart: Shop [Pterodactyl]", title: "[:shopping_cart:] CPGG",
description: `You have **insufficient** credits.`, description: `You have **insufficient** credits.`,
color: errorColor, color: errorColor,
fields: [ fields: [
@ -150,13 +154,21 @@ export default {
if (!apiCredentials) return; if (!apiCredentials) return;
const api = axios?.create({ const api = axios?.create({
baseURL: apiCredentials.url, baseURL: `${apiCredentials.url}/api/`,
headers: { headers: {
Authorization: `Bearer ${encryption.decrypt(apiCredentials.token)}`, 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 await api
@ -178,43 +190,47 @@ export default {
?.then(async () => { ?.then(async () => {
logger?.silly(`Successfully saved new credits.`); logger?.silly(`Successfully saved new credits.`);
await dmUser?.send({ if (!interaction.guild) throw new Error("Guild is undefined");
embeds: [
{ await dmUser
title: ":shopping_cart: Shop [Pterodactyl]", ?.send({
description: `Redeem this voucher [here](${shopUrl})!`, embeds: [
fields: [ {
{ name: "Code", value: `${code}`, inline: true }, 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", title: "[:shopping_cart:] CPGG",
value: `${optionAmount || userDB?.credits}`, description: `I have sent you the code in [DM](${msg.url})!`,
inline: true, 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) => { .catch(async (error) => {
@ -223,7 +239,7 @@ export default {
return interaction?.editReply({ return interaction?.editReply({
embeds: [ embeds: [
{ {
title: ":shopping_cart: Shop [Pterodactyl]", title: "[:shopping_cart:] CPGG",
description: "Something went wrong.", description: "Something went wrong.",
color: errorColor, color: errorColor,
timestamp: new Date(), timestamp: new Date(),
@ -243,7 +259,7 @@ export default {
return interaction?.editReply({ return interaction?.editReply({
embeds: [ embeds: [
{ {
title: ":shopping_cart: Shop [Pterodactyl]", title: "[:shopping_cart:] CPGG",
description: "Something went wrong.", description: "Something went wrong.",
color: errorColor, color: errorColor,
timestamp: new Date(), 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"; import { CommandInteraction } from "discord.js";
// Handlers // Handlers
import logger from "../../../../logger"; import logger from "../../../../../logger";
import getEmbedConfig from "../../../../helpers/getEmbedConfig"; import getEmbedConfig from "../../../../../helpers/getEmbedConfig";
// Modules // Modules
import modules from "./modules"; import modules from "./modules";
import guildSchema from "../../../../database/schemas/guild"; import guildSchema from "../../../../../models/guild";
export const moduleData = modules; export const moduleData = modules;

View file

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

View file

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

View file

@ -1,16 +1,9 @@
// Dependencies
import { SlashCommandBuilder } from "@discordjs/builders"; import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js"; import { CommandInteraction } from "discord.js";
// Modules import modules from "./modules";
import modules from "../../commands/utility/modules";
// Handlers
import logger from "../../logger";
export const moduleData = modules; export const moduleData = modules;
// Function
export const builder = new SlashCommandBuilder() export const builder = new SlashCommandBuilder()
.setName("utility") .setName("utility")
.setDescription("Common utility.") .setDescription("Common utility.")
@ -21,9 +14,7 @@ export const builder = new SlashCommandBuilder()
.addSubcommand(modules.avatar.builder); .addSubcommand(modules.avatar.builder);
export const execute = async (interaction: CommandInteraction) => { export const execute = async (interaction: CommandInteraction) => {
const { options } = interaction; switch (interaction.options.getSubcommand()) {
switch (options.getSubcommand()) {
case "lookup": case "lookup":
return modules.lookup.execute(interaction); return modules.lookup.execute(interaction);
case "about": case "about":
@ -33,6 +24,8 @@ export const execute = async (interaction: CommandInteraction) => {
case "avatar": case "avatar":
return modules.avatar.execute(interaction); return modules.avatar.execute(interaction);
default: 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 { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
@ -17,7 +17,6 @@ export default {
); );
}, },
execute: async (interaction: CommandInteraction) => { execute: async (interaction: CommandInteraction) => {
if (interaction.guild == null) return;
const { successColor, footerText, footerIcon } = await getEmbedConfig( const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild interaction.guild
); );

View file

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

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