♻️ Credits module

This commit is contained in:
Axel Olausson Holtenäs 2022-10-22 16:08:24 +02:00
parent 3c49580831
commit a9fd50f84e
No known key found for this signature in database
GPG key ID: BEDBB4D61E6C8462
5 changed files with 309 additions and 357 deletions

View file

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

View file

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

View file

@ -1,89 +1,77 @@
// Dependencies
// Models
import {
ChatInputCommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
} from "discord.js";
import transferCredits from "../../../../helpers/transferCredits";
// Configurations
import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData";
// Handlers
import { success as BaseEmbedSuccess } from "../../../../helpers/baseEmbeds";
import transferCredits from "../../../../helpers/transferCredits";
// Function
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("gift")
.setDescription(`Gift a user credits`)
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user you want to gift credits to.")
.setRequired(true)
)
.addIntegerOption((option) =>
option
.setName("amount")
.setDescription("The amount of credits you want to gift.")
.setRequired(true)
)
.addStringOption((option) =>
option.setName("reason").setDescription("Your reason.")
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("gift")
.setDescription(`Gift a user credits`)
.addUserOption((option) =>
option
.setName("target")
.setDescription("The user you want to gift credits to.")
.setRequired(true)
)
.addIntegerOption((option) =>
option
.setName("credits")
.setDescription("The amount of credits you want to gift.")
.setRequired(true)
)
.addStringOption((option) =>
option.setName("reason").setDescription("Your reason.")
);
const { options, user, guild, client } = interaction;
const optionUser = options.getUser("user");
const optionAmount = options.getInteger("amount");
const optionReason = options.getString("reason");
const embed = new EmbedBuilder()
.setTitle("[:dollar:] Gift")
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
if (!guild) throw new Error("Guild not found");
if (!optionUser) throw new Error("No receiver found");
if (optionAmount === null) throw new Error("Amount not found");
await transferCredits(guild, user, optionUser, optionAmount);
// Get DM user object
const dmUser = client.users.cache.get(optionUser.id);
if (!dmUser) throw new Error("User not found");
// Send DM to user
await dmUser.send({
embeds: [
embed
.setDescription(
`${user.tag} has gifted you ${optionAmount} credits with reason: ${
optionReason || "unspecified"
}`
)
.setColor(successColor),
],
});
return interaction.editReply({
embeds: [
embed
.setDescription(
`Successfully gifted ${optionAmount} credits to ${optionUser} with reason: ${
optionReason || "unspecified"
}`
)
.setColor(successColor),
],
});
},
};
export const execute = async (interaction: ChatInputCommandInteraction) => {
// 1. Defer reply as ephemeral.
await deferReply(interaction, true);
// 2. Destructure interaction object.
const { options, user, guild, client } = interaction;
if (!guild) throw new Error("Guild not found");
if (!user) throw new Error("User not found");
if (!client) throw new Error("Client not found");
if (!options) throw new Error("Options not found");
// 3. Get options from interaction.
const target = options.getUser("target");
const credits = options.getInteger("credits");
const reason = options.getString("reason");
if (!target) throw new Error("Target user not found");
if (typeof credits !== "number")
throw new Error("You need to specify a number of credits you want to gift");
// 4. Create base embeds.
const EmbedSuccess = await BaseEmbedSuccess(guild, "[:dollar:] Gift");
// 5. Start an transaction of the credits.
await transferCredits(guild, user, target, credits);
// 6. Tell the target that they have been gifted credits.
await target.send({
embeds: [
EmbedSuccess.setDescription(
reason
? `You received ${credits} credits from ${user} for the reason: ${reason}.`
: `You received ${credits} credits from ${user}.`
),
],
});
// 7. Tell the sender that they have gifted the credits.
await interaction.editReply({
embeds: [
EmbedSuccess.setDescription(
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,57 @@
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { GuildMember } from "@prisma/client";
import { CommandInteraction, EmbedBuilder } from "discord.js";
import {
CommandInteraction,
SlashCommandSubcommandBuilder,
userMention,
} from "discord.js";
import prisma from "../../../../handlers/database";
import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData";
import { success as BaseEmbedSuccess } from "../../../../helpers/baseEmbeds";
import logger from "../../../../middlewares/logger";
export default {
metadata: { guildOnly: true, ephemeral: false },
builder: (command: SlashCommandSubcommandBuilder) => {
return command.setName("top").setDescription(`View the top users`);
},
execute: async (interaction: CommandInteraction) => {
await deferReply(interaction, false);
const { errorColor, successColor, footerText, footerIcon } =
await getEmbedConfig(interaction.guild);
const { guild } = interaction;
const embed = new EmbedBuilder()
.setTitle("[:dollar:] Top")
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
if (guild === null) {
logger.silly(`Guild is null`);
return interaction.editReply({
embeds: [
embed
.setDescription(
"Guild is not found. Please try again with a valid guild."
)
.setColor(errorColor),
],
});
}
// const usersDB = await userSchema.find({ guildId: guild.id });
const topTen = await prisma.guildMember.findMany({
where: {
guildId: guild.id,
},
orderBy: {
creditsEarned: "desc",
},
take: 10,
});
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),
],
});
},
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command.setName("top").setDescription(`View the top users`);
};
export const execute = async (interaction: CommandInteraction) => {
// 1. Defer reply as permanent.
await deferReply(interaction, false);
// 2. Destructure interaction object.
const { guild, client } = interaction;
if (!guild) throw new Error("Guild not found");
if (!client) throw new Error("Client not found");
// 3. Create base embeds.
const EmbedSuccess = await BaseEmbedSuccess(guild, "[:dollar:] Top");
// 4. Get the top 10 users.
const topTen = await prisma.guildMember.findMany({
where: {
guildId: guild.id,
},
orderBy: {
creditsEarned: "desc",
},
take: 10,
});
logger.silly(topTen);
// 5. Create the top 10 list.
const entry = (guildMember: GuildMember, index: number) =>
`${index + 1}. ${userMention(guildMember.userId)} | :coin: ${
guildMember.creditsEarned
}`;
// 6. Send embed
return interaction.editReply({
embeds: [
EmbedSuccess.setDescription(
`The top 10 users in this server are:\n\n${topTen
.map(entry)
.join("\n")}`
),
],
});
};

View file

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