From 81d95ef102c44c13e9988c1a7033d806c129913c Mon Sep 17 00:00:00 2001
From: Ethan Lane <ethan@vylpes.com>
Date: Fri, 26 Jul 2024 18:09:48 +0100
Subject: [PATCH 1/5] Document how to start the bot

---
 README.md     |  60 ++++++++++++++++++++++++-
 docs/cards.md | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 179 insertions(+), 1 deletion(-)
 create mode 100644 docs/cards.md

diff --git a/README.md b/README.md
index 94f0c9c..ca4b03f 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,60 @@
-# card-drop
+# Card Drop
+
+Card Drop is a Discord Bot designed to allow users to "drop" random cards into
+a channel and have the ability to claim them for themselves or let others if
+they so choose.
+
+The cards are randomly chosen based on weights of their card type (i.e. Bronze
+is more common than Gold). The user who ran the drop command has 5 minutes to
+choose if they want the card to themselves before its claimable by anyone, or
+until the drop command is ran again.
+
+## Installation
+
+Downloads of the latest version can be found from the [GitHub Releases](https://github.com/vylpes/card-drop/releases)
+or [Forgejo Releases](https://git.vylpes.xyz/external/card-drop/releases) page.
+
+Copy the config template file and fill in the strings.
+
+## Requirements
+
+- NodeJS
+- Yarn
+- Docker
+
+## Usage
+
+Install the dependencies and build the app:
+
+```bash
+yarn Install
+yarn build
+```
+
+Setup the database (Recommended to use the docker-compose file
+
+```bash
+docker compose up -d
+```
+
+Copy and edit the settings file
+
+```bash
+cp .env.template .env
+```
+
+> **NOTE:** Make sure you do *not* check in these files! These contain
+sensitive information and should be treated as private.
+
+If you're not using `DB_SYNC=true` in `.env`, make sure to migrate the database
+
+```bash
+yarn db:up
+```
+
+Start the bot
+
+```bash
+yarn start
+```
 
diff --git a/docs/cards.md b/docs/cards.md
new file mode 100644
index 0000000..f0be28f
--- /dev/null
+++ b/docs/cards.md
@@ -0,0 +1,120 @@
+# Cards
+
+This document will describe how to add cards to the bot. This is from the
+perspective of the development side and doesn't go into details of syncing
+from an external place such as with the Google Drive Sync function.
+
+The cards will be put into the `$DATA_DIR/cards` folder. `$DATA_DIR` is
+configured in the `.env` file.
+
+## Folder Structure
+
+The general structure of the cards folder is as follows:
+
+```
+cards           # The main cards folder
+| Series 1      # Series folder
+| | BRONZE      # Type folder
+| | | 1000.jpg  # Card image
+| | | 1001.jpg
+| | 1.json      # Card metadata file
+| Series 2
+| | SILVER
+| | | 2000.jpg
+| | 2.json
+```
+
+- The root of the cards folder will have a folder foor each series
+- Each series will contain folders for each of the card types containing the
+  card images.
+- The series folder will also contain a metadata JSON folder containing the
+  metadata of the cards within that series.
+
+The bot when loading will search the cards folder recursively for each json,
+and then read them to determine what cards should be used for the bot.
+
+## Series Metadata
+
+An example of what the metadata files could look like are as follows:
+
+```json
+[
+    {
+        "id": 1,
+        "name": "Series 1",
+        "cards": [
+            {
+                "id": "1000",
+                "name": "Card 1000 of Series 1",
+                "type": 1,
+                "path": "Series 1/BRONZE/1000.jpg"
+            },
+            {
+                "id": "1001",
+                "name": "Card 1001 of Series 1",
+                "type": 1,
+                "path": "Series 2/BRONZE?1001.jpg",
+                "subseries": "Custom Series Name"
+            }
+        ]
+    }
+]
+```
+
+This file will load a series called "Series 1" with the id of 1, containing 2
+cards:
+- Card 1000, with type 1 (Bronze), with its image located at (from root)
+  "Series 1/BRONZE/1000.jpg"
+- Card 1001 is the same, except has a custom "subseries" name which will
+  override the main series name if shown, helpful for an "other" category.
+
+### Card Type
+
+<table>
+    <thead>
+        <tr>
+            <th>Number</th>
+            <th>Name</th>
+            <th>Chance</th>
+            <th>Sacrifice Cost (Coins)</th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr>
+            <td>0</td>
+            <td>Unknown</td>
+            <td>-</td>
+            <td>-</td>
+        </tr>
+        <tr>
+            <td>1</td>
+            <td>Bronze</td>
+            <td>62%</td>
+            <td>5</td>
+        </tr>
+        <tr>
+            <td>2</td>
+            <td>Silver</td>
+            <td>31%</td>
+            <td>10</td>
+        </tr>
+        <tr>
+            <td>3</td>
+            <td>Gold</td>
+            <td>4.4%</td>
+            <td>30</td>
+        </tr>
+        <tr>
+            <td>4</td>
+            <td>Manga</td>
+            <td>2%</td>
+            <td>40</td>
+        </tr>
+        <tr>
+            <td>5</td>
+            <td>Legendary</td>
+            <td>0.6%</td>
+            <td>100</td>
+        </tr>
+    </tbody>
+</table>

From fef80709eefc87056ca1bc9f0704ed05c18bbb80 Mon Sep 17 00:00:00 2001
From: Ethan Lane <ethan@vylpes.com>
Date: Fri, 26 Jul 2024 18:28:32 +0100
Subject: [PATCH 2/5] Resolve ws to ^8.17.1 (#319)

- Upgrade ws to ^8.17.1 to fix vulnerability

#269

Reviewed-on: https://git.vylpes.xyz/External/card-drop/pulls/319
Reviewed-by: VylpesTester <tester@vylpes.com>
Co-authored-by: Ethan Lane <ethan@vylpes.com>
Co-committed-by: Ethan Lane <ethan@vylpes.com>
---
 package.json | 8 ++++----
 yarn.lock    | 8 ++++----
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/package.json b/package.json
index f37eae6..102b607 100644
--- a/package.json
+++ b/package.json
@@ -15,11 +15,11 @@
     "db:create": "typeorm migration:create ./src/database/migrations/app/new",
     "release": "np --no-publish"
   },
-  "repository": "https://gitea.vylpes.xyz/External/card-drop.git",
+  "repository": "https://git.vylpes.xyz/External/card-drop.git",
   "author": "Ethan Lane <ethan@vylpes.com>",
   "license": "MIT",
   "bugs": {
-    "url": "https//gitea.vylpes.xyz/External/card-drop/issues",
+    "url": "https//git.vylpes.xyz/External/card-drop/issues",
     "email": "helpdesk@vylpes.com"
   },
   "homepage": "https://gitea.vylpes.xyz/External/card-drop",
@@ -49,8 +49,8 @@
     "winston-daily-rotate-file": "^5.0.0",
     "winston-discord-transport": "^1.3.0"
   },
-  "overrides": {
-    "undici": "^5.28.3"
+  "resolutions": {
+      "**/ws": "^8.17.1"
   },
   "devDependencies": {
     "@types/node": "^20.0.0",
diff --git a/yarn.lock b/yarn.lock
index 48fe278..b953c94 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6838,10 +6838,10 @@ write-file-atomic@^4.0.2:
     imurmurhash "^0.1.4"
     signal-exit "^3.0.7"
 
-ws@^8.16.0:
-  version "8.17.0"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea"
-  integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==
+ws@^8.16.0, ws@^8.17.1:
+  version "8.18.0"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
+  integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
 
 xdg-basedir@^5.0.1, xdg-basedir@^5.1.0:
   version "5.1.0"

From 097b7284e6b910352313ff0c6d581792c694e01c Mon Sep 17 00:00:00 2001
From: Ethan Lane <ethan@vylpes.com>
Date: Fri, 26 Jul 2024 18:29:57 +0100
Subject: [PATCH 3/5] Fix list not being sorted (#320)

- Fix list on `/allbalance` command not being sorted by currency

#260

Reviewed-on: https://git.vylpes.xyz/External/card-drop/pulls/320
Reviewed-by: VylpesTester <tester@vylpes.com>
Co-authored-by: Ethan Lane <ethan@vylpes.com>
Co-committed-by: Ethan Lane <ethan@vylpes.com>
---
 src/commands/allbalance.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/commands/allbalance.ts b/src/commands/allbalance.ts
index 7db050a..0874e54 100644
--- a/src/commands/allbalance.ts
+++ b/src/commands/allbalance.ts
@@ -16,7 +16,8 @@ export default class AllBalance extends Command {
     public override async execute(interaction: CommandInteraction) {
         const users = await User.FetchAll(User);
 
-        const filteredUsers = users.filter(x => x.Currency > 0);
+        const filteredUsers = users.filter(x => x.Currency > 0)
+            .sort((a, b) => b.Currency - a.Currency);
 
         const embed = new EmbedBuilder()
             .setColor(EmbedColours.Ok)

From ff9f3e458eaec933c95dc86745f1ebec88fc59b2 Mon Sep 17 00:00:00 2001
From: Ethan Lane <ethan@vylpes.com>
Date: Fri, 26 Jul 2024 18:31:06 +0100
Subject: [PATCH 4/5] Add some unit tests (#321)

- Add some unit tests to the project
- These aren't massive, more just checks to ensure certain things are as they should
  - Such as checking all commands are actually registered

#15

Reviewed-on: https://git.vylpes.xyz/External/card-drop/pulls/321
Reviewed-by: VylpesTester <tester@vylpes.com>
Co-authored-by: Ethan Lane <ethan@vylpes.com>
Co-committed-by: Ethan Lane <ethan@vylpes.com>
---
 package.json           |  2 +-
 tests/registry.test.ts | 66 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+), 1 deletion(-)
 create mode 100644 tests/registry.test.ts

diff --git a/package.json b/package.json
index 102b607..3488a56 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
     "clean": "rm -rf node_modules/ dist/",
     "build": "tsc",
     "start": "node ./dist/bot.js",
-    "test": "jest --passWithNoTests",
+    "test": "jest",
     "lint": "eslint .",
     "lint:fix": "eslint . --fix",
     "db:up": "typeorm migration:run -d dist/database/dataSources/appDataSource.js",
diff --git a/tests/registry.test.ts b/tests/registry.test.ts
new file mode 100644
index 0000000..71d80db
--- /dev/null
+++ b/tests/registry.test.ts
@@ -0,0 +1,66 @@
+import {CoreClient} from "../src/client/client";
+import Registry from "../src/registry";
+import fs from "fs";
+import path from "path";
+
+describe("RegisterCommands", () => {
+    test("EXPECT every command in the commands folder to be registered", () => {
+        const registeredCommands: string[] = [];
+
+        CoreClient.RegisterCommand = jest.fn().mockImplementation((name: string) => {
+            registeredCommands.push(name);
+        });
+
+        Registry.RegisterCommands();
+
+        const commandFiles = getFilesInDirectory(path.join(process.cwd(), "src", "commands"))
+            .filter(x => x.endsWith(".ts"));
+
+        for (const file of commandFiles) {
+            expect(registeredCommands).toContain(file.split("/").pop()!.split(".")[0]);
+        }
+
+        expect(commandFiles.length).toBe(registeredCommands.length);
+    });
+});
+
+describe("RegisterButtonEvents", () => {
+    test("EXEPCT every button event in the button events folder to be registered", () => {
+        const registeredButtonEvents: string[] = [];
+
+        CoreClient.RegisterButtonEvent = jest.fn().mockImplementation((name: string) => {
+            registeredButtonEvents.push(name);
+        });
+
+        Registry.RegisterButtonEvents();
+
+        const eventFiles = getFilesInDirectory(path.join(process.cwd(), "src", "buttonEvents"))
+            .filter(x => x.endsWith(".ts"));
+
+        for (const file of eventFiles) {
+            expect(registeredButtonEvents).toContain(file.split("/").pop()!.split(".")[0].toLowerCase());
+        }
+
+        expect(eventFiles.length).toBe(registeredButtonEvents.length);
+    });
+});
+
+function getFilesInDirectory(dir: string): string[] {
+    let results: string[] = [];
+    const list = fs.readdirSync(dir);
+
+    list.forEach(file => {
+        file = path.join(dir, file);
+        const stat = fs.statSync(file);
+
+        if (stat && stat.isDirectory()) {
+            /* recurse into a subdirectory */
+            results = results.concat(getFilesInDirectory(file));
+        } else {
+            /* is a file */
+            results.push(file);
+        }
+    });
+
+    return results;
+}

From 136bc0805ea3ad85611c9f3bb9488acf7b6f5443 Mon Sep 17 00:00:00 2001
From: Ethan Lane <ethan@vylpes.com>
Date: Fri, 26 Jul 2024 18:09:48 +0100
Subject: [PATCH 5/5] Document how to start the bot

---
 README.md     |  60 ++++++++++++++++++++++++-
 docs/cards.md | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 179 insertions(+), 1 deletion(-)
 create mode 100644 docs/cards.md

diff --git a/README.md b/README.md
index 94f0c9c..ca4b03f 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,60 @@
-# card-drop
+# Card Drop
+
+Card Drop is a Discord Bot designed to allow users to "drop" random cards into
+a channel and have the ability to claim them for themselves or let others if
+they so choose.
+
+The cards are randomly chosen based on weights of their card type (i.e. Bronze
+is more common than Gold). The user who ran the drop command has 5 minutes to
+choose if they want the card to themselves before its claimable by anyone, or
+until the drop command is ran again.
+
+## Installation
+
+Downloads of the latest version can be found from the [GitHub Releases](https://github.com/vylpes/card-drop/releases)
+or [Forgejo Releases](https://git.vylpes.xyz/external/card-drop/releases) page.
+
+Copy the config template file and fill in the strings.
+
+## Requirements
+
+- NodeJS
+- Yarn
+- Docker
+
+## Usage
+
+Install the dependencies and build the app:
+
+```bash
+yarn Install
+yarn build
+```
+
+Setup the database (Recommended to use the docker-compose file
+
+```bash
+docker compose up -d
+```
+
+Copy and edit the settings file
+
+```bash
+cp .env.template .env
+```
+
+> **NOTE:** Make sure you do *not* check in these files! These contain
+sensitive information and should be treated as private.
+
+If you're not using `DB_SYNC=true` in `.env`, make sure to migrate the database
+
+```bash
+yarn db:up
+```
+
+Start the bot
+
+```bash
+yarn start
+```
 
diff --git a/docs/cards.md b/docs/cards.md
new file mode 100644
index 0000000..f0be28f
--- /dev/null
+++ b/docs/cards.md
@@ -0,0 +1,120 @@
+# Cards
+
+This document will describe how to add cards to the bot. This is from the
+perspective of the development side and doesn't go into details of syncing
+from an external place such as with the Google Drive Sync function.
+
+The cards will be put into the `$DATA_DIR/cards` folder. `$DATA_DIR` is
+configured in the `.env` file.
+
+## Folder Structure
+
+The general structure of the cards folder is as follows:
+
+```
+cards           # The main cards folder
+| Series 1      # Series folder
+| | BRONZE      # Type folder
+| | | 1000.jpg  # Card image
+| | | 1001.jpg
+| | 1.json      # Card metadata file
+| Series 2
+| | SILVER
+| | | 2000.jpg
+| | 2.json
+```
+
+- The root of the cards folder will have a folder foor each series
+- Each series will contain folders for each of the card types containing the
+  card images.
+- The series folder will also contain a metadata JSON folder containing the
+  metadata of the cards within that series.
+
+The bot when loading will search the cards folder recursively for each json,
+and then read them to determine what cards should be used for the bot.
+
+## Series Metadata
+
+An example of what the metadata files could look like are as follows:
+
+```json
+[
+    {
+        "id": 1,
+        "name": "Series 1",
+        "cards": [
+            {
+                "id": "1000",
+                "name": "Card 1000 of Series 1",
+                "type": 1,
+                "path": "Series 1/BRONZE/1000.jpg"
+            },
+            {
+                "id": "1001",
+                "name": "Card 1001 of Series 1",
+                "type": 1,
+                "path": "Series 2/BRONZE?1001.jpg",
+                "subseries": "Custom Series Name"
+            }
+        ]
+    }
+]
+```
+
+This file will load a series called "Series 1" with the id of 1, containing 2
+cards:
+- Card 1000, with type 1 (Bronze), with its image located at (from root)
+  "Series 1/BRONZE/1000.jpg"
+- Card 1001 is the same, except has a custom "subseries" name which will
+  override the main series name if shown, helpful for an "other" category.
+
+### Card Type
+
+<table>
+    <thead>
+        <tr>
+            <th>Number</th>
+            <th>Name</th>
+            <th>Chance</th>
+            <th>Sacrifice Cost (Coins)</th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr>
+            <td>0</td>
+            <td>Unknown</td>
+            <td>-</td>
+            <td>-</td>
+        </tr>
+        <tr>
+            <td>1</td>
+            <td>Bronze</td>
+            <td>62%</td>
+            <td>5</td>
+        </tr>
+        <tr>
+            <td>2</td>
+            <td>Silver</td>
+            <td>31%</td>
+            <td>10</td>
+        </tr>
+        <tr>
+            <td>3</td>
+            <td>Gold</td>
+            <td>4.4%</td>
+            <td>30</td>
+        </tr>
+        <tr>
+            <td>4</td>
+            <td>Manga</td>
+            <td>2%</td>
+            <td>40</td>
+        </tr>
+        <tr>
+            <td>5</td>
+            <td>Legendary</td>
+            <td>0.6%</td>
+            <td>100</td>
+        </tr>
+    </tbody>
+</table>