Update card database to use JSON files #107
19 changed files with 575 additions and 363 deletions
419
package-lock.json
generated
419
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -31,11 +31,11 @@
|
||||||
"discord.js": "^14.3.0",
|
"discord.js": "^14.3.0",
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"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.3",
|
"minimatch": "9.0.3",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"sqlite3": "^5.1.6",
|
|
||||||
"ts-jest": "^29.0.0",
|
"ts-jest": "^29.0.0",
|
||||||
"typeorm": "0.3.17"
|
"typeorm": "0.3.17"
|
||||||
},
|
},
|
||||||
|
|
41
src/Functions/CardMetadataFunction.ts
Normal file
41
src/Functions/CardMetadataFunction.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { readFileSync } from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import Config from "../database/entities/app/Config";
|
||||||
|
import { glob } from "glob";
|
||||||
|
import SeriesMetadata from "../contracts/SeriesMetadata";
|
||||||
|
import { CoreClient } from "../client/client";
|
||||||
|
|
||||||
|
export default class CardMetadataFunction {
|
||||||
|
public static async Execute(overrideSafeMode: boolean = false): Promise<boolean> {
|
||||||
|
if (!overrideSafeMode && await Config.GetValue('safemode') == "true") return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
CoreClient.Cards = await this.FindMetadataJSONs();
|
||||||
|
|
||||||
|
console.log(`Loaded ${CoreClient.Cards.flatMap(x => x.cards).length} cards to database`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
|
||||||
|
await Config.SetValue('safemode', 'true');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async FindMetadataJSONs(): Promise<SeriesMetadata[]> {
|
||||||
|
const res: SeriesMetadata[] = [];
|
||||||
|
|
||||||
|
const seriesJSONs = await glob(path.join(process.cwd(), 'cards', '/**/*.json'));
|
||||||
|
|
||||||
|
for (let jsonPath of seriesJSONs) {
|
||||||
|
console.log(`Reading file ${jsonPath}`);
|
||||||
|
const jsonFile = readFileSync(jsonPath);
|
||||||
|
const parsedJson: SeriesMetadata[] = JSON.parse(jsonFile.toString());
|
||||||
|
|
||||||
|
res.push(...parsedJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,105 +0,0 @@
|
||||||
import { existsSync, readdirSync } from "fs";
|
|
||||||
import CardDataSource from "../database/dataSources/cardDataSource";
|
|
||||||
import Card from "../database/entities/card/Card";
|
|
||||||
import Series from "../database/entities/card/Series";
|
|
||||||
import path from "path";
|
|
||||||
import { CardRarity, CardRarityToString } from "../constants/CardRarity";
|
|
||||||
import Config from "../database/entities/app/Config";
|
|
||||||
|
|
||||||
export default class CardSetupFunction {
|
|
||||||
public static async Execute(): Promise<boolean> {
|
|
||||||
if (await Config.GetValue('safemode') == "true") return false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.ClearDatabase();
|
|
||||||
await this.ReadSeries();
|
|
||||||
await this.ReadCards();
|
|
||||||
} catch {
|
|
||||||
await Config.SetValue('safemode', 'true');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async ClearDatabase() {
|
|
||||||
const cardRepository = CardDataSource.getRepository(Card);
|
|
||||||
await cardRepository.clear();
|
|
||||||
|
|
||||||
const seriesRepository = CardDataSource.getRepository(Series);
|
|
||||||
await seriesRepository.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async ReadSeries() {
|
|
||||||
const seriesDir = readdirSync(path.join(process.cwd(), 'cards'));
|
|
||||||
|
|
||||||
const seriesRepository = CardDataSource.getRepository(Series);
|
|
||||||
|
|
||||||
const seriesToSave: Series[] = [];
|
|
||||||
|
|
||||||
for (let dir of seriesDir) {
|
|
||||||
const dirPart = dir.split(' ');
|
|
||||||
|
|
||||||
const seriesId = dirPart.shift();
|
|
||||||
const seriesName = dirPart.join(' ');
|
|
||||||
|
|
||||||
const series = new Series(seriesId!, seriesName, dir);
|
|
||||||
|
|
||||||
seriesToSave.push(series);
|
|
||||||
}
|
|
||||||
|
|
||||||
await seriesRepository.save(seriesToSave);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async ReadCards() {
|
|
||||||
const loadedSeries = await Series.FetchAll(Series, [ "Cards", "Cards.Series" ]);
|
|
||||||
|
|
||||||
const cardRepository = CardDataSource.getRepository(Card);
|
|
||||||
|
|
||||||
const cardsToSave: Card[] = [];
|
|
||||||
|
|
||||||
for (let series of loadedSeries) {
|
|
||||||
const cardDirBronze = this.GetCardFiles(CardRarity.Bronze, series);
|
|
||||||
const cardDirGold = this.GetCardFiles(CardRarity.Gold, series);
|
|
||||||
const cardDirLegendary = this.GetCardFiles(CardRarity.Legendary, series);
|
|
||||||
const cardDirSilver = this.GetCardFiles(CardRarity.Silver, series);
|
|
||||||
const cardDirManga = this.GetCardFiles(CardRarity.Manga, series);
|
|
||||||
|
|
||||||
cardsToSave.push(
|
|
||||||
...this.GenerateCardData(cardDirBronze, CardRarity.Bronze, series),
|
|
||||||
...this.GenerateCardData(cardDirGold, CardRarity.Gold, series),
|
|
||||||
...this.GenerateCardData(cardDirLegendary, CardRarity.Legendary, series),
|
|
||||||
...this.GenerateCardData(cardDirSilver, CardRarity.Silver, series),
|
|
||||||
...this.GenerateCardData(cardDirManga, CardRarity.Manga, series)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await cardRepository.save(cardsToSave);
|
|
||||||
|
|
||||||
console.log(`Loaded ${cardsToSave.length} cards to database`);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static GenerateCardData(files: string[], rarity: CardRarity, series: Series): Card[] {
|
|
||||||
const result: Card[] = [];
|
|
||||||
|
|
||||||
for (let file of files.filter(x => !x.startsWith('.') && (x.endsWith('.png') || x.endsWith('.jpg') || x.endsWith('.gif')))) {
|
|
||||||
const filePart = file.split('.');
|
|
||||||
|
|
||||||
const cardId = filePart[0];
|
|
||||||
const cardName = filePart[0];
|
|
||||||
|
|
||||||
const card = new Card(cardId, cardName, rarity, path.join(process.cwd(), 'cards', series.Path, CardRarityToString(rarity).toUpperCase(), file), file, series);
|
|
||||||
|
|
||||||
result.push(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static GetCardFiles(rarity: CardRarity, series: Series): string[] {
|
|
||||||
const folder = path.join(process.cwd(), 'cards', series.Path, CardRarityToString(rarity).toUpperCase());
|
|
||||||
const folderExists = existsSync(folder);
|
|
||||||
|
|
||||||
return folderExists ? readdirSync(folder) : [];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { AttachmentBuilder, ButtonInteraction, DiscordAPIError } from "discord.js";
|
import { AttachmentBuilder, ButtonInteraction, DiscordAPIError } from "discord.js";
|
||||||
import { ButtonEvent } from "../type/buttonEvent";
|
import { ButtonEvent } from "../type/buttonEvent";
|
||||||
import CardDropHelper from "../helpers/CardDropHelper";
|
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import { CoreClient } from "../client/client";
|
import { CoreClient } from "../client/client";
|
||||||
import Inventory from "../database/entities/app/Inventory";
|
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 path from "path";
|
||||||
|
|
||||||
export default class Reroll extends ButtonEvent {
|
export default class Reroll extends ButtonEvent {
|
||||||
public override async execute(interaction: ButtonInteraction) {
|
public override async execute(interaction: ButtonInteraction) {
|
||||||
|
@ -14,34 +15,40 @@ export default class Reroll extends ButtonEvent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await Config.GetValue('safemode') == "true")
|
if (await Config.GetValue('safemode') == "true") {
|
||||||
{
|
await interaction.reply('Safe Mode has been activated, please resync to continue.');
|
||||||
await interaction.reply('Safe Mode has been activated, please resync to contunue.');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!interaction.guild || !interaction.guildId) return;
|
const randomCard = CardDropHelperMetadata.GetRandomCard();
|
||||||
|
|
||||||
let randomCard = await CardDropHelper.GetRandomCard();
|
if (!randomCard) {
|
||||||
|
await interaction.reply('Unable to fetch card, please try again.');
|
||||||
if (process.env.DROP_RARITY && Number(process.env.DROP_RARITY) > 0) {
|
return;
|
||||||
randomCard = await CardDropHelper.GetRandomCardByRarity(Number(process.env.DROP_RARITY));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const image = readFileSync(randomCard.Path);
|
let image: Buffer;
|
||||||
|
const imageFileName = randomCard.card.path.split("/").pop()!;
|
||||||
|
|
||||||
|
try {
|
||||||
|
image = readFileSync(path.join(process.cwd(), 'cards', randomCard.card.path));
|
||||||
|
} catch {
|
||||||
|
await interaction.reply(`Unable to fetch image for card ${randomCard.card.id}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await interaction.deferReply();
|
await interaction.deferReply();
|
||||||
|
|
||||||
const attachment = new AttachmentBuilder(image, { name: randomCard.FileName });
|
const attachment = new AttachmentBuilder(image, { name: imageFileName });
|
||||||
|
|
||||||
const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, randomCard.CardNumber);
|
const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, randomCard.card.id);
|
||||||
const quantityClaimed = inventory ? inventory.Quantity : 0;
|
const quantityClaimed = inventory ? inventory.Quantity : 0;
|
||||||
|
|
||||||
const embed = CardDropHelper.GenerateDropEmbed(randomCard, quantityClaimed || 0);
|
const embed = CardDropHelperMetadata.GenerateDropEmbed(randomCard, quantityClaimed, imageFileName);
|
||||||
|
|
||||||
const claimId = v4();
|
const claimId = v4();
|
||||||
|
|
||||||
const row = CardDropHelper.GenerateDropButtons(randomCard, claimId, interaction.user.id);
|
const row = CardDropHelperMetadata.GenerateDropButtons(randomCard, claimId, interaction.user.id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
|
|
|
@ -7,13 +7,13 @@ import { Command } from "../type/command";
|
||||||
|
|
||||||
import { Events } from "./events";
|
import { Events } from "./events";
|
||||||
import { Util } from "./util";
|
import { Util } from "./util";
|
||||||
import CardSetupFunction from "../Functions/CardSetupFunction";
|
|
||||||
import CardDataSource from "../database/dataSources/cardDataSource";
|
|
||||||
import IButtonEventItem from "../contracts/IButtonEventItem";
|
import IButtonEventItem from "../contracts/IButtonEventItem";
|
||||||
import { ButtonEvent } from "../type/buttonEvent";
|
import { ButtonEvent } from "../type/buttonEvent";
|
||||||
import AppDataSource from "../database/dataSources/appDataSource";
|
import AppDataSource from "../database/dataSources/appDataSource";
|
||||||
import { Environment } from "../constants/Environment";
|
import { Environment } from "../constants/Environment";
|
||||||
import Webhooks from "../webhooks";
|
import Webhooks from "../webhooks";
|
||||||
|
import CardMetadataFunction from "../Functions/CardMetadataFunction";
|
||||||
|
import SeriesMetadata from "../contracts/SeriesMetadata";
|
||||||
|
|
||||||
export class CoreClient extends Client {
|
export class CoreClient extends Client {
|
||||||
private static _commandItems: ICommandItem[];
|
private static _commandItems: ICommandItem[];
|
||||||
|
@ -27,6 +27,7 @@ export class CoreClient extends Client {
|
||||||
public static ClaimId: string;
|
public static ClaimId: string;
|
||||||
public static Environment: Environment;
|
public static Environment: Environment;
|
||||||
public static AllowDrops: boolean;
|
public static AllowDrops: boolean;
|
||||||
|
public static Cards: SeriesMetadata[];
|
||||||
|
|
||||||
public static get commandItems(): ICommandItem[] {
|
public static get commandItems(): ICommandItem[] {
|
||||||
return this._commandItems;
|
return this._commandItems;
|
||||||
|
@ -68,14 +69,10 @@ export class CoreClient extends Client {
|
||||||
.then(() => console.log("App Data Source Initialised"))
|
.then(() => console.log("App Data Source Initialised"))
|
||||||
.catch(err => console.error("Error initialising App Data Source", err));
|
.catch(err => console.error("Error initialising App Data Source", err));
|
||||||
|
|
||||||
await CardDataSource.initialize()
|
|
||||||
.then(() => console.log("Card Data Source Initialised"))
|
|
||||||
.catch(err => console.error("Error initialising Card Data Source", 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);
|
||||||
|
|
||||||
await CardSetupFunction.Execute();
|
await CardMetadataFunction.Execute(true);
|
||||||
|
|
||||||
this._util.loadEvents(this, CoreClient._eventItems);
|
this._util.loadEvents(this, CoreClient._eventItems);
|
||||||
this._util.loadSlashCommands(this);
|
this._util.loadSlashCommands(this);
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { AttachmentBuilder, CommandInteraction, DiscordAPIError, SlashCommandBuilder } from "discord.js";
|
import { AttachmentBuilder, CommandInteraction, DiscordAPIError, SlashCommandBuilder } from "discord.js";
|
||||||
import { Command } from "../type/command";
|
import { Command } from "../type/command";
|
||||||
import CardDropHelper from "../helpers/CardDropHelper";
|
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { CoreClient } from "../client/client";
|
import { CoreClient } from "../client/client";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import Inventory from "../database/entities/app/Inventory";
|
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 path from "path";
|
||||||
|
|
||||||
export default class Drop extends Command {
|
export default class Drop extends Command {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -22,28 +23,40 @@ export default class Drop extends Command {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await Config.GetValue('safemode') == "true")
|
if (await Config.GetValue('safemode') == "true") {
|
||||||
{
|
await interaction.reply('Safe Mode has been activated, please resync to continue.');
|
||||||
await interaction.reply('Safe Mode has been activated, please resync to contunue.');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const randomCard = await CardDropHelper.GetRandomCard();
|
const randomCard = CardDropHelperMetadata.GetRandomCard();
|
||||||
|
|
||||||
const image = readFileSync(randomCard.Path);
|
if (!randomCard) {
|
||||||
|
await interaction.reply('Unable to fetch card, please try again.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let image: Buffer;
|
||||||
|
const imageFileName = randomCard.card.path.split("/").pop()!;
|
||||||
|
|
||||||
|
try {
|
||||||
|
image = readFileSync(path.join(process.cwd(), 'cards', randomCard.card.path));
|
||||||
|
} catch {
|
||||||
|
await interaction.reply(`Unable to fetch image for card ${randomCard.card.id}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await interaction.deferReply();
|
await interaction.deferReply();
|
||||||
|
|
||||||
const attachment = new AttachmentBuilder(image, { name: randomCard.FileName });
|
const attachment = new AttachmentBuilder(image, { name: imageFileName });
|
||||||
|
|
||||||
const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, randomCard.CardNumber);
|
const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, randomCard.card.id);
|
||||||
const quantityClaimed = inventory ? inventory.Quantity : 0;
|
const quantityClaimed = inventory ? inventory.Quantity : 0;
|
||||||
|
|
||||||
const embed = CardDropHelper.GenerateDropEmbed(randomCard, quantityClaimed || 0);
|
const embed = CardDropHelperMetadata.GenerateDropEmbed(randomCard, quantityClaimed, imageFileName);
|
||||||
|
|
||||||
const claimId = v4();
|
const claimId = v4();
|
||||||
|
|
||||||
const row = CardDropHelper.GenerateDropButtons(randomCard, claimId, interaction.user.id);
|
const row = CardDropHelperMetadata.GenerateDropButtons(randomCard, claimId, interaction.user.id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { CacheType, CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
|
import { CacheType, CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
|
||||||
import { Command } from "../type/command";
|
import { Command } from "../type/command";
|
||||||
import { ExecException, exec } from "child_process";
|
import { ExecException, exec } from "child_process";
|
||||||
import CardSetupFunction from "../Functions/CardSetupFunction";
|
|
||||||
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";
|
||||||
|
|
||||||
export default class Gdrivesync extends Command {
|
export default class Gdrivesync extends Command {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -34,7 +34,8 @@ export default class Gdrivesync extends Command {
|
||||||
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 {
|
||||||
await CardSetupFunction.Execute();
|
await CardMetadataFunction.Execute();
|
||||||
|
|
||||||
await interaction.editReply('Synced successfully.');
|
await interaction.editReply('Synced successfully.');
|
||||||
|
|
||||||
CoreClient.AllowDrops = true;
|
CoreClient.AllowDrops = true;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { CacheType, CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
|
import { CacheType, CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
|
||||||
import { Command } from "../type/command";
|
import { Command } from "../type/command";
|
||||||
import CardSetupFunction from "../Functions/CardSetupFunction";
|
|
||||||
import Config from "../database/entities/app/Config";
|
import Config from "../database/entities/app/Config";
|
||||||
|
import CardMetadataFunction from "../Functions/CardMetadataFunction";
|
||||||
|
|
||||||
export default class Resync extends Command {
|
export default class Resync extends Command {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -23,7 +23,9 @@ export default class Resync extends Command {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await CardSetupFunction.Execute()) {
|
let result = await CardMetadataFunction.Execute(true);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
if (await Config.GetValue('safemode') == "true") {
|
if (await Config.GetValue('safemode') == "true") {
|
||||||
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.");
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { AttachmentBuilder, CacheType, CommandInteraction, DiscordAPIError, SlashCommandBuilder } from "discord.js";
|
import { AttachmentBuilder, CacheType, CommandInteraction, DiscordAPIError, SlashCommandBuilder } from "discord.js";
|
||||||
import { Command } from "../../type/command";
|
import { Command } from "../../type/command";
|
||||||
import Card from "../../database/entities/card/Card";
|
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import Inventory from "../../database/entities/app/Inventory";
|
import Inventory from "../../database/entities/app/Inventory";
|
||||||
import CardDropHelper from "../../helpers/CardDropHelper";
|
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import { CoreClient } from "../../client/client";
|
import { CoreClient } from "../../client/client";
|
||||||
|
import path from "path";
|
||||||
|
import CardDropHelperMetadata from "../../helpers/CardDropHelperMetadata";
|
||||||
|
|
||||||
export default class Dropnumber extends Command {
|
export default class Dropnumber extends Command {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -31,29 +31,42 @@ export default class Dropnumber extends Command {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const card = await Card.FetchOneByCardNumber(cardNumber.value.toString(), [
|
const series = CoreClient.Cards.find(x => x.cards.find(y => y.id == cardNumber.toString()));
|
||||||
"Series"
|
|
||||||
]);
|
if (!series) {
|
||||||
|
await interaction.reply('Card not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const card = series.cards.find(x => x.id == cardNumber.toString());
|
||||||
|
|
||||||
if (!card) {
|
if (!card) {
|
||||||
await interaction.reply('Card not found');
|
await interaction.reply('Card not found');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const image = readFileSync(card.Path);
|
let image: Buffer;
|
||||||
|
const imageFileName = card.path.split("/").pop()!;
|
||||||
|
|
||||||
|
try {
|
||||||
|
image = readFileSync(path.join(process.cwd(), 'cards', card.path));
|
||||||
|
} catch {
|
||||||
|
await interaction.reply(`Unable to fetch image for card ${card.id}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await interaction.deferReply();
|
await interaction.deferReply();
|
||||||
|
|
||||||
const attachment = new AttachmentBuilder(image, { name: card.FileName });
|
const attachment = new AttachmentBuilder(image, { name: imageFileName });
|
||||||
|
|
||||||
const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, card.CardNumber);
|
const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, card.id);
|
||||||
const quantityClaimed = inventory ? inventory.Quantity : 0;
|
const quantityClaimed = inventory ? inventory.Quantity : 0;
|
||||||
|
|
||||||
const embed = CardDropHelper.GenerateDropEmbed(card, quantityClaimed || 0);
|
const embed = CardDropHelperMetadata.GenerateDropEmbed({ card, series }, quantityClaimed, imageFileName);
|
||||||
|
|
||||||
const claimId = v4();
|
const claimId = v4();
|
||||||
|
|
||||||
const row = CardDropHelper.GenerateDropButtons(card, claimId, interaction.user.id);
|
const row = CardDropHelperMetadata.GenerateDropButtons({ card, series }, claimId, interaction.user.id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { AttachmentBuilder, CacheType, CommandInteraction, DiscordAPIError, SlashCommandBuilder } from "discord.js";
|
import { AttachmentBuilder, CacheType, CommandInteraction, DiscordAPIError, SlashCommandBuilder } from "discord.js";
|
||||||
import { Command } from "../../type/command";
|
import { Command } from "../../type/command";
|
||||||
import { CardRarity, CardRarityParse } from "../../constants/CardRarity";
|
import { CardRarity, CardRarityParse } from "../../constants/CardRarity";
|
||||||
import CardDropHelper from "../../helpers/CardDropHelper";
|
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import Inventory from "../../database/entities/app/Inventory";
|
import Inventory from "../../database/entities/app/Inventory";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import { CoreClient } from "../../client/client";
|
import { CoreClient } from "../../client/client";
|
||||||
|
import CardDropHelperMetadata from "../../helpers/CardDropHelperMetadata";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
export default class Droprarity extends Command {
|
export default class Droprarity extends Command {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -38,27 +39,35 @@ export default class Droprarity extends Command {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const card = await CardDropHelper.GetRandomCardByRarity(rarityType);
|
const card = await CardDropHelperMetadata.GetRandomCardByRarity(rarityType);
|
||||||
|
|
||||||
if (!card) {
|
if (!card) {
|
||||||
await interaction.reply('Card not found');
|
await interaction.reply('Card not found');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const image = readFileSync(card.Path);
|
let image: Buffer;
|
||||||
|
const imageFileName = card.card.path.split("/").pop()!;
|
||||||
|
|
||||||
|
try {
|
||||||
|
image = readFileSync(path.join(process.cwd(), 'cards', card.card.path));
|
||||||
|
} catch {
|
||||||
|
await interaction.reply(`Unable to fetch image for card ${card.card.id}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await interaction.deferReply();
|
await interaction.deferReply();
|
||||||
|
|
||||||
const attachment = new AttachmentBuilder(image, { name: card.FileName });
|
const attachment = new AttachmentBuilder(image, { name: imageFileName });
|
||||||
|
|
||||||
const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, card.CardNumber);
|
const inventory = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, card.card.id);
|
||||||
const quantityClaimed = inventory ? inventory.Quantity : 0;
|
const quantityClaimed = inventory ? inventory.Quantity : 0;
|
||||||
|
|
||||||
const embed = CardDropHelper.GenerateDropEmbed(card, quantityClaimed || 0);
|
const embed = CardDropHelperMetadata.GenerateDropEmbed(card, quantityClaimed, imageFileName);
|
||||||
|
|
||||||
const claimId = v4();
|
const claimId = v4();
|
||||||
|
|
||||||
const row = CardDropHelper.GenerateDropButtons(card, claimId, interaction.user.id);
|
const row = CardDropHelperMetadata.GenerateDropButtons(card, claimId, interaction.user.id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
import { Column, DeepPartial, EntityTarget, PrimaryColumn, ObjectLiteral, FindOptionsWhere } from "typeorm";
|
|
||||||
import { v4 } from "uuid";
|
|
||||||
import AppDataSource from "../database/dataSources/appDataSource";
|
|
||||||
import CardDataSource from "../database/dataSources/cardDataSource";
|
|
||||||
|
|
||||||
export default class CardBaseEntity {
|
|
||||||
constructor() {
|
|
||||||
this.Id = v4();
|
|
||||||
|
|
||||||
this.WhenCreated = new Date();
|
|
||||||
this.WhenUpdated = new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
@PrimaryColumn()
|
|
||||||
Id: string;
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
WhenCreated: Date;
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
WhenUpdated: Date;
|
|
||||||
|
|
||||||
public async Save<T extends CardBaseEntity>(target: EntityTarget<T>, entity: DeepPartial<T>): Promise<void> {
|
|
||||||
this.WhenUpdated = new Date();
|
|
||||||
|
|
||||||
const repository = CardDataSource.getRepository<T>(target);
|
|
||||||
|
|
||||||
await repository.save(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Remove<T extends CardBaseEntity>(target: EntityTarget<T>, entity: T): Promise<void> {
|
|
||||||
const repository = CardDataSource.getRepository<T>(target);
|
|
||||||
|
|
||||||
await repository.remove(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async FetchAll<T extends CardBaseEntity>(target: EntityTarget<T>, relations?: string[]): Promise<T[]> {
|
|
||||||
const repository = CardDataSource.getRepository<T>(target);
|
|
||||||
|
|
||||||
const all = await repository.find({ relations: relations || [] });
|
|
||||||
|
|
||||||
return all;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async FetchOneById<T extends CardBaseEntity>(target: EntityTarget<T>, id: string, relations?: string[]): Promise<T | null> {
|
|
||||||
const repository = CardDataSource.getRepository<T>(target);
|
|
||||||
|
|
||||||
const single = await repository.findOne({ where: ({ Id: id } as FindOptionsWhere<T>), relations: relations || {} });
|
|
||||||
|
|
||||||
return single;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Any<T extends ObjectLiteral>(target: EntityTarget<T>): Promise<boolean> {
|
|
||||||
const repository = CardDataSource.getRepository<T>(target);
|
|
||||||
|
|
||||||
const any = await repository.find();
|
|
||||||
|
|
||||||
return any.length > 0;
|
|
||||||
}
|
|
||||||
}
|
|
19
src/contracts/SeriesMetadata.ts
Normal file
19
src/contracts/SeriesMetadata.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { CardRarity } from "../constants/CardRarity";
|
||||||
|
|
||||||
|
export default interface SeriesMetadata {
|
||||||
|
id: number,
|
||||||
|
name: string,
|
||||||
|
cards: CardMetadata[],
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CardMetadata {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
type: CardRarity,
|
||||||
|
path: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DropResult {
|
||||||
|
series: SeriesMetadata,
|
||||||
|
card: CardMetadata,
|
||||||
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
import { DataSource } from "typeorm";
|
|
||||||
import * as dotenv from "dotenv";
|
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
const CardDataSource = new DataSource({
|
|
||||||
type: "sqlite",
|
|
||||||
database: process.env.DB_CARD_FILE!,
|
|
||||||
synchronize: true,
|
|
||||||
logging: process.env.DB_LOGGING == "true",
|
|
||||||
entities: [
|
|
||||||
"dist/database/entities/card/**/*.js",
|
|
||||||
],
|
|
||||||
migrations: [
|
|
||||||
"dist/database/migrations/card/**/*.js",
|
|
||||||
],
|
|
||||||
subscribers: [
|
|
||||||
"dist/database/subscribers/card/**/*.js",
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
export default CardDataSource;
|
|
|
@ -1,53 +0,0 @@
|
||||||
import { Column, Entity, ManyToOne } from "typeorm";
|
|
||||||
import CardBaseEntity from "../../../contracts/CardBaseEntity";
|
|
||||||
import { CardRarity } from "../../../constants/CardRarity";
|
|
||||||
import Series from "./Series";
|
|
||||||
import CardDataSource from "../../dataSources/cardDataSource";
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
export default class Card extends CardBaseEntity {
|
|
||||||
constructor(cardNumber: string, name: string, rarity: CardRarity, path: string, fileName: string, series: Series) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.CardNumber = cardNumber;
|
|
||||||
this.Name = name;
|
|
||||||
this.Rarity = rarity;
|
|
||||||
this.Path = path;
|
|
||||||
this.FileName = fileName;
|
|
||||||
this.Series = series;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
CardNumber: string;
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
Name: string;
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
Rarity: CardRarity;
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
Path: string;
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
FileName: string;
|
|
||||||
|
|
||||||
@ManyToOne(() => Series, x => x.Cards)
|
|
||||||
Series: Series;
|
|
||||||
|
|
||||||
public static async FetchOneByCardNumber(cardNumber: string, relations?: string[]): Promise<Card | null> {
|
|
||||||
const repository = CardDataSource.getRepository(Card);
|
|
||||||
|
|
||||||
const single = await repository.findOne({ where: { CardNumber: cardNumber }, relations: relations || [] });
|
|
||||||
|
|
||||||
return single;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async FetchAllByRarity(rarity: CardRarity, relations?: string[]): Promise<Card[]> {
|
|
||||||
const repository = CardDataSource.getRepository(Card);
|
|
||||||
|
|
||||||
const all = await repository.find({ where: { Rarity: rarity }, relations: relations || [] });
|
|
||||||
|
|
||||||
return all;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
import { Column, Entity, OneToMany } from "typeorm";
|
|
||||||
import CardBaseEntity from "../../../contracts/CardBaseEntity";
|
|
||||||
import Card from "./Card";
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
export default class Series extends CardBaseEntity {
|
|
||||||
constructor(id: string, name: string, path: string) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.Id = id;
|
|
||||||
this.Name = name;
|
|
||||||
this.Path = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
Name: string;
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
Path: string;
|
|
||||||
|
|
||||||
@OneToMany(() => Card, x => x.Series)
|
|
||||||
Cards: Card[];
|
|
||||||
}
|
|
|
@ -1,10 +1,11 @@
|
||||||
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 Card from "../database/entities/card/Card";
|
import { DropResult } from "../contracts/SeriesMetadata";
|
||||||
|
import { CoreClient } from "../client/client";
|
||||||
|
|
||||||
export default class CardDropHelper {
|
export default class CardDropHelperMetadata {
|
||||||
public static async GetRandomCard(): Promise<Card> {
|
public static GetRandomCard(): DropResult | undefined {
|
||||||
const randomRarity = Math.random() * 100;
|
const randomRarity = Math.random() * 100;
|
||||||
|
|
||||||
let cardRarity: CardRarity;
|
let cardRarity: CardRarity;
|
||||||
|
@ -20,39 +21,50 @@ export default class CardDropHelper {
|
||||||
else if (randomRarity < mangaChance) cardRarity = CardRarity.Manga;
|
else if (randomRarity < mangaChance) cardRarity = CardRarity.Manga;
|
||||||
else cardRarity = CardRarity.Legendary;
|
else cardRarity = CardRarity.Legendary;
|
||||||
|
|
||||||
const randomCard = await this.GetRandomCardByRarity(cardRarity);
|
const randomCard = this.GetRandomCardByRarity(cardRarity);
|
||||||
|
|
||||||
return randomCard;
|
return randomCard;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async GetRandomCardByRarity(rarity: CardRarity): Promise<Card> {
|
public static GetRandomCardByRarity(rarity: CardRarity): DropResult | undefined {
|
||||||
const allCards = await Card.FetchAllByRarity(rarity, [ "Series" ]);
|
const allCards = CoreClient.Cards
|
||||||
|
.flatMap(x => x.cards)
|
||||||
|
.filter(x => x.type == rarity);
|
||||||
|
|
||||||
const randomCardIndex = Math.floor(Math.random() * allCards.length);
|
const randomCardIndex = Math.floor(Math.random() * allCards.length);
|
||||||
|
|
||||||
const card = allCards[randomCardIndex];
|
const card = allCards[randomCardIndex];
|
||||||
|
const series = CoreClient.Cards
|
||||||
|
.find(x => x.cards.includes(card));
|
||||||
|
|
||||||
return card;
|
if (!series) {
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GenerateDropEmbed(card: Card, quantityClaimed: Number): EmbedBuilder {
|
return {
|
||||||
|
series: series,
|
||||||
|
card: card,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GenerateDropEmbed(drop: DropResult, quantityClaimed: Number, imageFileName: string): EmbedBuilder {
|
||||||
let description = "";
|
let description = "";
|
||||||
description += `Series: ${card.Series.Name}\n`;
|
description += `Series: ${drop.series.name}\n`;
|
||||||
description += `Claimed: ${quantityClaimed}\n`;
|
description += `Claimed: ${quantityClaimed}\n`;
|
||||||
|
|
||||||
return new EmbedBuilder()
|
return new EmbedBuilder()
|
||||||
.setTitle(card.Name)
|
.setTitle(drop.card.name)
|
||||||
.setDescription(description)
|
.setDescription(description)
|
||||||
.setFooter({ text: CardRarityToString(card.Rarity) })
|
.setFooter({ text: CardRarityToString(drop.card.type) })
|
||||||
.setColor(CardRarityToColour(card.Rarity))
|
.setColor(CardRarityToColour(drop.card.type))
|
||||||
.setImage(`attachment://${card.FileName}`);
|
.setImage(`attachment://${imageFileName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GenerateDropButtons(card: Card, claimId: string, userId: string): ActionRowBuilder<ButtonBuilder> {
|
public static GenerateDropButtons(drop: DropResult, claimId: string, userId: string): ActionRowBuilder<ButtonBuilder> {
|
||||||
return new ActionRowBuilder<ButtonBuilder>()
|
return new ActionRowBuilder<ButtonBuilder>()
|
||||||
.addComponents(
|
.addComponents(
|
||||||
new ButtonBuilder()
|
new ButtonBuilder()
|
||||||
.setCustomId(`claim ${card.CardNumber} ${claimId} ${userId}`)
|
.setCustomId(`claim ${drop.card.id} ${claimId} ${userId}`)
|
||||||
.setLabel("Claim")
|
.setLabel("Claim")
|
||||||
.setStyle(ButtonStyle.Primary),
|
.setStyle(ButtonStyle.Primary),
|
||||||
new ButtonBuilder()
|
new ButtonBuilder()
|
|
@ -1,10 +1,10 @@
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import CardSetupFunction from "../Functions/CardSetupFunction";
|
import CardMetadataFunction from "../Functions/CardMetadataFunction";
|
||||||
|
|
||||||
export default async function ReloadDB(req: Request, res: Response) {
|
export default async function ReloadDB(req: Request, res: Response) {
|
||||||
console.log('Reloading Card DB...');
|
console.log('Reloading Card DB...');
|
||||||
|
|
||||||
await CardSetupFunction.Execute();
|
await CardMetadataFunction.Execute();
|
||||||
|
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
}
|
}
|
Loading…
Reference in a new issue