Compare commits

..

No commits in common. "c6d78cc37055498736d020fcf3cce69f36b0833a" and "370730cbea192a53ed770621de5aea9cb6837b47" have entirely different histories.

42 changed files with 584 additions and 1449 deletions

View file

@ -7,7 +7,7 @@
# any secret values. # any secret values.
BOT_TOKEN= BOT_TOKEN=
BOT_VER=0.5.0 BOT_VER=0.5.1
BOT_AUTHOR=Vylpes BOT_AUTHOR=Vylpes
BOT_OWNERID=147392775707426816 BOT_OWNERID=147392775707426816
BOT_CLIENTID=682942374040961060 BOT_CLIENTID=682942374040961060
@ -19,17 +19,16 @@ ABOUT_REPO=
DATA_DIR= DATA_DIR=
DB_HOST= DB_HOST=127.0.0.1
DB_PORT= DB_PORT=3301
DB_NAME= DB_NAME=carddrop
DB_AUTH_USER= DB_AUTH_USER=dev
DB_AUTH_PASS= DB_AUTH_PASS=dev
DB_SYNC= DB_SYNC=true
DB_LOGGING= DB_LOGGING=true
DB_DATA_LOCATION=~/.docker
DB_CARD_FILE=:memory: DB_CARD_FILE=:memory:
EXPRESS_PORT=3303 EXPRESS_PORT=3303
GDRIVESYNC_AUTO=true GDRIVESYNC_AUTO=true

View file

@ -1,71 +0,0 @@
name: Deploy To Production
on:
push:
branches:
- main
jobs:
build:
environment: prod
runs-on: node
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: 18.x
- run: npm ci
- run: npm run build
- run: npm test
- name: "Copy files over to location"
run: cp -r . ${{ secrets.PROD_REPO_PATH }}
deploy:
environment: prod
needs: build
runs-on: node
steps:
- uses: https://github.com/appleboy/ssh-action@v1.0.0
env:
DB_NAME: ${{ secrets.PROD_DB_NAME }}
DB_AUTH_USER: ${{ secrets.PROD_DB_AUTH_USER }}
DB_AUTH_PASS: ${{ secrets.PROD_DB_AUTH_PASS }}
DB_HOST: ${{ secrets.PROD_DB_HOST }}
DB_PORT: ${{ secrets.PROD_DB_PORT }}
DB_ROOT_HOST: ${{ secrets.PROD_DB_ROOT_HOST }}
DB_SYNC: ${{ secrets.PROD_DB_SYNC }}
DB_LOGGING: ${{ secrets.PROD_DB_LOGGING }}
DB_DATA_LOCATION: ${{ secrets.PROD_DB_DATA_LOCATION }}
SERVER_PATH: ${{ secrets.PROD_SSH_SERVER_PATH }}
BOT_TOKEN: ${{ secrets.PROD_BOT_TOKEN }}
BOT_VER: ${{ vars.PROD_BOT_VER }}
BOT_AUTHOR: ${{ vars.PROD_BOT_AUTHOR }}
BOT_OWNERID: ${{ vars.PROD_BOT_OWNERID }}
BOT_CLIENTID: ${{ vars.PROD_BOT_CLIENTID }}
BOT_ENV: ${{ vars.PROD_BOT_ENV }}
BOT_ADMINS: ${{ vars.PROD_BOT_ADMINS }}
ABOUT_FUNDING: ${{ vars.PROD_ABOUT_FUNDING }}
ABOUT_REPO: ${{ vars.PROD_ABOUT_REPO }}
DATA_DIR: ${{ secrets.PROD_DATA_DIR }}
GDRIVESYNC_AUTO: ${{ vars.PROD_GDRIVESYNC_AUTO }}
EXPRESS_PORT: ${{ secrets.PROD_EXPRESS_PORT }}
with:
host: ${{ secrets.PROD_SSH_HOST }}
username: ${{ secrets.PROD_SSH_USER }}
key: ${{ secrets.PROD_SSH_KEY }}
port: ${{ secrets.PROD_SSH_PORT }}
envs: DB_NAME,DB_AUTH_USER,DB_AUTH_PASS,DB_HOST,DB_PORT,DB_ROOT_HOST,DB_SYNC,DB_LOGGING,DB_DATA_LOCATION,BOT_TOKEN,BOT_VER,BOT_AUTHOR,BOT_OWNERID,BOT_CLIENTID,ABOUT_FUNDING,ABOUT_REPO,BOT_ENV,BOT_ADMINS,DATA_DIR,GDRIVESYNC_AUTO,SERVER_PATH,EXPRESS_PORT
script: |
source .sshrc \
&& cd /home/vylpes/apps/card-drop/card-drop_prod \
&& docker compose down \
&& (pm2 stop card-drop_prod || true) \
&& (pm2 delete card-drop_prod || true) \
&& docker compose up -d \
&& sleep 10 \
&& yarn run db:up \
&& pm2 start --name card-drop_prod dist/bot.js

View file

@ -1,71 +0,0 @@
name: Deploy To Stage
on:
push:
branches:
- develop
jobs:
build:
environment: stage
runs-on: node
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: 18.x
- run: npm ci
- run: npm run build
- run: npm test
- name: "Copy files over to location"
run: cp -r . ${{ secrets.STAGE_REPO_PATH }}
deploy:
environment: prod
needs: build
runs-on: node
steps:
- uses: https://github.com/appleboy/ssh-action@v1.0.0
env:
DB_NAME: ${{ secrets.STAGE_DB_NAME }}
DB_AUTH_USER: ${{ secrets.STAGE_DB_AUTH_USER }}
DB_AUTH_PASS: ${{ secrets.STAGE_DB_AUTH_PASS }}
DB_HOST: ${{ secrets.STAGE_DB_HOST }}
DB_PORT: ${{ secrets.STAGE_DB_PORT }}
DB_ROOT_HOST: ${{ secrets.STAGE_DB_ROOT_HOST }}
DB_SYNC: ${{ secrets.STAGE_DB_SYNC }}
DB_LOGGING: ${{ secrets.STAGE_DB_LOGGING }}
DB_DATA_LOCATION: ${{ secrets.STAGE_DB_DATA_LOCATION }}
SERVER_PATH: ${{ secrets.STAGE_SSH_SERVER_PATH }}
BOT_TOKEN: ${{ secrets.STAGE_BOT_TOKEN }}
BOT_VER: ${{ vars.STAGE_BOT_VER }}
BOT_AUTHOR: ${{ vars.STAGE_BOT_AUTHOR }}
BOT_OWNERID: ${{ vars.STAGE_BOT_OWNERID }}
BOT_CLIENTID: ${{ vars.STAGE_BOT_CLIENTID }}
BOT_ENV: ${{ vars.STAGE_BOT_ENV }}
BOT_ADMINS: ${{ vars.STAGE_BOT_ADMINS }}
ABOUT_FUNDING: ${{ vars.STAGE_ABOUT_FUNDING }}
ABOUT_REPO: ${{ vars.STAGE_ABOUT_REPO }}
DATA_DIR: ${{ secrets.STAGE_DATA_DIR }}
GDRIVESYNC_AUTO: ${{ vars.STAGE_GDRIVESYNC_AUTO }}
EXPRESS_PORT: ${{ secrets.STAGE_EXPRESS_PORT }}
with:
host: ${{ secrets.STAGE_SSH_HOST }}
username: ${{ secrets.STAGE_SSH_USER }}
key: ${{ secrets.STAGE_SSH_KEY }}
port: ${{ secrets.STAGE_SSH_PORT }}
envs: DB_NAME,DB_AUTH_USER,DB_AUTH_PASS,DB_HOST,DB_PORT,DB_ROOT_HOST,DB_SYNC,DB_LOGGING,DB_DATA_LOCATION,BOT_TOKEN,BOT_VER,BOT_AUTHOR,BOT_OWNERID,BOT_CLIENTID,ABOUT_FUNDING,ABOUT_REPO,BOT_ENV,BOT_ADMINS,DATA_DIR,GDRIVESYNC_AUTO,SERVER_PATH,EXPRESS_PORT
script: |
source .sshrc \
&& cd /home/vylpes/apps/card-drop/card-drop_stage \
&& docker compose down \
&& (pm2 stop card-drop_stage || true) \
&& (pm2 delete card-drop_stage || true) \
&& docker compose up -d \
&& sleep 10 \
&& yarn run db:up \
&& pm2 start --name card-drop_stage dist/bot.js

View file

@ -1,24 +0,0 @@
name: Test
on:
push:
branches:
- feature/*
- hotfix/*
- renovate/*
jobs:
build:
environment: stage
runs-on: node
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: 18.x
- run: npm ci
- run: npm run build
- run: npm test

34
.prod.env Normal file
View file

@ -0,0 +1,34 @@
# Security Warning! Do not commit this file to any VCS!
# This is a local file to speed up development process,
# so you don't have to change your environment variables.
#
# This is not applied to `.env.template`!
# Template files must be committed to the VCS, but must not contain
# any secret values.
BOT_TOKEN=
BOT_VER=0.5.1
BOT_AUTHOR=Vylpes
BOT_OWNERID=147392775707426816
BOT_CLIENTID=1093810443589529631
BOT_ENV=1
BOT_ADMINS=147392775707426816,887272961504071690
ABOUT_FUNDING=
ABOUT_REPO=
DATA_DIR=/home/vylpes/appdata/card-drop/card-drop_prod
DB_HOST=127.0.0.1
DB_PORT=3321
DB_NAME=carddrop
DB_AUTH_USER=prod
DB_AUTH_PASS=prod
DB_SYNC=false
DB_LOGGING=false
DB_CARD_FILE=:memory:
EXPRESS_PORT=3323
GDRIVESYNC_AUTO=false

34
.stage.env Normal file
View file

@ -0,0 +1,34 @@
# Security Warning! Do not commit this file to any VCS!
# This is a local file to speed up development process,
# so you don't have to change your environment variables.
#
# This is not applied to `.env.template`!
# Template files must be committed to the VCS, but must not contain
# any secret values.
BOT_TOKEN=
BOT_VER=0.5.1
BOT_AUTHOR=Vylpes
BOT_OWNERID=147392775707426816
BOT_CLIENTID=1147976642942214235
BOT_ENV=2
BOT_ADMINS=147392775707426816,887272961504071690
ABOUT_FUNDING=
ABOUT_REPO=
DATA_DIR=/home/vylpes/appdata/card-drop/card-drop_stage
DB_HOST=127.0.0.1
DB_PORT=3311
DB_NAME=carddrop
DB_AUTH_USER=stage
DB_AUTH_PASS=stage
DB_SYNC=false
DB_LOGGING=false
DB_CARD_FILE=:memory:
EXPRESS_PORT=3313
GDRIVESYNC_AUTO=false

View file

@ -28,8 +28,8 @@ steps:
- apk add rsync openssh-client - apk add rsync openssh-client
- eval `ssh-agent -s` - eval `ssh-agent -s`
- echo "$SSH_KEY" | tr -d '\r' | ssh-add - - echo "$SSH_KEY" | tr -d '\r' | ssh-add -
- rsync -e "ssh -o StrictHostKeyChecking=no" -r ./* vylpes@192.168.1.115:/home/vylpes/apps/card-drop/card-drop_stage - rsync -e "ssh -o StrictHostKeyChecking=no" -r ./* vylpes@192.168.68.120:/home/vylpes/apps/card-drop/card-drop_stage
- ssh vylpes@192.168.1.115 BOT_TOKEN='$${stage_bot_token}' 'bash -s' < ./scripts/deploy_stage.sh - ssh vylpes@192.168.68.120 BOT_TOKEN='$${stage_bot_token}' 'bash -s' < ./scripts/deploy_stage.sh
when: when:
event: push event: push
branch: [ develop ] branch: [ develop ]
@ -40,8 +40,8 @@ steps:
- apk add rsync openssh-client - apk add rsync openssh-client
- eval `ssh-agent -s` - eval `ssh-agent -s`
- echo "$SSH_KEY" | tr -d '\r' | ssh-add - - echo "$SSH_KEY" | tr -d '\r' | ssh-add -
- rsync -e "ssh -o StrictHostKeyChecking=no" -r ./* vylpes@192.168.1.115:/home/vylpes/apps/card-drop/card-drop_prod - rsync -e "ssh -o StrictHostKeyChecking=no" -r ./* vylpes@192.168.68.120:/home/vylpes/apps/card-drop/card-drop_prod
- ssh vylpes@192.168.1.115 BOT_TOKEN='$${prod_bot_token}' 'bash -s' < ./scripts/deploy_prod.sh - ssh vylpes@192.168.68.120 BOT_TOKEN='$${prod_bot_token}' 'bash -s' < ./scripts/deploy_prod.sh
when: when:
event: push event: push
branch: [ main ] branch: [ main ]

View file

@ -1,7 +0,0 @@
CREATE TABLE `user` (
`Id` varchar(255) NOT NULL,
`WhenCreated` datetime NOT NULL,
`WhenUpdated` datetime NOT NULL,
`Currency` int NOT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

31
docker-compose.prod.yml Normal file
View file

@ -0,0 +1,31 @@
version: "3.9"
volumes:
prod_database_data:
services:
# discord:
# build: .
database:
image: mysql/mysql-server
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
- MYSQL_DATABASE=carddrop
- MYSQL_USER=prod
- MYSQL_PASSWORD=prod
- MYSQL_ROOT_PASSWORD=root
- MYSQL_ROOT_HOST=0.0.0.0
ports:
- "3321:3306"
volumes:
- prod_database_data:/var/lib/mysql
phpmyadmin:
image: phpmyadmin
restart: always
ports:
- "3322:80"
environment:
- PMA_ARBITRARY=1

31
docker-compose.stage.yml Normal file
View file

@ -0,0 +1,31 @@
version: "3.9"
volumes:
stage_database_data:
services:
# discord:
# build: .
database:
image: mysql/mysql-server
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
- MYSQL_DATABASE=carddrop
- MYSQL_USER=stage
- MYSQL_PASSWORD=stage
- MYSQL_ROOT_PASSWORD=root
- MYSQL_ROOT_HOST=0.0.0.0
ports:
- "3311:3306"
volumes:
- stage_database_data:/var/lib/mysql
phpmyadmin:
image: phpmyadmin
restart: always
ports:
- "3312:80"
environment:
- PMA_ARBITRARY=1

View file

@ -1,17 +1,31 @@
version: "3.9" version: "3.9"
volumes:
dev_database_data:
services: services:
# discord:
# build: .
database: database:
image: mysql/mysql-server image: mysql/mysql-server
command: --default-authentication-plugin=mysql_native_password command: --default-authentication-plugin=mysql_native_password
restart: always restart: always
environment: environment:
- MYSQL_DATABASE=$DB_NAME - MYSQL_DATABASE=carddrop
- MYSQL_USER=$DB_AUTH_USER - MYSQL_USER=dev
- MYSQL_PASSWORD=$DB_AUTH_PASS - MYSQL_PASSWORD=dev
- MYSQL_ROOT_PASSWORD=$DB_AUTH_PASS - MYSQL_ROOT_PASSWORD=root
- MYSQL_ROOT_HOST=$DB_ROOT_HOST - MYSQL_ROOT_HOST=0.0.0.0
ports: ports:
- "$DB_PORT:3306" - "3301:3306"
volumes: volumes:
- $DB_DATA_LOCATION:/var/lib/mysql - dev_database_data:/var/lib/mysql
phpmyadmin:
image: phpmyadmin
restart: always
ports:
- "3302:80"
environment:
- PMA_ARBITRARY=1

725
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -38,11 +38,10 @@
"glob": "^10.3.10", "glob": "^10.3.10",
"jest": "^29.0.0", "jest": "^29.0.0",
"jest-mock-extended": "^3.0.0", "jest-mock-extended": "^3.0.0",
"minimatch": "9.0.4", "minimatch": "9.0.3",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"ts-jest": "^29.0.0", "ts-jest": "^29.0.0",
"typeorm": "0.3.20", "typeorm": "0.3.20"
"winston": "^3.11.0"
}, },
"overrides": { "overrides": {
"undici": "^5.28.3" "undici": "^5.28.3"

View file

@ -4,7 +4,6 @@ import Config from "../database/entities/app/Config";
import { glob } from "glob"; import { glob } from "glob";
import { SeriesMetadata } from "../contracts/SeriesMetadata"; import { SeriesMetadata } from "../contracts/SeriesMetadata";
import { CoreClient } from "../client/client"; import { CoreClient } from "../client/client";
import AppLogger from "../client/appLogger";
export interface CardMetadataResult { export interface CardMetadataResult {
IsSuccess: boolean; IsSuccess: boolean;
@ -22,22 +21,16 @@ export interface FindMetadataResult {
export default class CardMetadataFunction { export default class CardMetadataFunction {
public static async Execute(overrideSafeMode: boolean = false): Promise<CardMetadataResult> { public static async Execute(overrideSafeMode: boolean = false): Promise<CardMetadataResult> {
AppLogger.LogInfo("Functions/CardMetadataFunction", "Executing"); if (!overrideSafeMode && await Config.GetValue("safemode") == "true") return {
IsSuccess: false,
if (!overrideSafeMode && await Config.GetValue("safemode") == "true") { ErrorMessage: "Safe mode is on and not overridden",
AppLogger.LogWarn("Functions/CardMetadataFunction", "Safe Mode is active, refusing to resync"); };
return {
IsSuccess: false,
ErrorMessage: "Safe mode is on and not overridden",
};
}
const cardResult = await this.FindMetadataJSONs(); const cardResult = await this.FindMetadataJSONs();
if (cardResult.IsSuccess) { if (cardResult.IsSuccess) {
CoreClient.Cards = cardResult.Result!; CoreClient.Cards = cardResult.Result!;
AppLogger.LogInfo("Functions/CardMetadataFunction", `Loaded ${CoreClient.Cards.flatMap(x => x.cards).length} cards to database`); console.log(`Loaded ${CoreClient.Cards.flatMap(x => x.cards).length} cards to database`);
return { return {
IsSuccess: true, IsSuccess: true,
@ -45,7 +38,6 @@ export default class CardMetadataFunction {
} }
await Config.SetValue("safemode", "true"); await Config.SetValue("safemode", "true");
AppLogger.LogError("Functions/CardMetadataFunction", `Safe Mode activated due to error: ${cardResult.Error!.Message}`);
return { return {
IsSuccess: false, IsSuccess: false,
@ -60,13 +52,13 @@ export default class CardMetadataFunction {
for (const jsonPath of seriesJSONs) { for (const jsonPath of seriesJSONs) {
try { try {
AppLogger.LogVerbose("Functions/CardMetadataFunction", `Reading file ${jsonPath}`); console.log(`Reading file ${jsonPath}`);
const jsonFile = readFileSync(jsonPath); const jsonFile = readFileSync(jsonPath);
const parsedJson: SeriesMetadata[] = JSON.parse(jsonFile.toString()); const parsedJson: SeriesMetadata[] = JSON.parse(jsonFile.toString());
res.push(...parsedJson); res.push(...parsedJson);
} catch (e) { } catch (e) {
AppLogger.LogError("Functions/CardMetadataFunction", `Error reading file ${jsonPath}: ${e}`); console.error(e);
return { return {
IsSuccess: false, IsSuccess: false,

View file

@ -1,12 +1,8 @@
import { AttachmentBuilder, ButtonInteraction } from "discord.js"; import { ButtonInteraction } from "discord.js";
import { ButtonEvent } from "../type/buttonEvent"; import { ButtonEvent } from "../type/buttonEvent";
import Inventory from "../database/entities/app/Inventory"; import Inventory from "../database/entities/app/Inventory";
import { CoreClient } from "../client/client"; import { CoreClient } from "../client/client";
import { default as eClaim } from "../database/entities/app/Claim"; import { default as eClaim } from "../database/entities/app/Claim";
import AppLogger from "../client/appLogger";
import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
import { readFileSync } from "fs";
import path from "path";
export default class Claim extends ButtonEvent { export default class Claim extends ButtonEvent {
public override async execute(interaction: ButtonInteraction) { public override async execute(interaction: ButtonInteraction) {
@ -17,17 +13,17 @@ export default class Claim extends ButtonEvent {
const droppedBy = interaction.customId.split(" ")[3]; const droppedBy = interaction.customId.split(" ")[3];
const userId = interaction.user.id; const userId = interaction.user.id;
AppLogger.LogSilly("Button/Claim", `Parameters: cardNumber=${cardNumber}, claimId=${claimId}, droppedBy=${droppedBy}, userId=${userId}`); await interaction.deferReply();
const claimed = await eClaim.FetchOneByClaimId(claimId); const claimed = await eClaim.FetchOneByClaimId(claimId);
if (claimed) { if (claimed) {
await interaction.reply("This card has already been claimed"); await interaction.editReply("This card has already been claimed");
return; return;
} }
if (claimId == CoreClient.ClaimId && userId != droppedBy) { if (claimId == CoreClient.ClaimId && userId != droppedBy) {
await interaction.reply("The latest dropped card can only be claimed by the user who dropped it"); await interaction.editReply("The latest dropped card can only be claimed by the user who dropped it");
return; return;
} }
@ -46,24 +42,6 @@ export default class Claim extends ButtonEvent {
await claim.Save(eClaim, claim); await claim.Save(eClaim, claim);
const card = CardDropHelperMetadata.GetCardByCardNumber(cardNumber); await interaction.editReply(`Card claimed by ${interaction.user}`);
if (!card) {
return;
}
const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", card.card.path));
const imageFileName = card.card.path.split("/").pop()!;
const attachment = new AttachmentBuilder(image, { name: imageFileName });
const embed = CardDropHelperMetadata.GenerateDropEmbed(card, inventory.Quantity, imageFileName, interaction.user.username);
const row = CardDropHelperMetadata.GenerateDropButtons(card, claimId, interaction.user.id, true);
await interaction.update({
embeds: [ embed ],
files: [ attachment ],
components: [ row ],
});
} }
} }

View file

@ -1,7 +1,6 @@
import { ButtonInteraction } from "discord.js"; import { ButtonInteraction } from "discord.js";
import { ButtonEvent } from "../type/buttonEvent"; import { ButtonEvent } from "../type/buttonEvent";
import InventoryHelper from "../helpers/InventoryHelper"; import InventoryHelper from "../helpers/InventoryHelper";
import AppLogger from "../client/appLogger";
export default class Inventory extends ButtonEvent { export default class Inventory extends ButtonEvent {
public override async execute(interaction: ButtonInteraction) { public override async execute(interaction: ButtonInteraction) {
@ -10,8 +9,6 @@ export default class Inventory extends ButtonEvent {
const userid = interaction.customId.split(" ")[1]; const userid = interaction.customId.split(" ")[1];
const page = interaction.customId.split(" ")[2]; const page = interaction.customId.split(" ")[2];
AppLogger.LogSilly("Button/Inventory", `Parameters: userid=${userid}, page=${page}`);
const member = interaction.guild.members.cache.find(x => x.id == userid) || await interaction.guild.members.fetch(userid); const member = interaction.guild.members.cache.find(x => x.id == userid) || await interaction.guild.members.fetch(userid);
if (!member) { if (!member) {
@ -20,8 +17,6 @@ export default class Inventory extends ButtonEvent {
} }
try { try {
AppLogger.LogVerbose("Button/Inventory", `Generating inventory page ${page} for ${member.user.username} with id ${member.user.id}`);
const embed = await InventoryHelper.GenerateInventoryPage(member.user.username, member.user.id, Number(page)); const embed = await InventoryHelper.GenerateInventoryPage(member.user.username, member.user.id, Number(page));
await interaction.update({ await interaction.update({
@ -29,8 +24,7 @@ export default class Inventory extends ButtonEvent {
components: [ embed.row ], components: [ embed.row ],
}); });
} catch (e) { } catch (e) {
AppLogger.LogError("Button/Inventory", `Error generating inventory page for ${member.user.username} with id ${member.user.id}: ${e}`); console.error(e);
await interaction.reply("No page for user found."); await interaction.reply("No page for user found.");
} }
} }

View file

@ -7,7 +7,6 @@ import Inventory from "../database/entities/app/Inventory";
import Config from "../database/entities/app/Config"; import Config from "../database/entities/app/Config";
import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata"; import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
import path from "path"; import path from "path";
import AppLogger from "../client/appLogger";
export default class Reroll extends ButtonEvent { export default class Reroll extends ButtonEvent {
public override async execute(interaction: ButtonInteraction) { public override async execute(interaction: ButtonInteraction) {
@ -17,8 +16,6 @@ export default class Reroll extends ButtonEvent {
} }
if (await Config.GetValue("safemode") == "true") { if (await Config.GetValue("safemode") == "true") {
AppLogger.LogWarn("Button/Reroll", "Safe Mode is active, refusing to send next drop.");
await interaction.reply("Safe Mode has been activated, please resync to continue."); await interaction.reply("Safe Mode has been activated, please resync to continue.");
return; return;
} }
@ -33,8 +30,6 @@ export default class Reroll extends ButtonEvent {
await interaction.deferReply(); await interaction.deferReply();
try { try {
AppLogger.LogVerbose("Button/Reroll", `Sending next drop: ${randomCard.card.id} (${randomCard.card.name})`);
const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", randomCard.card.path)); const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", randomCard.card.path));
const imageFileName = randomCard.card.path.split("/").pop()!; const imageFileName = randomCard.card.path.split("/").pop()!;
@ -56,8 +51,9 @@ export default class Reroll extends ButtonEvent {
}); });
CoreClient.ClaimId = claimId; CoreClient.ClaimId = claimId;
} catch (e) { } catch (e) {
AppLogger.LogError("Button/Reroll", `Error sending next drop for card ${randomCard.card.id}: ${e}`); console.error(e);
await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. (${randomCard.card.id})`); await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. (${randomCard.card.id})`);
} }

View file

@ -1,45 +0,0 @@
import { ButtonInteraction } from "discord.js";
import { ButtonEvent } from "../type/buttonEvent";
import AppLogger from "../client/appLogger";
import SeriesHelper from "../helpers/SeriesHelper";
export default class Series extends ButtonEvent {
public override async execute(interaction: ButtonInteraction) {
const subaction = interaction.customId.split(" ")[1];
switch(subaction) {
case "view":
await this.ViewSeries(interaction);
break;
case "list":
await this.ListSeries(interaction);
break;
default:
AppLogger.LogWarn("Commands/Series", `Subaction doesn't exist: ${subaction}`);
interaction.reply("Subaction doesn't exist.");
}
}
private async ViewSeries(interaction: ButtonInteraction) {
const seriesid = interaction.customId.split(" ")[2];
const page = interaction.customId.split(" ")[3];
const embed = SeriesHelper.GenerateSeriesViewPage(Number(seriesid), Number(page));
await interaction.update({
embeds: [ embed!.embed ],
components: [ embed!.row ],
});
}
private async ListSeries(interaction: ButtonInteraction) {
const page = interaction.customId.split(" ")[2];
const embed = SeriesHelper.GenerateSeriesListPage(Number(page));
await interaction.update({
embeds: [ embed!.embed ],
components: [ embed!.row ],
});
}
}

View file

@ -1,211 +0,0 @@
import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, EmbedBuilder } from "discord.js";
import { ButtonEvent } from "../type/buttonEvent";
import { CoreClient } from "../client/client";
import Inventory from "../database/entities/app/Inventory";
import EmbedColours from "../constants/EmbedColours";
import AppLogger from "../client/appLogger";
export default class Trade extends ButtonEvent {
public override async execute(interaction: ButtonInteraction) {
const action = interaction.customId.split(" ")[1];
AppLogger.LogSilly("Button/Trade", `Parameters: action=${action}`);
switch (action) {
case "accept":
await this.AcceptTrade(interaction);
break;
case "decline":
await this.DeclineTrade(interaction);
break;
}
}
private async AcceptTrade(interaction: ButtonInteraction) {
const giveUserId = interaction.customId.split(" ")[2];
const receiveUserId = interaction.customId.split(" ")[3];
const giveCardNumber = interaction.customId.split(" ")[4];
const receiveCardNumber = interaction.customId.split(" ")[5];
const expiry = interaction.customId.split(" ")[6];
const timeoutId = interaction.customId.split(" ")[7];
AppLogger.LogSilly("Button/Trade/AcceptTrade", `Parameters: giveUserId=${giveUserId}, receiveUserId=${receiveUserId}, giveCardNumber=${giveCardNumber}, receiveCardNumber=${receiveCardNumber}, expiry=${expiry}, timeoutId=${timeoutId}`);
const expiryDate = new Date(expiry);
if (expiryDate < new Date()) {
await interaction.reply("Trade has expired");
return;
}
if (interaction.user.id !== receiveUserId) {
await interaction.reply("You are not the user who the trade is intended for");
return;
}
const giveItem = CoreClient.Cards
.flatMap(x => x.cards)
.find(x => x.id === giveCardNumber);
const receiveItem = CoreClient.Cards
.flatMap(x => x.cards)
.find(x => x.id === receiveCardNumber);
if (!giveItem || !receiveItem) {
await interaction.reply("One or more of the items you are trying to trade does not exist.");
return;
}
const giveUser = interaction.client.users.cache.get(giveUserId) || await interaction.client.users.fetch(giveUserId);
const receiveUser = interaction.client.users.cache.get(receiveUserId) || await interaction.client.users.fetch(receiveUserId);
const giveUserInventory1 = await Inventory.FetchOneByCardNumberAndUserId(giveUserId, giveCardNumber);
const receiveUserInventory1 = await Inventory.FetchOneByCardNumberAndUserId(receiveUserId, receiveCardNumber);
if (!giveUserInventory1 || !receiveUserInventory1) {
await interaction.reply("One or more of the items you are trying to trade does not exist.");
return;
}
if (giveUserInventory1.Quantity < 1 || receiveUserInventory1.Quantity < 1) {
await interaction.reply("One or more of the items you are trying to trade does not exist.");
return;
}
giveUserInventory1.SetQuantity(giveUserInventory1.Quantity - 1);
receiveUserInventory1.SetQuantity(receiveUserInventory1.Quantity - 1);
await giveUserInventory1.Save(Inventory, giveUserInventory1);
await receiveUserInventory1.Save(Inventory, receiveUserInventory1);
let giveUserInventory2 = await Inventory.FetchOneByCardNumberAndUserId(receiveUserId, giveCardNumber);
let receiveUserInventory2 = await Inventory.FetchOneByCardNumberAndUserId(giveUserId, receiveCardNumber);
if (!giveUserInventory2) {
giveUserInventory2 = new Inventory(receiveUserId, giveCardNumber, 1);
} else {
giveUserInventory2.SetQuantity(giveUserInventory2.Quantity + 1);
}
if (!receiveUserInventory2) {
receiveUserInventory2 = new Inventory(giveUserId, receiveCardNumber, 1);
} else {
receiveUserInventory2.SetQuantity(receiveUserInventory2.Quantity + 1);
}
await giveUserInventory2.Save(Inventory, giveUserInventory2);
await receiveUserInventory2.Save(Inventory, receiveUserInventory2);
clearTimeout(timeoutId);
const tradeEmbed = new EmbedBuilder()
.setTitle("Trade Accepted")
.setDescription(`Trade initiated between ${receiveUser.username} and ${giveUser.username}`)
.setColor(EmbedColours.Success)
.setImage("https://i.imgur.com/9w5f1ls.gif")
.addFields([
{
name: "I receieve",
value: `${receiveItem.id}: ${receiveItem.name}`,
inline: true,
},
{
name: "You receieve",
value: `${giveItem.id}: ${giveItem.name}`,
inline: true,
},
{
name: "Complete",
value: new Date().toLocaleString(),
}
]);
const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents([
new ButtonBuilder()
.setCustomId("trade expired accept")
.setLabel("Accept")
.setStyle(ButtonStyle.Success)
.setDisabled(true),
new ButtonBuilder()
.setCustomId("trade expired decline")
.setLabel("Decline")
.setStyle(ButtonStyle.Danger)
.setDisabled(true),
]);
await interaction.update({ embeds: [ tradeEmbed ], components: [ row ]});
}
private async DeclineTrade(interaction: ButtonInteraction) {
const giveUserId = interaction.customId.split(" ")[2];
const receiveUserId = interaction.customId.split(" ")[3];
const giveCardNumber = interaction.customId.split(" ")[4];
const receiveCardNumber = interaction.customId.split(" ")[5];
// No need to get expiry date
const timeoutId = interaction.customId.split(" ")[7];
AppLogger.LogSilly("Button/Trade/DeclineTrade", `Parameters: giveUserId=${giveUserId}, receiveUserId=${receiveUserId}, giveCardNumber=${giveCardNumber}, receiveCardNumber=${receiveCardNumber}, timeoutId=${timeoutId}`);
if (interaction.user.id != receiveUserId && interaction.user.id !==giveUserId) {
await interaction.reply("You are not the user who the trade is intended for");
return;
}
const giveUser = interaction.client.users.cache.get(giveUserId) || await interaction.client.users.fetch(giveUserId);
const receiveUser = interaction.client.users.cache.get(receiveUserId) || await interaction.client.users.fetch(receiveUserId);
const giveItem = CoreClient.Cards
.flatMap(x => x.cards)
.find(x => x.id === giveCardNumber);
const receiveItem = CoreClient.Cards
.flatMap(x => x.cards)
.find(x => x.id === receiveCardNumber);
if (!giveItem || !receiveItem) {
await interaction.reply("One or more of the items you are trying to trade does not exist.");
return;
}
clearTimeout(timeoutId);
const tradeEmbed = new EmbedBuilder()
.setTitle("Trade Declined")
.setDescription(`Trade initiated between ${receiveUser.username} and ${giveUser.username}`)
.setColor(EmbedColours.Error)
.setImage("https://i.imgur.com/9w5f1ls.gif")
.addFields([
{
name: "I Receive",
value: `${receiveItem.id}: ${receiveItem.name}`,
inline: true,
},
{
name: "You Receive",
value: `${giveItem.id}: ${giveItem.name}`,
inline: true,
},
{
name: "Declined",
value: new Date().toLocaleString(),
}
]);
const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents([
new ButtonBuilder()
.setCustomId("trade expired accept")
.setLabel("Accept")
.setStyle(ButtonStyle.Success)
.setDisabled(true),
new ButtonBuilder()
.setCustomId("trade expired decline")
.setLabel("Decline")
.setStyle(ButtonStyle.Danger)
.setDisabled(true),
]);
await interaction.update({ embeds: [ tradeEmbed ], components: [ row ]});
}
}

View file

@ -1,65 +0,0 @@
import { Logger, createLogger, format, transports } from "winston";
export default class AppLogger {
public static Logger: Logger;
public static InitialiseLogger(logLevel: string, outputToConsole: boolean) {
const customFormat = format.printf(({ level, message, timestamp, label }) => {
return `${timestamp} [${label}] ${level}: ${message}`;
});
const logger = createLogger({
level: logLevel,
format: format.combine(
format.timestamp({
format: "YYYY-MM-DD HH:mm:ss"
}),
format.errors({ stack: true }),
format.splat(),
customFormat,
),
defaultMeta: { service: "bot" },
transports: [
new transports.File({ filename: "error.log", level: "error" }),
new transports.File({ filename: "combined.log" }),
],
});
if (outputToConsole) {
logger.add(new transports.Console({
format: format.combine(
format.colorize(),
format.timestamp(),
customFormat,
)}));
}
AppLogger.Logger = logger;
AppLogger.LogInfo("AppLogger", `Log Level: ${logLevel}`);
}
public static LogError(label: string, message: string) {
AppLogger.Logger.error({ label, message });
}
public static LogWarn(label: string, message: string) {
AppLogger.Logger.warn({ label, message });
}
public static LogInfo(label: string, message: string) {
AppLogger.Logger.info({ label, message });
}
public static LogVerbose(label: string, message: string) {
AppLogger.Logger.verbose({ label, message });
}
public static LogDebug(label: string, message: string) {
AppLogger.Logger.debug({ label, message });
}
public static LogSilly(label: string, message: string) {
AppLogger.Logger.silly({ label, message });
}
}

View file

@ -13,7 +13,6 @@ import { Environment } from "../constants/Environment";
import Webhooks from "../webhooks"; import Webhooks from "../webhooks";
import CardMetadataFunction from "../Functions/CardMetadataFunction"; import CardMetadataFunction from "../Functions/CardMetadataFunction";
import { SeriesMetadata } from "../contracts/SeriesMetadata"; import { SeriesMetadata } from "../contracts/SeriesMetadata";
import AppLogger from "./appLogger";
export class CoreClient extends Client { export class CoreClient extends Client {
private static _commandItems: ICommandItem[]; private static _commandItems: ICommandItem[];
@ -45,14 +44,6 @@ export class CoreClient extends Client {
super({ intents: intents }); super({ intents: intents });
dotenv.config(); dotenv.config();
CoreClient.Environment = Number(process.env.BOT_ENV);
const loglevel = process.env.BOT_LOGLEVEL ?? "info";
AppLogger.InitialiseLogger(loglevel, CoreClient.Environment == Environment.Local);
AppLogger.LogInfo("Client", "Initialising Client");
CoreClient._commandItems = []; CoreClient._commandItems = [];
CoreClient._buttonEvents = []; CoreClient._buttonEvents = [];
@ -60,24 +51,21 @@ export class CoreClient extends Client {
this._util = new Util(); this._util = new Util();
this._webhooks = new Webhooks(); this._webhooks = new Webhooks();
AppLogger.LogInfo("Client", `Environment: ${CoreClient.Environment}`); CoreClient.Environment = Number(process.env.BOT_ENV);
console.log(`Bot Environment: ${CoreClient.Environment}`);
CoreClient.AllowDrops = true; CoreClient.AllowDrops = true;
} }
public async start() { public async start() {
if (!process.env.BOT_TOKEN) { if (!process.env.BOT_TOKEN) {
AppLogger.LogError("Client", "BOT_TOKEN is not defined in .env"); console.error("BOT_TOKEN is not defined in .env");
return; return;
} }
await AppDataSource.initialize() await AppDataSource.initialize()
.then(() => AppLogger.LogInfo("Client", "App Data Source Initialised")) .then(() => console.log("App Data Source Initialised"))
.catch(err => { .catch(err => console.error("Error initialising App Data Source", err));
AppLogger.LogError("Client", "App Data Source Initialisation Failed");
AppLogger.LogError("Client", err);
throw err;
});
super.on("interactionCreate", this._events.onInteractionCreate); super.on("interactionCreate", this._events.onInteractionCreate);
super.on("ready", this._events.onReady); super.on("ready", this._events.onReady);
@ -102,8 +90,6 @@ export class CoreClient extends Client {
if ((environment & CoreClient.Environment) == CoreClient.Environment) { if ((environment & CoreClient.Environment) == CoreClient.Environment) {
CoreClient._commandItems.push(item); CoreClient._commandItems.push(item);
AppLogger.LogVerbose("Client", `Registered Command: ${name}`);
} }
} }
@ -126,8 +112,6 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Channel Create Event");
} }
public static RegisterChannelDeleteEvent(fn: (channel: DMChannel | NonThreadGuildBasedChannel) => void) { public static RegisterChannelDeleteEvent(fn: (channel: DMChannel | NonThreadGuildBasedChannel) => void) {
@ -149,8 +133,6 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Channel Delete Event");
} }
public static RegisterChannelUpdateEvent(fn: (channel: DMChannel | NonThreadGuildBasedChannel) => void) { public static RegisterChannelUpdateEvent(fn: (channel: DMChannel | NonThreadGuildBasedChannel) => void) {
@ -172,8 +154,6 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Channel Update Event");
} }
public static RegisterGuildBanAddEvent(fn: (ban: GuildBan) => void) { public static RegisterGuildBanAddEvent(fn: (ban: GuildBan) => void) {
@ -195,8 +175,6 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Guild Ban Add Event");
} }
public static RegisterGuildBanRemoveEvent(fn: (channel: GuildBan) => void) { public static RegisterGuildBanRemoveEvent(fn: (channel: GuildBan) => void) {
@ -218,8 +196,6 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Guild Ban Remove Event");
} }
public static RegisterGuildCreateEvent(fn: (guild: Guild) => void) { public static RegisterGuildCreateEvent(fn: (guild: Guild) => void) {
@ -241,8 +217,6 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Guild Create Event");
} }
public static RegisterGuildMemberAddEvent(fn: (member: GuildMember) => void) { public static RegisterGuildMemberAddEvent(fn: (member: GuildMember) => void) {
@ -264,8 +238,6 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Guild Member Add Event");
} }
public static RegisterGuildMemberRemoveEvent(fn: (member: GuildMember | PartialGuildMember) => void) { public static RegisterGuildMemberRemoveEvent(fn: (member: GuildMember | PartialGuildMember) => void) {
@ -287,8 +259,6 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Guild Member Remove Event");
} }
public static GuildMemebrUpdate(fn: (oldMember: GuildMember | PartialGuildMember, newMember: GuildMember) => void) { public static GuildMemebrUpdate(fn: (oldMember: GuildMember | PartialGuildMember, newMember: GuildMember) => void) {
@ -310,8 +280,6 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Guild Member Update Event");
} }
public static RegisterMessageCreateEvent(fn: (message: Message<boolean>) => void) { public static RegisterMessageCreateEvent(fn: (message: Message<boolean>) => void) {
@ -333,8 +301,6 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Message Create Event");
} }
public static RegisterMessageDeleteEvent(fn: (message: Message<boolean> | PartialMessage) => void) { public static RegisterMessageDeleteEvent(fn: (message: Message<boolean> | PartialMessage) => void) {
@ -356,8 +322,6 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Message Delete Event");
} }
public static RegisterMessageUpdateEvent(fn: (oldMessage: Message<boolean> | PartialMessage, newMessage: Message<boolean> | PartialMessage) => void) { public static RegisterMessageUpdateEvent(fn: (oldMessage: Message<boolean> | PartialMessage, newMessage: Message<boolean> | PartialMessage) => void) {
@ -379,8 +343,6 @@ export class CoreClient extends Client {
MessageUpdate: [ fn ], MessageUpdate: [ fn ],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Message Update Event");
} }
public static RegisterButtonEvent(buttonId: string, event: ButtonEvent, environment: Environment = Environment.All) { public static RegisterButtonEvent(buttonId: string, event: ButtonEvent, environment: Environment = Environment.All) {
@ -392,8 +354,6 @@ export class CoreClient extends Client {
if ((environment & CoreClient.Environment) == CoreClient.Environment) { if ((environment & CoreClient.Environment) == CoreClient.Environment) {
CoreClient._buttonEvents.push(item); CoreClient._buttonEvents.push(item);
AppLogger.LogVerbose("Client", `Registered Button Event: ${buttonId}`);
} }
} }
} }

View file

@ -1,25 +1,22 @@
import { Interaction } from "discord.js"; import { Interaction } from "discord.js";
import ChatInputCommand from "./interactionCreate/ChatInputCommand"; import ChatInputCommand from "./interactionCreate/ChatInputCommand";
import Button from "./interactionCreate/Button"; import Button from "./interactionCreate/Button";
import AppLogger from "./appLogger";
export class Events { export class Events {
public async onInteractionCreate(interaction: Interaction) { public async onInteractionCreate(interaction: Interaction) {
if (!interaction.guildId) return; if (!interaction.guildId) return;
if (interaction.isChatInputCommand()) { if (interaction.isChatInputCommand()) {
AppLogger.LogVerbose("Client", `ChatInputCommand: ${interaction.commandName}`);
ChatInputCommand.onChatInput(interaction); ChatInputCommand.onChatInput(interaction);
} }
if (interaction.isButton()) { if (interaction.isButton()) {
AppLogger.LogVerbose("Client", `Button: ${interaction.customId}`);
Button.onButtonClicked(interaction); Button.onButtonClicked(interaction);
} }
} }
// Emit when bot is logged in and ready to use // Emit when bot is logged in and ready to use
public onReady() { public onReady() {
AppLogger.LogInfo("Client", "Ready"); console.log("Ready");
} }
} }

View file

@ -1,6 +1,5 @@
import { ButtonInteraction } from "discord.js"; import { ButtonInteraction } from "discord.js";
import { CoreClient } from "../client"; import { CoreClient } from "../client";
import AppLogger from "../appLogger";
export default class Button { export default class Button {
public static async onButtonClicked(interaction: ButtonInteraction) { public static async onButtonClicked(interaction: ButtonInteraction) {
@ -9,21 +8,10 @@ export default class Button {
const item = CoreClient.buttonEvents.find(x => x.ButtonId == interaction.customId.split(" ")[0]); const item = CoreClient.buttonEvents.find(x => x.ButtonId == interaction.customId.split(" ")[0]);
if (!item) { if (!item) {
AppLogger.LogVerbose("Button", `Event not found: ${interaction.customId}`);
await interaction.reply("Event not found"); await interaction.reply("Event not found");
return; return;
} }
try { item.Event.execute(interaction);
AppLogger.LogDebug("Button", `Executing ${interaction.customId}`);
item.Event.execute(interaction);
} catch (e) {
AppLogger.LogError("Button", `Error occurred while executing event: ${interaction.customId}`);
AppLogger.LogError("Button", e as string);
await interaction.reply("An error occurred while executing the event");
}
} }
} }

View file

@ -1,7 +1,6 @@
import { Interaction } from "discord.js"; import { Interaction } from "discord.js";
import { CoreClient } from "../client"; import { CoreClient } from "../client";
import ICommandItem from "../../contracts/ICommandItem"; import ICommandItem from "../../contracts/ICommandItem";
import AppLogger from "../appLogger";
export default class ChatInputCommand { export default class ChatInputCommand {
public static async onChatInput(interaction: Interaction) { public static async onChatInput(interaction: Interaction) {
@ -14,8 +13,6 @@ export default class ChatInputCommand {
if (!itemForServer) { if (!itemForServer) {
if (!item) { if (!item) {
AppLogger.LogVerbose("ChatInputCommand", `Command not found: ${interaction.commandName}`);
await interaction.reply("Command not found"); await interaction.reply("Command not found");
return; return;
} }
@ -25,15 +22,6 @@ export default class ChatInputCommand {
itemToUse = itemForServer; itemToUse = itemForServer;
} }
try { itemToUse.Command.execute(interaction);
AppLogger.LogDebug("Command", `Executing ${interaction.commandName}`);
itemToUse.Command.execute(interaction);
} catch (e) {
AppLogger.LogError("ChatInputCommand", `Error occurred while executing command: ${interaction.commandName}`);
AppLogger.LogError("ChatInputCommand", e as string);
await interaction.reply("An error occurred while executing the command");
}
} }
} }

View file

@ -1,7 +1,6 @@
import { Client, REST, Routes, SlashCommandBuilder } from "discord.js"; import { Client, REST, Routes, SlashCommandBuilder } from "discord.js";
import EventExecutors from "../contracts/EventExecutors"; import EventExecutors from "../contracts/EventExecutors";
import { CoreClient } from "./client"; import { CoreClient } from "./client";
import AppLogger from "./appLogger";
export class Util { export class Util {
public loadSlashCommands(client: Client) { public loadSlashCommands(client: Client) {
@ -30,8 +29,6 @@ export class Util {
const rest = new REST({ version: "10" }).setToken(process.env.BOT_TOKEN!); const rest = new REST({ version: "10" }).setToken(process.env.BOT_TOKEN!);
AppLogger.LogVerbose("Util", `REST PUT: ${globalCommandData.flatMap(x => x.name).join(", ")}`);
rest.put( rest.put(
Routes.applicationCommands(process.env.BOT_CLIENTID!), Routes.applicationCommands(process.env.BOT_CLIENTID!),
{ {
@ -52,8 +49,6 @@ export class Util {
if (!client.guilds.cache.has(guild)) continue; if (!client.guilds.cache.has(guild)) continue;
AppLogger.LogVerbose("Util", `REST PUT: ${guild} - ${guildCommandData.flatMap(x => x.name).join(", ")}`);
rest.put( rest.put(
Routes.applicationGuildCommands(process.env.BOT_CLIENTID!, guild), Routes.applicationGuildCommands(process.env.BOT_CLIENTID!, guild),
{ {

View file

@ -7,7 +7,6 @@ import Inventory from "../database/entities/app/Inventory";
import Config from "../database/entities/app/Config"; import Config from "../database/entities/app/Config";
import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata"; import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
import path from "path"; import path from "path";
import AppLogger from "../client/appLogger";
export default class Drop extends Command { export default class Drop extends Command {
constructor() { constructor() {
@ -25,8 +24,6 @@ export default class Drop extends Command {
} }
if (await Config.GetValue("safemode") == "true") { if (await Config.GetValue("safemode") == "true") {
AppLogger.LogWarn("Commands/Drop", "Safe Mode is active, refusing to send next drop.");
await interaction.reply("Safe Mode has been activated, please resync to continue."); await interaction.reply("Safe Mode has been activated, please resync to continue.");
return; return;
} }
@ -34,8 +31,6 @@ export default class Drop extends Command {
const randomCard = CardDropHelperMetadata.GetRandomCard(); const randomCard = CardDropHelperMetadata.GetRandomCard();
if (!randomCard) { if (!randomCard) {
AppLogger.LogWarn("Commands/Drop", "Unable to fetch card, please try again. (randomCard is null)");
await interaction.reply("Unable to fetch card, please try again."); await interaction.reply("Unable to fetch card, please try again.");
return; return;
} }
@ -66,7 +61,7 @@ export default class Drop extends Command {
CoreClient.ClaimId = claimId; CoreClient.ClaimId = claimId;
} catch (e) { } catch (e) {
AppLogger.LogError("Commands/Drop", `Error sending next drop for card ${randomCard.card.id}: ${e}`); console.error(e);
await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. (${randomCard.card.id})`); await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. (${randomCard.card.id})`);
} }

View file

@ -4,7 +4,6 @@ import { ExecException, exec } from "child_process";
import { CoreClient } from "../client/client"; import { CoreClient } from "../client/client";
import Config from "../database/entities/app/Config"; import Config from "../database/entities/app/Config";
import CardMetadataFunction from "../Functions/CardMetadataFunction"; import CardMetadataFunction from "../Functions/CardMetadataFunction";
import AppLogger from "../client/appLogger";
export default class Gdrivesync extends Command { export default class Gdrivesync extends Command {
constructor() { constructor() {
@ -33,28 +32,19 @@ export default class Gdrivesync extends Command {
CoreClient.AllowDrops = false; CoreClient.AllowDrops = false;
AppLogger.LogInfo("Commands/GDriveSync", "Syncing google drive to the bot");
exec(`rclone sync card-drop-gdrive: ${process.env.DATA_DIR}/cards`, async (error: ExecException | null) => { exec(`rclone sync card-drop-gdrive: ${process.env.DATA_DIR}/cards`, async (error: ExecException | null) => {
if (error) { if (error) {
AppLogger.LogError("Commands/GDriveSync", `Error while running sync command: ${error.code}, ${error.message}`);
AppLogger.LogWarn("Commands/GDriveSync", "Safe mode activated");
await interaction.editReply(`Error while running sync command. Safe Mode has been activated. Code: ${error.code}`); await interaction.editReply(`Error while running sync command. Safe Mode has been activated. Code: ${error.code}`);
await Config.SetValue("safemode", "true"); await Config.SetValue("safemode", "true");
} else { } else {
const result = await CardMetadataFunction.Execute(true); const result = await CardMetadataFunction.Execute(true);
if (result.IsSuccess) { if (result.IsSuccess) {
AppLogger.LogInfo("Commands/GDriveSync", "Synced successfully");
await interaction.editReply("Synced successfully."); await interaction.editReply("Synced successfully.");
CoreClient.AllowDrops = true; CoreClient.AllowDrops = true;
await Config.SetValue("safemode", "false"); await Config.SetValue("safemode", "false");
} else { } else {
AppLogger.LogError("Commands/GDriveSync", `Error while running sync command: ${result.ErrorMessage}`);
await interaction.editReply(`Sync failed \`\`\`${result.ErrorMessage}\`\`\``); await interaction.editReply(`Sync failed \`\`\`${result.ErrorMessage}\`\`\``);
} }
} }

View file

@ -4,7 +4,6 @@ import { CoreClient } from "../client/client";
import Config from "../database/entities/app/Config"; import Config from "../database/entities/app/Config";
import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata"; import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
import Inventory from "../database/entities/app/Inventory"; import Inventory from "../database/entities/app/Inventory";
import AppLogger from "../client/appLogger";
export default class Give extends Command { export default class Give extends Command {
constructor() { constructor() {
@ -47,8 +46,6 @@ export default class Give extends Command {
const cardNumber = interaction.options.get("cardnumber", true); const cardNumber = interaction.options.get("cardnumber", true);
const user = interaction.options.getUser("user", true); const user = interaction.options.getUser("user", true);
AppLogger.LogSilly("Commands/Give", `Parameters: cardNumber=${cardNumber.value}, user=${user.id}`);
const card = CardDropHelperMetadata.GetCardByCardNumber(cardNumber.value!.toString()); const card = CardDropHelperMetadata.GetCardByCardNumber(cardNumber.value!.toString());
if (!card) { if (!card) {
@ -56,16 +53,16 @@ export default class Give extends Command {
return; return;
} }
let inventory = await Inventory.FetchOneByCardNumberAndUserId(user.id, card.card.id); let inventory = await Inventory.FetchOneByCardNumberAndUserId(user.id, card.id);
if (!inventory) { if (!inventory) {
inventory = new Inventory(user.id, card.card.id, 1); inventory = new Inventory(user.id, card.id, 1);
} else { } else {
inventory.SetQuantity(inventory.Quantity + 1); inventory.SetQuantity(inventory.Quantity + 1);
} }
await inventory.Save(Inventory, inventory); await inventory.Save(Inventory, inventory);
await interaction.reply(`${card.card.name} given to ${user.username}, they now have ${inventory.Quantity}`); await interaction.reply(`${card.name} given to ${interaction.user}, they now have ${inventory.Quantity}`);
} }
} }

View file

@ -1,7 +1,6 @@
import { CommandInteraction, SlashCommandBuilder } from "discord.js"; import { CommandInteraction, SlashCommandBuilder } from "discord.js";
import { Command } from "../type/command"; import { Command } from "../type/command";
import InventoryHelper from "../helpers/InventoryHelper"; import InventoryHelper from "../helpers/InventoryHelper";
import AppLogger from "../client/appLogger";
export default class Inventory extends Command { export default class Inventory extends Command {
constructor() { constructor() {
@ -24,8 +23,6 @@ export default class Inventory extends Command {
const page = interaction.options.get("page"); const page = interaction.options.get("page");
const user = interaction.options.getUser("user") || interaction.user; const user = interaction.options.getUser("user") || interaction.user;
AppLogger.LogSilly("Commands/Inventory", `Parameters: page=${page?.value}, user=${user.id}`);
try { try {
let pageNumber = 0; let pageNumber = 0;
@ -39,9 +36,7 @@ export default class Inventory extends Command {
embeds: [ embed.embed ], embeds: [ embed.embed ],
components: [ embed.row ], components: [ embed.row ],
}); });
} catch (e) { } catch {
AppLogger.LogError("Commands/Inventory", e as string);
await interaction.reply("No page for user found."); await interaction.reply("No page for user found.");
} }
} }

View file

@ -2,7 +2,6 @@ import { CacheType, CommandInteraction, PermissionsBitField, SlashCommandBuilder
import { Command } from "../type/command"; import { Command } from "../type/command";
import Config from "../database/entities/app/Config"; import Config from "../database/entities/app/Config";
import CardMetadataFunction from "../Functions/CardMetadataFunction"; import CardMetadataFunction from "../Functions/CardMetadataFunction";
import AppLogger from "../client/appLogger";
export default class Resync extends Command { export default class Resync extends Command {
constructor() { constructor() {
@ -24,14 +23,10 @@ export default class Resync extends Command {
return; return;
} }
AppLogger.LogInfo("Commands/Resync", "Resyncing database");
const result = await CardMetadataFunction.Execute(true); const result = await CardMetadataFunction.Execute(true);
if (result) { if (result) {
if (await Config.GetValue("safemode") == "true") { if (await Config.GetValue("safemode") == "true") {
AppLogger.LogInfo("Commands/Resync", "Resync successful, safe mode disabled");
await Config.SetValue("safemode", "false"); await Config.SetValue("safemode", "false");
await interaction.reply("Resynced database and disabled safe mode."); await interaction.reply("Resynced database and disabled safe mode.");
@ -39,8 +34,6 @@ export default class Resync extends Command {
} }
await interaction.reply("Resynced database."); await interaction.reply("Resynced database.");
} else { } else {
AppLogger.LogWarn("Commands/Resync", "Resync failed, safe mode activated");
await interaction.reply("Resync failed, safe mode has been activated until successful resync."); await interaction.reply("Resync failed, safe mode has been activated until successful resync.");
} }
} }

View file

@ -1,71 +0,0 @@
import { CommandInteraction, SlashCommandBuilder } from "discord.js";
import { Command } from "../type/command";
import { CoreClient } from "../client/client";
import AppLogger from "../client/appLogger";
import SeriesHelper from "../helpers/SeriesHelper";
export default class Series extends Command {
constructor() {
super();
this.CommandBuilder = new SlashCommandBuilder()
.setName("series")
.setDescription("View details on a series")
.addSubcommand(x =>
x
.setName("view")
.setDescription("View a specifiic series by id")
.addStringOption(y =>
y
.setName("id")
.setDescription("The series id")
.setRequired(true)))
.addSubcommand(x =>
x
.setName("list")
.setDescription("List all series")) as SlashCommandBuilder;
}
public override async execute(interaction: CommandInteraction) {
if (!interaction.isChatInputCommand()) return;
switch (interaction.options.getSubcommand()) {
case "view":
await this.ViewSeries(interaction);
break;
case "list":
await this.ListSeries(interaction);
break;
default:
AppLogger.LogWarn("Commands/Series", `Subcommand doesn't exist: ${interaction.options.getSubcommand()}`);
await interaction.reply("Subcommand doesn't exist.");
}
}
private async ViewSeries(interaction: CommandInteraction) {
const id = interaction.options.get("id");
AppLogger.LogSilly("Commands/Series/View", `Parameters: id=${id?.value}`);
if (!id) return;
const series = CoreClient.Cards.find(x => x.id == id.value);
if (!series) {
AppLogger.LogVerbose("Commands/Series/View", "Series not found.");
await interaction.reply("Series not found.");
return;
}
const embed = SeriesHelper.GenerateSeriesViewPage(series.id, 0);
await interaction.reply({ embeds: [ embed!.embed ], components: [ embed!.row ]});
}
private async ListSeries(interaction: CommandInteraction) {
const embed = SeriesHelper.GenerateSeriesListPage(0);
await interaction.reply({ embeds: [ embed!.embed ], components: [ embed!.row ]});
}
}

View file

@ -1,148 +0,0 @@
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js";
import { Command } from "../type/command";
import Inventory from "../database/entities/app/Inventory";
import { CoreClient } from "../client/client";
import EmbedColours from "../constants/EmbedColours";
import AppLogger from "../client/appLogger";
export default class Trade extends Command {
constructor() {
super();
this.CommandBuilder = new SlashCommandBuilder()
.setName("trade")
.setDescription("Initiate a trade with another user.")
.addUserOption(x =>
x
.setName("user")
.setDescription("User to trade with")
.setRequired(true))
.addStringOption(x =>
x
.setName("give")
.setDescription("Item to give")
.setRequired(true))
.addStringOption(x =>
x
.setName("receive")
.setDescription("Item to receive")
.setRequired(true));
}
public override async execute(interaction: CommandInteraction) {
const user = interaction.options.getUser("user")!;
const give = interaction.options.get("give")!;
const receive = interaction.options.get("receive")!;
AppLogger.LogSilly("Commands/Trade", `Parameters: user=${user.id}, give=${give.value}, receive=${receive.value}`);
const giveItemEntity = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, give.value!.toString());
const receiveItemEntity = await Inventory.FetchOneByCardNumberAndUserId(user.id, receive.value!.toString());
if (!giveItemEntity) {
await interaction.reply("You do not have the item you are trying to trade.");
return;
}
if (!receiveItemEntity) {
await interaction.reply("The user you are trying to trade with does not have the item you are trying to trade for.");
return;
}
const giveItem = CoreClient.Cards
.flatMap(x => x.cards)
.find(x => x.id === give.value!.toString());
const receiveItem = CoreClient.Cards
.flatMap(x => x.cards)
.find(x => x.id === receive.value!.toString());
if (!giveItem || !receiveItem) {
await interaction.reply("One or more of the items you are trying to trade does not exist.");
return;
}
const now = new Date();
const expiry = now.setMinutes(now.getMinutes() + 15);
const tradeEmbed = new EmbedBuilder()
.setTitle("⚠️ Trade Offer ⚠️")
.setDescription(`Trade initiated between ${interaction.user.username} and ${user.username}`)
.setColor(EmbedColours.Grey)
.setImage("https://media1.tenor.com/m/KkZwKl2AQ2QAAAAd/trade-offer.gif")
.addFields([
{
name: "I Receive",
value: `${receiveItem.id}: ${receiveItem.name}`,
inline: true,
},
{
name: "You Receive",
value: `${giveItem.id}: ${giveItem.name}`,
inline: true,
},
{
name: "Expires",
value: new Date(expiry).toLocaleString(),
}
]);
const timeoutId = setTimeout(async () => this.autoDecline(interaction, interaction.user.username, user.username, giveItem.id, receiveItem.id, giveItem.name, receiveItem.name), 1000 * 60 * 15); // 15 minutes
const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents([
new ButtonBuilder()
.setCustomId(`trade accept ${interaction.user.id} ${user.id} ${giveItem.id} ${receiveItem.id} ${expiry} ${timeoutId}`)
.setLabel("Accept")
.setStyle(ButtonStyle.Success),
new ButtonBuilder()
.setCustomId(`trade decline ${interaction.user.id} ${user.id} ${giveItem.id} ${receiveItem.id} ${expiry} ${timeoutId}`)
.setLabel("Decline")
.setStyle(ButtonStyle.Danger),
]);
await interaction.reply({ content: `${user}`, embeds: [ tradeEmbed ], components: [ row ] });
}
private async autoDecline(interaction: CommandInteraction, giveUsername: string, receiveUsername: string, giveCardNumber: string, receiveCardNumber: string, giveCardName: string, receiveCardName: string) {
AppLogger.LogSilly("Commands/Trade/AutoDecline", `Auto declining trade between ${giveUsername} and ${receiveUsername}`);
const tradeEmbed = new EmbedBuilder()
.setTitle("Trade Expired")
.setDescription(`Trade initiated between ${receiveUsername} and ${giveUsername}`)
.setColor(EmbedColours.Error)
.setImage("https://media1.tenor.com/m/KkZwKl2AQ2QAAAAd/trade-offer.gif")
.addFields([
{
name: "I Receive",
value: `${receiveCardNumber}: ${receiveCardName}`,
inline: true,
},
{
name: "You Receive",
value: `${giveCardNumber}: ${giveCardName}`,
inline: true,
},
{
name: "Expired",
value: new Date().toLocaleString(),
}
]);
const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents([
new ButtonBuilder()
.setCustomId("trade expired accept")
.setLabel("Accept")
.setStyle(ButtonStyle.Success)
.setDisabled(true),
new ButtonBuilder()
.setCustomId("trade expired declined")
.setLabel("Decline")
.setStyle(ButtonStyle.Danger)
.setDisabled(true),
]);
await interaction.editReply({ embeds: [ tradeEmbed ], components: [ row ]});
}
}

View file

@ -5,7 +5,6 @@ import { readFileSync } from "fs";
import path from "path"; import path from "path";
import Inventory from "../database/entities/app/Inventory"; import Inventory from "../database/entities/app/Inventory";
import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata"; import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
import AppLogger from "../client/appLogger";
export default class View extends Command { export default class View extends Command {
constructor() { constructor() {
@ -24,8 +23,6 @@ export default class View extends Command {
public override async execute(interaction: CommandInteraction) { public override async execute(interaction: CommandInteraction) {
const cardNumber = interaction.options.get("cardnumber"); const cardNumber = interaction.options.get("cardnumber");
AppLogger.LogSilly("Commands/View", `Parameters: cardNumber=${cardNumber?.value}`);
if (!cardNumber || !cardNumber.value) { if (!cardNumber || !cardNumber.value) {
await interaction.reply("Card number is required."); await interaction.reply("Card number is required.");
return; return;
@ -49,8 +46,6 @@ export default class View extends Command {
try { try {
image = readFileSync(path.join(process.env.DATA_DIR!, "cards", card.path)); image = readFileSync(path.join(process.env.DATA_DIR!, "cards", card.path));
} catch { } catch {
AppLogger.LogError("Commands/View", `Unable to fetch image for card ${card.id}.`);
await interaction.reply(`Unable to fetch image for card ${card.id}.`); await interaction.reply(`Unable to fetch image for card ${card.id}.`);
return; return;
} }
@ -70,7 +65,7 @@ export default class View extends Command {
files: [ attachment ], files: [ attachment ],
}); });
} catch (e) { } catch (e) {
AppLogger.LogError("Commands/View", `Error sending view for card ${card.id}: ${e}`); console.error(e);
if (e instanceof DiscordAPIError) { if (e instanceof DiscordAPIError) {
await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. Code: ${e.code}.`); await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. Code: ${e.code}.`);

View file

@ -1,6 +1,5 @@
export default class EmbedColours { export default class EmbedColours {
public static readonly Ok = 0x3050ba; public static readonly Ok = 0x3050ba;
public static readonly Success = 0x50c878;
public static readonly Error = 0xff0000; public static readonly Error = 0xff0000;
public static readonly Grey = 0xd3d3d3; public static readonly Grey = 0xd3d3d3;
public static readonly BronzeCard = 0xcd7f32; public static readonly BronzeCard = 0xcd7f32;

View file

@ -1,19 +0,0 @@
import { Column, Entity } from "typeorm";
import AppBaseEntity from "../../../contracts/AppBaseEntity";
@Entity()
export default class User extends AppBaseEntity {
constructor(userId: string, currency: number) {
super();
this.Id = userId;
this.Currency = currency;
}
@Column()
Currency: number;
public UpdateCurrency(currency: number) {
this.Currency = currency;
}
}

View file

@ -1,15 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
import MigrationHelper from "../../../../helpers/MigrationHelper";
export class User1713289062969 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
MigrationHelper.Up("1713289062969-user", "0.6", [
"01-table/User",
], queryRunner);
}
public async down(): Promise<void> {
}
}

View file

@ -1,9 +1,8 @@
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js"; import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js";
import { CardRarity, CardRarityToColour, CardRarityToString } from "../constants/CardRarity"; import { CardRarity, CardRarityToColour, CardRarityToString } from "../constants/CardRarity";
import CardRarityChances from "../constants/CardRarityChances"; import CardRarityChances from "../constants/CardRarityChances";
import { DropResult } from "../contracts/SeriesMetadata"; import { CardMetadata, DropResult } from "../contracts/SeriesMetadata";
import { CoreClient } from "../client/client"; import { CoreClient } from "../client/client";
import AppLogger from "../client/appLogger";
export default class CardDropHelperMetadata { export default class CardDropHelperMetadata {
public static GetRandomCard(): DropResult | undefined { public static GetRandomCard(): DropResult | undefined {
@ -24,14 +23,10 @@ export default class CardDropHelperMetadata {
const randomCard = this.GetRandomCardByRarity(cardRarity); const randomCard = this.GetRandomCardByRarity(cardRarity);
AppLogger.LogSilly("CardDropHelperMetadata/GetRandomCard", `Random card: ${randomCard?.card.id} ${randomCard?.card.name}`);
return randomCard; return randomCard;
} }
public static GetRandomCardByRarity(rarity: CardRarity): DropResult | undefined { public static GetRandomCardByRarity(rarity: CardRarity): DropResult | undefined {
AppLogger.LogSilly("CardDropHelperMetadata/GetRandomCardByRarity", `Parameters: rarity=${rarity}`);
const allCards = CoreClient.Cards const allCards = CoreClient.Cards
.flatMap(x => x.cards) .flatMap(x => x.cards)
.filter(x => x.type == rarity); .filter(x => x.type == rarity);
@ -43,53 +38,28 @@ export default class CardDropHelperMetadata {
.find(x => x.cards.includes(card)); .find(x => x.cards.includes(card));
if (!series) { if (!series) {
AppLogger.LogWarn("CardDropHelperMetadata/GetRandomCardByRarity", `Series not found for card ${card.id}`);
return undefined; return undefined;
} }
AppLogger.LogSilly("CardDropHelperMetadata/GetRandomCardByRarity", `Random card: ${card.id} ${card.name}`);
return { return {
series: series, series: series,
card: card, card: card,
}; };
} }
public static GetCardByCardNumber(cardNumber: string): DropResult | undefined { public static GetCardByCardNumber(cardNumber: string): CardMetadata | undefined {
AppLogger.LogSilly("CardDropHelperMetadata/GetCardByCardNumber", `Parameters: cardNumber=${cardNumber}`);
const card = CoreClient.Cards const card = CoreClient.Cards
.flatMap(x => x.cards) .flatMap(x => x.cards)
.find(x => x.id == cardNumber); .find(x => x.id == cardNumber);
const series = CoreClient.Cards return card;
.find(x => x.cards.find(y => y.id == card?.id));
AppLogger.LogSilly("CardDropHelperMetadata/GetCardByCardNumber", `Card: ${card?.id} ${card?.name}`);
AppLogger.LogSilly("CardDropHelperMetadata/GetCardByCardNumber", `Series: ${series?.id} ${series?.name}`);
if (!card || !series) {
AppLogger.LogVerbose("CardDropHelperMetadata/GetCardByCardNumber", `Unable to find card metadata: ${cardNumber}`);
return undefined;
}
return { card, series };
} }
public static GenerateDropEmbed(drop: DropResult, quantityClaimed: number, imageFileName: string, claimedBy?: string): EmbedBuilder { public static GenerateDropEmbed(drop: DropResult, quantityClaimed: number, imageFileName: string): EmbedBuilder {
AppLogger.LogSilly("CardDropHelperMetadata/GenerateDropEmbed", `Parameters: drop=${drop.card.id}, quantityClaimed=${quantityClaimed}, imageFileName=${imageFileName}`);
let description = ""; let description = "";
description += `Series: ${drop.series.name}\n`; description += `Series: ${drop.series.name}\n`;
description += `Claimed: ${quantityClaimed}\n`; description += `Claimed: ${quantityClaimed}\n`;
if (claimedBy != null) {
description += `Claimed by: ${claimedBy}\n`;
} else {
description += "Claimed by: (UNCLAIMED)\n";
}
return new EmbedBuilder() return new EmbedBuilder()
.setTitle(drop.card.name) .setTitle(drop.card.name)
.setDescription(description) .setDescription(description)
@ -98,16 +68,13 @@ export default class CardDropHelperMetadata {
.setImage(`attachment://${imageFileName}`); .setImage(`attachment://${imageFileName}`);
} }
public static GenerateDropButtons(drop: DropResult, claimId: string, userId: string, disabled: boolean = false): ActionRowBuilder<ButtonBuilder> { public static GenerateDropButtons(drop: DropResult, claimId: string, userId: string): ActionRowBuilder<ButtonBuilder> {
AppLogger.LogSilly("CardDropHelperMetadata/GenerateDropButtons", `Parameters: drop=${drop.card.id}, claimId=${claimId}, userId=${userId}`);
return new ActionRowBuilder<ButtonBuilder>() return new ActionRowBuilder<ButtonBuilder>()
.addComponents( .addComponents(
new ButtonBuilder() new ButtonBuilder()
.setCustomId(`claim ${drop.card.id} ${claimId} ${userId}`) .setCustomId(`claim ${drop.card.id} ${claimId} ${userId}`)
.setLabel("Claim") .setLabel("Claim")
.setStyle(ButtonStyle.Primary) .setStyle(ButtonStyle.Primary),
.setDisabled(disabled),
new ButtonBuilder() new ButtonBuilder()
.setCustomId("reroll") .setCustomId("reroll")
.setLabel("Reroll") .setLabel("Reroll")

View file

@ -4,7 +4,6 @@ import { CoreClient } from "../client/client";
import EmbedColours from "../constants/EmbedColours"; import EmbedColours from "../constants/EmbedColours";
import { CardRarity, CardRarityToString } from "../constants/CardRarity"; import { CardRarity, CardRarityToString } from "../constants/CardRarity";
import cloneDeep from "clone-deep"; import cloneDeep from "clone-deep";
import AppLogger from "../client/appLogger";
interface InventoryPage { interface InventoryPage {
id: number, id: number,
@ -22,8 +21,6 @@ interface InventoryPageCards {
export default class InventoryHelper { export default class InventoryHelper {
public static async GenerateInventoryPage(username: string, userid: string, page: number): Promise<{ embed: EmbedBuilder, row: ActionRowBuilder<ButtonBuilder> }> { public static async GenerateInventoryPage(username: string, userid: string, page: number): Promise<{ embed: EmbedBuilder, row: ActionRowBuilder<ButtonBuilder> }> {
AppLogger.LogSilly("Helpers/InventoryHelper", `Parameters: username=${username}, userid=${userid}, page=${page}`);
const cardsPerPage = 15; const cardsPerPage = 15;
const inventory = await Inventory.FetchAllByUserId(userid); const inventory = await Inventory.FetchAllByUserId(userid);
@ -76,7 +73,7 @@ export default class InventoryHelper {
const currentPage = pages[page]; const currentPage = pages[page];
if (!currentPage) { if (!currentPage) {
AppLogger.LogError("Helpers/InventoryHelper", "Unable to find page"); console.error("Unable to find page");
return Promise.reject("Unable to find page"); return Promise.reject("Unable to find page");
} }

View file

@ -1,99 +0,0 @@
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js";
import AppLogger from "../client/appLogger";
import cloneDeep from "clone-deep";
import { CoreClient } from "../client/client";
import EmbedColours from "../constants/EmbedColours";
import { CardRarityToString } from "../constants/CardRarity";
export default class SeriesHelper {
public static GenerateSeriesViewPage(seriesId: number, page: number): { embed: EmbedBuilder, row: ActionRowBuilder<ButtonBuilder> } | null {
AppLogger.LogSilly("Helpers/SeriesHelper", `Parameters: seriesId=${seriesId}, page=${page}`);
const itemsPerPage = 15;
const series = cloneDeep(CoreClient.Cards)
.find(x => x.id == seriesId);
if (!series) {
AppLogger.LogVerbose("Helpers/SeriesHelper", `Unable to find series: ${seriesId}`);
return null;
}
const totalPages = Math.ceil(series.cards.length / itemsPerPage);
if (page > totalPages) {
AppLogger.LogVerbose("Helpers/SeriesHelper", `Trying to find page greater than what exists for this series. Page: ${page} but there are only ${totalPages} pages`);
return null;
}
const cardsOnPage = series.cards.splice(page * itemsPerPage, itemsPerPage);
const description = cardsOnPage
.map(x => `[${x.id}] ${x.name} ${CardRarityToString(x.type).toUpperCase()}`)
.join("\n");
const embed = new EmbedBuilder()
.setTitle(series.name)
.setColor(EmbedColours.Ok)
.setDescription(description)
.setFooter({ text: `${series.id} · ${series.cards.length} cards · Page ${page + 1} of ${totalPages}` });
const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId(`series view ${seriesId} ${page - 1}`)
.setLabel("Previous")
.setStyle(ButtonStyle.Primary)
.setDisabled(page == 0),
new ButtonBuilder()
.setCustomId(`series view ${seriesId} ${page + 1}`)
.setLabel("Next")
.setStyle(ButtonStyle.Primary)
.setDisabled(page + 1 > totalPages));
return { embed, row };
}
public static GenerateSeriesListPage(page: number): { embed: EmbedBuilder, row: ActionRowBuilder<ButtonBuilder> } | null {
AppLogger.LogSilly("Helpers/InventoryHelper", `Parameters: page=${page}`);
const itemsPerPage = 15;
const series = cloneDeep(CoreClient.Cards)
.sort((a, b) => a.id - b.id);
const totalPages = Math.ceil(series.length / itemsPerPage);
if (page > totalPages) {
AppLogger.LogVerbose("Helpers/SeriesHelper", `Trying to find page greater than what exists for this series. Page: ${page} but there are only ${totalPages} pages`);
return null;
}
const seriesOnPage = series.splice(page * itemsPerPage, itemsPerPage);
const description = seriesOnPage
.map(x => `[${x.id}] ${x.name}`)
.join("\n");
const embed = new EmbedBuilder()
.setTitle("Series")
.setColor(EmbedColours.Ok)
.setDescription(description)
.setFooter({ text: `${CoreClient.Cards.length} series · Page ${page + 1} of ${totalPages}` });
const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId(`series list ${page - 1}`)
.setLabel("Previous")
.setStyle(ButtonStyle.Primary)
.setDisabled(page == 0),
new ButtonBuilder()
.setCustomId(`series list ${page + 1}`)
.setLabel("Next")
.setStyle(ButtonStyle.Primary)
.setDisabled(page + 1 > totalPages));
return { embed, row };
}
}

View file

@ -1,9 +1,8 @@
import { Request, Response } from "express"; import { Request, Response } from "express";
import CardMetadataFunction from "../Functions/CardMetadataFunction"; import CardMetadataFunction from "../Functions/CardMetadataFunction";
import AppLogger from "../client/appLogger";
export default async function ReloadDB(req: Request, res: Response) { export default async function ReloadDB(req: Request, res: Response) {
AppLogger.LogInfo("Hooks/ReloadDB", "Reloading Card DB..."); console.log("Reloading Card DB...");
await CardMetadataFunction.Execute(); await CardMetadataFunction.Execute();

View file

@ -8,8 +8,6 @@ import Gdrivesync from "./commands/gdrivesync";
import Give from "./commands/give"; import Give from "./commands/give";
import Inventory from "./commands/inventory"; import Inventory from "./commands/inventory";
import Resync from "./commands/resync"; import Resync from "./commands/resync";
import Series from "./commands/series";
import Trade from "./commands/trade";
import View from "./commands/view"; import View from "./commands/view";
// Test Command Imports // Test Command Imports
@ -20,8 +18,6 @@ import Droprarity from "./commands/stage/droprarity";
import Claim from "./buttonEvents/Claim"; import Claim from "./buttonEvents/Claim";
import InventoryButtonEvent from "./buttonEvents/Inventory"; import InventoryButtonEvent from "./buttonEvents/Inventory";
import Reroll from "./buttonEvents/Reroll"; import Reroll from "./buttonEvents/Reroll";
import SeriesEvent from "./buttonEvents/Series";
import TradeButtonEvent from "./buttonEvents/Trade";
export default class Registry { export default class Registry {
public static RegisterCommands() { public static RegisterCommands() {
@ -32,8 +28,6 @@ export default class Registry {
CoreClient.RegisterCommand("give", new Give()); CoreClient.RegisterCommand("give", new Give());
CoreClient.RegisterCommand("inventory", new Inventory()); CoreClient.RegisterCommand("inventory", new Inventory());
CoreClient.RegisterCommand("resync", new Resync()); CoreClient.RegisterCommand("resync", new Resync());
CoreClient.RegisterCommand("series", new Series());
CoreClient.RegisterCommand("trade", new Trade());
CoreClient.RegisterCommand("view", new View()); CoreClient.RegisterCommand("view", new View());
// Test Commands // Test Commands
@ -47,9 +41,7 @@ export default class Registry {
public static RegisterButtonEvents() { public static RegisterButtonEvents() {
CoreClient.RegisterButtonEvent("claim", new Claim()); CoreClient.RegisterButtonEvent("claim", new Claim());
CoreClient.RegisterButtonEvent("inventory", new InventoryButtonEvent()); CoreClient.RegisterButtonEvent("inventory", new InventoryButtonEvent);
CoreClient.RegisterButtonEvent("reroll", new Reroll()); CoreClient.RegisterButtonEvent("reroll", new Reroll());
CoreClient.RegisterButtonEvent("series", new SeriesEvent());
CoreClient.RegisterButtonEvent("trade", new TradeButtonEvent());
} }
} }

View file

@ -1,7 +1,6 @@
import bodyParser from "body-parser"; import bodyParser from "body-parser";
import express, { Application } from "express"; import express, { Application } from "express";
import ReloadDB from "./hooks/ReloadDB"; import ReloadDB from "./hooks/ReloadDB";
import AppLogger from "./client/appLogger";
export default class Webhooks { export default class Webhooks {
private app: Application; private app: Application;
@ -25,7 +24,7 @@ export default class Webhooks {
private setupListen() { private setupListen() {
this.app.listen(this.port, () => { this.app.listen(this.port, () => {
AppLogger.LogInfo("Webhooks", `API listening on port ${this.port}`); console.log(`API listening on port ${this.port}`);
}); });
} }
} }