Skip to content
This repository has been archived by the owner on Jul 25, 2024. It is now read-only.

Commit

Permalink
feat: prisma+mongo db, cooldowns, and more!
Browse files Browse the repository at this point in the history
  • Loading branch information
janleigh committed Jul 8, 2023
1 parent 3399a12 commit 4737c01
Show file tree
Hide file tree
Showing 33 changed files with 789 additions and 115 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
DISCORD_TOKEN="YOUR-TOKEN-HERE"
DATABASE_URL="YOUR-MONGODB-CONNECTION-STRING-HERE"
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"curly": ["error", "multi-line", "consistent"],
"dot-location": ["error", "property"],
"handle-callback-err": "off",
"indent": ["error", "tab"],
"indent": ["off", "tab"],
"keyword-spacing": "error",
"max-nested-callbacks": ["error", { "max": 4 }],
"max-statements-per-line": ["error", { "max": 2 }],
Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@
"license": "MIT",
"main": "build/index.js",
"scripts": {
"build": "yarn build:clean && yarn compile",
"build:production": "yarn build:clean && yarn format:check && yarn lint && yarn compile",
"build:clean": "rm -rvf ./build/",
"compile": "npx sucrase src -d build --transforms typescript,imports && cp -rvf src/languages/ build/",
"build": "npx tsup && cp -rvf src/languages/ build/",
"build:production": "yarn format:check && yarn lint && yarn build",
"format": "prettier --write src",
"format:check": "prettier --check src",
"lint": "eslint src --ext .ts",
Expand All @@ -26,11 +24,14 @@
"husky": "^8.0.3",
"lint-staged": "^13.2.0",
"prettier": "^2.8.2",
"sucrase": "^3.29.0",
"prisma": "^4.16.2",
"tsup": "^7.1.0",
"typescript": "^5.0.0"
},
"packageManager": "yarn@3.6.1",
"dependencies": {
"@prisma/client": "4.16.2",
"@sapphire/decorators": "^6.0.1",
"@sapphire/framework": "^4.2.0",
"@sapphire/pieces": "^3.6.3",
"@sapphire/plugin-i18next": "^5.0.3",
Expand Down
14 changes: 14 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}

model GuildConfig {
id String @id @default(auto()) @map("_id") @db.ObjectId
guildId String @unique
language String
}
51 changes: 51 additions & 0 deletions src/commands/configuration/LanguageCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { ChatInputCommand, Command, RegisterBehavior } from "@sapphire/framework";
import { ApplyOptions } from "@sapphire/decorators";
import { BaseEmbedBuilder } from "../../libraries/structures/components";
import { parseEmojiByID } from "../../libraries/utils/common/parsers";

@ApplyOptions<Command.Options>({
name: "language",
fullCategory: ["Configuration"]
})
export class LanguageCommand extends Command {
public override registerApplicationCommands(registry: ChatInputCommand.Registry) {
registry.registerChatInputCommand(
(builder) =>
builder
.setName("language")
.setDescription("Change the language of the bot for the server.")
.addStringOption((option) =>
option
.setName("lang")
.setDescription("The language you want to set for the server.")
.setRequired(false)
.addChoices({ name: "English", value: "en-US" })
),
{ behaviorWhenNotIdentical: RegisterBehavior.Overwrite }
);
}

public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
const lang = interaction.options.getString("lang") as Language;
const dbLang = await this.getCurrentLang(interaction.guildId as string);
const infoEmoji = parseEmojiByID("1126390222620463214");
const embed = new BaseEmbedBuilder();

if (!lang) {
embed.setDescription(`${infoEmoji} The current language for this server is: **\`${dbLang}\`**`);
} else {
embed.isErrorEmbed().setDescription("To be implemented.");
}
return interaction.reply({ embeds: [embed] });
}

private async getCurrentLang(guildId: string) {
const db = await this.container.database.guildConfig.findUnique({
where: { guildId: guildId }
});

return db?.language;
}
}

type Language = "en-US";
33 changes: 19 additions & 14 deletions src/commands/core/HelpCommand.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
/* eslint-disable indent */
import { ChatInputCommand, Command, RegisterBehavior } from "@sapphire/framework";
import { ApplyOptions } from "@sapphire/decorators";
import { resolveKey } from "@sapphire/plugin-i18next";
import { ComponentType } from "discord.js";
import { BaseEmbedBuilder, githubBtn, inviteBtn } from "../../libraries/structures/components";
import { parseEmojiByID } from "../../libraries/utils/common/parsers";
import { ComponentType } from "discord.js";
import { resolveKey } from "@sapphire/plugin-i18next";
import { LanguageKeys } from "../../libraries/language";

@ApplyOptions<Command.Options>({
name: "help",
fullCategory: ["Core"]
})
export class HelpCommand extends Command {
public constructor(context: Command.Context, options: Command.Options) {
super(context, {
...options,
name: "help",
fullCategory: ["Core"]
});
}

public override registerApplicationCommands(registry: ChatInputCommand.Registry) {
registry.registerChatInputCommand(
(builder) =>
Expand All @@ -39,11 +35,11 @@ export class HelpCommand extends Command {
value: `
${transparent} </help:1126303427203448938> - ${await resolveKey(
interaction,
LanguageKeys.Commands.Help.HELP_HELPCMD_DESCRIPTION
LanguageKeys.Commands.Core.HelpCommand.HELP_HELPCMD_DESCRIPTION
)}
${transparent} </ping:1125599325431533578> - ${await resolveKey(
interaction,
LanguageKeys.Commands.Help.HELP_PINGCMD_DESCRIPTION
LanguageKeys.Commands.Core.HelpCommand.HELP_PINGCMD_DESCRIPTION
)}
`
},
Expand All @@ -52,7 +48,16 @@ export class HelpCommand extends Command {
value: `
${transparent} </chat:1126124768576417892> - ${await resolveKey(
interaction,
LanguageKeys.Commands.Help.HELP_CHATCMD_DESCRIPTION
LanguageKeys.Commands.Core.HelpCommand.HELP_CHATCMD_DESCRIPTION
)}
`
},
{
name: "— **CONFIGURATION **",
value: `
${transparent} </language:1126383838688444527> - ${await resolveKey(
interaction,
LanguageKeys.Commands.Core.HelpCommand.HELP_LANGCMD_DESCRIPTION
)}
`
}
Expand Down
28 changes: 14 additions & 14 deletions src/commands/core/PingCommand.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { isMessageInstance } from "@sapphire/discord.js-utilities";
import { ChatInputCommand, Command, RegisterBehavior } from "@sapphire/framework";
import { BaseEmbedBuilder } from "../../libraries/structures/components";
import { isMessageInstance } from "@sapphire/discord.js-utilities";
import { ApplyOptions } from "@sapphire/decorators";
import { resolveKey } from "@sapphire/plugin-i18next";
import { BaseEmbedBuilder } from "../../libraries/structures/components";
import { LanguageKeys } from "../../libraries/language";

@ApplyOptions<Command.Options>({
name: "ping",
fullCategory: ["Core"]
})
export class PingCommand extends Command {
public constructor(context: Command.Context, options: Command.Options) {
super(context, {
...options,
name: "ping",
fullCategory: ["Core"]
});
}

public override registerApplicationCommands(registry: ChatInputCommand.Registry) {
registry.registerChatInputCommand(
(builder) => builder.setName("ping").setDescription("Check if the bot is alive."),
Expand All @@ -22,25 +19,28 @@ export class PingCommand extends Command {

public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
const msg = await interaction.reply({
content: `> ${await resolveKey(interaction, LanguageKeys.Commands.Ping.PING_WAITING)}`,
content: `> ${await resolveKey(interaction, LanguageKeys.Commands.Core.PingCommand.PING_WAITING)}`,
ephemeral: false,
fetchReply: true
});
const embed = new BaseEmbedBuilder()
.isErrorEmbed()
.setDescription(await resolveKey(interaction, LanguageKeys.Commands.Ping.PING_FAILED));
.setDescription(await resolveKey(interaction, LanguageKeys.Commands.Core.PingCommand.PING_FAILED));

if (isMessageInstance(msg)) {
const diff = msg.createdTimestamp - interaction.createdTimestamp;
const ping = Math.round(this.container.client.ws.ping);

embed.isSuccessEmbed(false);
embed.setDescription(
await resolveKey(interaction, LanguageKeys.Commands.Ping.PING_SUCCESS_DESCRIPTION, { ping, diff })
await resolveKey(interaction, LanguageKeys.Commands.Core.PingCommand.PING_SUCCESS_DESCRIPTION, {
ping,
diff
})
);

return interaction.editReply({
content: `${await resolveKey(interaction, LanguageKeys.Commands.Ping.PING_SUCCESS)}`,
content: `${await resolveKey(interaction, LanguageKeys.Commands.Core.PingCommand.PING_SUCCESS)}`,
embeds: [embed]
});
}
Expand Down
14 changes: 6 additions & 8 deletions src/commands/developer/EvaluateCommand.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { ChatInputCommand, Command, RegisterBehavior } from "@sapphire/framework";
import { ApplyOptions } from "@sapphire/decorators";
import { ComponentType } from "discord.js";
import { clean } from "../../libraries/utils/common/text";
import { BaseEmbedBuilder, deleteBtn } from "../../libraries/structures/components";

@ApplyOptions<Command.Options>({
name: "eval",
fullCategory: ["Developer"],
preconditions: ["DeveloperOnlyPrecondition"]
})
export class EvalCommand extends Command {
public constructor(context: Command.Context, options: Command.Options) {
super(context, {
...options,
fullCategory: ["Developer"],
preconditions: ["DeveloperOnlyPrecondition"]
});
}

public override registerApplicationCommands(registry: ChatInputCommand.Registry) {
registry.registerChatInputCommand(
(builder) =>
Expand Down
57 changes: 35 additions & 22 deletions src/commands/entertainment/ChatCommand.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { ChatInputCommand, Command, RegisterBehavior } from "@sapphire/framework";
import { fetchResponseFromAI } from "../../libraries/utils/common/fetch";
import { BaseEmbedBuilder } from "../../libraries/structures/components";
import { ApplyOptions } from "@sapphire/decorators";
import { resolveKey } from "@sapphire/plugin-i18next";
import { fetchResponseFromAI, pingServer } from "../../libraries/utils/common/fetch";
import { BaseEmbedBuilder } from "../../libraries/structures/components";
import { LanguageKeys } from "../../libraries/language";
import { removeSymbols } from "../../libraries/utils/common/text";

@ApplyOptions<Command.Options>({
name: "chat",
fullCategory: ["Entertainment"]
})
export class ChatCommand extends Command {
public constructor(context: Command.Context, options: Command.Options) {
super(context, {
...options,
name: "chat",
fullCategory: ["Entertainment"]
});
}

public override registerApplicationCommands(registry: ChatInputCommand.Registry) {
registry.registerChatInputCommand(
(builder) =>
Expand All @@ -35,22 +33,37 @@ export class ChatCommand extends Command {
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
const message = interaction.options.getString("message");
const silent = interaction.options.getBoolean("silent") ?? false;
const serverStatus = await pingServer();
const embed = new BaseEmbedBuilder();
const response: { role: string; content: string } = await fetchResponseFromAI(
String(message),
interaction.user.id
);

await interaction.deferReply({ ephemeral: silent });

if (response.content) {
await new Promise((resolve) => setTimeout(resolve, 2000));
return interaction.editReply({ content: response.content });
if (serverStatus === true) {
await fetchResponseFromAI(String(message), removeSymbols(interaction.user.username))
.then((res) => {
return interaction.editReply({
content: res.content
});
})
.catch(async () => {
embed
.isErrorEmbed()
.setDescription(
await resolveKey(interaction, LanguageKeys.Commands.Entertainment.ChatCommand.CHAT_FAILED)
);
return interaction.editReply({
embeds: [embed]
});
});
} else {
embed
.isErrorEmbed()
.setDescription(
await resolveKey(interaction, LanguageKeys.Commands.Entertainment.ChatCommand.CHAT_SERVER_DOWN)
);
return interaction.editReply({
embeds: [embed]
});
}

embed.isErrorEmbed().setDescription(await resolveKey(interaction, LanguageKeys.Commands.Chat.CHAT_FAILED));
return interaction.editReply({
embeds: [embed]
});
}
}
32 changes: 28 additions & 4 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { BucketScope, LogLevel } from "@sapphire/framework";
import { container } from "@sapphire/pieces";
import { InternationalizationContext } from "@sapphire/plugin-i18next";
import { ClientOptions, GatewayIntentsString } from "discord.js";

Expand Down Expand Up @@ -34,14 +36,36 @@ const INTENTS: GatewayIntentsString[] = [
export const CLIENT_OPTIONS: ClientOptions = {
intents: INTENTS,
allowedMentions: { parse: ["users", "roles"], repliedUser: true },
defaultCooldown: { delay: 3000 },
defaultCooldown: {
delay: 10_000,
filteredUsers: DEV_USER_IDS,
limit: 2,
scope: BucketScope.User
},
defaultPrefix: "/",
loadMessageCommandListeners: false,
enableLoaderTraceLoggings: false,
i18n: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
fetchLanguage(context: InternationalizationContext) {
return "en-US";
fetchLanguage: async (context: InternationalizationContext) => {
if (!context.guild) return "en-US";

const dbLang = await container.database.guildConfig.findUnique({
where: { guildId: context.guild.id }
});

if (!dbLang) {
await container.database.guildConfig.create({
data: {
guildId: context.guild.id,
language: "en-US"
}
});
}

return dbLang?.language as string;
}
},
logger: {
level: process.env.NODE_ENV === "production" ? LogLevel.Info : LogLevel.Debug
}
};
3 changes: 0 additions & 3 deletions src/languages/en-US/commands/chat.json

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"HELP_HELPCMD_DESCRIPTION": "Display the help menu or help for a specific command.",
"HELP_PINGCMD_DESCRIPTION": "Check if the bot is alive.",
"HELP_CHATCMD_DESCRIPTION": "Chat to Akari."
"HELP_CHATCMD_DESCRIPTION": "Chat to Akari.",
"HELP_LANGCMD_DESCRIPTION": "Change the language of the bot for the server."
}
5 changes: 5 additions & 0 deletions src/languages/en-US/commands/entertainment/chat.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"CHAT_FAILED": "An error occurred while fetching response from AI. Please try again.",
"CHAT_TIMEOUT": "AI server is taking too long to respond. Please try again later.",
"CHAT_SERVER_DOWN": "AI server is down. Please try again later."
}
1 change: 0 additions & 1 deletion src/libraries/language/keys/commands/chat.ts

This file was deleted.

4 changes: 4 additions & 0 deletions src/libraries/language/keys/commands/core/help.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const HELP_HELPCMD_DESCRIPTION = "commands/core/help:HELP_HELPCMD_DESCRIPTION";
export const HELP_PINGCMD_DESCRIPTION = "commands/core/help:HELP_PINGCMD_DESCRIPTION";
export const HELP_CHATCMD_DESCRIPTION = "commands/core/help:HELP_CHATCMD_DESCRIPTION";
export const HELP_LANGCMD_DESCRIPTION = "commands/core/help:HELP_LANGCMD_DESCRIPTION";
Loading

0 comments on commit 4737c01

Please sign in to comment.