Merge pull request #240 from VermiumSifell/dev

Development for 2022.5.0
This commit is contained in:
Axel Olausson Holtenäs 2022-05-17 09:48:16 +02:00 committed by GitHub
commit a7f37bbbdc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
85 changed files with 1390 additions and 1206 deletions

View file

@ -5,7 +5,7 @@
"main": "src/index.ts",
"scripts": {
"test": "jest",
"start": "nodemon | pino-pretty -i pid,hostname -t yyyy-mm-dd HH:MM:s",
"start": "node .",
"prettier-format": "prettier \"src/**/*.ts\" --write",
"lint": "eslint ./src --ext .ts",
"prepare": "husky install"
@ -27,6 +27,7 @@
"email": "vermium@zyner.org"
},
"dependencies": {
"@crowdin/ota-client": "^0.7.0",
"@discordjs/builders": "^0.13.0",
"@discordjs/rest": "^0.4.0",
"axios": "^0.27.0",
@ -36,6 +37,9 @@
"discord-api-types": "^0.33.0",
"discord.js": "^13.6.0",
"i18next": "^21.6.13",
"i18next-async-backend": "^2.0.0",
"i18next-http-backend": "^1.4.0",
"i18next-resources-to-backend": "^1.0.0",
"mongoose": "^6.2.3",
"node-schedule": "^2.1.0",
"ts-node": "^10.7.0",

View file

@ -8,13 +8,15 @@ import logger from "@logger";
import { url } from "@config/database";
export default async () => {
mongoose.connect(url).then(async (connection) => {
logger?.info(`Connected to database: ${connection.connection.name}`);
await mongoose.connect(url).then(async (connection) => {
logger.info(`Connected to database: ${connection.connection.name}`);
});
mongoose.connection.on("error", (error) => {
logger?.error(error);
mongoose.connection.on("error", async (error) => {
logger.error(error);
});
mongoose.connection.on("warn", (warning) => {
logger?.warn(warning);
mongoose.connection.on("warn", async (warning) => {
logger.warn(warning);
});
};

View file

@ -7,7 +7,6 @@ import fetchGuild from "@helpers/fetchGuild";
import logger from "@logger";
export default {
name: "guildCreate",
async execute(guild: Guild) {
const { client } = guild;
@ -15,5 +14,7 @@ export default {
await fetchGuild(guild);
await updatePresence(client);
logger.silly(`guildCreate: ${guild}`);
},
};

View file

@ -7,7 +7,6 @@ import dropGuild from "@helpers/dropGuild";
import logger from "@logger";
export default {
name: "guildDelete",
async execute(guild: Guild) {
const { client } = guild;
@ -15,5 +14,7 @@ export default {
await dropGuild(guild);
await updatePresence(client);
logger.silly(`guildDelete: ${guild}`);
},
};

View file

@ -20,24 +20,35 @@ export default {
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,
}),
],
});
(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

@ -9,7 +9,6 @@ import joinMessage from "../guildMemberAdd/joinMessage";
import audits from "../guildMemberAdd/audits";
export default {
name: "guildMemberAdd",
async execute(member: GuildMember) {
const { client, user, guild } = member;

View file

@ -20,21 +20,32 @@ export default {
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,
}),
],
});
(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

@ -9,7 +9,6 @@ import leaveMessage from "./leaveMessage";
import audits from "./audits";
export default {
name: "guildMemberRemove",
async execute(member: GuildMember) {
const { client, user, guild } = member;

View file

@ -26,23 +26,34 @@ export default {
if (channel === null) return;
(channel as TextChannel).send({
embeds: [
new MessageEmbed()
.setColor(successColor)
.setDescription(
`
(channel as TextChannel)
.send({
embeds: [
new MessageEmbed()
.setColor(successColor)
.setDescription(
`
**Interaction created by** ${interaction.user.username} **in** ${interaction.channel}
`
)
.setThumbnail(interaction.user.displayAvatarURL())
.addFields([{ name: "Event", value: "interactionCreate" }])
.setTimestamp()
.setFooter({
text: footerText,
iconURL: footerIcon,
}),
],
});
)
.setThumbnail(interaction.user.displayAvatarURL())
.addFields([{ name: "Event", value: "interactionCreate" }])
.setTimestamp()
.setFooter({
text: footerText,
iconURL: footerIcon,
}),
],
})
.then(async () => {
logger.info(
`Audit log sent for event interactionCreate in guild ${interaction?.guild?.name} (${interaction?.guild?.id})`
);
})
.catch(async () => {
logger.error(
`Audit log failed to send for event interactionCreate in guild ${interaction?.guild?.name} (${interaction?.guild?.id})`
);
});
},
};

View file

@ -4,16 +4,83 @@ import { CommandInteraction, MessageEmbed } from "discord.js";
import logger from "@logger";
import { errorColor, footerText, footerIcon } from "@config/embed";
import i18next from "i18next";
import deferReply from "@root/helpers/deferReply";
import getCommandMeta from "@root/helpers/getCommandMeta";
export default async (interaction: CommandInteraction) => {
if (!interaction.isCommand()) return;
const { client, guild, commandName, user } = interaction;
const { client, guild, commandName, user, memberPermissions } = interaction;
const currentCommand = client.commands.get(commandName);
if (!currentCommand) return;
await interaction.deferReply({ ephemeral: true });
if (currentCommand == null) {
logger.silly(`Command ${commandName} not found`);
}
const meta = await getCommandMeta(interaction, currentCommand);
await deferReply(interaction, meta.ephemeral || false);
if (
meta.permissions &&
meta.guildOnly &&
!memberPermissions?.has(meta.permissions)
) {
return interaction?.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:toolbox:] Manage")
.setDescription(`You do not have the permission to manage the bot.`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
}
if (meta.guildOnly) {
if (!guild) {
logger.verbose(`Guild is null`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setDescription(
i18next.t("guildOnly", {
lng: interaction.locale,
ns: "errors",
})
)
.setColor(errorColor)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
}
}
if (meta.dmOnly) {
if (guild) {
logger.verbose(`Guild exist`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setDescription(
i18next.t("dmOnly", {
lng: interaction.locale,
ns: "errors",
})
)
.setColor(errorColor)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
}
}
await currentCommand
.execute(interaction)
@ -23,7 +90,7 @@ export default async (interaction: CommandInteraction) => {
);
})
.catch(async (error: any) => {
logger?.error(error);
logger?.error(`${error}`);
return interaction.editReply({
embeds: [

View file

@ -7,7 +7,6 @@ import logger from "@logger";
import audits from "./audits";
export default {
name: "interactionCreate",
async execute(interaction: CommandInteraction) {
const { guild, id } = interaction;

View file

@ -2,7 +2,6 @@ import { Message } from "discord.js";
import modules from "@events/messageCreate/modules";
export default {
name: "messageCreate",
async execute(message: Message) {
await modules.credits.execute(message);
await modules.points.execute(message);

View file

@ -26,26 +26,37 @@ export default {
if (channel === null) return;
(channel as TextChannel).send({
embeds: [
new MessageEmbed()
.setColor(successColor)
.setAuthor({
name: message.author.username,
iconURL: message.author.displayAvatarURL(),
})
.setDescription(
`
(channel as TextChannel)
.send({
embeds: [
new MessageEmbed()
.setColor(successColor)
.setAuthor({
name: message.author.username,
iconURL: message.author.displayAvatarURL(),
})
.setDescription(
`
**Message sent by** ${message.author} **deleted in** ${message.channel}
${message.content}
`
)
.setTimestamp()
.setFooter({
text: footerText,
iconURL: footerIcon,
}),
],
});
)
.setTimestamp()
.setFooter({
text: footerText,
iconURL: footerIcon,
}),
],
})
.then(async () => {
logger.info(
`Audit log sent for event messageDelete in guild ${message?.guild?.name} (${message?.guild?.id})`
);
})
.catch(async () => {
logger.error(
`Audit log failed to send for event messageDelete in guild ${message?.guild?.name} (${message?.guild?.id})`
);
});
},
};

View file

@ -3,7 +3,6 @@ import audits from "@events/messageDelete/audits";
import counter from "./modules/counter";
export default {
name: "messageDelete",
async execute(message: Message) {
await audits.execute(message);
await counter(message);

View file

@ -29,25 +29,36 @@ export default {
if (channel === null) return;
(channel as TextChannel).send({
embeds: [
new MessageEmbed()
.setColor(successColor)
.setAuthor({
name: newMessage.author.username,
iconURL: newMessage.author.displayAvatarURL(),
})
.setDescription(
`
(channel as TextChannel)
.send({
embeds: [
new MessageEmbed()
.setColor(successColor)
.setAuthor({
name: newMessage.author.username,
iconURL: newMessage.author.displayAvatarURL(),
})
.setDescription(
`
**Message edited in** ${newMessage.channel} [jump to message](https://discord.com/channels/${newMessage.guild.id}/${newMessage.channel.id}/${newMessage.id})
`
)
.setTimestamp()
.setFooter({
text: footerText,
iconURL: footerIcon,
}),
],
});
)
.setTimestamp()
.setFooter({
text: footerText,
iconURL: footerIcon,
}),
],
})
.then(async () => {
logger.info(
`Audit log sent for event messageUpdate in guild ${newMessage?.guild?.name} (${newMessage?.guild?.id})`
);
})
.catch(async () => {
logger.error(
`Audit log failed to send for event messageUpdate in guild ${newMessage?.guild?.name} (${newMessage?.guild?.id})`
);
});
},
};

View file

@ -8,7 +8,6 @@ import counter from "./modules/counter";
import audits from "./audits";
export default {
name: "messageUpdate",
async execute(oldMessage: Message, newMessage: Message) {
const { author, guild } = newMessage;

View file

@ -8,7 +8,6 @@ import deployCommands from "@handlers/deployCommands";
import devMode from "@handlers/devMode";
export default {
name: "ready",
once: true,
async execute(client: Client) {
logger.info(`${client.user?.tag} (${client.user?.id}) is ready`);

View file

@ -15,19 +15,18 @@ export default async (client: Client) => {
plugins.map(async (pluginName) => {
const plugin = await import(`../plugins/${pluginName}`);
await client?.commands?.set(
plugin?.default?.data?.name,
plugin?.default
await client.commands.set(
plugin.default.data.name,
plugin.default,
plugin.default.meta
);
logger.verbose(`Loaded plugin: ${pluginName}`);
})
)
.then(async () => {
logger.debug("Successfully loaded plugins.");
})
.catch(async (err) => {
logger.error(err);
logger.error(`${err}`);
});
});
};

View file

@ -15,12 +15,12 @@ export default async (client: Client) => {
logger.verbose(`Loaded event: ${eventName}`);
if (event.once) {
return client.once(event.default.name, async (...args) =>
return client.once(eventName, async (...args) =>
event.default.execute(...args)
);
}
return client.on(event.default.name, async (...args) =>
return client.on(eventName, async (...args) =>
event.default.execute(...args)
);
})

22
src/helpers/deferReply.ts Normal file
View file

@ -0,0 +1,22 @@
import { CommandInteraction, MessageEmbed } from "discord.js";
import { waitColor, footerText, footerIcon } from "@config/embed";
export default async (interaction: CommandInteraction, ephemeral: boolean) => {
await interaction.deferReply({
ephemeral,
});
await interaction.editReply({
embeds: [
new MessageEmbed()
.setFooter({
text: footerText,
iconURL: footerIcon,
})
.setTimestamp(new Date())
.setTitle("Processing your request")
.setColor(waitColor)
.setDescription("Please wait..."),
],
});
};

View file

@ -0,0 +1,9 @@
import { footerText, footerIcon } from "@config/embed";
import { MessageEmbed } from "discord.js";
export default new MessageEmbed()
.setFooter({
text: footerText,
iconURL: footerIcon,
})
.setTimestamp(new Date());

View file

@ -0,0 +1,12 @@
import { CommandInteraction } from "discord.js";
export default async (interaction: CommandInteraction, currentCommand: any) => {
const subcommand = interaction.options.getSubcommand();
const subcommandGroup = interaction.options.getSubcommandGroup(false);
if (!subcommandGroup) {
return currentCommand.modules[subcommand].meta;
}
return currentCommand.groups[subcommandGroup].modules[subcommand].meta;
};

View file

@ -4,7 +4,6 @@ import { token, intents } from "@config/discord";
import { Client } from "discord.js"; // discord.js
import locale from "@locale";
import database from "@database";
import schedules from "@schedules";
import events from "@handlers/events";
@ -15,7 +14,6 @@ async function main() {
intents,
});
await locale();
await database();
await schedules(client);

View file

@ -1,205 +0,0 @@
import i18next from "i18next";
import logger from "@logger";
export default async () => {
await i18next
.init({
lng: "en", // if you're using a language detector, do not define the lng option
// debug: true,
fallbackLng: "en",
resources: {
en: {
general: { not_available: "Not Available" },
commands: {
credits: {
general: {
credits_one: "{{count}} credit",
credits_other: "{{count}} credits",
},
addons: {
balance: { embed: { title: "Credits" } },
gift: { embed: { title: "Gift" } },
},
},
reputation: {
addons: {
give: {
version01: {
embed: {
title: ":medal: Reputation",
description:
"You have given reputation within the last day, you can not repute now!",
},
},
version02: {
embed: {
title: ":medal: Reputation",
description:
"You have given {{user}} a {{type}} reputation!",
},
},
version03: {
embed: {
title: ":medal: Reputation",
description: "You can not repute yourself.",
},
},
},
},
},
profile: {
addons: {
view: {
embed: {
title: "Profile",
reputation: "Reputation (Global)",
level: "Level (Guild)",
points: "Points (Guild)",
credits: "Credits (Guild)",
language_code: "Language Code (Global)",
},
},
settings: {
embed: {
title: "Profile",
description: "Following settings is set",
fields: { language: "Language" },
},
},
},
},
},
},
sv: {
general: { not_available: "Otillgänglig" },
commands: {
credits: {
general: {
credits_one: "{{count}} krona",
credits_other: "{{count}} kronor",
},
addons: {
balance: { embed: { title: "Krediter" } },
gift: { embed: { title: "Gåva" } },
},
},
reputation: {
addons: {
give: {
version01: {
embed: {
title: ":medal: Omdöme",
description:
"Du har redan gett omdöme inom den senaste dagen, du kan inte ge ett omdöme just nu!",
},
},
version02: {
embed: {
title: ":medal: Omdöme",
description: "Du har gett {{user}} ett {{type}} omdöme!",
},
},
version03: {
embed: {
title: ":medal: Omdöme",
description: "Du kan inte ge dig själv ett omdöme.",
},
},
},
},
},
profile: {
addons: {
view: {
embed: {
title: "Profil",
reputation: "Omdöme (Globalt)",
level: "Nivå (Server)",
points: "Poäng (Server)",
credits: "Krediter (Server)",
language_code: "Språkkod (Globalt)",
},
},
settings: {
embed: {
title: "Profil",
description: "Följande inställningar är satta",
fields: { language: "Språk" },
},
},
},
},
},
},
de: {
general: { not_available: "Nicht verfügbar" },
commands: {
credits: {
general: {
credits_one: "{{count}} Guthaben",
credits_other: "{{count}} Guthaben",
},
addons: {
balance: { embed: { title: "Guthaben" } },
gift: { embed: { title: "Geschenk" } },
},
},
reputation: {
addons: {
give: {
version01: {
embed: {
title: ":medal: Ruf",
description:
"Du hast dir am letzten Tag einen Ruf verschafft, den du jetzt nicht rühmen kannst!",
},
},
version02: {
embed: {
title: ":medal: Ruf",
description:
"Du hast {{user}} einen {{type}} Ruf gegeben!",
},
},
version03: {
embed: {
title: ":medal: Ruf",
description: "Du kannst dich nicht selbst rühmen.",
},
},
},
},
},
profile: {
addons: {
view: {
embed: {
title: "Profil",
reputation: "Ruf (Weltweit)",
level: "Level (Gilde)",
points: "Punkte (Gilde)",
credits: "Guthaben (Gilde)",
language_code: "Sprachcode (Weltweit)",
},
},
settings: {
embed: {
title: "Profile",
description: "Folgende Einstellungen werden vorgenommen",
fields: { language: "Sprache" },
},
},
},
},
},
},
},
})
.then(async () => {
logger.debug(`i18next initialized`);
})
.catch(async (error) => {
logger.error(`i18next failed to initialize: ${error}`);
});
};

View file

@ -1,17 +1,18 @@
// Dependencies
import { CommandInteraction } from "discord.js";
import { SlashCommandBuilder } from "@discordjs/builders";
import logger from "@logger";
import modules from "@root/plugins/counters/modules";
import modules from "@plugins/counters/modules";
export default {
metadata: { author: "Zyner" },
modules,
data: new SlashCommandBuilder()
.setName("counters")
.setDescription("View guild counters")
.addSubcommand(modules.view.data),
async execute(interaction: CommandInteraction) {
const { options } = interaction;

View file

@ -1,3 +1,3 @@
import view from "./view";
import view from "@plugins/counters/modules/view";
export default { view };

View file

@ -1,11 +1,3 @@
// Dependencies
import { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { ChannelType } from "discord-api-types/v10";
import counterSchema from "@schemas/counter";
// Configuration
import {
errorColor,
successColor,
@ -13,7 +5,16 @@ import {
footerIcon,
} from "@config/embed";
import { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { ChannelType } from "discord-api-types/v10";
import counterSchema from "@schemas/counter";
import i18next from "i18next";
export default {
meta: { guildOnly: true, ephemeral: false },
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("view")
@ -25,14 +26,28 @@ export default {
`The channel that contains the counter you want to view`
)
.setRequired(true)
.addChannelType(ChannelType.GuildText as number)
.addChannelTypes(ChannelType.GuildText)
);
},
execute: async (interaction: CommandInteraction) => {
const { options, guild } = interaction;
const { options, guild, locale } = interaction;
const discordChannel = options?.getChannel("channel");
const embed = new MessageEmbed()
.setTitle(
i18next.t("counters:modules:view:general:title", {
lng: locale,
ns: "plugins",
})
)
.setTimestamp(new Date())
.setFooter({
text: footerText,
iconURL: footerIcon,
});
const counter = await counterSchema?.findOne({
guildId: guild?.id,
channelId: discordChannel?.id,
@ -41,32 +56,31 @@ export default {
if (counter === null) {
return interaction?.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:1234:] Counters (View)")
.setDescription(`No counter found for channel ${discordChannel}!`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({
text: footerText,
iconURL: footerIcon,
}),
embed
.setDescription(
i18next.t("counters:modules:view:error01:description", {
lng: locale,
ns: "plugins",
channel: discordChannel,
})
)
.setColor(errorColor),
],
});
}
return interaction?.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:1234:] Counters (View)")
embed
.setDescription(
`Viewing counter for channel ${discordChannel} with count ${counter.counter}.`
i18next.t("counters:modules:view:success01:description", {
lng: locale,
ns: "plugins",
channel: discordChannel,
amount: counter.counter,
})
)
.setTimestamp(new Date())
.setColor(successColor)
.setFooter({
text: footerText,
iconURL: footerIcon,
}),
.setColor(successColor),
],
});
},

View file

@ -1,44 +1,39 @@
// Dependencies
import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
import logger from "@logger";
// Modules
import modules from "@root/plugins/credits/modules";
import modules from "@plugins/credits/modules";
export default {
metadata: { author: "Zyner" },
modules,
data: new SlashCommandBuilder()
.setName("credits")
.setDescription("Manage your credits.")
.addSubcommand(modules.balance.data)
.addSubcommand(modules.gift.data)
.addSubcommand(modules.top.data)
.addSubcommand(modules.work.data),
async execute(interaction: CommandInteraction) {
const { options } = interaction;
if (options?.getSubcommand() === "balance") {
logger?.verbose(`Executing balance subcommand`);
return modules.balance.execute(interaction);
switch (options.getSubcommand()) {
case "balance":
await modules.balance.execute(interaction);
break;
case "gift":
await modules.gift.execute(interaction);
break;
case "top":
await modules.top.execute(interaction);
break;
case "work":
await modules.work.execute(interaction);
break;
default:
logger.verbose(`Unknown subcommand ${options.getSubcommand()}`);
}
if (options?.getSubcommand() === "gift") {
logger?.verbose(`Executing gift subcommand`);
return modules.gift.execute(interaction);
}
if (options?.getSubcommand() === "top") {
logger?.verbose(`Executing top command`);
return modules.top.execute(interaction);
}
if (options?.getSubcommand() === "work") {
logger?.verbose(`Executing work command`);
return modules.work.execute(interaction);
}
logger?.verbose(`Unknown subcommand ${options?.getSubcommand()}`);
},
};

View file

@ -1,10 +1,3 @@
// Dependencies
import { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import logger from "@logger";
// Configurations
import {
errorColor,
successColor,
@ -12,41 +5,53 @@ import {
footerIcon,
} from "@config/embed";
// Helpers
import pluralize from "@helpers/pluralize";
import i18next from "i18next";
import { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import logger from "@logger";
import fetchUser from "@helpers/fetchUser";
export default {
meta: { guildOnly: true, ephemeral: true },
data: (command: SlashCommandSubcommandBuilder) => {
return (
command
.setName("balance")
.setDescription(`View a user's balance`)
// User
.addUserOption((option) =>
option
.setName("user")
.setDescription(`The user whose balance you want to view`)
)
);
return command
.setName("balance")
.setDescription(`View a user's balance`)
.addUserOption((option) =>
option
.setName("user")
.setDescription(`The user whose balance you want to view`)
);
},
execute: async (interaction: CommandInteraction) => {
const { options, user, guild } = interaction;
const { options, user, guild, locale } = interaction;
const discordUser = options?.getUser("user");
const discordUser = options.getUser("user");
const embed = new MessageEmbed()
.setTitle(
i18next.t("credits:modules:balance:general:title", {
lng: locale,
ns: "plugins",
})
)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
if (guild === null) {
logger?.verbose(`Guild is null`);
logger.verbose(`Guild is null`);
return interaction?.editReply({
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Balance)")
.setDescription(`You can only use this command in a guild!`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
embed
.setDescription(
i18next.t("guildOnly", {
lng: locale,
ns: "errors",
})
)
.setColor(errorColor),
],
});
}
@ -54,50 +59,55 @@ export default {
const userObj = await fetchUser(discordUser || user, guild);
if (userObj === null) {
logger?.verbose(`User not found`);
logger.verbose(`User not found`);
return interaction?.editReply({
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Balance)")
.setDescription(`Could not find user ${discordUser || user}`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
embed
.setDescription(
i18next.t("userNotFound", {
lng: locale,
ns: "errors",
user: discordUser || user,
})
)
.setColor(errorColor),
],
});
}
if (userObj.credits === null) {
logger?.verbose(`User has no credits`);
logger.verbose(`User has no credits`);
return interaction?.editReply({
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Balance)")
.setDescription(`${discordUser || user} has no credits!`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
embed
.setDescription(
i18next.t("credits:modules:balance:error01:description", {
lng: locale,
ns: "plugins",
user: discordUser || user,
})
)
.setColor(errorColor),
],
});
}
logger?.verbose(`Found user ${discordUser || user}`);
logger.verbose(`Found user ${discordUser || user}`);
return interaction?.editReply({
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Balance)")
embed
.setDescription(
`${discordUser || user} has ${pluralize(
userObj.credits,
`credit`
)}!`
i18next.t("credits:modules:balance:success01:description", {
lng: locale,
ns: "plugins",
user: discordUser || user,
amount: userObj.credits,
})
)
.setTimestamp(new Date())
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
.setColor(successColor),
],
});
},

View file

@ -18,9 +18,12 @@ import saveUser from "@helpers/saveUser";
// Models
import fetchUser from "@helpers/fetchUser";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import i18next from "i18next";
// Function
export default {
meta: { guildOnly: true, ephemeral: true },
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("gift")
@ -42,38 +45,52 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
const { options, user, guild, client } = interaction;
const { options, user, guild, client, locale } = interaction;
const optionUser = options?.getUser("user");
const optionAmount = options?.getInteger("amount");
const optionReason = options?.getString("reason");
const optionUser = options.getUser("user");
const optionAmount = options.getInteger("amount");
const optionReason = options.getString("reason");
const embed = new MessageEmbed()
.setTitle(
i18next.t("credits:modules:gift:general:title", {
lng: locale,
ns: "plugins",
})
)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
if (guild === null) {
logger?.verbose(`Guild is null`);
logger.verbose(`Guild is null`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Gift)")
.setDescription(`We can not find your guild!`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
embed
.setDescription(
i18next.t("guildOnly", {
lng: locale,
ns: "errors",
})
)
.setColor(errorColor),
],
});
}
if (optionUser === null) {
logger?.verbose(`User not found`);
logger.verbose(`User not found`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Gift)")
.setDescription(`We can not find your requested user!`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
embed
.setDescription(
i18next.t("userNotFound", {
lng: locale,
ns: "errors",
})
)
.setColor(errorColor),
],
});
}
@ -85,119 +102,126 @@ export default {
const toUserDB = await fetchUser(optionUser, guild);
if (fromUserDB === null) {
logger?.verbose(`User not found`);
logger.verbose(`User not found`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Gift)")
embed
.setDescription(
`We can not find your requested from user in our database!`
i18next.t("userNotFound", {
lng: locale,
ns: "errors",
})
)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
.setColor(errorColor),
],
});
}
if (toUserDB === null) {
logger?.verbose(`User not found`);
logger.verbose(`User not found`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Gift)")
embed
.setDescription(
`We can not find your requested to user in our database!`
i18next.t("userNotFound", {
lng: locale,
ns: "errors",
})
)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
.setColor(errorColor),
],
});
}
// If receiver is same as sender
if (optionUser?.id === user?.id) {
logger?.verbose(`User is same as sender`);
if (optionUser.id === user.id) {
logger.verbose(`User is same as sender`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Gift)")
.setDescription(`You can not pay yourself!`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
embed
.setDescription(
i18next.t("credits:modules:gift:error01:description", {
lng: locale,
ns: "plugins",
})
)
.setColor(errorColor),
],
});
}
// If amount is null
if (optionAmount === null) {
logger?.verbose(`Amount is null`);
logger.verbose(`Amount is null`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Gift)")
.setDescription(`We could not read your requested amount!`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
embed
.setDescription(
i18next.t("amountNotFound", {
lng: locale,
ns: "errors",
})
)
.setColor(errorColor),
],
});
}
// If amount is zero or below
if (optionAmount <= 0) {
logger?.verbose(`Amount is zero or below`);
logger.verbose(`Amount is zero or below`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Gift)")
.setDescription(`You can't gift zero or below!`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
embed
.setDescription(
i18next.t("credits:modules:gift:error02:description", {
lng: locale,
ns: "plugins",
})
)
.setColor(errorColor),
],
});
}
// If user has below gifting amount
if (fromUserDB?.credits < optionAmount) {
logger?.verbose(`User has below gifting amount`);
if (fromUserDB.credits < optionAmount) {
logger.verbose(`User has below gifting amount`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Gift)")
embed
.setDescription(
`You have insufficient credits. Your balance is ${fromUserDB?.credits}!`
i18next.t("credits:modules:gift:error03:description", {
lng: locale,
ns: "plugins",
amount: fromUserDB.credits,
})
)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
.setColor(errorColor),
],
});
}
// If toUserDB has no credits
if (toUserDB === null) {
logger?.verbose(`User has no credits`);
logger.verbose(`User has no credits`);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Gift)")
embed
.setDescription(
`We can not find your requested to user in our database!`
i18next.t("userNotFound", {
lng: locale,
ns: "errors",
})
)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
.setColor(errorColor),
],
});
}
@ -209,46 +233,50 @@ export default {
toUserDB.credits += optionAmount;
// Save users
await saveUser(fromUserDB, toUserDB)?.then(async () => {
await saveUser(fromUserDB, toUserDB).then(async () => {
// Get DM user object
const dmUser = client?.users?.cache?.get(optionUser?.id);
const dmUser = client.users.cache.get(optionUser.id);
if (dmUser == null) return;
// Send DM to user
await dmUser
?.send({
.send({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Gift)")
embed
.setDescription(
`You have received ${optionAmount} credits from ${
user?.tag
} with reason ${
optionReason ? ` with reason: ${optionReason}` : ""
}!`
i18next.t("credits:modules:gift:error03:description", {
lng: locale,
ns: "plugins",
user: user.tag,
amount: optionAmount,
reason: optionReason || "unspecified",
})
)
.setTimestamp(new Date())
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
.setColor(successColor),
],
})
.catch(async (error) =>
logger?.error(`[Gift] Error sending DM to user: ${error}`)
logger.error(`[Gift] Error sending DM to user: ${error}`)
);
logger?.verbose(
`[Gift] Successfully gifted ${optionAmount} credits to ${optionUser?.tag}`
logger.verbose(
`[Gift] Successfully gifted ${optionAmount} credits to ${optionUser.tag}`
);
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Gift)")
embed
.setDescription(
`Successfully gifted ${optionAmount} credits to ${optionUser?.tag}!`
i18next.t("credits:modules:gift:success02:description", {
lng: locale,
ns: "plugins",
user: user,
amount: optionAmount,
reason: optionReason || "unspecified",
})
)
.setTimestamp(new Date())
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
.setColor(successColor),
],
});
});

View file

@ -1,6 +1,6 @@
import balance from "./balance";
import gift from "./gift";
import top from "./top";
import work from "./work";
import balance from "@plugins/credits/modules/balance";
import gift from "@plugins/credits/modules/gift";
import top from "@plugins/credits/modules/top";
import work from "@plugins/credits/modules/work";
export default { balance, gift, top, work };

View file

@ -1,48 +1,86 @@
// Dependencies
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import {
successColor,
errorColor,
footerText,
footerIcon,
} from "@config/embed";
import i18next from "i18next";
import { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import logger from "@logger";
import userSchema from "@schemas/user";
// Configurations
import { successColor, footerText, footerIcon } from "@config/embed";
// Helpers
import pluralize from "@helpers/pluralize";
import userSchema, { IUser } from "@schemas/user";
export default {
meta: { guildOnly: true, ephemeral: false },
data: (command: SlashCommandSubcommandBuilder) => {
return command.setName("top").setDescription(`View the top users`);
},
execute: async (interaction: CommandInteraction) => {
// Get all users in the guild
const { locale, guild } = interaction;
const usersDB = await userSchema.find({ guildId: interaction?.guild?.id });
const embed = new MessageEmbed()
.setTitle(
i18next.t("credits:modules:top:general:title", {
lng: locale,
ns: "plugins",
})
)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
if (guild === null) {
logger.verbose(`Guild is null`);
return interaction.editReply({
embeds: [
embed
.setDescription(
i18next.t("guildOnly", {
lng: locale,
ns: "errors",
})
)
.setColor(errorColor),
],
});
}
const usersDB = await userSchema.find({ guildId: guild.id });
const topTen = usersDB
// Sort them after credits amount (ascending)
.sort((a, b) => (a?.credits > b?.credits ? -1 : 1))
.sort((a, b) => (a.credits > b.credits ? -1 : 1))
// Return the top 10
.slice(0, 10);
// Create entry object
const entry = (x: any, index: number) =>
`${index + 1}. <@${x?.userId}> - ${pluralize(x?.credits, "credit")}`;
const entry = (x: IUser, index: number) =>
i18next.t("credits:modules:top:entry", {
lng: locale,
ns: "plugins",
index: index + 1,
user: x.userId,
amount: x.credits,
});
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Top)")
embed
.setDescription(
`Top 10 users with the most credits.
` ${i18next.t("credits:modules:top:success01:description", {
lng: locale,
ns: "plugins",
})}
${topTen.map(entry).join("\n")}`
${topTen.map(entry).join("\n")}
`
)
.setTimestamp(new Date())
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
.setColor(successColor),
],
});
},

View file

@ -4,7 +4,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import Chance from "chance";
// Configurations
import { successColor, footerText, footerIcon } from "@config/embed";
import {
successColor,
errorColor,
footerText,
footerIcon,
} from "@config/embed";
// Handlers
import logger from "@logger";
@ -15,14 +20,30 @@ import timeoutSchema from "@schemas/timeout";
// Helpers
import fetchUser from "@helpers/fetchUser";
import fetchGuild from "@helpers/fetchGuild";
import i18next from "i18next";
export default {
meta: { guildOnly: true, ephemeral: true },
data: (command: SlashCommandSubcommandBuilder) => {
return command.setName("work").setDescription(`Work to earn credits`);
},
execute: async (interaction: CommandInteraction) => {
// Destructure member
const { guild, user } = interaction;
const { guild, user, locale } = interaction;
const embed = new MessageEmbed()
.setTitle(
i18next.t("credits:modules:work:general:title", {
lng: locale,
ns: "plugins",
})
)
.setTimestamp(new Date())
.setFooter({
text: footerText,
iconURL: footerIcon,
});
// Chance module
const chance = new Chance();
@ -46,14 +67,15 @@ export default {
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Work)")
embed
.setDescription(
`You can not work while on timeout, please wait ${guildDB?.credits.workTimeout} seconds.`
i18next.t("credits:modules:work:error01:description", {
lng: locale,
ns: "plugins",
time: guildDB?.credits.workTimeout,
})
)
.setTimestamp(new Date())
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
.setColor(errorColor),
],
});
}
@ -78,12 +100,16 @@ export default {
return interaction.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:dollar:] Credits (Work)")
.setDescription(`You worked and earned ${creditsEarned} credits`)
.setTimestamp(new Date())
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
embed
.setDescription(
i18next.t("credits:modules:work:success01:description", {
lng: locale,
ns: "plugins",
time: guildDB?.credits.workTimeout,
amount: creditsEarned,
})
)
.setColor(successColor),
],
});
});

27
src/plugins/fun/index.ts Normal file
View file

@ -0,0 +1,27 @@
import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
import logger from "@logger";
import modules from "@plugins/fun/modules";
export default {
modules,
data: new SlashCommandBuilder()
.setName("fun")
.setDescription("Fun commands.")
.addSubcommand(modules.meme.data),
async execute(interaction: CommandInteraction) {
const { options } = interaction;
switch (options.getSubcommand()) {
case "meme":
await modules.meme.execute(interaction);
break;
default:
logger.verbose(`Unknown subcommand ${options.getSubcommand()}`);
}
},
};

View file

@ -0,0 +1,5 @@
import meme from "@plugins/fun/modules/meme";
export default {
meme,
};

View file

@ -0,0 +1,37 @@
import { successColor, footerText, footerIcon } from "@config/embed";
import axios from "axios";
import { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import logger from "@logger";
export default {
meta: { guildOnly: false, ephemeral: false },
data: (command: SlashCommandSubcommandBuilder) => {
return command.setName("meme").setDescription("Get a meme from r/memes)");
},
execute: async (interaction: CommandInteraction) => {
await axios
.get("https://www.reddit.com/r/memes/random/.json")
.then(async (res) => {
const response = res.data[0].data.children;
const content = response[0].data;
const embed = new MessageEmbed()
.setTitle(content.title)
.setTimestamp(new Date())
.setImage(content.url)
.setFooter({
text: `👍 ${content.ups}︱👎 ${content.downs}\n${footerText}`,
iconURL: footerIcon,
})
.setColor(successColor);
return interaction.editReply({ embeds: [embed] });
})
.catch((error) => {
logger.error(`${error}`);
});
},
};

View file

@ -5,31 +5,33 @@ import { CommandInteraction } from "discord.js";
import logger from "@logger";
// Modules
import moduleCreate from "./modules/create";
import moduleDelete from "./modules/delete";
import modules from "./modules";
// Function
export default {
modules,
data: (group: SlashCommandSubcommandGroupBuilder) => {
return group
.setName("counters")
.setDescription("Manage guild counters.")
.addSubcommand(moduleCreate.data)
.addSubcommand(moduleDelete.data);
.addSubcommand(modules.add.data)
.addSubcommand(modules.remove.data);
},
execute: async (interaction: CommandInteraction) => {
const { options } = interaction;
if (options?.getSubcommand() === "create") {
if (options?.getSubcommand() === "add") {
logger?.verbose(`Executing create subcommand`);
return moduleCreate.execute(interaction);
return modules.add.execute(interaction);
}
if (options?.getSubcommand() === "delete") {
if (options?.getSubcommand() === "remove") {
logger?.verbose(`Executing delete subcommand`);
return moduleDelete.execute(interaction);
return modules.remove.execute(interaction);
}
logger?.verbose(`Unknown subcommand ${options?.getSubcommand()}`);

View file

@ -1,5 +1,5 @@
// Dependencies
import { MessageEmbed, CommandInteraction } from "discord.js";
import { MessageEmbed, CommandInteraction, Permissions } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { ChannelType } from "discord-api-types/v10";
@ -16,19 +16,26 @@ import logger from "@logger";
// Models
import counterSchema from "@schemas/counter";
import i18next from "i18next";
// Function
export default {
meta: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("create")
.setName("add")
.setDescription("Add a counter to your guild.")
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("The channel to send the counter to.")
.setRequired(true)
.addChannelType(ChannelType.GuildText as number)
.addChannelTypes(ChannelType.GuildText)
)
.addStringOption((option) =>
option
@ -43,12 +50,22 @@ export default {
);
},
execute: async (interaction: CommandInteraction) => {
const { options, guild } = interaction;
const { options, guild, locale } = interaction;
const discordChannel = options?.getChannel("channel");
const countingWord = options?.getString("word");
const startValue = options?.getNumber("start");
const embed = new MessageEmbed()
.setTitle(
i18next.t("manage:groups:counters:modules:add:general:title", {
lng: locale,
ns: "plugins",
})
)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
const counter = await counterSchema?.findOne({
guildId: guild?.id,
channelId: discordChannel?.id,
@ -57,12 +74,18 @@ export default {
if (counter) {
return interaction?.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:toolbox:] Manage - Counters (Create)")
.setDescription(`A counter already exists for this channel.`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
embed
.setDescription(
i18next.t(
"manage:groups:counters:modules:add:error01:description",
{
lng: locale,
ns: "plugins",
channel: discordChannel,
}
)
)
.setColor(errorColor),
],
});
}
@ -79,12 +102,18 @@ export default {
return interaction?.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:toolbox:] Manage - Counters (Create)")
.setDescription(`Created counter for ${discordChannel}`)
.setTimestamp(new Date())
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
embed
.setDescription(
i18next.t(
"manage:groups:counters:modules:create:success01:description",
{
lng: locale,
ns: "plugins",
channel: discordChannel,
}
)
)
.setColor(successColor),
],
});
});

View file

@ -0,0 +1,4 @@
import add from "@plugins/manage/groups/counters/modules/add";
import remove from "@plugins/manage/groups/counters/modules/remove";
export default { add, remove };

View file

@ -1,5 +1,5 @@
// Dependencies
import { CommandInteraction, MessageEmbed } from "discord.js";
import { CommandInteraction, MessageEmbed, Permissions } from "discord.js";
// Configurations
import {
@ -16,26 +16,43 @@ import logger from "@logger";
import counterSchema from "@schemas/counter";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { ChannelType } from "discord-api-types/v10";
import i18next from "i18next";
// Function
export default {
meta: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("delete")
.setName("remove")
.setDescription(`Delete a counter from your guild.`)
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("The channel to delete the counter from.")
.setRequired(true)
.addChannelType(ChannelType.GuildText as number)
.addChannelTypes(ChannelType.GuildText)
);
},
execute: async (interaction: CommandInteraction) => {
const { options, guild } = interaction;
const { options, guild, locale } = interaction;
const discordChannel = options?.getChannel("channel");
const embed = new MessageEmbed()
.setTitle(
i18next.t("manage:groups:counters:modules:remove:general:title", {
lng: locale,
ns: "plugins",
})
)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
const counter = await counterSchema?.findOne({
guildId: guild?.id,
channelId: discordChannel?.id,
@ -46,12 +63,17 @@ export default {
return interaction?.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:toolbox:] Manage - Counters (Delete)")
.setDescription(`The counter for this channel does not exist.`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
embed
.setDescription(
i18next.t(
"manage:groups:counters:modules:remove:error01:description",
{
lng: locale,
ns: "plugins",
}
)
)
.setColor(errorColor),
],
});
}
@ -66,12 +88,17 @@ export default {
return interaction?.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:toolbox:] Manage - Counters (Delete)")
.setDescription(`The counter for this channel has been deleted.`)
.setTimestamp(new Date())
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
embed
.setDescription(
i18next.t(
"manage:groups:counters:modules:remove:success01:description",
{
lng: locale,
ns: "plugins",
}
)
)
.setColor(successColor),
],
});
})

View file

@ -1,53 +1,43 @@
// Dependencies
import { CommandInteraction } from "discord.js";
import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders";
import logger from "@logger";
// Modules
import moduleGive from "./modules/give";
import moduleSet from "./modules/set";
import moduleTake from "./modules/take";
import moduleTransfer from "./modules/transfer";
import modules from "./modules";
// Function
export default {
modules,
data: (group: SlashCommandSubcommandGroupBuilder) => {
return group
.setName("credits")
.setDescription("Manage the credits of a user.")
.addSubcommand(moduleGive.data)
.addSubcommand(moduleSet.data)
.addSubcommand(moduleTake.data)
.addSubcommand(moduleTransfer.data);
.addSubcommand(modules.give.data)
.addSubcommand(modules.set.data)
.addSubcommand(modules.take.data)
.addSubcommand(modules.transfer.data);
},
execute: async (interaction: CommandInteraction) => {
const { options } = interaction;
if (options?.getSubcommand() === "give") {
logger?.verbose(`Executing give subcommand`);
switch (options.getSubcommand()) {
case "give":
logger.verbose(`Executing give subcommand`);
return moduleGive.execute(interaction);
return modules.give.execute(interaction);
case "set":
logger.verbose(`Executing set subcommand`);
return modules.set.execute(interaction);
case "take":
logger.verbose(`Executing take subcommand`);
return modules.take.execute(interaction);
case "transfer":
logger.verbose(`Executing transfer subcommand`);
return modules.transfer.execute(interaction);
default:
logger.verbose(`Unknown subcommand ${options.getSubcommand()}`);
}
if (options?.getSubcommand() === "set") {
logger?.verbose(`Executing set subcommand`);
return moduleSet.execute(interaction);
}
if (options?.getSubcommand() === "take") {
logger?.verbose(`Executing take subcommand`);
return moduleTake.execute(interaction);
}
if (options?.getSubcommand() === "transfer") {
logger?.verbose(`Executing transfer subcommand`);
return moduleTransfer.execute(interaction);
}
logger?.verbose(`No subcommand found`);
},
};

View file

@ -1,5 +1,5 @@
// Dependencies
import { CommandInteraction, MessageEmbed } from "discord.js";
import { CommandInteraction, MessageEmbed, Permissions } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Configurations
@ -21,6 +21,12 @@ import fetchUser from "@helpers/fetchUser";
// Function
export default {
meta: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("give")

View file

@ -0,0 +1,6 @@
import give from "@plugins/manage/groups/credits/modules/give";
import set from "@plugins/manage/groups/credits/modules/set";
import take from "@plugins/manage/groups/credits/modules/take";
import transfer from "@plugins/manage/groups/credits/modules/transfer";
export default { give, set, take, transfer };

View file

@ -1,5 +1,5 @@
// Dependencies
import { CommandInteraction, MessageEmbed } from "discord.js";
import { CommandInteraction, MessageEmbed, Permissions } from "discord.js";
// Configurations
import {
@ -20,6 +20,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
meta: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("set")

View file

@ -1,5 +1,5 @@
// Dependencies
import { CommandInteraction, MessageEmbed } from "discord.js";
import { CommandInteraction, MessageEmbed, Permissions } from "discord.js";
// Configurations
import {
@ -21,6 +21,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
meta: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("take")

View file

@ -1,5 +1,5 @@
// Dependencies
import { CommandInteraction, MessageEmbed } from "discord.js";
import { CommandInteraction, MessageEmbed, Permissions } from "discord.js";
// Configurations
import {
@ -21,6 +21,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
meta: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("transfer")

View file

@ -0,0 +1,4 @@
import counters from "@plugins/manage/groups/counters";
import credits from "@plugins/manage/groups/credits";
export default { counters, credits };

View file

@ -1,52 +1,35 @@
//Dependencies
import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction, Permissions, MessageEmbed } from "discord.js";
// Configurations
import { errorColor, footerText, footerIcon } from "@config/embed";
import { CommandInteraction } from "discord.js";
// Groups
import credits from "./groups/credits";
import counters from "./groups/counters";
import groups from "@plugins/manage/groups";
import logger from "@logger";
// Function
export default {
metadata: { author: "Zyner" },
groups,
data: new SlashCommandBuilder()
.setName("manage")
.setDescription("Manage the bot.")
.addSubcommandGroup(counters.data)
.addSubcommandGroup(credits.data),
.addSubcommandGroup(groups.counters.data)
.addSubcommandGroup(groups.credits.data),
async execute(interaction: CommandInteraction) {
// Destructure
const { memberPermissions, options } = interaction;
// Check permission
if (!memberPermissions?.has(Permissions?.FLAGS?.MANAGE_GUILD)) {
return interaction?.editReply({
embeds: [
new MessageEmbed()
.setTitle("[:toolbox:] Manage")
.setDescription(`You do not have the permission to manage the bot.`)
.setTimestamp(new Date())
.setColor(errorColor)
.setFooter({ text: footerText, iconURL: footerIcon }),
],
});
}
const { options } = interaction;
if (options?.getSubcommandGroup() === "credits") {
logger?.verbose(`Subcommand group is credits`);
return credits.execute(interaction);
return groups.credits.execute(interaction);
}
if (options?.getSubcommandGroup() === "counters") {
logger?.verbose(`Subcommand group is counters`);
return counters.execute(interaction);
return groups.counters.execute(interaction);
}
logger?.verbose(`Subcommand group is not credits or counters`);

View file

@ -3,34 +3,26 @@ import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
// Modules
import view from "./modules/view";
import modules from "@plugins/profile/modules";
// Handlers
import logger from "@logger";
// Function
export default {
metadata: { author: "Zyner" },
modules,
data: new SlashCommandBuilder()
.setName("profile")
.setDescription("Check a profile.")
.addSubcommand((subcommand) =>
subcommand
.setName("view")
.setDescription("View a profile.")
.addUserOption((option) =>
option
.setName("target")
.setDescription("The profile you wish to view")
)
),
.addSubcommand(modules.view.data),
async execute(interaction: CommandInteraction) {
const { options } = interaction;
if (options?.getSubcommand() === "view") {
logger?.verbose(`Executing view subcommand`);
return view(interaction);
return modules.view.execute(interaction);
}
logger?.verbose(`No subcommand found`);

View file

@ -0,0 +1,3 @@
import view from "@plugins/profile/modules/view";
export default { view };

View file

@ -8,68 +8,82 @@ import { successColor, footerText, footerIcon } from "@config/embed";
import fetchUser from "@helpers/fetchUser";
import logger from "@logger";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default async (interaction: CommandInteraction) => {
// Destructure
const { client, options, user, guild } = interaction;
export default {
meta: { guildOnly: true, ephemeral: false },
// Target information
const target = options?.getUser("target");
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("view")
.setDescription("View a profile.")
.addUserOption((option) =>
option.setName("target").setDescription("The profile you wish to view")
);
},
// Discord User Information
const discordUser = await client?.users?.fetch(
`${target ? target?.id : user?.id}`
);
execute: async (interaction: CommandInteraction) => {
// Destructure
const { client, options, user, guild } = interaction;
if (guild === null) {
return logger?.verbose(`Guild is null`);
}
// Target information
const target = options?.getUser("target");
// User Information
const userObj = await fetchUser(discordUser, guild);
// Discord User Information
const discordUser = await client?.users?.fetch(
`${target ? target?.id : user?.id}`
);
// Embed object
const embed = {
author: {
name: `${discordUser?.username}#${discordUser?.discriminator}`,
icon_url: discordUser?.displayAvatarURL(),
},
color: successColor,
fields: [
{
name: `:dollar: Credits`,
value: `${userObj?.credits || "Not found"}`,
inline: true,
if (guild === null) {
return logger?.verbose(`Guild is null`);
}
// User Information
const userObj = await fetchUser(discordUser, guild);
// Embed object
const embed = {
author: {
name: `${discordUser?.username}#${discordUser?.discriminator}`,
icon_url: discordUser?.displayAvatarURL(),
},
{
name: `:squeeze_bottle: Level`,
value: `${userObj?.level || "Not found"}`,
inline: true,
color: successColor,
fields: [
{
name: `:dollar: Credits`,
value: `${userObj?.credits || "Not found"}`,
inline: true,
},
{
name: `:squeeze_bottle: Level`,
value: `${userObj?.level || "Not found"}`,
inline: true,
},
{
name: `:squeeze_bottle: Points`,
value: `${userObj?.points || "Not found"}`,
inline: true,
},
{
name: `:loudspeaker: Reputation`,
value: `${userObj?.reputation || "Not found"}`,
inline: true,
},
{
name: `:rainbow_flag: Language`,
value: `${userObj?.language || "Not found"}`,
inline: true,
},
],
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
{
name: `:squeeze_bottle: Points`,
value: `${userObj?.points || "Not found"}`,
inline: true,
},
{
name: `:loudspeaker: Reputation`,
value: `${userObj?.reputation || "Not found"}`,
inline: true,
},
{
name: `:rainbow_flag: Language`,
value: `${userObj?.language || "Not found"}`,
inline: true,
},
],
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
};
};
// Return interaction reply
return interaction?.editReply({ embeds: [embed] });
// Return interaction reply
return interaction?.editReply({ embeds: [embed] });
},
};

View file

@ -3,25 +3,25 @@ import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
// Modules
import give from "./modules/give";
import modules from "./modules";
// Handlers
import logger from "@logger";
// Function
export default {
metadata: { author: "Zyner" },
modules,
data: new SlashCommandBuilder()
.setName("reputation")
.setDescription("Manage reputation.")
.addSubcommand(give.data),
.addSubcommand(modules.give.data),
async execute(interaction: CommandInteraction) {
const { options } = interaction;
if (options?.getSubcommand() === "give") {
logger?.verbose(`Executing give subcommand`);
await give.execute(interaction);
await modules.give.execute(interaction);
}
logger?.verbose(`No subcommand found`);

View file

@ -21,6 +21,8 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
meta: { guildOnly: true, ephemeral: true },
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("give")
@ -36,8 +38,13 @@ export default {
.setName("type")
.setDescription("What type of reputation you want to repute")
.setRequired(true)
.addChoice("Positive", "positive")
.addChoice("Negative", "negative")
.addChoices(
{ name: "Positive", value: "positive" },
{
name: "Negative",
value: "negative",
}
)
);
},
execute: async (interaction: CommandInteraction) => {

View file

@ -0,0 +1,3 @@
import give from "@plugins/reputation/modules/give";
export default { give };

View file

@ -0,0 +1,60 @@
// Dependencies
import { CommandInteraction } from "discord.js";
// Handlers
import logger from "@logger";
// Modules
import modules from "./modules";
import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders";
// Function
export default {
modules,
data: (group: SlashCommandSubcommandGroupBuilder) => {
return group
.setName("guild")
.setDescription("Guild settings.")
.addSubcommand(modules.pterodactyl.data)
.addSubcommand(modules.credits.data)
.addSubcommand(modules.points.data)
.addSubcommand(modules.welcome.data)
.addSubcommand(modules.audits.data)
.addSubcommand(modules.shop.data);
},
execute: async (interaction: CommandInteraction) => {
// Destructure member
const { options } = interaction;
switch (options?.getSubcommand()) {
case "pterodactyl":
logger?.verbose(`Subcommand is pterodactyl`);
return modules.pterodactyl.execute(interaction);
case "credits":
logger?.verbose(`Subcommand is credits`);
return modules.credits.execute(interaction);
case "points":
logger?.verbose(`Subcommand is points`);
return modules.points.execute(interaction);
case "welcome":
logger?.verbose(`Subcommand is welcome`);
return modules.welcome.execute(interaction);
case "audits":
logger?.verbose(`Subcommand is audits`);
return modules.audits.execute(interaction);
case "shop":
logger?.verbose(`Subcommand is shop`);
return modules.shop.execute(interaction);
default:
logger?.verbose(`Subcommand is not found`);
}
},
};

View file

@ -1,5 +1,5 @@
// Dependencies
import { CommandInteraction } from "discord.js";
import { CommandInteraction, Permissions } from "discord.js";
// Configurations
import { successColor, footerText, footerIcon } from "@config/embed";
@ -14,6 +14,12 @@ import { ChannelType } from "discord-api-types/v10";
// Function
export default {
meta: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("audits")
@ -25,7 +31,7 @@ export default {
option
.setName("channel")
.setDescription("Channel for audit messages.")
.addChannelType(ChannelType.GuildText as number)
.addChannelTypes(ChannelType.GuildText)
);
},
execute: async (interaction: CommandInteraction) => {

View file

@ -1,5 +1,5 @@
// Dependencies
import { CommandInteraction } from "discord.js";
import { CommandInteraction, Permissions } from "discord.js";
// Configurations
import { successColor, footerText, footerIcon } from "@config/embed";
@ -13,6 +13,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
meta: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("credits")

View file

@ -0,0 +1,8 @@
import audits from "@plugins/settings/groups/guild/modules/audits";
import credits from "@plugins/settings/groups/guild/modules/credits";
import points from "@plugins/settings/groups/guild/modules/points";
import pterodactyl from "@plugins/settings/groups/guild/modules/pterodactyl";
import shop from "@plugins/settings/groups/guild/modules/shop";
import welcome from "@plugins/settings/groups/guild/modules/welcome";
export default { audits, credits, points, pterodactyl, shop, welcome };

View file

@ -1,5 +1,5 @@
// Dependencies
import { CommandInteraction } from "discord.js";
import { CommandInteraction, Permissions } from "discord.js";
// Configurations
import { successColor, footerText, footerIcon } from "@config/embed";
@ -13,6 +13,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
meta: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("points")

View file

@ -1,5 +1,5 @@
// Dependencies
import { CommandInteraction } from "discord.js";
import { CommandInteraction, Permissions } from "discord.js";
// Configurations
import { successColor, footerText, footerIcon } from "@config/embed";
@ -14,6 +14,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
meta: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("pterodactyl")

View file

@ -1,5 +1,5 @@
// Dependencies
import { CommandInteraction } from "discord.js";
import { CommandInteraction, Permissions } from "discord.js";
// Configurations
import { successColor, footerText, footerIcon } from "@config/embed";
@ -10,10 +10,15 @@ import logger from "@logger";
// Models
import guildSchema from "@schemas/guild";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { ChannelType } from "discord-api-types/v10";
// Function
export default {
meta: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("shop")

View file

@ -1,5 +1,5 @@
// Dependencies
import { CommandInteraction } from "discord.js";
import { CommandInteraction, Permissions } from "discord.js";
// Configurations
import { successColor, footerText, footerIcon } from "@config/embed";
@ -14,6 +14,12 @@ import { ChannelType } from "discord-api-types/v10";
// Function
export default {
meta: {
guildOnly: true,
ephemeral: true,
permissions: [Permissions.FLAGS.MANAGE_GUILD],
},
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("welcome")
@ -25,14 +31,16 @@ export default {
option
.setName("join-channel")
.setDescription("Channel for join messages.")
.addChannelType(ChannelType.GuildText as number)
.addChannelTypes(ChannelType.GuildText)
)
.addChannelOption((option) =>
option
.setName("leave-channel")
.setDescription("Channel for leave messages.")
.addChannelType(ChannelType.GuildText as number)
.addChannelTypes(ChannelType.GuildText)
)
.addStringOption((option) =>
option
.setName("leave-message")

View file

@ -0,0 +1,3 @@
import guild from "@plugins/settings/groups/guild";
export default { guild };

View file

@ -1,94 +0,0 @@
// Dependencies
import { Permissions, CommandInteraction } from "discord.js";
// Configurations
import { errorColor, footerText, footerIcon } from "@config/embed";
// Handlers
import logger from "@logger";
// Modules
import pterodactyl from "./modules/pterodactyl";
import credits from "./modules/credits";
import points from "./modules/points";
import welcome from "./modules/welcome";
import audits from "./modules/audits";
import shop from "./modules/shop";
import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders";
// Function
export default {
data: (group: SlashCommandSubcommandGroupBuilder) => {
return group
.setName("guild")
.setDescription("Guild settings.")
.addSubcommand(pterodactyl.data)
.addSubcommand(credits.data)
.addSubcommand(points.data)
.addSubcommand(welcome.data)
.addSubcommand(audits.data)
.addSubcommand(shop.data);
},
execute: async (interaction: CommandInteraction) => {
// Destructure member
const { memberPermissions, options } = interaction;
// Check permission
if (!memberPermissions?.has(Permissions?.FLAGS?.MANAGE_GUILD)) {
logger?.verbose(`User does not have permission to execute command.`);
return interaction?.editReply({
embeds: [
{
title: ":tools: Settings - Guild",
color: errorColor,
description: "You do not have permission to use this command.",
timestamp: new Date(),
footer: {
iconURL: footerIcon as string,
text: footerText as string,
},
},
],
});
}
if (options?.getSubcommand() === "pterodactyl") {
logger?.verbose(`Executing pterodactyl subcommand`);
return pterodactyl.execute(interaction);
}
if (options?.getSubcommand() === "credits") {
logger?.verbose(`Executing credits subcommand`);
return credits.execute(interaction);
}
if (options?.getSubcommand() === "points") {
logger?.verbose(`Executing points subcommand`);
return points.execute(interaction);
}
if (options?.getSubcommand() === "welcome") {
logger?.verbose(`Executing welcome subcommand`);
return welcome.execute(interaction);
}
if (options?.getSubcommand() === "audits") {
logger?.verbose(`Executing audit subcommand`);
return audits.execute(interaction);
}
if (options?.getSubcommand() === "shop") {
logger?.verbose(`Executing shop subcommand`);
return shop.execute(interaction);
}
logger?.verbose(`No subcommand found`);
},
};

View file

@ -3,20 +3,20 @@ import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
// Groups
import guildGroup from "./guild";
import userGroup from "./user";
import groups from "./groups";
// Handlers
import logger from "@logger";
// Function
export default {
metadata: { author: "Zyner" },
groups,
data: new SlashCommandBuilder()
.setName("settings")
.setDescription("Manage settings.")
.addSubcommandGroup(guildGroup.data)
.addSubcommandGroup(userGroup.data),
.addSubcommandGroup(groups.guild.data),
async execute(interaction: CommandInteraction) {
const { options } = interaction;
@ -24,13 +24,7 @@ export default {
if (options.getSubcommandGroup() === "guild") {
logger.verbose(`Executing guild subcommand`);
return guildGroup.execute(interaction);
}
if (options.getSubcommandGroup() === "user") {
logger.verbose(`Executing user subcommand`);
return userGroup.execute(interaction);
return groups.guild.execute(interaction);
}
logger.verbose(`No subcommand group found`);

View file

@ -1,41 +0,0 @@
// Dependencies
import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
// Handlers
import logger from "@logger";
// Modules
import appearance from "./modules/appearance";
// Function
export default {
data: (group: SlashCommandSubcommandGroupBuilder) => {
return group
.setName("user")
.setDescription("User settings.")
.addSubcommand((command) =>
command
.setName("appearance")
.setDescription("User appearance settings.")
.addStringOption((option) =>
option
.setName("language")
.setDescription("Set the language.")
.addChoice("English", "en")
.addChoice("Swedish", "sv")
)
);
},
execute: async (interaction: CommandInteraction) => {
const { options } = interaction;
if (options?.getSubcommand() === "appearance") {
logger?.verbose(`Executing appearance subcommand`);
await appearance(interaction);
}
logger?.verbose(`No subcommand found`);
},
};

View file

@ -1,61 +0,0 @@
// Dependencies
import { CommandInteraction } from "discord.js";
// Configurations
import { successColor, footerText, footerIcon } from "@config/embed";
// Handlers
import logger from "@logger";
// Models
import fetchUser from "@helpers/fetchUser";
// Function
export default async (interaction: CommandInteraction) => {
// Destructure member
const { options, user, guild } = interaction;
// Get options
const language = options?.getString("language");
if (guild === null) {
return logger?.verbose(`Guild is null`);
}
// Get user object
const userDB = await fetchUser(user, guild);
if (userDB === null) {
return logger?.verbose(`User is null`);
}
// Modify values
userDB.language = language !== null ? language : userDB?.language;
// Save guild
await userDB?.save()?.then(async () => {
logger?.verbose(`Updated user language.`);
return interaction?.editReply({
embeds: [
{
title: ":hammer: Settings - User [Appearance]",
description: "Successfully updated user settings.",
color: successColor,
fields: [
{
name: "🏳️‍🌈 Language",
value: `${userDB?.language}`,
inline: true,
},
],
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
},
],
});
});
};

View file

@ -0,0 +1,3 @@
import roles from "./roles";
export default { roles };

View file

@ -3,24 +3,25 @@ import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
// Handlers
import logger from "../../../logger";
import logger from "@logger";
import { errorColor, footerText, footerIcon } from "@config/embed";
// Modules
import buy from "./modules/buy";
import cancel from "./modules/cancel";
import modules from "./modules";
import guildSchema from "@schemas/guild";
// Function
export default {
modules,
data: (group: SlashCommandSubcommandGroupBuilder) => {
return group
.setName("roles")
.setDescription("Shop for custom roles.")
.addSubcommand(buy.data)
.addSubcommand(cancel.data);
.addSubcommand(modules.buy.data)
.addSubcommand(modules.cancel.data);
},
execute: async (interaction: CommandInteraction) => {
const { options, guild } = interaction;
@ -53,13 +54,13 @@ export default {
if (options?.getSubcommand() === "buy") {
logger.verbose(`Executing buy subcommand`);
await buy.execute(interaction);
await modules.buy.execute(interaction);
}
if (options?.getSubcommand() === "cancel") {
logger.verbose(`Executing cancel subcommand`);
await cancel.execute(interaction);
await modules.cancel.execute(interaction);
}
},
};

View file

@ -25,6 +25,8 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
meta: { guildOnly: true, ephemeral: true },
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("buy")
@ -33,11 +35,13 @@ export default {
option
.setName("name")
.setDescription("Name of the role you wish to buy.")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("color")
.setDescription("Color of the role you wish to buy.")
.setRequired(true)
);
},
execute: async (interaction: CommandInteraction) => {

View file

@ -20,12 +20,17 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
meta: { guildOnly: true, ephemeral: true },
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("cancel")
.setDescription("Cancel a purchase.")
.addRoleOption((option) =>
option.setName("role").setDescription("Role you wish to cancel.")
option
.setName("role")
.setDescription("Role you wish to cancel.")
.setRequired(true)
);
},
execute: async (interaction: CommandInteraction) => {

View file

@ -0,0 +1,7 @@
import buy from "./buy";
import cancel from "./cancel";
export default {
buy,
cancel,
};

View file

@ -3,35 +3,37 @@ import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
// Modules
import pterodactyl from "./modules/pterodactyl";
import modules from "./modules";
// Groups
import roles from "./roles";
import groups from "./groups";
// Handlers
import logger from "../../logger";
// Function
export default {
metadata: { author: "Zyner" },
modules,
groups,
data: new SlashCommandBuilder()
.setName("shop")
.setDescription("Shop for credits and custom roles.")
.addSubcommand(pterodactyl.data)
.addSubcommandGroup(roles.data),
.addSubcommand(modules.pterodactyl.data)
.addSubcommandGroup(groups.roles.data),
async execute(interaction: CommandInteraction) {
const { options } = interaction;
if (options?.getSubcommand() === "pterodactyl") {
logger.verbose(`Executing pterodactyl subcommand`);
return pterodactyl.execute(interaction);
return modules.pterodactyl.execute(interaction);
}
if (options?.getSubcommandGroup() === "roles") {
logger?.verbose(`Subcommand group is roles`);
return roles.execute(interaction);
return groups.roles.execute(interaction);
}
logger?.verbose(`No subcommand found.`);

View file

@ -0,0 +1,3 @@
import pterodactyl from "@plugins/shop/modules/pterodactyl";
export default { pterodactyl };

View file

@ -1,9 +1,7 @@
// Dependencies
import { CommandInteraction } from "discord.js";
import { v4 as uuidv4 } from "uuid";
import axios from "axios";
// Configurations
import {
successColor,
errorColor,
@ -11,20 +9,18 @@ import {
footerIcon,
} from "@config/embed";
// Handlers
import logger from "@logger";
import encryption from "@handlers/encryption";
// Helpers
import pluralize from "@helpers/pluralize";
// Models
import apiSchema from "@schemas/api";
import fetchUser from "@helpers/fetchUser";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
meta: { guildOnly: true, ephemeral: true },
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("pterodactyl")
@ -38,10 +34,8 @@ export default {
execute: async (interaction: CommandInteraction) => {
const { options, guild, user, client } = interaction;
// Get options
const optionAmount = options?.getInteger("amount");
// If amount is null
if (optionAmount === null) {
logger?.verbose(`Amount is null.`);
@ -65,17 +59,14 @@ export default {
return logger?.verbose(`Guild is null`);
}
// Get user object
const userDB = await fetchUser(user, guild);
if (userDB === null) {
return logger?.verbose(`User is null`);
}
// Get DM user object
const dmUser = client?.users?.cache?.get(user?.id);
// Stop if amount or user credits is below 100
if ((optionAmount || userDB?.credits) < 100) {
logger?.verbose(`Amount or user credits is below 100.`);
@ -101,7 +92,6 @@ export default {
});
}
// Stop if amount or user credits is above 1.000.000
if ((optionAmount || userDB?.credits) > 1000000) {
logger?.verbose(`Amount or user credits is above 1.000.000.`);
@ -128,7 +118,6 @@ export default {
});
}
// Stop if user credits is below amount
if (userDB?.credits < optionAmount) {
logger?.verbose(`User credits is below amount.`);
@ -154,15 +143,12 @@ export default {
});
}
// Generate a unique voucher for the user
const code = uuidv4();
// Get api object
const apiCredentials = await apiSchema?.findOne({
guildId: guild?.id,
});
// Create a api instance
const api = axios?.create({
baseURL: apiCredentials?.url,
headers: {
@ -170,13 +156,10 @@ export default {
},
});
// Get shop URL
const shopUrl = apiCredentials?.url?.replace("/api", "/store");
// Make API request
await api
// Make a post request to the API
?.post("vouchers", {
uses: 1,
code,
@ -184,17 +167,14 @@ export default {
memo: `${interaction?.createdTimestamp} - ${interaction?.user?.id}`,
})
// If successful
?.then(async () => {
logger?.verbose(`Successfully created voucher.`);
// Withdraw amount from user credits
userDB.credits -= optionAmount || userDB?.credits;
// Save new credits
await userDB
?.save()
// If successful
?.then(async () => {
logger?.verbose(`Successfully saved new credits.`);
@ -237,7 +217,6 @@ export default {
});
})
// If error occurs
.catch(async (error) => {
logger?.verbose(`Error saving new credits. - ${error}`);
@ -258,7 +237,6 @@ export default {
});
})
// If error occurs
.catch(async (error: any) => {
logger?.verbose(`Error creating voucher. - ${error}`);

View file

@ -1,45 +0,0 @@
// Dependencies
import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
// Modules
import lookup from "./modules/lookup";
import about from "./modules/about";
import stats from "./modules/stats";
// Handlers
import logger from "../../logger";
// Function
export default {
metadata: { author: "Zyner" },
data: new SlashCommandBuilder()
.setName("utilities")
.setDescription("Common utilities.")
.addSubcommand(lookup.data)
.addSubcommand(about.data)
.addSubcommand(stats.data),
async execute(interaction: CommandInteraction) {
const { options } = interaction;
if (options?.getSubcommand() === "lookup") {
logger.verbose(`Executing lookup subcommand`);
return lookup.execute(interaction);
}
if (options?.getSubcommand() === "about") {
logger.verbose(`Executing about subcommand`);
return about.execute(interaction);
}
if (options?.getSubcommand() === "stats") {
logger.verbose(`Executing stats subcommand`);
return stats.execute(interaction);
}
logger.verbose(`No subcommand found.`);
},
};

View file

@ -1,134 +0,0 @@
// Dependencies
import axios from "axios";
import { CommandInteraction } from "discord.js";
// Configurations
import {
successColor,
errorColor,
footerText,
footerIcon,
} from "@config/embed";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Handlers
import logger from "@logger";
// Function
export default {
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("lookup")
.setDescription(
"Lookup a domain or ip. (Request sent over HTTP, proceed with caution!)"
)
.addStringOption((option) =>
option
.setName("query")
.setDescription("The query you want to look up.")
.setRequired(true)
);
},
execute: async (interaction: CommandInteraction) => {
const { options } = interaction;
// Get lookup query
const query = options?.getString("query");
// Make API request
await axios
// Make a get request
?.get(`http://ip-api.com/json/${query}`)
// If successful
?.then(async (res) => {
// If query failed
if (res?.data?.status === "fail") {
// Create embed object
const embed = {
title: ":hammer: Utilities - Lookup",
description: `${res?.data?.message}: ${res?.data?.query}`,
color: errorColor,
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
};
// Send interaction reply
await interaction?.editReply({ embeds: [embed] });
}
// If query is successful
else if (res?.data?.status === "success") {
// Create embed object
const embed = {
title: ":hammer: Utilities - Lookup",
fields: [
{
name: "AS",
value: `${res?.data?.as || "Not available"}`,
},
{
name: "Country",
value: `${res?.data?.country || "Not available"}`,
},
{
name: "Country Code",
value: `${res?.data?.countryCode || "Not available"}`,
},
{
name: "Region",
value: `${res?.data?.region || "Not available"}`,
},
{
name: "Region Name",
value: `${res?.data?.regionName || "Not available"}`,
},
{
name: "City",
value: `${res?.data?.city || "Not available"}`,
},
{
name: "ZIP Code",
value: `${res?.data?.zip || "Not available"}`,
},
{
name: "Latitude",
value: `${res?.data?.lat || "Not available"}`,
},
{
name: "Longitude",
value: `${res?.data?.lon || "Not available"}`,
},
{
name: "Timezone",
value: `${res?.data?.timezone || "Not available"}`,
},
{
name: "ISP",
value: `${res?.data?.isp || "Not available"}`,
},
{
name: "Organization",
value: `${res?.data?.org || "Not available"}`,
},
],
color: successColor,
timestamp: new Date(),
footer: {
iconURL: footerIcon,
text: footerText,
},
};
// Send interaction reply
await interaction?.editReply({ embeds: [embed] });
}
})
.catch(async (e) => {
logger?.error(e);
});
},
};

View file

@ -0,0 +1,40 @@
// Dependencies
import { SlashCommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
// Modules
import modules from "@plugins/utility/modules";
// Handlers
import logger from "../../logger";
// Function
export default {
modules,
data: new SlashCommandBuilder()
.setName("utility")
.setDescription("Common utility.")
.addSubcommand(modules.lookup.data)
.addSubcommand(modules.about.data)
.addSubcommand(modules.stats.data)
.addSubcommand(modules.avatar.data),
async execute(interaction: CommandInteraction) {
const { options } = interaction;
switch (options.getSubcommand()) {
case "lookup":
return modules.lookup.execute(interaction);
case "about":
return modules.about.execute(interaction);
case "stats":
return modules.stats.execute(interaction);
case "avatar":
return modules.avatar.execute(interaction);
default:
logger.error(`Unknown subcommand ${options.getSubcommand()}`);
}
},
};

View file

@ -9,6 +9,8 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
// Function
export default {
meta: { guildOnly: false, ephemeral: false },
data: (command: SlashCommandSubcommandBuilder) => {
return command.setName("about").setDescription("About this bot!)");
},

View file

@ -0,0 +1,52 @@
import { successColor, footerText, footerIcon } from "@config/embed";
import i18next from "i18next";
import { CommandInteraction, MessageEmbed } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
export default {
meta: { guildOnly: false, ephemeral: false },
data: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("avatar")
.setDescription("Check someones avatar!)")
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user whose avatar you want to check")
);
},
execute: async (interaction: CommandInteraction) => {
const { locale } = interaction;
const userOption = interaction.options.getUser("user");
const targetUser = userOption || interaction.user;
const embed = new MessageEmbed()
.setTitle(
i18next.t("utility:modules:avatar:general:title", {
lng: locale,
ns: "plugins",
})
)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
return interaction.editReply({
embeds: [
embed
.setDescription(
i18next.t("utility:modules:avatar:success01:description", {
lng: locale,
ns: "plugins",
user: targetUser,
})
)
.setThumbnail(targetUser.displayAvatarURL())
.setColor(successColor),
],
});
},
};

View file

@ -0,0 +1,11 @@
import avatar from "@plugins/utility/modules/avatar";
import about from "@plugins/utility/modules/about";
import lookup from "@plugins/utility/modules/lookup";
import stats from "@plugins/utility/modules/stats";
export default {
avatar,
about,
lookup,
stats,
};

View file

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

View file

@ -2,6 +2,8 @@ import { successColor, footerText, footerIcon } from "@config/embed";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { CommandInteraction } from "discord.js";
export default {
meta: { guildOnly: false, ephemeral: false },
data: (command: SlashCommandSubcommandBuilder) => {
return command.setName("stats").setDescription("Check bot statistics!)");
},

View file

@ -55,13 +55,25 @@ export default async (client: Client) => {
const rRole = rMember.roles.cache.get(roleId);
if (!rRole) {
logger.error(`Role ${roleId} not found for shop role ${roleId}.`);
return;
}
if (!rMember) {
if (!rMember || !rRole) {
logger.error(`Member ${userId} not found for shop role ${roleId}.`);
await shopRoleSchema
.deleteOne({
userId,
roleId,
guildId,
})
.then(async () => {
logger.verbose(
`Shop role document ${roleId} has been deleted from user ${userId}.`
);
})
.catch(async (error) => {
logger.error(
`Error deleting shop role document ${roleId} from user ${userId}.`,
error
);
});
return;
}
@ -79,23 +91,6 @@ export default async (client: Client) => {
if (!rMember) {
logger.error(`Member ${userId} not found for shop role ${roleId}.`);
await shopRoleSchema
.deleteOne({
userId,
roleId,
guildId,
})
.then(async () => {
logger.verbose(
`Shop role document ${roleId} has been deleted from user ${userId}.`
);
})
.catch(async (error) => {
logger.error(
`Error deleting shop role document ${roleId} from user ${userId}.`,
error
);
});
return;
}

View file

@ -12,6 +12,7 @@
"moduleResolution": "node",
"isolatedModules": true,
"outDir": "./build",
"resolveJsonModule": true,
"baseUrl": "./src",
"typeRoots": ["/types/common", "./node_modules/@types"],
"paths": {