diff --git a/.env.example b/.env.example index 674b0f8..9c92b48 100644 --- a/.env.example +++ b/.env.example @@ -7,7 +7,7 @@ # any secret values. BOT_TOKEN= -BOT_VER=3.2.1 +BOT_VER=3.3.0 BOT_AUTHOR=Vylpes BOT_OWNERID=147392775707426816 BOT_CLIENTID=682942374040961060 @@ -23,4 +23,6 @@ DB_NAME=vylbot DB_AUTH_USER=dev DB_AUTH_PASS=dev DB_SYNC=true -DB_LOGGING=true \ No newline at end of file +DB_LOGGING=true +DB_ROOT_HOST=0.0.0.0 +DB_DATA_LOCATION=./.temp/database diff --git a/.gitignore b/.gitignore index ce1b308..c6754e1 100644 --- a/.gitignore +++ b/.gitignore @@ -106,4 +106,4 @@ dist config.json .DS_Store ormconfig.json -.temp/ \ No newline at end of file +.temp/ diff --git a/database/3.3.0/1719856023429-CreateMoon/Up/01-Moon.sql b/database/3.3.0/1719856023429-CreateMoon/Up/01-Moon.sql new file mode 100644 index 0000000..e7aecab --- /dev/null +++ b/database/3.3.0/1719856023429-CreateMoon/Up/01-Moon.sql @@ -0,0 +1,10 @@ +CREATE TABLE `moon` ( + `Id` varchar(255) NOT NULL, + `WhenCreated` datetime NOT NULL, + `WhenUpdated` datetime NOT NULL, + `MoonNumber` int NOT NULL, + `UserId` varchar(255) NOT NULL, + `Description` varchar(255) NOT NULL + `WhenArchived` datetime NULL, +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + diff --git a/src/buttonEvents/moons.ts b/src/buttonEvents/moons.ts new file mode 100644 index 0000000..726f209 --- /dev/null +++ b/src/buttonEvents/moons.ts @@ -0,0 +1,14 @@ +import {ButtonInteraction} from "discord.js"; +import {ButtonEvent} from "../type/buttonEvent"; +import List from "./moons/list"; + +export default class Moons extends ButtonEvent { + public override async execute(interaction: ButtonInteraction): Promise { + const action = interaction.customId.split(" ")[1]; + + switch (action) { + case "list": + await List(interaction); + } + } +} diff --git a/src/buttonEvents/moons/list.ts b/src/buttonEvents/moons/list.ts new file mode 100644 index 0000000..9309dbe --- /dev/null +++ b/src/buttonEvents/moons/list.ts @@ -0,0 +1,53 @@ +import {ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, EmbedBuilder} from "discord.js"; +import Moon from "../../database/entities/Moon"; +import EmbedColours from "../../constants/EmbedColours"; + +export default async function List(interaction: ButtonInteraction) { + if (!interaction.guild) return; + + const userId = interaction.customId.split(" ")[2]; + const page = interaction.customId.split(" ")[3]; + + if (!userId || !page) return; + + const pageNumber = Number(page); + + const member = interaction.guild.members.cache.find(x => x.user.id == userId); + + const pageLength = 10; + + const moons = await Moon.FetchPaginatedMoonsByUserId(userId, pageLength, pageNumber); + + if (!moons || moons[0].length == 0) { + await interaction.reply(`${member?.user.username ?? "This user"} does not have any moons or page is invalid.`); + return; + } + + const totalPages = Math.ceil(moons[1] / pageLength); + + const description = moons[0].flatMap(x => `${x.MoonNumber}. ${x.Description.slice(0, 15)}`); + + const embed = new EmbedBuilder() + .setTitle(`${member?.user.username}'s Moons`) + .setColor(EmbedColours.Ok) + .setDescription(description.join("\n")) + .setFooter({ text: `Page ${page + 1} of ${totalPages} · ${moons[1]} moons` }); + + const row = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId(`moons list ${userId} ${pageNumber - 1}`) + .setLabel("Previous") + .setStyle(ButtonStyle.Primary) + .setDisabled(pageNumber == 0), + new ButtonBuilder() + .setCustomId(`moons list ${userId} ${pageNumber + 1}`) + .setLabel("Next") + .setStyle(ButtonStyle.Primary) + .setDisabled(pageNumber + 1 == totalPages)); + + await interaction.update({ + embeds: [ embed ], + components: [ row ], + }); +} diff --git a/src/commands/304276391837302787/moons.ts b/src/commands/304276391837302787/moons.ts new file mode 100644 index 0000000..13f0f98 --- /dev/null +++ b/src/commands/304276391837302787/moons.ts @@ -0,0 +1,35 @@ +import { Command } from "../../type/command"; +import { CommandInteraction, SlashCommandBuilder } from "discord.js"; +import ListMoons from "./moons/list"; + +export default class Moons extends Command { + constructor() { + super(); + + this.CommandBuilder = new SlashCommandBuilder() + .setName("moons") + .setDescription("View and create moons") + .addSubcommand(subcommand => + subcommand + .setName('list') + .setDescription('List moons you have obtained') + .addUserOption(option => + option + .setName("user") + .setDescription("The user to view (Defaults to yourself)")) + .addNumberOption(option => + option + .setName("page") + .setDescription("The page to start with"))); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + + switch (interaction.options.getSubcommand()) { + case "list": + await ListMoons(interaction); + break; + } + } +} diff --git a/src/commands/304276391837302787/moons/list.ts b/src/commands/304276391837302787/moons/list.ts new file mode 100644 index 0000000..8727124 --- /dev/null +++ b/src/commands/304276391837302787/moons/list.ts @@ -0,0 +1,45 @@ +import {ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder} from "discord.js"; +import Moon from "../../../database/entities/Moon"; +import EmbedColours from "../../../constants/EmbedColours"; + +export default async function ListMoons(interaction: CommandInteraction) { + const user = interaction.options.get("user")?.user ?? interaction.user; + const page = interaction.options.get("page")?.value as number ?? 0; + + const pageLength = 10; + + const moons = await Moon.FetchPaginatedMoonsByUserId(user.id, pageLength, page); + + if (!moons || moons[0].length == 0) { + await interaction.reply(`${user.username} does not have any moons or page is invalid.`); + return; + } + + const totalPages = Math.ceil(moons[1] / pageLength); + + const description = moons[0].flatMap(x => `${x.MoonNumber}. ${x.Description.slice(0, 15)}`); + + const embed = new EmbedBuilder() + .setTitle(`${user.username}'s Moons`) + .setColor(EmbedColours.Ok) + .setDescription(description.join("\n")) + .setFooter({ text: `Page ${page + 1} of ${totalPages} · ${moons[1]} moons` }); + + const row = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId(`moons list ${user.id} ${page - 1}`) + .setLabel("Previous") + .setStyle(ButtonStyle.Primary) + .setDisabled(page == 0), + new ButtonBuilder() + .setCustomId(`moons list ${user.id} ${page + 1}`) + .setLabel("Next") + .setStyle(ButtonStyle.Primary) + .setDisabled(page + 1 == totalPages)); + + await interaction.reply({ + embeds: [ embed ], + components: [ row ], + }); +} diff --git a/src/database/entities/Moon.ts b/src/database/entities/Moon.ts new file mode 100644 index 0000000..566d1d4 --- /dev/null +++ b/src/database/entities/Moon.ts @@ -0,0 +1,49 @@ +import { Column, Entity, IsNull } from "typeorm"; +import BaseEntity from "../../contracts/BaseEntity"; +import AppDataSource from "../dataSources/appDataSource"; + +@Entity() +export default class Moon extends BaseEntity { + constructor(moonNumber: number, description: string, userId: string) { + super(); + + this.MoonNumber = moonNumber; + this.Description = description; + this.UserId = userId; + } + + @Column() + MoonNumber: number; + + @Column() + Description: string; + + @Column({ nullable: true }) + WhenArchived?: Date; + + @Column() + UserId: string; + + public static async FetchMoonsByUserId(userId: string): Promise { + const repository = AppDataSource.getRepository(Moon); + + const all = await repository.find({ where: { UserId: userId } }); + + return all; + } + + public static async FetchPaginatedMoonsByUserId(userId: string, pageLength: number, page: number): Promise<[ Moon[], number ]> { + const rangeStart = page * pageLength; + + const repository = AppDataSource.getRepository(Moon); + + const moons = await repository.findAndCount({ + where: { UserId: userId, WhenArchived: IsNull() }, + order: { MoonNumber: "ASC" }, + skip: rangeStart, + take: pageLength, + }); + + return moons; + } +} diff --git a/src/database/migrations/3.3/1719856023429-CreateMoon.ts b/src/database/migrations/3.3/1719856023429-CreateMoon.ts new file mode 100644 index 0000000..0ccc820 --- /dev/null +++ b/src/database/migrations/3.3/1719856023429-CreateMoon.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm" +import MigrationHelper from "../../../helpers/MigrationHelper" + +export class CreateMoon1719856023429 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + MigrationHelper.Up('1719856023429-CreateMoon', '3.3.0', [ + "01-Moon", + ], queryRunner); + } + + public async down(queryRunner: QueryRunner): Promise { + } + +} diff --git a/src/registry.ts b/src/registry.ts index 4414ab2..6c80edb 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -28,6 +28,9 @@ import AddLobby from "./commands/501231711271780357/Lobby/add"; import RemoveLobby from "./commands/501231711271780357/Lobby/remove"; import ListLobby from "./commands/501231711271780357/Lobby/list"; +// Command Imports: Potato Talk +import Moons from "./commands/304276391837302787/moons"; + // Event Imports import GuildMemberAdd from "./events/MemberEvents/GuildMemberAdd"; import GuildMemberRemove from "./events/MemberEvents/GuildMemberRemove"; @@ -38,6 +41,7 @@ import MessageCreate from "./events/MessageEvents/MessageCreate"; // Button Event Imports import Verify from "./buttonEvents/verify"; +import MoonsButtonEvent from "./buttonEvents/moons"; export default class Registry { public static RegisterCommands() { @@ -68,12 +72,16 @@ export default class Registry { CoreClient.RegisterCommand("listlobby", new ListLobby(), "501231711271780357"); CoreClient.RegisterCommand("entry", new Entry(), "501231711271780357"); + // Exclusive Commands: Potato Talk + CoreClient.RegisterCommand("moons", new Moons(), "304276391837302787"); + // Add Exclusive Commands to Test Server CoreClient.RegisterCommand("lobby", new Lobby(), "442730357897429002"); CoreClient.RegisterCommand("addlobby", new AddLobby(), "442730357897429002"); CoreClient.RegisterCommand("removelobby", new RemoveLobby(), "442730357897429002"); CoreClient.RegisterCommand("listlobby", new ListLobby(), "442730357897429002"); CoreClient.RegisterCommand("entry", new Entry(), "442730357897429002"); + CoreClient.RegisterCommand("moons", new Moons(), "442730357897429002"); } public static RegisterEvents() { @@ -88,5 +96,6 @@ export default class Registry { public static RegisterButtonEvents() { CoreClient.RegisterButtonEvent("verify", new Verify()); + CoreClient.RegisterButtonEvent("moons", new MoonsButtonEvent()); } }