Merge pull request #456 from VermiumSifell/dev-2022.11.01

♻️ Credits module
This commit is contained in:
Axel Olausson Holtenäs 2022-10-22 16:12:43 +02:00 committed by GitHub
commit 506737cff9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 319 additions and 358 deletions

View file

@ -1,42 +1,42 @@
import { SlashCommandBuilder } from "@discordjs/builders"; import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
import { ChatInputCommandInteraction } from "discord.js";
import logger from "../../middlewares/logger";
// Modules // Modules
import moduleBalance from "./modules/balance"; import {
import moduleGift from "./modules/gift"; builder as BalanceBuilder,
import moduleTop from "./modules/top"; execute as BalanceExecute,
import moduleWork from "./modules/work"; } from "./modules/balance";
import { builder as GiftBuilder, execute as GiftExecute } from "./modules/gift";
import { builder as TopBuilder, execute as TopExecute } from "./modules/top";
import { builder as WorkBuilder, execute as WorkExecute } from "./modules/work";
// 1. Export a builder function.
export const builder = new SlashCommandBuilder() export const builder = new SlashCommandBuilder()
.setName("credits") .setName("credits")
.setDescription("Manage your credits.") .setDescription("Manage your credits.")
.setDMPermission(false) .setDMPermission(false)
// Modules // Modules
.addSubcommand(moduleBalance.builder) .addSubcommand(BalanceBuilder)
.addSubcommand(moduleGift.builder) .addSubcommand(GiftBuilder)
.addSubcommand(moduleTop.builder) .addSubcommand(TopBuilder)
.addSubcommand(moduleWork.builder); .addSubcommand(WorkBuilder);
// Execute function // 2. Export an execute function.
export const execute = async (interaction: ChatInputCommandInteraction) => { export const execute = async (interaction: ChatInputCommandInteraction) => {
const { options } = interaction; switch (interaction.options.getSubcommand()) {
switch (options.getSubcommand()) {
case "balance": case "balance":
await moduleBalance.execute(interaction); await BalanceExecute(interaction);
break; break;
case "gift": case "gift":
await moduleGift.execute(interaction); await GiftExecute(interaction);
break; break;
case "top": case "top":
await moduleTop.execute(interaction); await TopExecute(interaction);
break; break;
case "work": case "work":
await moduleWork.execute(interaction); await WorkExecute(interaction);
break; break;
default: default:
logger.silly(`Unknown subcommand ${options.getSubcommand()}`); throw new Error("Subcommand not found");
} }
}; };

View file

@ -1,95 +1,86 @@
import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { CommandInteraction, SlashCommandSubcommandBuilder } from "discord.js";
import { CommandInteraction, EmbedBuilder } from "discord.js";
import prisma from "../../../../handlers/database"; import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply"; import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData"; import { success as BaseEmbedSuccess } from "../../../../helpers/baseEmbeds";
import logger from "../../../../middlewares/logger"; import logger from "../../../../middlewares/logger";
export default { // 1. Export a builder function.
builder: (command: SlashCommandSubcommandBuilder) => { export const builder = (command: SlashCommandSubcommandBuilder) => {
return command return command
.setName("balance") .setName("balance")
.setDescription(`View a user's balance`) .setDescription(`View a user's balance`)
.addUserOption((option) => .addUserOption((option) =>
option option
.setName("user") .setName("target")
.setDescription(`The user whose balance you want to view`) .setDescription(`The user whose balance you want to view`)
); );
}, };
execute: async (interaction: CommandInteraction) => {
await deferReply(interaction, true); // 2. Export an execute function.
export const execute = async (interaction: CommandInteraction) => {
const { errorColor, successColor, footerText, footerIcon } = // 1. Defer reply as ephemeral.
await getEmbedConfig(interaction.guild); await deferReply(interaction, true);
const { options, user, guild } = interaction;
// 2. Destructure interaction object.
const discordUser = options.getUser("user"); const { options, user, guild } = interaction;
if (!guild) throw new Error("Guild not found");
const embed = new EmbedBuilder() if (!user) throw new Error("User not found");
.setTitle("[:dollar:] Balance") if (!options) throw new Error("Options not found");
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon }); // 3. Get options from interaction.
const target = options.getUser("target");
if (guild === null) {
logger.silly(`Guild is null`); // 4. Create base embeds.
const EmbedSuccess = await BaseEmbedSuccess(guild, "[:dollar:] Balance");
return interaction.editReply({
embeds: [ // 5. Upsert the user in the database.
embed.setDescription("Guild is not found").setColor(errorColor), const createGuildMember = await prisma.guildMember.upsert({
], where: {
}); userId_guildId: {
} userId: (target || user).id,
guildId: guild.id,
const createGuildMember = await prisma.guildMember.upsert({ },
where: { },
userId_guildId: { update: {},
userId: (discordUser || user).id, create: {
guildId: guild.id, user: {
}, connectOrCreate: {
}, create: {
update: {}, id: (target || user).id,
create: { },
user: { where: {
connectOrCreate: { id: (target || user).id,
create: { },
id: (discordUser || user).id, },
}, },
where: { guild: {
id: (discordUser || user).id, connectOrCreate: {
}, create: {
}, id: guild.id,
}, },
guild: { where: {
connectOrCreate: { id: guild.id,
create: { },
id: guild.id, },
}, },
where: { },
id: guild.id, include: {
}, user: true,
}, guild: true,
}, },
}, });
include: { logger.silly(createGuildMember);
user: true, if (!createGuildMember) throw new Error("No guild member exists.");
guild: true,
}, // 6. Send embed.
}); await interaction.editReply({
embeds: [
logger.silly(createGuildMember); EmbedSuccess.setDescription(
target
if (!createGuildMember) throw new Error("No guild member exists."); ? `${target} has ${createGuildMember.creditsEarned} credits.`
: `You have ${createGuildMember.creditsEarned} credits.`
return interaction.editReply({ ),
embeds: [ ],
embed });
.setDescription(
`${discordUser || user} currently has ${
createGuildMember.creditsEarned
} credits.`
)
.setColor(successColor),
],
});
},
}; };

View file

@ -1,89 +1,79 @@
// Dependencies
// Models
import { import {
ChatInputCommandInteraction, ChatInputCommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder, SlashCommandSubcommandBuilder,
} from "discord.js"; } from "discord.js";
import transferCredits from "../../../../helpers/transferCredits";
// Configurations
import deferReply from "../../../../handlers/deferReply"; import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData"; import { success as BaseEmbedSuccess } from "../../../../helpers/baseEmbeds";
// Handlers import transferCredits from "../../../../helpers/transferCredits";
// Function // 1. Export a builder function.
export default { export const builder = (command: SlashCommandSubcommandBuilder) => {
builder: (command: SlashCommandSubcommandBuilder) => { return command
return command .setName("gift")
.setName("gift") .setDescription(`Gift a user credits`)
.setDescription(`Gift a user credits`) .addUserOption((option) =>
.addUserOption((option) => option
option .setName("target")
.setName("user") .setDescription("The user you want to gift credits to.")
.setDescription("The user you want to gift credits to.") .setRequired(true)
.setRequired(true) )
) .addIntegerOption((option) =>
.addIntegerOption((option) => option
option .setName("credits")
.setName("amount") .setDescription("The amount of credits you want to gift.")
.setDescription("The amount of credits you want to gift.") .setRequired(true)
.setRequired(true) )
) .addStringOption((option) =>
.addStringOption((option) => option.setName("reason").setDescription("Your reason.")
option.setName("reason").setDescription("Your reason.")
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
); );
const { options, user, guild, client } = interaction; };
const optionUser = options.getUser("user"); // 2. Export an execute function.
const optionAmount = options.getInteger("amount"); export const execute = async (interaction: ChatInputCommandInteraction) => {
const optionReason = options.getString("reason"); // 1. Defer reply as ephemeral.
await deferReply(interaction, true);
const embed = new EmbedBuilder()
.setTitle("[:dollar:] Gift") // 2. Destructure interaction object.
.setTimestamp(new Date()) const { options, user, guild, client } = interaction;
.setFooter({ text: footerText, iconURL: footerIcon }); if (!guild) throw new Error("Guild not found");
if (!user) throw new Error("User not found");
if (!guild) throw new Error("Guild not found"); if (!client) throw new Error("Client not found");
if (!optionUser) throw new Error("No receiver found"); if (!options) throw new Error("Options not found");
if (optionAmount === null) throw new Error("Amount not found");
// 3. Get options from interaction.
await transferCredits(guild, user, optionUser, optionAmount); const target = options.getUser("target");
const credits = options.getInteger("credits");
// Get DM user object const reason = options.getString("reason");
const dmUser = client.users.cache.get(optionUser.id); if (!target) throw new Error("Target user not found");
if (typeof credits !== "number")
if (!dmUser) throw new Error("User not found"); throw new Error("You need to specify a number of credits you want to gift");
// Send DM to user // 4. Create base embeds.
await dmUser.send({ const EmbedSuccess = await BaseEmbedSuccess(guild, "[:dollar:] Gift");
embeds: [
embed // 5. Start an transaction of the credits.
.setDescription( await transferCredits(guild, user, target, credits);
`${user.tag} has gifted you ${optionAmount} credits with reason: ${
optionReason || "unspecified" // 6. Tell the target that they have been gifted credits.
}` await target.send({
) embeds: [
.setColor(successColor), EmbedSuccess.setDescription(
], reason
}); ? `You received ${credits} credits from ${user} for the reason: ${reason}.`
: `You received ${credits} credits from ${user}.`
return interaction.editReply({ ),
embeds: [ ],
embed });
.setDescription(
`Successfully gifted ${optionAmount} credits to ${optionUser} with reason: ${ // 7. Tell the sender that they have gifted the credits.
optionReason || "unspecified" await interaction.editReply({
}` embeds: [
) EmbedSuccess.setDescription(
.setColor(successColor), reason
], ? `You have successfully gifted ${credits} credits to ${target} with reason: ${reason}.`
}); : `You have successfully gifted ${credits} credits to ${target}.`
}, ),
],
});
}; };

View file

@ -1,73 +1,59 @@
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { GuildMember } from "@prisma/client"; import { GuildMember } from "@prisma/client";
import { CommandInteraction, EmbedBuilder } from "discord.js"; import {
CommandInteraction,
SlashCommandSubcommandBuilder,
userMention,
} from "discord.js";
import prisma from "../../../../handlers/database"; import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply"; import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData"; import { success as BaseEmbedSuccess } from "../../../../helpers/baseEmbeds";
import logger from "../../../../middlewares/logger"; import logger from "../../../../middlewares/logger";
export default { // 1. Export a builder function.
metadata: { guildOnly: true, ephemeral: false }, export const builder = (command: SlashCommandSubcommandBuilder) => {
return command.setName("top").setDescription(`View the top users`);
builder: (command: SlashCommandSubcommandBuilder) => { };
return command.setName("top").setDescription(`View the top users`);
}, // 2. Export an execute function.
execute: async (interaction: CommandInteraction) => { export const execute = async (interaction: CommandInteraction) => {
await deferReply(interaction, false); // 1. Defer reply as permanent.
await deferReply(interaction, false);
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild); // 2. Destructure interaction object.
const { guild } = interaction; const { guild, client } = interaction;
if (!guild) throw new Error("Guild not found");
const embed = new EmbedBuilder() if (!client) throw new Error("Client not found");
.setTitle("[:dollar:] Top")
.setTimestamp(new Date()) // 3. Create base embeds.
.setFooter({ text: footerText, iconURL: footerIcon }); const EmbedSuccess = await BaseEmbedSuccess(guild, "[:dollar:] Top");
if (guild === null) { // 4. Get the top 10 users.
logger.silly(`Guild is null`); const topTen = await prisma.guildMember.findMany({
where: {
return interaction.editReply({ guildId: guild.id,
embeds: [ },
embed orderBy: {
.setDescription( creditsEarned: "desc",
"Guild is not found. Please try again with a valid guild." },
) take: 10,
.setColor(errorColor), });
], logger.silly(topTen);
});
} // 5. Create the top 10 list.
const entry = (guildMember: GuildMember, index: number) =>
// const usersDB = await userSchema.find({ guildId: guild.id }); `${index + 1}. ${userMention(guildMember.userId)} | :coin: ${
guildMember.creditsEarned
const topTen = await prisma.guildMember.findMany({ }`;
where: {
guildId: guild.id, // 6. Send embed
}, return interaction.editReply({
orderBy: { embeds: [
creditsEarned: "desc", EmbedSuccess.setDescription(
}, `The top 10 users in this server are:\n\n${topTen
take: 10, .map(entry)
}); .join("\n")}`
),
logger.silly(topTen); ],
});
// Create entry object
const entry = (guildMember: GuildMember, index: number) =>
`${index + 1}. <@${guildMember.userId}> - ${
guildMember.creditsEarned
} credits`;
return interaction.editReply({
embeds: [
embed
.setDescription(
`Below are the top ten members in this guild.
${topTen.map(entry).join("\n")}`
)
.setColor(successColor),
],
});
},
}; };

View file

@ -1,108 +1,102 @@
// Dependencies
import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import Chance from "chance"; import Chance from "chance";
import { CommandInteraction, EmbedBuilder } from "discord.js"; import { CommandInteraction } from "discord.js";
// Models
import { command as CooldownCommand } from "../../../../handlers/cooldown"; import { command as CooldownCommand } from "../../../../handlers/cooldown";
// Configurations
import getEmbedConfig from "../../../../helpers/getEmbedData";
// Helpers
// Handlers
import prisma from "../../../../handlers/database"; import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply"; import deferReply from "../../../../handlers/deferReply";
import { success as BaseEmbedSuccess } from "../../../../helpers/baseEmbeds";
import logger from "../../../../middlewares/logger"; import logger from "../../../../middlewares/logger";
export default { // 1. Export a builder function.
builder: (command: SlashCommandSubcommandBuilder) => { export const builder = (command: SlashCommandSubcommandBuilder) => {
return command.setName("work").setDescription(`Work to earn credits`); return command.setName("work").setDescription(`Work to earn credits`);
}, };
execute: async (interaction: CommandInteraction) => {
await deferReply(interaction, true); // 2. Export an execute function.
export const execute = async (interaction: CommandInteraction) => {
const { successColor, footerText, footerIcon } = await getEmbedConfig( // 1. Defer reply as ephemeral.
interaction.guild await deferReply(interaction, true);
); // Destructure member
const { guild, user } = interaction; // 2. Destructure interaction object.
const { guild, user } = interaction;
const embed = new EmbedBuilder() if (!guild) throw new Error("Guild not found");
.setTitle("[:dollar:] Work") if (!user) throw new Error("User not found");
.setTimestamp(new Date())
.setFooter({ // 3. Create base embeds.
text: footerText, const EmbedSuccess = await BaseEmbedSuccess(guild, "[:dollar:] Work");
iconURL: footerIcon,
}); // 4. Create new Chance instance.
const chance = new Chance();
// Chance module
const chance = new Chance(); // 5. Upsert the guild in the database.
const createGuild = await prisma.guild.upsert({
if (guild === null) { where: {
return logger?.silly(`Guild is null`); id: guild.id,
} },
update: {},
const createGuild = await prisma.guild.upsert({ create: {
where: { id: guild.id,
id: guild.id, },
}, });
update: {}, logger.silly(createGuild);
create: { if (!createGuild) throw new Error("Guild not found");
id: guild.id,
}, // 6. Create a cooldown for the user.
}); await CooldownCommand(interaction, createGuild.creditsWorkTimeout);
logger.silly(createGuild); // 6. Generate a random number between 0 and creditsWorkRate.
const creditsEarned = chance.integer({
await CooldownCommand(interaction, createGuild.creditsWorkTimeout); min: 0,
max: createGuild.creditsWorkRate,
const creditsEarned = chance.integer({ });
min: 0,
max: createGuild.creditsWorkRate, // 7. Upsert the guildMember in the database.
}); const createGuildMember = await prisma.guildMember.upsert({
where: {
const createGuildMember = await prisma.guildMember.upsert({ userId_guildId: {
where: { userId: user.id,
userId_guildId: { guildId: guild.id,
userId: user.id, },
guildId: guild.id, },
}, update: { creditsEarned: { increment: creditsEarned } },
}, create: {
update: { creditsEarned: { increment: creditsEarned } }, creditsEarned,
create: { user: {
creditsEarned, connectOrCreate: {
user: { create: {
connectOrCreate: { id: user.id,
create: { },
id: user.id, where: {
}, id: user.id,
where: { },
id: user.id, },
}, },
}, guild: {
}, connectOrCreate: {
guild: { create: {
connectOrCreate: { id: guild.id,
create: { },
id: guild.id, where: {
}, id: guild.id,
where: { },
id: guild.id, },
}, },
}, },
}, include: {
}, user: true,
include: { guild: true,
user: true, },
guild: true, });
}, logger.silly(createGuildMember);
}); if (!createGuildMember) throw new Error("GuildMember not found");
logger.silly(createGuildMember); // 8. Send embed.
await interaction.editReply({
return interaction.editReply({ embeds: [
embeds: [ EmbedSuccess.setDescription(
embed `You worked and earned **${creditsEarned}** credits! You now have **${createGuildMember.creditsEarned}** credits. :tada:`
.setDescription(`You worked and earned ${creditsEarned} credits.`) ),
.setColor(successColor), ],
], });
});
},
}; };