【Discord.js V14】スラッシュコマンドのオプションで、既に選択済みの選択肢を２つ目の引数のリストから消す。

皆さん初めまして。Morichanと申します。
下記の※の記述を理解したうえで、記事を読んでください。

※DiscordBot開発初心者の書いた記事です。
（また、プログラミング自体、２年ほどのブランクあり）

２つのBOTを別々のVCに参加させるまでの流れに関しては、[別の記事](https://qiita.com/Mori-chan/items/7d598933753d0abe1b91)にまとめてあります。

関連記事：
* [【Windows11】Discord BOTを開発するための開発環境を整える。](https://qiita.com/Mori-chan/items/add6f63b04f70ff53732)
* [【Discord.js V14】Discord BOTにスラッシュコマンドを実装してみる](https://qiita.com/Mori-chan/items/2e869c0e2060b78f0380)
* [【Discord.js V14】Discord BOTをVCに参加させてみる](https://qiita.com/Mori-chan/items/2df4ad15adc79cf2a167)
* [【Discord.js V14】２つのBOTを別々のVCに参加させてみる](https://qiita.com/Mori-chan/items/7d598933753d0abe1b91)



## 目次
1. [開発環境](#開発環境)
2. [スラッシュコマンドのオプションで、既に選択済みの選択肢を２つ目の引数のリストから消す。](#スラッシュコマンドのオプションで既に選択済みの選択肢を２つ目の引数のリストから消す)
3. [関連記事](#関連記事)


## 開発環境
OS (Windows11)
discord.js (14.7.1)
node.js (18.13.0)
npm (8.19.3)
@discordjs/voice (^0.14.0)


## スラッシュコマンドのオプションで、既に選択済みの選択肢を２つ目の引数のリストから消す。

* Autocompleteを使って、スラッシュコマンドのオプションに渡される選択肢を変更する。
[公式ガイド](https://discordjs.guide/slash-commands/autocomplete.html#autocomplete)に沿って記述していきます。

* `index.js`にautocompleteを呼び出す記述を追加する。
```
// コマンドが送られてきた際の処理
client1.on(Events.InteractionCreate, async interaction => {
    // コマンドでなかった場合は処理せずさよなら。
    // autocompleteが実行されないと困るので、autocompleteでなかった場合も追加。
	if (!interaction.isChatInputCommand() && !interaction.isAutocomplete()) return;

	const command = interaction.client.commands.get(interaction.commandName);
```

```
		else {
			await command.execute(interaction);
		}
        // autocompleteの場合に呼び出す。記述を追記。
		if (interaction.isAutocomplete()) {
			await command.autocomplete(interaction);
		}
	} catch (error) {
		console.error(error);
		await interaction.reply({ content: 'コマンドを実行中にエラーが発生しました。', ephemeral: true });
	}
```

* `join.js`にautocompleteの記述を追記する。
※addStringOptionでないとsetAutocompleteが正常に機能しないため、VCの形で取得してきたい場合は、今回の方法は使えない。
VCの形で取得してくる方法を見つけた方は、ぜひ教えてください。

```
        .addChannelOption((option) =>
			option
				.setName('channel1')
				.setDescription('The channel that Listener-bot join')
				.setRequired(true)
				.addChannelTypes(ChannelType.GuildVoice),
		)
        // addStringOptionでないとsetAutocompleteが正常に機能しない。
		.addStringOption((option) =>
			option
				.setName('channel2')
				.setDescription('The channel that Speaker-bot join')
                // ここでAutocompleteを使うよと宣言する
				.setAutocomplete(true)
				.setRequired(true),
		),
    // Autocomplete時に呼ばれる処理
	async autocomplete(interaction) {
        // ユーザーが途中まで入力すると表示される選択肢が絞られる機能
		const focusedValue = interaction.options.getFocused();
        // options.getChannelではまだ取得できないため、options.get()で取得。(チャンネルIDが格納されたオブジェクトが返る)
		const vc = interaction.options.get('channel1');
        // すべてのChatを取得(VC以外のテキスト系チャンネルも)
		const chats = interaction.guild.channels.cache;
        // type:2(おそらくVoiceChat形式)を取得することで、全てのVCを取得。
		const voiceChannels = chats.filter(file => file.type === 2);
		let unSelectedVoiceChannels = [];

        // 'channel1'で選択されたVC以外のすべてのVCを変数に格納
		for (const voiceChannel of voiceChannels) {
			if (voiceChannel[0] !== vc.value) {
				unSelectedVoiceChannels.push(voiceChannel);
			}
		}
		
        // フォーカスされているVCを一番最初に持ってくる
		const filtered = unSelectedVoiceChannels.filter(unSelectedVoiceChannel => unSelectedVoiceChannel[1].name.startsWith(focusedValue));

        // 選択肢を渡す処理
		await interaction.respond(
			//Discordの制約で、選択肢は25個以下である必要があるため、.slice()を使って制限をかける。
			filtered.map(unSelectedVoiceChannel => ({ name: unSelectedVoiceChannel[1].name, value: unSelectedVoiceChannel[1].id })).slice(0, 25)
		);
	},
```

```
		const voiceChannel1 = interaction.options.getChannel('channel1');
        // addStringOptionに変えたため、取得方法も修正。
		const voiceChannel2 = interaction.options.getString('channel2');
```
```
        // Speaker-botがVCに参加する処理
			const connection2 = joinVoiceChannel({
				group: 'speaker',
				guildId: interaction.guildId,
                // channelIdがそのまま渡ってくるので、記述を修正
				channelId: voiceChannel2,
				adapterCreator: client2.guilds.cache.get(interaction.guildId).voiceAdapterCreator,
				selfMute: false,
				selfDeaf: true,
			});
			await interaction.reply('VCに参加しました！');
			return [connection1, connection2];
		}
        // Autocomplete前にここも一度処理されるため、エラーが出ないように削除。(そもそも両方.setRequired(true)のため不要)
		// else {
			// await interaction.reply('BOTを参加させるVCを指定してください！');
		// }
	},
```

## 関連記事
* [【Windows11】Discord BOTを開発するための開発環境を整える。](https://qiita.com/Mori-chan/items/add6f63b04f70ff53732)
* [【Discord.js V14】Discord BOTにスラッシュコマンドを実装してみる](https://qiita.com/Mori-chan/items/2e869c0e2060b78f0380)
* [【Discord.js V14】Discord BOTをVCに参加させてみる](https://qiita.com/Mori-chan/items/2df4ad15adc79cf2a167)
* [【Discord.js V14】２つのBOTを別々のVCに参加させてみる](https://qiita.com/Mori-chan/items/7d598933753d0abe1b91)
* 【Discord.js V14】スラッシュコマンドのオプションで、既に選択済みの選択肢を２つ目の引数のリストから消す。
* [【Discord.js V14】Discord BOTにVCに音楽を流す機能を実装してみる](https://qiita.com/Mori-chan/items/84622c733dc43c419dc5)
* [【Discord.js V14】Discord BOTにVCから１人分の音声を取得して、録音する機能を実装してみる](https://qiita.com/Mori-chan/items/8fcb1770c4d0020a8284)
* [【Discord.js V14】VC１からVC２に音声をStreaming（中継）するBOTを作成する。](https://qiita.com/Mori-chan/items/e9ced87c0d68a9eb2a9a)
* [【Discord.js V14】取得するVCの音声をロールによって判別する](https://qiita.com/Mori-chan/items/41cd914bb9a93d930bff)