Skip to content

Commit

Permalink
implement cmd for drawing random card of game
Browse files Browse the repository at this point in the history
  • Loading branch information
soryy708 committed May 26, 2023
1 parent 2d37e8b commit a7a8dc7
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 18 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/node_modules/
/dist/
/cards/
.env
130 changes: 130 additions & 0 deletions src/server/discord/command/draw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import fs from 'fs';
import path from 'path';
import {
ApplicationCommandOptionType,
AttachmentBuilder,
ChatInputCommandInteraction,
EmbedBuilder,

Check failure on line 7 in src/server/discord/command/draw.ts

View workflow job for this annotation

GitHub Actions / Continuous Integration

'EmbedBuilder' is defined but never used

Check failure on line 7 in src/server/discord/command/draw.ts

View workflow job for this annotation

GitHub Actions / Continuous Integration

'EmbedBuilder' is defined but never used
} from 'discord.js';
import { DiscordCommand, DiscordCommandOption } from './interface';

export class DrawCommand implements DiscordCommand {
public name = 'draw';
public description = 'Draws a playing card from a deck of some game';

public async getOptions(): Promise<DiscordCommandOption[]> {
const cardsPath = path.join(process.cwd(), 'cards');
// eslint-disable-next-line no-sync
if (!fs.existsSync(cardsPath)) {
await fs.promises.mkdir(cardsPath);
console.warn(
"Cards directory didn't exist. Created empty one at: " +
cardsPath,
);
}
const cardsDirectoryContents = await fs.promises.readdir(
path.join(process.cwd(), 'cards'),
{ withFileTypes: true },
);
const games = cardsDirectoryContents.filter((dirent) =>
dirent.isDirectory(),
);
const options = [
{
type: ApplicationCommandOptionType.SubcommandGroup,
name: 'game',
description: 'What game are we drawing from?',
options: await Promise.all(
games.map(async (game) => {
const cards = await this.getCardsOfGame(game.name);
return {
type: ApplicationCommandOptionType.Subcommand,
name: this.directoryToCommand(game.name),
description: game.name,
options: cards.map((card) => ({
type: ApplicationCommandOptionType.Number,
name: this.directoryToCommand(card),
description: card + ' card(s)',
min_value: 1,
})),
};
}),
),
},
];
return options;
}

public async run(interaction: ChatInputCommandInteraction): Promise<void> {
const subcommand = interaction.options.getSubcommand();
switch (subcommand) {
case 'paranoia':
case 'executive': {
await this.runGame(subcommand, interaction);
break;
}
}
}

private async getCardsOfGame(game: string): Promise<string[]> {
const subdirectoryContents = await fs.promises.readdir(
path.join(process.cwd(), 'cards', game),
{ withFileTypes: true },
);
const cardTypes = subdirectoryContents.filter((dirent) =>
dirent.isDirectory(),
);
return cardTypes.map((cardType) => cardType.name);
}

private directoryToCommand(directory: string): string {
return directory.replace(/ /gu, '-').toLowerCase();
}

private async runGame(
game: string,
interaction: ChatInputCommandInteraction,
): Promise<void> {
const allCards = await this.getCardsOfGame(game);
await Promise.all(
allCards.map(async (card) => {
const count = interaction.options.getNumber(card);
if (count <= 0) {
return;
}
const cards = (
await Promise.all(
[...Array(count).keys()].map((_) =>
this.getCard(game, card),
),
)
).filter((c) => c !== null);
await interaction.reply({
files: cards.map((c) => new AttachmentBuilder(c)),
});
}),
);
}

private async getCard(game: string, type: string): Promise<string> {
const cardsDirectory = path.join(process.cwd(), 'cards', game, type);
const cardsDirectoryContents = await fs.promises.readdir(
cardsDirectory,
{ withFileTypes: true },
);
const images = cardsDirectoryContents.filter(
(dirent) =>
dirent.isFile() && this.fileExtensionIsImage(dirent.name),
);
if (images.length === 0) {
return null;
}
const index = Math.floor(Math.random() * images.length);
const imageName = images[index].name;
return path.join(cardsDirectory, imageName);
}

private fileExtensionIsImage(fileName: string): boolean {
return ['.png'].includes(path.extname(fileName).toLowerCase());
}
}
4 changes: 2 additions & 2 deletions src/server/discord/command/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { PingCommand } from './ping';
import { DrawCommand } from './draw';

export default [new PingCommand()];
export default [new DrawCommand()];
14 changes: 13 additions & 1 deletion src/server/discord/command/interface.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import { ChatInputCommandInteraction } from 'discord.js';
import {
ApplicationCommandOptionType,
ChatInputCommandInteraction,
} from 'discord.js';

export interface DiscordCommandOption {
type: ApplicationCommandOptionType;
name: string;
description: string;
min_value?: number;
options?: DiscordCommandOption[];
}

export interface DiscordCommand {
name: string;
description: string;
getOptions(): Promise<DiscordCommandOption[]>;
run(interaction: ChatInputCommandInteraction): Promise<void>;
}
11 changes: 0 additions & 11 deletions src/server/discord/command/ping.ts

This file was deleted.

9 changes: 5 additions & 4 deletions src/server/discord/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ export async function bootstrapDiscord(): Promise<void> {
await restApi.put(
Routes.applicationCommands(getConfig('DISCORD_CLIENT_ID')),
{
body: commands.map((command) => {
return {
body: await Promise.all(
commands.map(async (command) => ({
name: command.name,
description: command.description,
};
}),
options: await command.getOptions(),
})),
),
},
);

Expand Down

0 comments on commit a7a8dc7

Please sign in to comment.