feat: ✨ add quote feature
Now users can post quotes about each other if guild has enabled it!
This commit is contained in:
parent
21a5cd3eab
commit
a2e1fa7a98
7 changed files with 300 additions and 4 deletions
40
prisma/migrations/20230529133956_add_quotes/migration.sql
Normal file
40
prisma/migrations/20230529133956_add_quotes/migration.sql
Normal file
|
@ -0,0 +1,40 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE `GuildSettings` ADD COLUMN `guildQuotesSettingsId` VARCHAR(191) NULL;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `GuildQuotesSettings` (
|
||||
`id` VARCHAR(191) NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
`quoteChannelId` VARCHAR(191) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `GuildQuotesSettings_id_key`(`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `Quotes` (
|
||||
`id` VARCHAR(191) NOT NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
`userId` VARCHAR(191) NOT NULL,
|
||||
`guildId` VARCHAR(191) NOT NULL,
|
||||
`message` VARCHAR(191) NOT NULL,
|
||||
`posterUserId` VARCHAR(191) NOT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `GuildSettings` ADD CONSTRAINT `GuildSettings_guildQuotesSettingsId_fkey` FOREIGN KEY (`guildQuotesSettingsId`) REFERENCES `GuildQuotesSettings`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `GuildQuotesSettings` ADD CONSTRAINT `GuildQuotesSettings_id_fkey` FOREIGN KEY (`id`) REFERENCES `Guild`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `Quotes` ADD CONSTRAINT `Quotes_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `Quotes` ADD CONSTRAINT `Quotes_guildId_fkey` FOREIGN KEY (`guildId`) REFERENCES `Guild`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `Quotes` ADD CONSTRAINT `Quotes_posterUserId_fkey` FOREIGN KEY (`posterUserId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
@ -0,0 +1,2 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE `GuildQuotesSettings` ADD COLUMN `status` BOOLEAN NOT NULL DEFAULT false;
|
|
@ -8,14 +8,17 @@ datasource db {
|
|||
}
|
||||
|
||||
model Guild {
|
||||
id String @unique
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
id String @unique
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
guildMembers GuildMember[]
|
||||
guildSettings GuildSettings?
|
||||
guildCreditsSettings GuildCreditsSettings?
|
||||
guildQuotesSettings GuildQuotesSettings?
|
||||
apiCredentials ApiCredentials[]
|
||||
cooldowns Cooldown[]
|
||||
Quotes Quotes[]
|
||||
}
|
||||
|
||||
model User {
|
||||
|
@ -27,6 +30,8 @@ model User {
|
|||
cooldowns Cooldown[]
|
||||
|
||||
userReputation UserReputation?
|
||||
Quotes Quotes[] @relation(name: "Quotes")
|
||||
PostedQuotes Quotes[] @relation(name: "PostedQuotes")
|
||||
}
|
||||
|
||||
model GuildMember {
|
||||
|
@ -81,6 +86,8 @@ model GuildSettings {
|
|||
creditsSettings GuildCreditsSettings? @relation(fields: [guildCreditsSettingsId], references: [id], onDelete: Cascade)
|
||||
|
||||
guildCreditsSettingsId String?
|
||||
GuildQuotesSettings GuildQuotesSettings? @relation(fields: [guildQuotesSettingsId], references: [id])
|
||||
guildQuotesSettingsId String?
|
||||
}
|
||||
|
||||
model GuildCreditsSettings {
|
||||
|
@ -102,6 +109,36 @@ model GuildCreditsSettings {
|
|||
guildSettings GuildSettings[]
|
||||
}
|
||||
|
||||
model GuildQuotesSettings {
|
||||
id String @unique
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
guild Guild @relation(fields: [id], references: [id], onDelete: Cascade)
|
||||
|
||||
status Boolean @default(false)
|
||||
quoteChannelId String
|
||||
|
||||
guildSettings GuildSettings[]
|
||||
}
|
||||
|
||||
model Quotes {
|
||||
id String @id @default(uuid())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
user User @relation(fields: [userId], references: [id], name: "Quotes")
|
||||
userId String
|
||||
|
||||
guild Guild @relation(fields: [guildId], references: [id], onDelete: Cascade)
|
||||
guildId String
|
||||
|
||||
message String
|
||||
|
||||
posterUserId String
|
||||
posterUser User @relation(fields: [posterUserId], references: [id], name: "PostedQuotes")
|
||||
}
|
||||
|
||||
model ApiCredentials {
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now())
|
||||
|
|
23
src/commands/quote/index.ts
Normal file
23
src/commands/quote/index.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
// Subcommands
|
||||
import {
|
||||
SubcommandHandlers,
|
||||
executeSubcommand,
|
||||
} from "../../handlers/executeSubcommand";
|
||||
import * as post from "./subcommands/post";
|
||||
|
||||
const subcommandHandlers: SubcommandHandlers = {
|
||||
post: post.execute,
|
||||
};
|
||||
|
||||
export const builder = new SlashCommandBuilder()
|
||||
.setName("quote")
|
||||
.setDescription("Fun commands.")
|
||||
|
||||
.addSubcommand(post.builder);
|
||||
|
||||
// Execute function
|
||||
export const execute = async (interaction: ChatInputCommandInteraction) => {
|
||||
await executeSubcommand(interaction, subcommandHandlers);
|
||||
};
|
101
src/commands/quote/subcommands/post/index.ts
Normal file
101
src/commands/quote/subcommands/post/index.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
import {
|
||||
ChannelType,
|
||||
ChatInputCommandInteraction,
|
||||
EmbedBuilder,
|
||||
SlashCommandSubcommandBuilder,
|
||||
} from "discord.js";
|
||||
import CooldownManager from "../../../../handlers/CooldownManager";
|
||||
import prisma from "../../../../handlers/prisma";
|
||||
import generateCooldownName from "../../../../helpers/generateCooldownName";
|
||||
import deferReply from "../../../../utils/deferReply";
|
||||
import sendResponse from "../../../../utils/sendResponse";
|
||||
|
||||
const cooldownManager = new CooldownManager();
|
||||
|
||||
export const builder = (command: SlashCommandSubcommandBuilder) => {
|
||||
return command
|
||||
.setName("post")
|
||||
.setDescription("Post a quote someone said in this server")
|
||||
.addUserOption((option) =>
|
||||
option
|
||||
.setName("user")
|
||||
.setDescription("The user who said this")
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("message")
|
||||
.setDescription("What the user said")
|
||||
.setRequired(true)
|
||||
);
|
||||
};
|
||||
|
||||
export const execute = async (
|
||||
interaction: ChatInputCommandInteraction
|
||||
): Promise<void> => {
|
||||
await deferReply(interaction, true);
|
||||
|
||||
const { options, guild, user } = interaction;
|
||||
|
||||
const quoteUser = options.getUser("user", true);
|
||||
const quoteString = options.getString("message", true);
|
||||
|
||||
if (!guild) throw new Error("A guild is required.");
|
||||
|
||||
const guildQuotesSettings = await prisma.guildQuotesSettings.findUnique({
|
||||
where: { id: guild.id },
|
||||
});
|
||||
|
||||
if (!guildQuotesSettings) throw new Error("No configuration available.");
|
||||
|
||||
if (guildQuotesSettings.status !== true)
|
||||
throw new Error("Quotes are disabled in this server.");
|
||||
|
||||
const channel = await interaction.client.channels.fetch(
|
||||
guildQuotesSettings.quoteChannelId
|
||||
);
|
||||
|
||||
if (!channel) throw new Error("No channel found.");
|
||||
|
||||
if (channel.type !== ChannelType.GuildText)
|
||||
throw new Error("The channel is not a text channel.");
|
||||
|
||||
await prisma.quotes.create({
|
||||
data: {
|
||||
guildId: guild.id,
|
||||
userId: quoteUser.id,
|
||||
posterUserId: user.id,
|
||||
message: quoteString,
|
||||
},
|
||||
});
|
||||
|
||||
const quoteEmbed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: `Quote of ${quoteUser.username}`,
|
||||
iconURL: quoteUser.displayAvatarURL(),
|
||||
})
|
||||
.setColor(process.env.EMBED_COLOR_SUCCESS)
|
||||
.setDescription(quoteString)
|
||||
.setFooter({
|
||||
text: `Posted by ${user.username}`,
|
||||
iconURL: user.displayAvatarURL(),
|
||||
});
|
||||
|
||||
const sentMessage = await channel.send({ embeds: [quoteEmbed] });
|
||||
|
||||
await sentMessage.react("👍");
|
||||
await sentMessage.react("👎");
|
||||
|
||||
const postEmbed = new EmbedBuilder()
|
||||
.setColor(process.env.EMBED_COLOR_SUCCESS)
|
||||
.setDescription("Successfully posted the quote!");
|
||||
|
||||
await sendResponse(interaction, { embeds: [postEmbed] });
|
||||
|
||||
await cooldownManager.setCooldown(
|
||||
await generateCooldownName(interaction),
|
||||
guild,
|
||||
user,
|
||||
5 * 60
|
||||
);
|
||||
};
|
|
@ -6,10 +6,12 @@ import {
|
|||
} from "../../handlers/executeSubcommand";
|
||||
import * as credits from "./subcommands/credits";
|
||||
import * as ctrlpanel from "./subcommands/ctrlpanel";
|
||||
import * as quotes from "./subcommands/quotes";
|
||||
|
||||
const subcommandHandlers: SubcommandHandlers = {
|
||||
ctrlpanel: ctrlpanel.execute,
|
||||
credits: credits.execute,
|
||||
quotes: quotes.execute,
|
||||
};
|
||||
|
||||
export const builder = new SlashCommandBuilder()
|
||||
|
@ -17,7 +19,8 @@ export const builder = new SlashCommandBuilder()
|
|||
.setDescription("Manage guild configurations.")
|
||||
.setDMPermission(false)
|
||||
.addSubcommand(ctrlpanel.builder)
|
||||
.addSubcommand(credits.builder);
|
||||
.addSubcommand(credits.builder)
|
||||
.addSubcommand(quotes.builder);
|
||||
|
||||
export const execute = async (interaction: ChatInputCommandInteraction) => {
|
||||
await executeSubcommand(interaction, subcommandHandlers);
|
||||
|
|
90
src/commands/settings/subcommands/quotes/index.ts
Normal file
90
src/commands/settings/subcommands/quotes/index.ts
Normal file
|
@ -0,0 +1,90 @@
|
|||
import {
|
||||
ChatInputCommandInteraction,
|
||||
EmbedBuilder,
|
||||
PermissionsBitField,
|
||||
SlashCommandSubcommandBuilder,
|
||||
} from "discord.js";
|
||||
import prisma from "../../../../handlers/prisma";
|
||||
import checkPermission from "../../../../utils/checkPermission";
|
||||
import deferReply from "../../../../utils/deferReply";
|
||||
import sendResponse from "../../../../utils/sendResponse";
|
||||
|
||||
export const builder = (command: SlashCommandSubcommandBuilder) => {
|
||||
return command
|
||||
.setName("quotes")
|
||||
.setDescription(`Configure quotes module`)
|
||||
.addBooleanOption((option) =>
|
||||
option.setName("status").setDescription("Status").setRequired(true)
|
||||
)
|
||||
.addChannelOption((option) =>
|
||||
option.setName("channel").setDescription("channel").setRequired(true)
|
||||
);
|
||||
};
|
||||
|
||||
export const execute = async (interaction: ChatInputCommandInteraction) => {
|
||||
await deferReply(interaction, true);
|
||||
|
||||
checkPermission(interaction, PermissionsBitField.Flags.ManageGuild);
|
||||
|
||||
const { guild, options, user } = interaction;
|
||||
|
||||
const quoteStatus = options.getBoolean("status", true);
|
||||
const quoteChannel = options.getChannel("channel", true);
|
||||
|
||||
if (!guild) {
|
||||
throw new Error("Guild not found.");
|
||||
}
|
||||
|
||||
const upsertGuildQuotesSettings = await prisma.guildQuotesSettings.upsert({
|
||||
where: {
|
||||
id: guild.id,
|
||||
},
|
||||
update: {
|
||||
quoteChannelId: quoteChannel.id,
|
||||
status: quoteStatus,
|
||||
},
|
||||
create: {
|
||||
id: guild.id,
|
||||
quoteChannelId: quoteChannel.id,
|
||||
status: quoteStatus,
|
||||
guildSettings: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id: guild.id,
|
||||
},
|
||||
create: {
|
||||
id: guild.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const embedSuccess = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: "Configuration of Quotes",
|
||||
})
|
||||
.setColor(process.env.EMBED_COLOR_SUCCESS)
|
||||
.setFooter({
|
||||
text: `Successfully configured by ${user.username}`,
|
||||
iconURL: user.displayAvatarURL(),
|
||||
})
|
||||
.setTimestamp();
|
||||
|
||||
await sendResponse(interaction, {
|
||||
embeds: [
|
||||
embedSuccess
|
||||
.setDescription("Configuration updated successfully!")
|
||||
.addFields({
|
||||
name: "Status",
|
||||
value: `${upsertGuildQuotesSettings.status}`,
|
||||
inline: true,
|
||||
})
|
||||
.addFields({
|
||||
name: "Channel ID",
|
||||
value: `${upsertGuildQuotesSettings.quoteChannelId}`,
|
||||
inline: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
Loading…
Add table
Reference in a new issue