🚀 Bot is fully migrated over to Prisma

This commit is contained in:
Axel Olausson Holtenäs 2022-10-21 17:36:34 +02:00
parent 193b778910
commit 8898959c84
22 changed files with 541 additions and 516 deletions

View file

@ -0,0 +1,16 @@
-- CreateTable
CREATE TABLE "GuildShopRoles" (
"guildId" TEXT NOT NULL,
"channelId" TEXT NOT NULL,
"roleId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"pricePerHour" INTEGER NOT NULL DEFAULT 5,
"lastPayed" DATETIME NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "GuildShopRoles_guildId_fkey" FOREIGN KEY ("guildId") REFERENCES "Guild" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "GuildShopRoles_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "GuildShopRoles_guildId_channelId_key" ON "GuildShopRoles"("guildId", "channelId");

View file

@ -0,0 +1,25 @@
/*
Warnings:
- You are about to drop the column `channelId` on the `GuildShopRoles` table. All the data in the column will be lost.
*/
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_GuildShopRoles" (
"guildId" TEXT NOT NULL,
"roleId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"pricePerHour" INTEGER NOT NULL DEFAULT 5,
"lastPayed" DATETIME NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "GuildShopRoles_guildId_fkey" FOREIGN KEY ("guildId") REFERENCES "Guild" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "GuildShopRoles_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_GuildShopRoles" ("createdAt", "guildId", "lastPayed", "pricePerHour", "roleId", "updatedAt", "userId") SELECT "createdAt", "guildId", "lastPayed", "pricePerHour", "roleId", "updatedAt", "userId" FROM "GuildShopRoles";
DROP TABLE "GuildShopRoles";
ALTER TABLE "new_GuildShopRoles" RENAME TO "GuildShopRoles";
CREATE UNIQUE INDEX "GuildShopRoles_guildId_userId_roleId_key" ON "GuildShopRoles"("guildId", "userId", "roleId");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

View file

@ -0,0 +1,20 @@
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_GuildShopRoles" (
"guildId" TEXT NOT NULL,
"roleId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"pricePerHour" INTEGER NOT NULL DEFAULT 5,
"lastPayed" DATETIME NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "GuildShopRoles_guildId_fkey" FOREIGN KEY ("guildId") REFERENCES "Guild" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "GuildShopRoles_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "GuildShopRoles_guildId_userId_fkey" FOREIGN KEY ("guildId", "userId") REFERENCES "GuildMember" ("guildId", "userId") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_GuildShopRoles" ("createdAt", "guildId", "lastPayed", "pricePerHour", "roleId", "updatedAt", "userId") SELECT "createdAt", "guildId", "lastPayed", "pricePerHour", "roleId", "updatedAt", "userId" FROM "GuildShopRoles";
DROP TABLE "GuildShopRoles";
ALTER TABLE "new_GuildShopRoles" RENAME TO "GuildShopRoles";
CREATE UNIQUE INDEX "GuildShopRoles_guildId_userId_roleId_key" ON "GuildShopRoles"("guildId", "userId", "roleId");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

View file

@ -0,0 +1,20 @@
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_GuildShopRoles" (
"guildId" TEXT NOT NULL,
"roleId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"pricePerHour" INTEGER NOT NULL DEFAULT 5,
"lastPayed" DATETIME NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "GuildShopRoles_guildId_fkey" FOREIGN KEY ("guildId") REFERENCES "Guild" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "GuildShopRoles_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "GuildShopRoles_userId_guildId_fkey" FOREIGN KEY ("userId", "guildId") REFERENCES "GuildMember" ("userId", "guildId") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_GuildShopRoles" ("createdAt", "guildId", "lastPayed", "pricePerHour", "roleId", "updatedAt", "userId") SELECT "createdAt", "guildId", "lastPayed", "pricePerHour", "roleId", "updatedAt", "userId" FROM "GuildShopRoles";
DROP TABLE "GuildShopRoles";
ALTER TABLE "new_GuildShopRoles" RENAME TO "GuildShopRoles";
CREATE UNIQUE INDEX "GuildShopRoles_guildId_userId_roleId_key" ON "GuildShopRoles"("guildId", "userId", "roleId");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

View file

@ -60,6 +60,7 @@ model Guild {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
GuildShopRoles GuildShopRoles[]
}
model User {
@ -74,6 +75,7 @@ model User {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
GuildShopRoles GuildShopRoles[]
}
model GuildMember {
@ -91,6 +93,7 @@ model GuildMember {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
GuildShopRoles GuildShopRoles[]
// Unique Identifier
@@unique([userId, guildId])
@ -122,3 +125,20 @@ model Cooldown {
@@unique([guildId, userId, timeoutId])
}
model GuildShopRoles {
guildId String
roleId String
userId String
pricePerHour Int @default(5)
lastPayed DateTime
guild Guild @relation(fields: [guildId], references: [id])
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
member GuildMember? @relation(fields: [userId, guildId], references: [userId, guildId])
@@unique([guildId, userId, roleId])
}

View file

@ -8,7 +8,7 @@ import { ChatInputCommandInteraction } from "discord.js";
import moduleBuy from "./modules/buy";
import moduleCancel from "./modules/cancel";
import guildSchema from "../../../../models/guild";
import prisma from "../../../../handlers/database";
export default {
builder: (group: SlashCommandSubcommandGroupBuilder) => {
@ -26,13 +26,12 @@ export default {
if (!interaction.guild) return;
const { options, guild } = interaction;
const guildDB = await guildSchema?.findOne({
guildId: guild?.id,
const getGuild = await prisma.guild.findUnique({
where: { id: guild.id },
});
if (!getGuild) throw new Error("Guild not found");
if (guildDB === null) return;
if (!guildDB.shop.roles.status)
if (!getGuild.shopRolesEnabled)
throw new Error("This server has disabled shop roles.");
if (options?.getSubcommand() === "buy") {

View file

@ -1,12 +1,22 @@
// Dependencies
// Helpers
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { ChatInputCommandInteraction } from "discord.js";
import {
ChatInputCommandInteraction,
ColorResolvable,
EmbedBuilder,
GuildMemberRoleManager,
} from "discord.js";
import deferReply from "../../../../../../handlers/deferReply";
import getEmbedData from "../../../../../../helpers/getEmbedData";
import logger from "../../../../../../middlewares/logger";
// Configurations
// import fetchUser from "../../../../../../helpers/userData";
// Models
import prisma from "../../../../../../handlers/database";
import pluralize from "../../../../../../helpers/pluralize";
// Function
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
@ -29,65 +39,140 @@ export default {
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
// const { successColor, footerText, footerIcon } = await getEmbedConfig(
// interaction.guild
// );
// const { options, guild, user, member } = interaction;
// const optionName = options?.getString("name");
// const optionColor = options?.getString("color");
// // If amount is null
// if (optionName === null)
// throw new Error("We could not read your requested name");
// await guild?.roles
// .create({
// name: optionName,
// color: optionColor as ColorResolvable,
// reason: `${user?.id} bought from shop`,
// })
// .then(async (role) => {
// // Get guild object
// const guildDB = await guildSchema?.findOne({
// guildId: guild?.id,
// });
// const userDB = await fetchUser(user, guild);
// if (userDB === null) {
// return logger?.silly(`User is null`);
// }
// if (guildDB === null) {
// return logger?.silly(`Guild is null`);
// }
// if (guildDB.shop === null) {
// return logger?.silly(`Shop is null`);
// }
// const { pricePerHour } = guildDB.shop.roles;
// userDB.credits -= pricePerHour;
// await userDB?.save();
// await shopRolesSchema?.create({
// roleId: role?.id,
// userId: user?.id,
// guildId: guild?.id,
// pricePerHour,
// lastPayed: new Date(),
// });
// await (member?.roles as GuildMemberRoleManager)?.add(role?.id);
// logger?.silly(`Role ${role?.name} was bought by ${user?.tag}`);
// const interactionEmbed = new EmbedBuilder()
// .setTitle("[:shopping_cart:] Buy")
// .setDescription(
// `You bought **${optionName}** for **${pluralize(
// pricePerHour,
// "credit"
// )}**.`
// )
// .setTimestamp()
// .setColor(successColor)
// .setFooter({ text: footerText, iconURL: footerIcon });
// return interaction?.editReply({
// embeds: [interactionEmbed],
// });
// })
// .catch(() => {
// throw new Error("Failed creating role.");
// });
const { successColor, footerText, footerIcon } = await getEmbedData(
interaction.guild
);
const { options, guild, user, member } = interaction;
const optionName = options?.getString("name");
const optionColor = options?.getString("color");
// If amount is null
if (optionName === null)
throw new Error("We could not read your requested name");
await guild?.roles
.create({
name: optionName,
color: optionColor as ColorResolvable,
reason: `${user?.id} bought from shop`,
})
.then(async (role) => {
const userId = "SNOWFLKAE";
const guildId = "SNOWFLAKE";
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId,
guildId,
},
},
update: {},
create: {
user: {
connectOrCreate: {
create: {
id: userId,
},
where: {
id: userId,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guildId,
},
where: {
id: guildId,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createGuildMember);
// Get guild object
const pricePerHour = createGuildMember.guild.shopRolesPricePerHour;
const updateGuildMember = await prisma.guildMember.update({
where: {
userId_guildId: {
userId,
guildId,
},
},
data: {
creditsEarned: { decrement: pricePerHour },
},
});
logger.silly(updateGuildMember);
const createShopRole = await prisma.guildShopRoles.upsert({
where: {
guildId_userId_roleId: {
guildId: guild.id,
userId: user.id,
roleId: role.id,
},
},
update: {},
create: {
roleId: role.id,
lastPayed: new Date(),
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(createShopRole);
await (member?.roles as GuildMemberRoleManager)?.add(role?.id);
logger?.silly(`Role ${role?.name} was bought by ${user?.tag}`);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:shopping_cart:] Buy")
.setDescription(
`You bought **${optionName}** for **${pluralize(
pricePerHour,
"credit"
)}**.`
)
.setTimestamp()
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon });
return interaction?.editReply({
embeds: [interactionEmbed],
});
})
.catch(() => {
throw new Error("Failed creating role.");
});
},
};

View file

@ -1,11 +1,21 @@
// Dependencies
// Helpers
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { ChatInputCommandInteraction } from "discord.js";
import {
ChatInputCommandInteraction,
EmbedBuilder,
GuildMemberRoleManager,
} from "discord.js";
// Configurations
// import fetchUser from "../../../../../../helpers/userData";
// Models
import deferReply from "../../../../../../handlers/deferReply";
import logger from "../../../../../../middlewares/logger";
// Configurations
// Models
import prisma from "../../../../../../handlers/database";
import getEmbedData from "../../../../../../helpers/getEmbedData";
import pluralize from "../../../../../../helpers/pluralize";
// Function
export default {
@ -23,45 +33,95 @@ export default {
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
// const { successColor, footerText, footerIcon } = await getEmbedConfig(
// interaction.guild
// );
// const { options, guild, user, member } = interaction;
// const optionRole = options.getRole("role");
// if (optionRole === null)
// throw new Error("We could not read your requested role.");
// const roleExist = await shopRolesSchema?.findOne({
// guildId: guild?.id,
// userId: user?.id,
// roleId: optionRole?.id,
// });
// if (roleExist === null) return;
// await (member?.roles as GuildMemberRoleManager)?.remove(optionRole?.id);
// await guild?.roles
// .delete(optionRole?.id, `${user?.id} canceled from shop`)
// .then(async () => {
// const userDB = await fetchUser(user, guild);
// if (userDB === null) {
// return logger?.silly(`User is null`);
// }
// await shopRolesSchema?.deleteOne({
// roleId: optionRole?.id,
// userId: user?.id,
// guildId: guild?.id,
// });
// const interactionEmbed = new EmbedBuilder()
// .setTitle("[:shopping_cart:] Cancel")
// .setDescription(`You have canceled ${optionRole.name}.`)
// .setTimestamp()
// .setColor(successColor)
// .addFields({
// name: "Your balance",
// value: `${pluralize(userDB?.credits, "credit")}`,
// })
// .setFooter({ text: footerText, iconURL: footerIcon });
// return interaction?.editReply({
// embeds: [interactionEmbed],
// });
// });
const { successColor, footerText, footerIcon } = await getEmbedData(
interaction.guild
);
const { options, guild, user, member } = interaction;
const optionRole = options.getRole("role");
if (optionRole === null)
throw new Error("We could not read your requested role.");
if (!guild) throw new Error("No guild specified");
if (!user) throw new Error("No user specified");
const roleExist = await prisma.guildShopRoles.findUnique({
where: {
guildId_userId_roleId: {
guildId: guild.id,
userId: user.id,
roleId: optionRole.id,
},
},
});
if (roleExist === null) return;
await (member?.roles as GuildMemberRoleManager)?.remove(optionRole?.id);
await guild?.roles
.delete(optionRole?.id, `${user?.id} canceled from shop`)
.then(async () => {
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId: user.id,
guildId: guild.id,
},
},
update: {},
create: {
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("Guild member not created");
const deleteShopRole = await prisma.guildShopRoles.delete({
where: {
guildId_userId_roleId: {
guildId: guild?.id,
userId: user?.id,
roleId: optionRole?.id,
},
},
});
logger.silly(deleteShopRole);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:shopping_cart:] Cancel")
.setDescription(`You have canceled ${optionRole.name}.`)
.setTimestamp()
.setColor(successColor)
.addFields({
name: "Your balance",
value: `${pluralize(createGuildMember.creditsEarned, "credit")}`,
})
.setFooter({ text: footerText, iconURL: footerIcon });
return interaction?.editReply({
embeds: [interactionEmbed],
});
});
},
};

View file

@ -1,25 +1,28 @@
import { ChannelType, EmbedBuilder, GuildMember } from "discord.js";
import prisma from "../../handlers/database";
import getEmbedConfig from "../../helpers/getEmbedData";
import logger from "../../middlewares/logger";
import guildSchema from "../../models/guild";
export default {
execute: async (member: GuildMember) => {
const { client, guild } = member;
const guildData = await guildSchema.findOne({ guildId: member.guild.id });
if (!guildData) {
throw new Error("Could not find guild");
}
if (guildData.audits.status !== true) return;
if (!guildData.audits.channelId) {
const getGuild = await prisma.guild.findUnique({
where: { id: member.guild.id },
});
if (!getGuild) throw new Error("Guild not found");
if (getGuild.auditsEnabled !== true) return;
if (!getGuild.auditsChannelId) {
throw new Error("Channel not found");
}
const embedConfig = await getEmbedConfig(guild);
const channel = client.channels.cache.get(guildData.audits.channelId);
if (channel?.type !== ChannelType.GuildText) {
const channel = client.channels.cache.get(getGuild.auditsChannelId);
if (!channel) throw new Error("Channel not found");
if (channel.type !== ChannelType.GuildText) {
throw new Error("Channel must be a text channel");
}

View file

@ -1,24 +1,25 @@
import { ChannelType, EmbedBuilder, GuildMember } from "discord.js";
import prisma from "../../handlers/database";
import getEmbedConfig from "../../helpers/getEmbedData";
import logger from "../../middlewares/logger";
import guildSchema from "../../models/guild";
export default {
execute: async (member: GuildMember) => {
const { client, guild } = member;
const guildData = await guildSchema.findOne({ guildId: member.guild.id });
if (!guildData) {
throw new Error("Could not find guild");
}
if (guildData.audits.status !== true) return;
if (!guildData.audits.channelId) {
const getGuild = await prisma.guild.findUnique({
where: { id: member.guild.id },
});
if (!getGuild) throw new Error("Guild not found");
if (getGuild.auditsEnabled !== true) return;
if (!getGuild.auditsChannelId) {
throw new Error("Channel not found");
}
const embedConfig = await getEmbedConfig(guild);
const channel = client.channels.cache.get(guildData.audits.channelId);
const channel = client.channels.cache.get(getGuild.auditsChannelId);
if (channel?.type !== ChannelType.GuildText) {
throw new Error("Channel must be a text channel");
}

View file

@ -1,7 +1,7 @@
import { BaseInteraction, ChannelType, EmbedBuilder } from "discord.js";
import prisma from "../../handlers/database";
import getEmbedConfig from "../../helpers/getEmbedData";
import logger from "../../middlewares/logger";
import guildSchema from "../../models/guild";
export default {
execute: async (interaction: BaseInteraction) => {
@ -9,22 +9,21 @@ export default {
if (interaction.guild === null) return;
const getGuild = await prisma.guild.findUnique({
where: { id: interaction.guild.id },
});
if (!getGuild) throw new Error("Guild not found");
const { footerText, footerIcon, successColor } = await getEmbedConfig(
interaction.guild
);
const guildData = await guildSchema.findOne({
guildId: interaction.guild.id,
});
const { client } = interaction;
if (guildData === null) return;
if (getGuild.auditsEnabled !== true) return;
if (!getGuild.auditsChannelId) return;
if (guildData.audits.status !== true) return;
if (!guildData.audits.channelId) return;
const channel = client.channels.cache.get(`${guildData.audits.channelId}`);
const channel = client.channels.cache.get(`${getGuild.auditsChannelId}`);
if (!channel) return;
if (channel.type !== ChannelType.GuildText) return;

View file

@ -1,7 +1,7 @@
import { ChannelType, EmbedBuilder, Message } from "discord.js";
import prisma from "../../handlers/database";
import getEmbedConfig from "../../helpers/getEmbedData";
import logger from "../../middlewares/logger";
import guildSchema from "../../models/guild";
export default {
execute: async (message: Message) => {
@ -13,18 +13,19 @@ export default {
message.guild
);
const guildData = await guildSchema.findOne({
guildId: message.guild.id,
const getGuild = await prisma.guild.findUnique({
where: { id: message.guild.id },
});
if (!getGuild) throw new Error("Guild not found");
const { client } = message;
if (guildData === null) return;
if (!getGuild) throw new Error("Guild not found");
if (guildData.audits.status !== true) return;
if (!guildData.audits.channelId) return;
if (getGuild.auditsEnabled !== true) return;
if (!getGuild.auditsChannelId) return;
const channel = client.channels.cache.get(`${guildData.audits.channelId}`);
const channel = client.channels.cache.get(`${getGuild.auditsChannelId}`);
if (!channel) return;
if (channel.type !== ChannelType.GuildText) return;

View file

@ -1,8 +1,8 @@
/* eslint-disable no-loops/no-loops */
import { ChannelType, EmbedBuilder, Message } from "discord.js";
import prisma from "../../handlers/database";
import getEmbedConfig from "../../helpers/getEmbedData";
import logger from "../../middlewares/logger";
import guildSchema from "../../models/guild";
export default {
execute: async (oldMessage: Message, newMessage: Message) => {
@ -16,18 +16,17 @@ export default {
newMessage.guild
);
const guildData = await guildSchema.findOne({
guildId: oldMessage.guild.id,
const getGuild = await prisma.guild.findUnique({
where: { id: oldMessage.guild.id },
});
if (!getGuild) throw new Error("Guild not found");
const { client } = oldMessage;
if (guildData === null) return;
if (getGuild.auditsEnabled !== true) return;
if (!getGuild.auditsChannelId) return;
if (guildData.audits.status !== true) return;
if (!guildData.audits.channelId) return;
const channel = client.channels.cache.get(`${guildData.audits.channelId}`);
const channel = client.channels.cache.get(`${getGuild.auditsChannelId}`);
if (!channel) return;
if (channel.type !== ChannelType.GuildText) return;

View file

@ -1,142 +0,0 @@
import { ColorResolvable } from "discord.js";
import { model, Schema } from "mongoose";
interface IGuild {
guildId: string;
credits: {
status: boolean;
rate: number;
timeout: number;
workRate: number;
minimumLength: number;
workTimeout: number;
};
embeds: {
successColor: ColorResolvable;
waitColor: ColorResolvable;
errorColor: ColorResolvable;
footerIcon: string;
footerText: string;
};
shop: { roles: { status: boolean; pricePerHour: number } };
points: {
status: boolean;
rate: number;
minimumLength: number;
timeout: number;
};
welcome: {
status: boolean;
joinChannel: string;
leaveChannel: string;
joinChannelMessage: string;
leaveChannelMessage: string;
};
audits: { status: boolean; channelId: string };
}
const guildSchema = new Schema<IGuild>(
{
guildId: {
type: String,
required: true,
unique: true,
index: true,
},
credits: {
status: {
type: Boolean,
default: true,
},
rate: {
type: Number,
default: 1,
},
minimumLength: {
type: Number,
default: 5,
},
timeout: {
type: Number,
default: 5,
},
workRate: {
type: Number,
default: 15,
},
workTimeout: {
type: Number,
default: 900,
},
},
embeds: {
successColor: {
type: String,
default: "#22bb33",
},
waitColor: {
type: String,
default: "#f0ad4e",
},
errorColor: {
type: String,
default: "#bb2124",
},
footerText: {
type: String,
default: "https://github.com/ZynerOrg/xyter",
},
footerIcon: {
type: String,
default: "https://github.com/ZynerOrg.png",
},
},
shop: {
roles: {
status: {
type: Boolean,
default: false,
},
pricePerHour: {
type: Number,
default: 5,
},
},
},
points: {
status: {
type: Boolean,
default: false,
},
rate: {
type: Number,
default: 1,
},
minimumLength: {
type: Number,
default: 5,
},
timeout: {
type: Number,
default: 5,
},
},
welcome: {
status: {
type: Boolean,
default: false,
},
joinChannel: { type: String },
leaveChannel: { type: String },
joinChannelMessage: { type: String },
leaveChannelMessage: { type: String },
},
audits: {
status: { type: Boolean, default: false },
channelId: { type: String },
},
},
{ timestamps: true }
);
export default model<IGuild>("guild", guildSchema);

View file

@ -1,48 +0,0 @@
import { Snowflake } from "discord.js";
import { model, Schema } from "mongoose";
export interface IShopRole {
roleId: Snowflake;
userId: Snowflake;
guildId: Snowflake;
pricePerHour: number;
lastPayed: Date;
}
const shopRoleSchema = new Schema<IShopRole>(
{
roleId: {
type: String,
required: true,
unique: false,
index: true,
},
userId: {
type: String,
required: true,
unique: false,
index: true,
},
guildId: {
type: String,
required: true,
unique: false,
index: true,
},
pricePerHour: {
type: Number,
required: true,
unique: false,
index: true,
default: 5,
},
lastPayed: {
type: Date,
unique: false,
index: true,
},
},
{ timestamps: true }
);
export default model<IShopRole>("shopRole", shopRoleSchema);

View file

@ -1,38 +0,0 @@
import { Snowflake } from "discord.js";
import { model, Schema } from "mongoose";
export interface ITimeout {
userId: Snowflake;
guildId: Snowflake;
cooldown: number;
timeoutId: string;
createdAt: Date;
updatedAt: Date;
}
const timeoutSchema = new Schema<ITimeout>(
{
userId: {
type: String,
required: true,
unique: false,
index: true,
},
guildId: {
type: String,
required: true,
unique: false,
index: true,
},
cooldown: {
type: Number,
required: true,
unique: false,
index: true,
},
timeoutId: { type: String },
},
{ timestamps: true }
);
export default model<ITimeout>("timeout", timeoutSchema);

View file

@ -1,42 +0,0 @@
import { Snowflake } from "discord.js";
import { model, Schema } from "mongoose";
export interface IUser {
guildId: Snowflake;
userId: Snowflake;
language: string;
reputation: number;
credits: number;
level: number;
points: number;
updatedAt: Date;
createdAt: Date;
}
const userSchema = new Schema(
{
guildId: {
type: String,
required: true,
unique: false,
index: true,
},
userId: {
type: String,
required: true,
unique: false,
index: true,
},
language: {
type: String,
default: "en",
},
reputation: { type: Number, default: 0 },
credits: { type: Number, default: 0 },
level: { type: Number, default: 0 },
points: { type: Number, default: 0 },
},
{ timestamps: true }
);
export default model<IUser>("user", userSchema);

View file

@ -1,9 +1,9 @@
import { Client } from "discord.js";
import logger from "../../../../../middlewares/logger";
import { IShopRole } from "../../../../../interfaces/ShopRole";
import { GuildShopRoles } from "@prisma/client";
export const execute = async (_client: Client, role: IShopRole) => {
export const execute = async (_client: Client, role: GuildShopRoles) => {
const { roleId } = role;
logger.silly(`Shop role ${roleId} is not due for payment.`);

View file

@ -1,22 +1,14 @@
import { Client } from "discord.js";
import logger from "../../../../../middlewares/logger";
import { IShopRole } from "../../../../../interfaces/ShopRole";
import guildSchema from "../../../../../models/guild";
import shopRoleSchema from "../../../../../models/shopRole";
import userSchema from "../../../../../models/user";
import { GuildShopRoles } from "@prisma/client";
import prisma from "../../../../../handlers/database";
// Execute the component
export const execute = async (client: Client, role: IShopRole) => {
export const execute = async (client: Client, role: GuildShopRoles) => {
const { guildId, userId, roleId } = role;
if (!userId) throw new Error("User ID not found for shop role.");
const guildData = await guildSchema.findOne({ guildId });
if (!guildData) throw new Error("Guild not found.");
const userData = await userSchema.findOne({ guildId, userId });
if (!userData) throw new Error("User not found.");
const rGuild = client.guilds.cache.get(guildId);
if (!rGuild) throw new Error("Guild not found.");
@ -28,27 +20,45 @@ export const execute = async (client: Client, role: IShopRole) => {
logger.debug(`Shop role ${roleId} is due for payment.`);
const { pricePerHour } = guildData.shop.roles;
const getGuildMember = await prisma.guildMember.findUnique({
where: {
userId_guildId: {
userId,
guildId,
},
},
include: {
user: true,
guild: true,
},
});
if (userData.credits < pricePerHour) {
logger.silly(getGuildMember);
if (!getGuildMember) throw new Error("Could not find guild member.");
const pricePerHour = getGuildMember.guild.shopRolesPricePerHour;
if (getGuildMember.creditsEarned < pricePerHour) {
await rMember.roles
.remove(roleId)
.then(async () => {
await shopRoleSchema
.deleteOne({
const deleteShopRole = await prisma.guildShopRoles.delete({
where: {
guildId_userId_roleId: {
guildId,
userId,
roleId,
guildId,
})
.then(() => {
},
},
});
logger.silly(deleteShopRole);
logger.silly(
`Shop role document ${roleId} has been deleted from user ${userId}.`
);
})
.catch(() => {
throw new Error("Failed deleting shop role from user.");
});
})
.catch(() => {
throw new Error(`Failed removing role from user.`);
});
@ -56,25 +66,63 @@ export const execute = async (client: Client, role: IShopRole) => {
throw new Error("User does not have enough credits.");
}
userData.credits -= pricePerHour;
await userData
.save()
.then(async () => {
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId,
guildId,
},
},
update: { creditsEarned: { decrement: pricePerHour } },
create: {
creditsEarned: -pricePerHour,
user: {
connectOrCreate: {
create: {
id: userId,
},
where: {
id: userId,
},
},
},
guild: {
connectOrCreate: {
create: {
id: guildId,
},
where: {
id: guildId,
},
},
},
},
include: {
user: true,
guild: true,
},
});
logger.silly(createGuildMember);
logger.silly(`User ${userId} has been updated.`);
role.lastPayed = new Date();
await role
.save()
.then(() => {
logger.silly(`Shop role ${roleId} has been updated.`);
})
.catch(() => {
throw new Error("Failed updating shop role.");
const updateGuildShopRole = await prisma.guildShopRoles.update({
where: {
guildId_userId_roleId: {
guildId,
userId,
roleId,
},
},
data: {
lastPayed: new Date(),
},
});
logger.silly(updateGuildShopRole);
logger.silly(`Shop role ${roleId} has been updated.`);
logger.debug(`Shop role ${roleId} has been paid.`);
})
.catch(() => {
throw new Error("Failed updating user.");
});
};

View file

@ -1,20 +1,16 @@
/* eslint-disable no-loops/no-loops */
import { Client } from "discord.js";
import { IShopRole } from "../../../../interfaces/ShopRole";
import shopRoleSchema from "../../../../models/shopRole";
import prisma from "../../../../handlers/database";
import * as dueForPayment from "./components/dueForPayment";
import * as overDueForPayment from "./components/overDueForPayment";
export const execute = async (client: Client) => {
const roles = await shopRoleSchema.find();
const roles = await prisma.guildShopRoles.findMany();
await Promise.all(
roles.map(async (role: IShopRole) => {
for await (const role of roles) {
const { lastPayed } = role;
const nextPayment = new Date(
lastPayed.setHours(lastPayed.getHours() + 1)
);
const nextPayment = new Date(lastPayed.setHours(lastPayed.getHours() + 1));
const now = new Date();
@ -27,6 +23,5 @@ export const execute = async (client: Client) => {
if (nextPayment < now) {
await overDueForPayment.execute(client, role);
}
})
);
}
};

View file

@ -1,36 +1,40 @@
/* eslint-disable no-loops/no-loops */
import logger from "../../middlewares/logger";
import timeoutSchema from "../../models/timeout";
import addSeconds from "../../helpers/addSeconds";
import prisma from "../../handlers/database";
export const options = {
schedule: "*/30 * * * *", // https://crontab.guru/
};
// Execute the job
export const execute = async () => {
const timeouts = await timeoutSchema.find();
await Promise.all(
timeouts.map(async (timeout) => {
const getCooldown = await prisma.cooldown.findMany();
for await (const timeout of getCooldown) {
const { guildId, userId, timeoutId, cooldown, createdAt } = timeout;
const overDue = (await addSeconds(cooldown, createdAt)) < new Date();
if (overDue) {
timeoutSchema
.deleteOne({
logger.info(timeout);
const deleteCooldown = await prisma.cooldown.delete({
where: {
guildId_userId_timeoutId: {
guildId,
userId,
timeoutId,
cooldown,
})
.then(() => {
},
},
});
logger.silly(deleteCooldown);
logger.debug(
`Timeout document ${timeoutId} has been deleted from user ${userId}.`
);
});
}
})
);
}
};