From 7a8c646ca67672fbf60a9d6e6a3744eee229df2f Mon Sep 17 00:00:00 2001 From: na2na-p Date: Wed, 7 Feb 2024 22:39:03 +0900 Subject: [PATCH] =?UTF-8?q?In-Memory=E3=81=AA=E3=82=B9=E3=83=86=E3=83=BC?= =?UTF-8?q?=E3=83=88=E7=AE=A1=E7=90=86=E3=81=A8=E3=81=97=E3=81=A6=E5=88=87?= =?UTF-8?q?=E3=82=8A=E5=87=BA=E3=81=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/core/index.ts | 6 +++ .../internal/Client/internal/Client.class.ts | 18 ++++++- .../internal/Client/internal/Client.types.ts | 2 + src/features/core/internal/Store/index.ts | 11 ++++ .../internal/InMemoryStore.class.spec.ts | 51 +++++++++++++++++++ .../Store/internal/InMemoryStore.class.ts | 31 +++++++++++ .../internal/Store/internal/Store.class.ts | 11 ++++ .../Store/internal/Store.constants.ts | 3 ++ src/index.ts | 3 +- 9 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 src/features/core/internal/Store/index.ts create mode 100644 src/features/core/internal/Store/internal/InMemoryStore.class.spec.ts create mode 100644 src/features/core/internal/Store/internal/InMemoryStore.class.ts create mode 100644 src/features/core/internal/Store/internal/Store.class.ts create mode 100644 src/features/core/internal/Store/internal/Store.constants.ts diff --git a/src/features/core/index.ts b/src/features/core/index.ts index d69b76c4..8ba21e7c 100644 --- a/src/features/core/index.ts +++ b/src/features/core/index.ts @@ -7,3 +7,9 @@ export { getVoiceInstance, } from './internal/Voice/index.js'; export type { Voice } from './internal/Voice/index.js'; +export { + getInMemoryStoreInstance, + type InMemoryStore, + type Store, + STORE_TYPES, +} from './internal/Store/index.js'; diff --git a/src/features/core/internal/Client/internal/Client.class.ts b/src/features/core/internal/Client/internal/Client.class.ts index fa6ebe25..292aa339 100644 --- a/src/features/core/internal/Client/internal/Client.class.ts +++ b/src/features/core/internal/Client/internal/Client.class.ts @@ -2,12 +2,17 @@ import { exit } from 'process'; import type { CommandBase } from '@/features/commands/index.js'; import type { getConfig } from '@/features/config/index.js'; +import { + getInMemoryStoreInstance, + type Store, + STORE_TYPES, +} from '@/features/core/index.js'; import type { ChatInputCommandInteraction } from '@/features/library/index.js'; import { + chalk, Client as DiscordJsClient, GatewayIntentBits, InteractionType, - chalk, } from '@/features/library/index.js'; import { log } from '@/features/others/log/index.js'; @@ -16,8 +21,9 @@ import type { ClassConstructorArgs } from './Client.types.js'; export class Client extends DiscordJsClient { readonly #config: ReturnType; private interactionCommands: ReadonlyArray = []; + #store: Store; - constructor({ config, commands }: ClassConstructorArgs) { + constructor({ config, commands, storeDriver }: ClassConstructorArgs) { super({ intents: [ GatewayIntentBits.Guilds, @@ -34,6 +40,14 @@ export class Client extends DiscordJsClient { exit(1); } + switch (storeDriver) { + case STORE_TYPES.IN_MEMORY: + this.#store = getInMemoryStoreInstance(); + break; + default: + throw new Error('Invalid store driver'); + } + const commandsRegisteredResult = this.#installModules(commands); this.on('ready', client => { diff --git a/src/features/core/internal/Client/internal/Client.types.ts b/src/features/core/internal/Client/internal/Client.types.ts index a852a878..bb1d1616 100644 --- a/src/features/core/internal/Client/internal/Client.types.ts +++ b/src/features/core/internal/Client/internal/Client.types.ts @@ -1,7 +1,9 @@ import type { CommandBase } from '@/features/commands/index.js'; import type { getConfig } from '@/features/config/index.js'; +import type { STORE_TYPES } from '@/features/core/index.js'; export type ClassConstructorArgs = { config: ReturnType; commands: ReadonlyArray; + storeDriver: keyof typeof STORE_TYPES; }; diff --git a/src/features/core/internal/Store/index.ts b/src/features/core/internal/Store/index.ts new file mode 100644 index 00000000..c7f3f810 --- /dev/null +++ b/src/features/core/internal/Store/index.ts @@ -0,0 +1,11 @@ +import { singleton } from '@/features/others/singleton/index.js'; + +import { InMemoryStore } from './internal/InMemoryStore.class.js'; + +export type { Store } from './internal/Store.class.js'; +export { STORE_TYPES } from './internal/Store.constants.js'; +export type { InMemoryStore }; + +const createInMemoryStoreInstance = () => new InMemoryStore(); + +export const getInMemoryStoreInstance = singleton(createInMemoryStoreInstance); diff --git a/src/features/core/internal/Store/internal/InMemoryStore.class.spec.ts b/src/features/core/internal/Store/internal/InMemoryStore.class.spec.ts new file mode 100644 index 00000000..82108a19 --- /dev/null +++ b/src/features/core/internal/Store/internal/InMemoryStore.class.spec.ts @@ -0,0 +1,51 @@ +import type { Store } from '@/features/core/index.js'; +import type { AudioPlayer, VoiceConnection } from '@/features/library/index.js'; + +import { InMemoryStore } from './InMemoryStore.class.js'; + +describe('InMemoryStore', () => { + let store: Store; + const guildId = 'guildId123'; + const voiceConnection = {} as VoiceConnection; + const audioPlayer = {} as AudioPlayer; + + beforeEach(() => { + store = new InMemoryStore(); + }); + + describe('getVoiceConnection', () => { + it('should return undefined for a non-existing guildId', () => { + expect(store.getVoiceConnection(guildId)).toBeUndefined(); + }); + + it('should return the correct VoiceConnection for an existing guildId', () => { + store.setVoiceConnection(guildId, voiceConnection); + expect(store.getVoiceConnection(guildId)).toBe(voiceConnection); + }); + }); + + describe('setVoiceConnection', () => { + it('should set the VoiceConnection for a guildId', () => { + store.setVoiceConnection(guildId, voiceConnection); + expect(store.getVoiceConnection(guildId)).toBe(voiceConnection); + }); + }); + + describe('getVoicePlayer', () => { + it('should return undefined for a non-existing guildId', () => { + expect(store.getVoicePlayer(guildId)).toBeUndefined(); + }); + + it('should return the correct AudioPlayer for an existing guildId', () => { + store.setVoicePlayer(guildId, audioPlayer); + expect(store.getVoicePlayer(guildId)).toBe(audioPlayer); + }); + }); + + describe('setVoicePlayer', () => { + it('should set the AudioPlayer for a guildId', () => { + store.setVoicePlayer(guildId, audioPlayer); + expect(store.getVoicePlayer(guildId)).toBe(audioPlayer); + }); + }); +}); diff --git a/src/features/core/internal/Store/internal/InMemoryStore.class.ts b/src/features/core/internal/Store/internal/InMemoryStore.class.ts new file mode 100644 index 00000000..c063bc4c --- /dev/null +++ b/src/features/core/internal/Store/internal/InMemoryStore.class.ts @@ -0,0 +1,31 @@ +import type { + AudioPlayer, + Guild, + VoiceConnection, +} from '@/features/library/index.js'; + +import { Store } from './Store.class.js'; + +export type VoiceConnections = Map; +export type VoicePlayers = Map; + +export class InMemoryStore extends Store { + #voiceConnections: VoiceConnections = new Map(); + #voicePlayers: VoicePlayers = new Map(); + + public getVoiceConnection(guildId: Guild['id']) { + return this.#voiceConnections.get(guildId); + } + + public setVoiceConnection(guildId: Guild['id'], connection: VoiceConnection) { + this.#voiceConnections.set(guildId, connection); + } + + public getVoicePlayer(guildId: Guild['id']) { + return this.#voicePlayers.get(guildId); + } + + public setVoicePlayer(guildId: Guild['id'], player: AudioPlayer) { + this.#voicePlayers.set(guildId, player); + } +} diff --git a/src/features/core/internal/Store/internal/Store.class.ts b/src/features/core/internal/Store/internal/Store.class.ts new file mode 100644 index 00000000..6303f09c --- /dev/null +++ b/src/features/core/internal/Store/internal/Store.class.ts @@ -0,0 +1,11 @@ +import type { AudioPlayer, VoiceConnection } from '@/features/library/index.js'; + +export abstract class Store { + abstract getVoiceConnection(guildId: string): VoiceConnection | undefined; + abstract setVoiceConnection( + guildId: string, + connection: VoiceConnection + ): void; + abstract getVoicePlayer(guildId: string): AudioPlayer | undefined; + abstract setVoicePlayer(guildId: string, player: AudioPlayer): void; +} diff --git a/src/features/core/internal/Store/internal/Store.constants.ts b/src/features/core/internal/Store/internal/Store.constants.ts new file mode 100644 index 00000000..88e232bc --- /dev/null +++ b/src/features/core/internal/Store/internal/Store.constants.ts @@ -0,0 +1,3 @@ +export const STORE_TYPES = { + IN_MEMORY: 'IN_MEMORY', +} as const satisfies Record; diff --git a/src/index.ts b/src/index.ts index 27058e96..77b8e7dd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,9 +2,10 @@ import 'dotenv/config'; import { Ping, VoiceChannel, YouTube } from './features/commands/index.js'; import { getConfig } from './features/config/index.js'; -import { Client } from './features/core/index.js'; +import { Client, STORE_TYPES } from './features/core/index.js'; new Client({ config: getConfig(), commands: [new Ping(), new VoiceChannel(), new YouTube()], + storeDriver: STORE_TYPES.IN_MEMORY, });