diff --git a/@types/DiscordClient.d.ts b/@types/DiscordClient.d.ts index 4f7ad7f..3105857 100644 --- a/@types/DiscordClient.d.ts +++ b/@types/DiscordClient.d.ts @@ -1,6 +1,7 @@ import { GatewayIntentBits } from 'discord.js'; import { MusicPlayerOptions } from './MusicPlayer.js'; import { If } from './Shared.js'; +import { CommandOption, CommandOptionDefinition } from '@navy.gif/commandparser'; export type ClientOptions = { rootDir: string, diff --git a/package.json b/package.json index 641016e..db9eb50 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "dependencies": { "@discordjs/opus": "^0.9.0", "@discordjs/voice": "^0.16.1", - "@navy.gif/commandparser": "^1.6.4", + "@navy.gif/commandparser": "^1.6.5", "@navy.gif/logger": "^2.5.4", "@navy.gif/timestring": "^6.0.6", "bufferutil": "^4.0.8", diff --git a/src/client/DiscordClient.ts b/src/client/DiscordClient.ts index 6c275c7..48f077e 100644 --- a/src/client/DiscordClient.ts +++ b/src/client/DiscordClient.ts @@ -217,6 +217,11 @@ class DiscordClient extends Client return this.#built; } + get musicPlayer () + { + return this.#musicPlayer; + } + } process.once('message', (msg: IPCMessage) => diff --git a/src/client/components/MusicPlayer.ts b/src/client/components/MusicPlayer.ts index 974caf1..6313ca5 100644 --- a/src/client/components/MusicPlayer.ts +++ b/src/client/components/MusicPlayer.ts @@ -30,6 +30,7 @@ class MusicPlayer implements Initialisable #currentResource!: AudioResource; #options: MusicPlayerOptions; + #volume: number; constructor (client: DiscordClient, options: MusicPlayerOptions) { @@ -43,6 +44,7 @@ class MusicPlayer implements Initialisable this.#options = options; this.#library = []; this.#currentIdx = 0; + this.#volume = 0.3; this.#player.on(AudioPlayerStatus.Idle, this.playNext.bind(this)); } @@ -64,7 +66,7 @@ class MusicPlayer implements Initialisable metadata: { display } }); - this.#currentResource.volume?.setVolume(0.3); + this.#currentResource.volume?.setVolume(this.#volume); this.#logger.info(`Now playing ${display}`); this.#player.play(this.#currentResource); this.#currentIdx++; @@ -86,12 +88,24 @@ class MusicPlayer implements Initialisable // await channel.bulkDelete(filtered); for (const [ , msg ] of filtered) { - if (msg.deletable) + if (msg.deletable && msg.content.startsWith('Now playing')) await msg.delete(); } }); } + get volume () + { + return this.#volume * 100; + } + + set volume (vol: number) + { + vol /= 100; + this.#volume = vol; + this.#currentResource.volume?.setVolume(vol); + } + stop (): void | Promise { this.#logger.info('Disconnecting all guilds'); @@ -149,12 +163,20 @@ class MusicPlayer implements Initialisable this.#logger.info(`Connected to voice in ${guild.name}`); } - const library = Util.readdirRecursive(this.#options.library); - this.#logger.info(`Loaded music library with ${library.length} tracks`); - this.#library = Util.shuffle(library); + this.scanLibrary(); this.playNext(); } + scanLibrary () + { + const current = this.#library.length; + const library = Util.readdirRecursive(this.#options.library); + this.#logger.info(`Scanned music library, found ${library.length} tracks`); + this.#library = Util.shuffle(library); + const after = this.#library.length; + return [ after, after - current ]; + } + } export default MusicPlayer; \ No newline at end of file diff --git a/src/client/components/commands/Rescan.ts b/src/client/components/commands/Rescan.ts new file mode 100644 index 0000000..e3d77de --- /dev/null +++ b/src/client/components/commands/Rescan.ts @@ -0,0 +1,21 @@ +import Command from '../../../interfaces/Command.js'; +import DiscordClient from '../../DiscordClient.js'; + +class PingCommand extends Command +{ + constructor (client: DiscordClient) + { + super(client, { + name: 'rescan', + restricted: true + }); + } + + async execute () + { + const [ songs, diff ] = this.client.musicPlayer.scanLibrary(); + return `Found ${songs} tracks with ${diff} new ones`; + } +} + +export default PingCommand; \ No newline at end of file diff --git a/src/client/components/commands/Skip.ts b/src/client/components/commands/Skip.ts new file mode 100644 index 0000000..08bc6b0 --- /dev/null +++ b/src/client/components/commands/Skip.ts @@ -0,0 +1,21 @@ +import Command from '../../../interfaces/Command.js'; +import DiscordClient from '../../DiscordClient.js'; + +class SkipCommand extends Command +{ + constructor (client: DiscordClient) + { + super(client, { + name: 'skip', + guildOnly: true + }); + } + + async execute () + { + this.client.musicPlayer.playNext(); + return 'Song skipped'; + } +} + +export default SkipCommand; \ No newline at end of file diff --git a/src/client/components/commands/Volume.ts b/src/client/components/commands/Volume.ts new file mode 100644 index 0000000..7fc610f --- /dev/null +++ b/src/client/components/commands/Volume.ts @@ -0,0 +1,43 @@ +import { CommandOpts, OptionType } from '@navy.gif/commandparser'; +import Command from '../../../interfaces/Command.js'; +import DiscordClient from '../../DiscordClient.js'; +import { Message } from 'discord.js'; + +class VolumeCommand extends Command +{ + constructor (client: DiscordClient) + { + super(client, { + name: 'volume', + aliases: [ 'v' ], + options: [ + { + name: 'volume', + type: OptionType.INTEGER, + // required: true, + minimum: 0, + maximum: 100 + } + ], + guildOnly: true + }); + } + + async execute (message: Message, { args }: CommandOpts) + { + const { volume } = args; + if (!volume) + return `Volume is currently at ${this.client.musicPlayer.volume}`; + + const { member, author, guild } = message; + const { me } = guild.members; + if (!member?.voice || member.voice.channelId !== me?.voice.channelId) + return 'Only vc participants can adjust volume'; + + this.logger.info(`${author.username} (${author.id}) is adjusting volume to ${volume.value}`); + this.client.musicPlayer.volume = (volume.value as number); + return `Volume set to ${volume.value}`; + } +} + +export default VolumeCommand; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 79fa775..692b70b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1628,10 +1628,10 @@ __metadata: languageName: node linkType: hard -"@navy.gif/commandparser@npm:^1.6.4": - version: 1.6.4 - resolution: "@navy.gif/commandparser@npm:1.6.4" - checksum: 10/1d26df52d66fcb7b97dc869a71ba7be321a1731bc8c4d2d22f2c340615344211b0a1ec0027b0146ee3d80e43d4cfc0bc1e5d9e0124d8118bb7d98085f48a4533 +"@navy.gif/commandparser@npm:^1.6.5": + version: 1.6.5 + resolution: "@navy.gif/commandparser@npm:1.6.5" + checksum: 10/f0bc838ab7785dea28a91ca07e96a52ca58596032fe0d92badda246e90e245987f2d00e5ea5776fa156033c7f6ffd6fe6e1f9fb7d58096c60d3f33f08510532c languageName: node linkType: hard @@ -3493,7 +3493,7 @@ __metadata: "@babel/preset-typescript": "npm:^7.24.1" "@discordjs/opus": "npm:^0.9.0" "@discordjs/voice": "npm:^0.16.1" - "@navy.gif/commandparser": "npm:^1.6.4" + "@navy.gif/commandparser": "npm:^1.6.5" "@navy.gif/logger": "npm:^2.5.4" "@navy.gif/timestring": "npm:^6.0.6" "@types/babel__core": "npm:^7"