From 14a121754958dae488386c92a1e9c2a9eed194fe Mon Sep 17 00:00:00 2001 From: TheClashFruit Date: Sat, 10 Feb 2024 10:59:02 +0100 Subject: [PATCH] feat: a lot of improvements --- .gitignore | 3 ++- data/default.json5 | 20 +++++++++++------ package.json | 5 +++++ pnpm-lock.yaml | 14 ++++++++++++ src/commands/craft.ts | 30 +++++++++++++++----------- src/commands/export.ts | 43 +++++++++++++++++++++++++++++++++++++ src/commands/import.ts | 38 ++++++++++++++++++++++++++------ src/commands/inventory.ts | 23 +++++++++----------- src/index.ts | 27 +++++++++++++++++------ src/types.d.ts | 30 ++++++++++++++++++++++++++ src/types/command.type.ts | 8 ------- src/types/inventory.type.ts | 7 ------ src/util/Config.ts | 26 ++++++++++++++++++++++ src/util/Data.ts | 28 ++++++++++++++++++++++++ src/util/ErrorMessage.ts | 21 ++++++++++++++++++ src/util/Inventory.ts | 40 ++++++++++++++++++++++++++++++++++ src/util/InventoryUtil.ts | 42 ------------------------------------ tsconfig.json | 7 +++++- 18 files changed, 308 insertions(+), 104 deletions(-) create mode 100644 src/commands/export.ts create mode 100644 src/types.d.ts delete mode 100644 src/types/command.type.ts delete mode 100644 src/types/inventory.type.ts create mode 100644 src/util/Config.ts create mode 100644 src/util/Data.ts create mode 100644 src/util/ErrorMessage.ts create mode 100644 src/util/Inventory.ts delete mode 100644 src/util/InventoryUtil.ts diff --git a/.gitignore b/.gitignore index 5a7bd5e..db70dcc 100644 --- a/.gitignore +++ b/.gitignore @@ -130,7 +130,8 @@ dist .yarn/install-state.gz .pnp.* -# data +# data, tmp +tmp/ data/*.json5 !data/default.json5 diff --git a/data/default.json5 b/data/default.json5 index 6b17a27..bb53737 100644 --- a/data/default.json5 +++ b/data/default.json5 @@ -1,6 +1,14 @@ -[ - { text: 'Water', emoji: '💧', discovered: false }, - { text: 'Fire', emoji: '🔥', discovered: false }, - { text: 'Wind', emoji: '🌬️' , discovered: false }, - { text: 'Earth', emoji: '🌍', discovered: false } -] \ No newline at end of file +{ + config: { + showUsername: true, + inventory: { + items: 10 + } + }, + inventory: [ + { text: 'Water', emoji: '💧', discovered: false }, + { text: 'Fire', emoji: '🔥', discovered: false }, + { text: 'Wind', emoji: '🌬️' , discovered: false }, + { text: 'Earth', emoji: '🌍', discovered: false } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 247a526..93988b8 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,17 @@ "discord.js": "^14.14.1", "dotenv": "^16.4.1", "json5": "^2.2.3", + "module-alias": "^2.2.3", "node-fetch": "^3.3.2" }, "devDependencies": { + "@types/module-alias": "^2.0.4", "@types/node": "^20.11.17", "nodemon": "^3.0.3", "ts-node": "^10.9.2", "typescript": "^5.3.3" + }, + "_moduleAliases": { + "@": "./src" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2a30cd1..68191f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,11 +14,17 @@ dependencies: json5: specifier: ^2.2.3 version: 2.2.3 + module-alias: + specifier: ^2.2.3 + version: 2.2.3 node-fetch: specifier: ^3.3.2 version: 3.3.2 devDependencies: + '@types/module-alias': + specifier: ^2.0.4 + version: 2.0.4 '@types/node': specifier: ^20.11.17 version: 20.11.17 @@ -164,6 +170,10 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true + /@types/module-alias@2.0.4: + resolution: {integrity: sha512-5+G/QXO/DvHZw60FjvbDzO4JmlD/nG5m2/vVGt25VN1eeP3w2bCoks1Wa7VuptMPM1TxJdx6RjO70N9Fw0nZPA==} + dev: true + /@types/node@20.11.17: resolution: {integrity: sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==} dependencies: @@ -417,6 +427,10 @@ packages: brace-expansion: 1.1.11 dev: true + /module-alias@2.2.3: + resolution: {integrity: sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==} + dev: false + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true diff --git a/src/commands/craft.ts b/src/commands/craft.ts index fa7aa9c..cf8c9a9 100644 --- a/src/commands/craft.ts +++ b/src/commands/craft.ts @@ -1,10 +1,10 @@ import { - SlashCommandBuilder, - Interaction + SlashCommandBuilder } from 'discord.js'; -import Inventory from "../types/inventory.type"; -import InventoryUtil from '../util/InventoryUtil'; +import { Item } from '@/types'; + +import Data from '@/util/Data'; module.exports = { builder: new SlashCommandBuilder() @@ -26,23 +26,27 @@ module.exports = { ), async autocomplete(interaction: any) { const focusedValue = interaction.options.getFocused().toLowerCase(); - const choices = new InventoryUtil(interaction.user.id).getInventory(); - const filtered = choices.filter(choice => choice.text.toLowerCase().startsWith(focusedValue)); + await interaction.respond( - filtered.map(choice => ({ name: `${choice.emoji} ${choice.text}`, value: choice.text.toLowerCase() })).slice(0, 25), + new Data(interaction.user.id) + .inventory + .getInventory() + .filter(choice => choice.text.toLowerCase().startsWith(focusedValue)) + .map(choice => ({ name: `${choice.emoji} ${choice.text}`, value: choice.text.toLowerCase() })) + .slice(0, 25) ); }, async execute(interaction: any) { - const invUtil = new InventoryUtil(interaction.user.id); + const userData = new Data(interaction.user.id); - const inv: Inventory[] = invUtil.getInventory(); + const inv: Item[] = userData.inventory.getInventory(); const firstItem = interaction.options.getString('first_item'); const secondItem = interaction.options.getString('second_item'); if ( - invUtil.hasItem(firstItem) && - invUtil.hasItem(secondItem) + userData.inventory.hasItem(firstItem) && + userData.inventory.hasItem(secondItem) ) { await interaction.deferReply(); @@ -56,8 +60,8 @@ module.exports = { .then(data => { interaction.editReply('```json\n' + JSON.stringify(data, null, 2) + '```'); - if(!invUtil.hasItem(data.result)) { - invUtil.addItem({ + if(!userData.inventory.hasItem(data.result)) { + userData.inventory.addItem({ text: data.result, emoji: data.emoji, discovered: data.isNew diff --git a/src/commands/export.ts b/src/commands/export.ts new file mode 100644 index 0000000..38bc669 --- /dev/null +++ b/src/commands/export.ts @@ -0,0 +1,43 @@ +import { + SlashCommandBuilder +} from "discord.js"; + +import fs from 'fs'; + +import Data from "@/util/Data"; +import errorMessage from "@/util/ErrorMessage"; + +module.exports = { + builder: new SlashCommandBuilder() + .setName('export') + .setDescription('Export inventory to a json file.') + .addUserOption(option => + option + .setName('user') + .setDescription('User to export the inventory of.') + .setRequired(false) + ), + async execute(interaction: any) { + try { + if(interaction.options.getUser('user')) { + const invUtil = new Data(interaction.options.getUser('user').id).inventory; + + const inv = invUtil.getInventory(); + + fs.writeFileSync(`./tmp/${interaction.options.getUser('user').id}.json`, JSON.stringify({elements: inv}, null, 2)); + + await interaction.reply({ files: [ `./tmp/${interaction.options.getUser('user').id}.json` ] }); + } + + const invUtil = new Data(interaction.user.id).inventory; + + const inv = invUtil.getInventory(); + + fs.writeFileSync(`./tmp/${interaction.user.id}.json`, JSON.stringify({elements: inv}, null, 2)); + + await interaction.reply({ files: [ `./tmp/${interaction.user.id}.json` ] }); + } catch (e: any) { + await interaction.reply({ embeds: [ errorMessage('There was an error while exporting!', e) ], ephemeral: true }); + } + } +} \ No newline at end of file diff --git a/src/commands/import.ts b/src/commands/import.ts index c61b3f2..0484890 100644 --- a/src/commands/import.ts +++ b/src/commands/import.ts @@ -1,5 +1,9 @@ -import {SlashCommandBuilder} from "discord.js"; -import InventoryUtil from "../util/InventoryUtil"; +import { + SlashCommandBuilder +} from "discord.js"; + +import Data from "@/util/Data"; +import errorMessage from "@/util/ErrorMessage"; module.exports = { builder: new SlashCommandBuilder() @@ -12,22 +16,42 @@ module.exports = { .setRequired(true) ), async execute(interaction: any) { - const invUtil = new InventoryUtil(interaction.user.id); + const invUtil = new Data(interaction.user.id).inventory; try { + if(interaction.options.getAttachment('file').size > 1048576) { + const size = (interaction.options.getAttachment('file').size / 1024) / 1024; + + await interaction.reply(`The file you provided is too large. It must be less than 1 MiB, your file is ${size.toFixed(2)} MiB.`); + + return; + } + fetch(interaction.options.getAttachment('file').attachment) .then(res => res.text()) .then(async data => { try { + if(!JSON.parse(data).elements) { + await interaction.reply('The file you provided is not a valid json file.'); + + return; + } + invUtil.setInventory(JSON.parse(data).elements); await interaction.reply('Import successful!'); - } catch (e) { - await interaction.reply('There was an error while importing.\n```\n' + e + '\n```'); + } catch (e: any) { + if(e.name == 'SyntaxError') { + await interaction.reply('The file you provided is not a valid json file.'); + + return; + } + + await interaction.reply({ embeds: [ errorMessage('There was an error while importing!', e) ], ephemeral: true }); } }); - } catch (e) { - await interaction.reply('There was an error while importing.\n```\n' + e + '\n```'); + } catch (e: any) { + await interaction.reply({ embeds: [ errorMessage('There was an error while importing!', e) ], ephemeral: true }); } } } \ No newline at end of file diff --git a/src/commands/inventory.ts b/src/commands/inventory.ts index db4a298..d410455 100644 --- a/src/commands/inventory.ts +++ b/src/commands/inventory.ts @@ -1,14 +1,11 @@ import { SlashCommandBuilder, - Interaction, - EmbedBuilder, Embed + EmbedBuilder, } from 'discord.js'; -import fs from "fs"; -import Inventory from "../types/inventory.type"; -import JSON5 from 'json5'; -import InventoryUtil from "../util/InventoryUtil"; -import {it} from "node:test"; +import { Item } from '@/types'; + +import Data from '@/util/Data'; module.exports = { builder: new SlashCommandBuilder() @@ -28,7 +25,7 @@ module.exports = { .setRequired(false) ), async execute(interaction: any) { - const inv: Inventory[] = new InventoryUtil(interaction.user.id).getInventory(); + const inv: Item[] = new Data(interaction.user.id).inventory.getInventory(); let resEmbed: EmbedBuilder = new EmbedBuilder(); @@ -43,7 +40,7 @@ module.exports = { if(interaction.options.getUser('user')) { const user = interaction.options.getUser('user').id; - const userInv: Inventory[] = new InventoryUtil(user).getInventory(); + const userInv: Item[] = new Data(user).inventory.getInventory(); resEmbed.setColor(interaction.options.getUser('user').accentColor | 0x5865F2); resEmbed.setTitle(`${interaction.options.getUser('user').displayName}'${interaction.options.getUser('user').displayName.endsWith('s') ? '' : 's'} Inventory`); @@ -62,7 +59,7 @@ module.exports = { inline: true }); - if (items.split('\n').slice(10).length > 0) { + if (items.split('\n').slice(10).length > 1) { fields.push({ name: ' ', value: items.split('\n').slice(10).join('\n'), @@ -76,7 +73,7 @@ module.exports = { text: `Page ${page} of ${Math.ceil(userInv.length / 20)} (${userInv.length} items)` }); - await interaction.reply({ embeds: [resEmbed] }); + await interaction.reply({ embeds: [ resEmbed ], ephemeral: true }); return; } @@ -101,7 +98,7 @@ module.exports = { inline: true }); - if (items.split('\n').slice(10).length > 0) { + if (items.split('\n').slice(10).length > 1) { fields.push({ name: ' ', value: items.split('\n').slice(10).join('\n'), @@ -111,6 +108,6 @@ module.exports = { resEmbed.addFields(fields); - await interaction.reply({ embeds: [resEmbed] }); + await interaction.reply({ embeds: [ resEmbed ], ephemeral: true }); }, }; diff --git a/src/index.ts b/src/index.ts index e32acc1..d7596ff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,10 @@ -import 'dotenv/config' +import 'dotenv/config'; +import 'module-alias/register'; -import {Activity, Client, ActivityType, GatewayIntentBits, REST, Routes} from 'discord.js'; +import {Activity, Client, ActivityType, GatewayIntentBits, REST, Routes, EmbedBuilder} from 'discord.js'; import fs from 'fs'; -import Command from "./types/command.type"; +import { Command } from '@/types'; const client = new Client({ intents: [GatewayIntentBits.Guilds] }); @@ -31,7 +32,7 @@ const rest = new REST().setToken(process.env.TOKEN!); // The put method is used to fully refresh all commands in the guild with the current set const data = await rest.put( - Routes.applicationCommands('872411739155726336'), + Routes.applicationGuildCommands('872411739155726336', '1127731341283307520'), { body: commandsArray.map(cmd => cmd.builder) }, ); @@ -56,9 +57,23 @@ client.on('interactionCreate', async interaction => { try { await command.execute(interaction); - } catch (error) { + } catch (error: any) { console.error(error); - await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true }); + + const errorEmbed = + new EmbedBuilder() + .setTitle('Error') + .setDescription('There was an error while executing this command!') + .setColor(0xff0000) + .addFields([ + { + name: 'Error', + value: `\`\`\`\n${error.stack}\n\`\`\`` + } + ]) + .setTimestamp(); + + await interaction.reply({ embeds: [ errorEmbed ], ephemeral: true }); } } diff --git a/src/types.d.ts b/src/types.d.ts new file mode 100644 index 0000000..2696f28 --- /dev/null +++ b/src/types.d.ts @@ -0,0 +1,30 @@ +import { + SlashCommandBuilder +} from 'discord.js'; + +type Item = { + text: string, + emoji: string, + discovered: boolean +} + +type Data = { + config: { + showUsername: boolean, + inventory: { + items: number + } + }, + inventory: Item[] +} + +type Command = { + builder: SlashCommandBuilder, + execute: (interaction: any) => void +} + +export { + Item, + Command, + Data +}; \ No newline at end of file diff --git a/src/types/command.type.ts b/src/types/command.type.ts deleted file mode 100644 index 5becf8f..0000000 --- a/src/types/command.type.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {SlashCommandBuilder} from "discord.js"; - -type Command = { - builder: SlashCommandBuilder, - execute: (interaction: any) => void -} - -export default Command; \ No newline at end of file diff --git a/src/types/inventory.type.ts b/src/types/inventory.type.ts deleted file mode 100644 index b217e8e..0000000 --- a/src/types/inventory.type.ts +++ /dev/null @@ -1,7 +0,0 @@ -type Inventory = { - text: string, - emoji: string, - discovered: boolean -} - -export default Inventory; \ No newline at end of file diff --git a/src/util/Config.ts b/src/util/Config.ts new file mode 100644 index 0000000..eff1ac9 --- /dev/null +++ b/src/util/Config.ts @@ -0,0 +1,26 @@ +import fs from 'fs'; +import JSON5 from 'json5'; +import Inventory from "@/util/Inventory"; + +class Config { + id: string; + inventory: Inventory; + + constructor(id: string) { + this.id = id; + this.inventory = new Inventory(id); + } + + getConfig() { + if(!fs.existsSync(`./data/${this.id}.json5`)) + fs.writeFileSync(`./data/${this.id}.json5`, JSON5.stringify(Config.getDefaultConfig(), null, 2)); + + return JSON5.parse(fs.readFileSync(`./data/${this.id}.json5`, 'utf-8')); + } + + static getDefaultConfig() { + return JSON5.parse(fs.readFileSync('./data/default.json5', 'utf-8')); + } +} + +export default Config; \ No newline at end of file diff --git a/src/util/Data.ts b/src/util/Data.ts new file mode 100644 index 0000000..85dbe05 --- /dev/null +++ b/src/util/Data.ts @@ -0,0 +1,28 @@ +import Config from "@/util/Config"; +import Inventory from "@/util/Inventory"; + +import fs from 'fs'; +import JSON5 from 'json5'; + +class Data { + id: string; + + config: Config; + inventory: Inventory; + + constructor(id: string) { + this.id = id; + + if(!fs.existsSync(`./data/${this.id}.json5`)) + fs.writeFileSync(`./data/${this.id}.json5`, JSON5.stringify(Data.getDefaultData(), null, 2)); + + this.config = new Config(id); + this.inventory = new Inventory(id); + } + + static getDefaultData() { + return JSON5.parse(fs.readFileSync('./data/default.json5', 'utf-8')); + } +} + +export default Data; \ No newline at end of file diff --git a/src/util/ErrorMessage.ts b/src/util/ErrorMessage.ts new file mode 100644 index 0000000..5fb4061 --- /dev/null +++ b/src/util/ErrorMessage.ts @@ -0,0 +1,21 @@ +import { + EmbedBuilder +} from "discord.js"; + +const errorMessage = (msg: string, e: any) => { + console.error(e); + + return new EmbedBuilder() + .setTitle('Error') + .setDescription('There was an error while importing!') + .setColor(0xff0000) + .addFields([ + { + name: 'Stack Trace', + value: `\`\`\`\n${e.stack}\n\`\`\`` + } + ]) + .setTimestamp(); +} + +export default errorMessage; \ No newline at end of file diff --git a/src/util/Inventory.ts b/src/util/Inventory.ts new file mode 100644 index 0000000..1b49663 --- /dev/null +++ b/src/util/Inventory.ts @@ -0,0 +1,40 @@ +import { Item as InvType, Data as DataType } from "@/types"; + +import JSON5 from "json5"; +import fs from "fs"; + +class Inventory { + id: string; + + constructor(id: string) { + this.id = id; + } + + getInventory(): InvType[] { + return JSON5.parse(fs.readFileSync(`./data/${this.id}.json5`, 'utf-8')).inventory; + } + + hasItem(item: string): boolean { + const inv: InvType[] = this.getInventory(); + + return inv.some(i => i.text.toLowerCase() === item.toLowerCase()); + } + + addItem(item: InvType): void { + const inv: InvType[] = this.getInventory(); + + inv.push(item); + + this.setInventory(inv); + } + + setInventory(inv: InvType[]): void { + const data: DataType = JSON5.parse(fs.readFileSync(`./data/${this.id}.json5`, 'utf-8')); + + data.inventory = inv; + + fs.writeFileSync(`./data/${this.id}.json5`, JSON5.stringify(data, null, 2)); + } +} + +export default Inventory; \ No newline at end of file diff --git a/src/util/InventoryUtil.ts b/src/util/InventoryUtil.ts deleted file mode 100644 index 21e9131..0000000 --- a/src/util/InventoryUtil.ts +++ /dev/null @@ -1,42 +0,0 @@ -import Inventory from "../types/inventory.type"; -import JSON5 from "json5"; -import fs from "fs"; - -class InventoryUtil { - id: string; - - constructor(id: string) { - this.id = id; - } - - getInventory(): Inventory[] { - if(!fs.existsSync(`./data/${this.id}.json5`)) - fs.writeFileSync(`./data/${this.id}.json5`, JSON5.stringify(InventoryUtil.getDefaultInventory())); - - return JSON5.parse(fs.readFileSync(`./data/${this.id}.json5`, 'utf-8')); - } - - static getDefaultInventory(): Inventory[] { - return JSON5.parse(fs.readFileSync('./data/default.json5', 'utf-8')); - } - - hasItem(item: string): boolean { - const inv: Inventory[] = this.getInventory(); - - return inv.some(i => i.text.toLowerCase() === item.toLowerCase()); - } - - addItem(item: Inventory): void { - const inv: Inventory[] = this.getInventory(); - - inv.push(item); - - fs.writeFileSync(`./data/${this.id}.json5`, JSON5.stringify(inv)); - } - - setInventory(inv: Inventory[]): void { - fs.writeFileSync(`./data/${this.id}.json5`, JSON5.stringify(inv)); - } -} - -export default InventoryUtil; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 242c6f0..7c2f71d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -104,6 +104,11 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "skipLibCheck": true, /* Skip type checking all .d.ts files. */ + + "baseUrl": "./src", + "paths": { + "@/*": ["*"], + } } }