Skip to content

Commit

Permalink
feat: add type declarations
Browse files Browse the repository at this point in the history
- Added type declarations for:
  - Wordle
  - MatchPairs
  - TicTacToe
  - Emojify
  - FastType
  - FindEmoji
  - Fishy
  - Flood
  - GuessThePokemon
  - Minesweeper
  - Slots
  - Trivia
  - WouldYouRather
  - Connect4
  - RockPaperScissors
  - Events
- Added Tuple type helper
  • Loading branch information
Juhan280 committed Sep 11, 2024
1 parent 502b42f commit 5774193
Show file tree
Hide file tree
Showing 22 changed files with 999 additions and 1 deletion.
23 changes: 23 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export const version: string;

export * from './types/Base';

export * from './types/2048';
export * from './types/Connect4';
export * from './types/FastType';
export * from './types/FindEmoji';
export * from './types/Fishy';
export * from './types/Flood';
export * from './types/GuessThePokemon';
export * from './types/Hangman';
export * from './types/MatchPairs';
export * from './types/Minesweeper';
export * from './types/RockPaperScissors';
export * from './types/Slots';
export * from './types/Snake';
export * from './types/TicTacToe';
export * from './types/Trivia';
export * from './types/Wordle';
export * from './types/WouldYouRather';

export * from './types/Emojify';
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "4.2.0",
"description": "Discord Gamecord is a powerful npm package with a collection of minigames for your discord bot",
"main": "index.js",
"types": "index.d.ts",
"scripts": {
"test": "cd test && node ."
},
Expand Down Expand Up @@ -30,4 +31,5 @@
"directories": {
"src": "src"
}
}
}

54 changes: 54 additions & 0 deletions types/2048.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
AttachmentBuilder,
InteractionEditReplyOptions,
Message,
MessageEditOptions,
MessagePayload,
User,
} from 'discord.js';
import { EventEmitter } from 'node:events';
import { BaseConstructorOptions, ButtonStyle, DeepRequired, MessageType, Position } from './Base';

export interface TwoZeroFourEightConstructorOptions<IsSlashGame extends boolean>
extends BaseConstructorOptions<IsSlashGame> {
embed?: {
title?: string;
color?: string;
};
emojis?: {
up?: string;
down?: string;
left?: string;
right?: string;
};
timeoutTime?: number;
buttonStyle?: ButtonStyle;
}

export class TwoZeroFourEight<IsSlashGame extends boolean = false> extends EventEmitter {
options: DeepRequired<TwoZeroFourEightConstructorOptions<IsSlashGame>>;
message: MessageType<IsSlashGame>;
gameBoard: string[];
mergedPos: Position[];
length: number;
score: number;

on(eventName: 'gameOver', listener: (result: { result: 'win' | 'lose'; player: User; score: number }) => void): this;
once(...args: Parameters<this['on']>): this;

constructor(options: TwoZeroFourEightConstructorOptions<IsSlashGame>);

sendMessage(
content: string | MessagePayload | (IsSlashGame extends true ? InteractionEditReplyOptions : MessageEditOptions)
): Promise<Message>;
getBoardImage(): Promise<AttachmentBuilder>; // There is literally no reason to make this asynchronous
startGame(): Promise<void>;
placeRandomTile(): void;
handleButtons(msg: Message): void;
gameOver(msg: Message): Promise<Message>;
isGameOver(): boolean;
shiftVertical(dir: 'up' | 'down'): boolean;
shiftHorizontal(dir: 'left' | 'right'): boolean;
isInsideBlock(pos: Position): boolean;
shift(pos: Position, dir: 'up' | 'down' | 'left' | 'right'): boolean;
}
31 changes: 31 additions & 0 deletions types/Approve.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Message, User } from 'discord.js';
import { EventEmitter } from 'node:events';
import { DeepRequired, MessageType } from './Base';

export interface ApproveConstructorOptions {
embed?: {
requestTitle?: string;
requestColor?: string;
rejectTitle?: string;
rejectColor?: string;
};
buttons?: {
accept?: string;
reject?: string;
};
reqTimeoutTime?: number;
mentionUser?: boolean;
requestMessage?: string;
rejectMessage?: string;
reqTimeoutMessage?: string;
}

export class Approve extends EventEmitter {
options: DeepRequired<ApproveConstructorOptions>;
message: MessageType<boolean>;
opponent: User;

constructor(options: ApproveConstructorOptions);
approve(): Promise<Message | false>;
formatTurnMessage<Options extends object>(options: Options, contentMsg: keyof Options): string;
}
21 changes: 21 additions & 0 deletions types/Base.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ChatInputCommandInteraction, Message } from 'discord.js';

// Helper type
export type DeepRequired<T> = T extends object ? { [K in keyof T]-?: DeepRequired<T[K]> } : T;
export type Tuple<N extends number, T> = N extends N ? (number extends N ? T[] : _TupleOf<T, N, []>) : never;
type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>;

export type ButtonStyle = 'PRIMARY' | 'SECONDARY' | 'SUCCESS' | 'DANGER';

export interface Position {
x: number;
y: number;
}

export type MessageType<IsSlashGame extends boolean> = IsSlashGame extends true ? ChatInputCommandInteraction : Message;

export interface BaseConstructorOptions<IsSlashGame extends boolean> {
isSlashGame?: IsSlashGame;
message: MessageType<IsSlashGame>;
playerOnlyMessage: string | false; // Wouldn't string | null be more natural?
}
54 changes: 54 additions & 0 deletions types/Connect4.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { InteractionEditReplyOptions, Message, MessageEditOptions, MessagePayload, User } from 'discord.js';
import { Approve, ApproveConstructorOptions } from './Approve';
import { BaseConstructorOptions, ButtonStyle, DeepRequired, MessageType } from './Base';

export interface Connect4ConstructorOptions<IsSlashGame extends boolean> extends BaseConstructorOptions<IsSlashGame> {
opponent: User;
embed?: {
title?: string;
statusTitle?: string;
color?: string;
};
emojis?: {
board?: string;
player1?: string;
player2?: string;
};
timeoutTime?: number;
buttonStyle?: ButtonStyle;
turnMessage?: string;
winMessage?: string;
tieMessage?: string;
timeoutMessage?: string;
requestMessage?: string;
rejectMessage?: string;
}

export class Connect4<IsSlashGame extends boolean = false> extends Approve {
options: DeepRequired<ApproveConstructorOptions & Connect4ConstructorOptions<IsSlashGame>>;
message: MessageType<IsSlashGame>;
opponent: User;
player1Turn: boolean;
gameBoard: string[];

on(
eventName: 'gameOver',
listener: (result: { result: 'win' | 'tie' | 'timeout'; player: User; opponent: User }) => void
): this;
once(...args: Parameters<this['on']>): this;

constructor(options: ApproveConstructorOptions & Connect4ConstructorOptions<IsSlashGame>);

getBoardContent(): string;
sendMessage(
content: string | MessagePayload | (IsSlashGame extends true ? InteractionEditReplyOptions : MessageEditOptions)
): Promise<Message>;
startGame(): Promise<void>;
connect4Game(msg: Message): Promise<void>;
handleButtons(msg: Message): void;
gameOver(msg: Message, result: 'win' | 'tie' | 'timeout'): Promise<Message>;
getPlayerEmoji(): string;
getTurnMessage(msg?: string): string;
isBoardFull(): boolean;
foundCheck(blockX: number, blockY: number): boolean;
}
26 changes: 26 additions & 0 deletions types/Emojify.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
type CharsKeys =
| '0'
| '1'
| '2'
| '3'
| '4'
| '5'
| '6'
| '7'
| '8'
| '9'
| '#'
| '*'
| '?'
| '!'
| '+'
| '-'
| '×'
| '$'
| '/'
| ' ';

export const Emojify: {
(content: string): string;
chars: Record<CharsKeys, string>;
};
36 changes: 36 additions & 0 deletions types/FastType.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { InteractionEditReplyOptions, Message, MessageEditOptions, MessagePayload, User } from 'discord.js';
import { EventEmitter } from 'node:events';
import { BaseConstructorOptions, DeepRequired, MessageType } from './Base';

export interface FastTypeConstructorOptions<IsSlashGame extends boolean> extends BaseConstructorOptions<IsSlashGame> {
embed?: {
title?: string;
color?: string;
description?: string;
};
sentense?: string;
timeoutTime?: number;
winMessage?: string;
loseMessage?: string;
}

export class FastType<IsSlashGame extends boolean = false> extends EventEmitter {
options: DeepRequired<FastTypeConstructorOptions<IsSlashGame>>;
message: MessageType<IsSlashGame>;
timeTaken: number | null;
wpm: number;

on(
eventName: 'gameOver',
listener: (result: { result: 'win' | 'lose'; player: User; timeTaken: number; wpm: number }) => void
): this;
once(...args: Parameters<this['on']>): this;

constructor(options: FastTypeConstructorOptions<IsSlashGame>);

sendMessage(
content: string | MessagePayload | (IsSlashGame extends true ? InteractionEditReplyOptions : MessageEditOptions)
): Promise<Message>;
startGame(): Promise<void>;
gameOver(msg: Message, result: boolean): Promise<Message>;
}
55 changes: 55 additions & 0 deletions types/FindEmoji.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {
ActionRowBuilder,
ButtonBuilder,
InteractionEditReplyOptions,
Message,
MessageEditOptions,
MessagePayload,
User,
} from 'discord.js';
import { EventEmitter } from 'node:events';
import { BaseConstructorOptions, ButtonStyle, DeepRequired, MessageType } from './Base';

export interface FindEmojiConstructorOptions<IsSlashGame extends boolean> extends BaseConstructorOptions<IsSlashGame> {
embed?: {
title?: string;
color?: string;
description?: string;
findDescription?: string;
};
timeoutTime?: number;
hideEmojiTime?: number;
buttonStyle?: ButtonStyle;
emojis?: string[];
winMessage?: string;
loseMessage?: string;
timeoutMessage?: string;
}

export class FindEmoji<IsSlashGame extends boolean = false> extends EventEmitter {
options: DeepRequired<FindEmojiConstructorOptions<IsSlashGame>>;
message: MessageType<IsSlashGame>;
emojis: string[];
selected: string | null;
emoji: string | null;

on(
eventName: 'gameOver',
listener: (result: {
result: 'win' | 'lose' | 'timeout';
player: User;
selectedEmoji: string | null;
correctEmoji: string | null;
}) => void
): this;
once(...args: Parameters<this['on']>): this;

constructor(options: FindEmojiConstructorOptions<IsSlashGame>);

sendMessage(
content: string | MessagePayload | (IsSlashGame extends true ? InteractionEditReplyOptions : MessageEditOptions)
): Promise<Message>;
startGame(): Promise<void>;
gameOver(msg: Message, result: boolean): Promise<Message>;
getComponents(showEmoji: boolean): ActionRowBuilder<ButtonBuilder>[];
}
55 changes: 55 additions & 0 deletions types/Fishy.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { InteractionEditReplyOptions, Message, MessageEditOptions, MessagePayload, Snowflake, User } from 'discord.js';
import { EventEmitter } from 'node:events';
import { BaseConstructorOptions, DeepRequired, MessageType } from './Base';

export interface Fish {
emoji: string;
price: number;
}

export type Fishes = Record<'junk' | 'common' | 'uncommon' | 'rare', Fish>;

// XXX: Someone suggest a better name, this name has a lot of ambiguity
export interface Player {
id: Snowflake;
balance: number;
fishes: object;
}

export interface FishyConstructorOptions<IsSlashGame extends boolean> extends BaseConstructorOptions<IsSlashGame> {
embed?: {
title?: string;
color?: string;
};
player?: Partial<Player>;
fishes?: Partial<Fishes>;
fishyRodPrice?: number;
catchMessage?: string;
sellMessage?: string;
noBalanceMessage?: string;
invalidTypeMessage?: string;
invalidAmountMessage?: string;
noItemMesaage?: string;
}

export class Fishy<IsSlashGame extends boolean = false> extends EventEmitter {
options: DeepRequired<FishyConstructorOptions<IsSlashGame>>;
message: MessageType<IsSlashGame>;
player: Player;
fishes: Fishes;

on(
eventName: 'catchFish' | 'sellFish',
listener: (fishy: { player: User; fishType: keyof Fishes; fish: Fish }) => void
): this;
once(...args: Parameters<this['on']>): this;

constructor(options: FishyConstructorOptions<IsSlashGame>);

sendMessage(
content: string | MessagePayload | (IsSlashGame extends true ? InteractionEditReplyOptions : MessageEditOptions)
): Promise<Message>;
catchFish(): Promise<Message>;
sellFish(type: string, amount: number): Promise<Message>;
fishyInventory(): Promise<Message>;
}
Loading

0 comments on commit 5774193

Please sign in to comment.