diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..b58b603
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/discord_read_bot.iml b/.idea/discord_read_bot.iml
new file mode 100644
index 0000000..24643cc
--- /dev/null
+++ b/.idea/discord_read_bot.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..83a7844
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/development._env b/config/development._env
index 8c9d5b5..fd3b08d 100644
--- a/config/development._env
+++ b/config/development._env
@@ -1,3 +1,4 @@
NODE_ENV=development
LOG_LEVEL=debug
BOT_TOKEN=YOUR_DEVELOPMENT_BOT_TOKEN
+APPLICATION_ID=YOUR_DEVELOPMENT_APPLICATION_ID
diff --git a/config/production._env b/config/production._env
index 32228dc..578c07b 100644
--- a/config/production._env
+++ b/config/production._env
@@ -1,3 +1,4 @@
NODE_ENV=production
LOG_LEVEL=error
BOT_TOKEN=YOUR_PRODUCTION_BOT_TOKEN
+APPLICATION_ID=YOUR_PRODUCTION_APPLICATION_ID
diff --git a/package-lock.json b/package-lock.json
index 6817c0b..f336124 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,9 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
- "discord.js": "^14.16.3"
+ "@discordjs/voice": "^0.17.0",
+ "discord.js": "^14.16.3",
+ "dotenv": "^16.4.5"
},
"devDependencies": {
"nodemon": "^3.1.7"
@@ -125,6 +127,31 @@
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
+ "node_modules/@discordjs/voice": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.17.0.tgz",
+ "integrity": "sha512-hArn9FF5ZYi1IkxdJEVnJi+OxlwLV0NJYWpKXsmNOojtGtAZHxmsELA+MZlu2KW1F/K1/nt7lFOfcMXNYweq9w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/ws": "^8.5.10",
+ "discord-api-types": "0.37.83",
+ "prism-media": "^1.3.5",
+ "tslib": "^2.6.2",
+ "ws": "^8.16.0"
+ },
+ "engines": {
+ "node": ">=16.11.0"
+ },
+ "funding": {
+ "url": "https://github.com/discordjs/discord.js?sponsor"
+ }
+ },
+ "node_modules/@discordjs/voice/node_modules/discord-api-types": {
+ "version": "0.37.83",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.83.tgz",
+ "integrity": "sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==",
+ "license": "MIT"
+ },
"node_modules/@discordjs/ws": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.1.1.tgz",
@@ -367,6 +394,18 @@
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
+ "node_modules/dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -567,6 +606,32 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/prism-media": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.5.tgz",
+ "integrity": "sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "@discordjs/opus": ">=0.8.0 <1.0.0",
+ "ffmpeg-static": "^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0",
+ "node-opus": "^0.3.3",
+ "opusscript": "^0.0.8"
+ },
+ "peerDependenciesMeta": {
+ "@discordjs/opus": {
+ "optional": true
+ },
+ "ffmpeg-static": {
+ "optional": true
+ },
+ "node-opus": {
+ "optional": true
+ },
+ "opusscript": {
+ "optional": true
+ }
+ }
+ },
"node_modules/pstree.remy": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
diff --git a/package.json b/package.json
index a9cf89e..78e482d 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,9 @@
"author": "",
"license": "ISC",
"dependencies": {
- "discord.js": "^14.16.3"
+ "@discordjs/voice": "^0.17.0",
+ "discord.js": "^14.16.3",
+ "dotenv": "^16.4.5"
},
"devDependencies": {
"nodemon": "^3.1.7"
diff --git a/src/commands/disconnect.js b/src/commands/disconnect.js
new file mode 100644
index 0000000..954261f
--- /dev/null
+++ b/src/commands/disconnect.js
@@ -0,0 +1,17 @@
+const { SlashCommandBuilder } = require('discord.js');
+const { getVoiceConnection } = require('@discordjs/voice');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('disconnect')
+ .setDescription('Disconnect from the voice channel'),
+ async execute(interaction) {
+ const connection = getVoiceConnection(interaction.guild.id);
+ if (connection) {
+ connection.destroy();
+ await interaction.reply('ボイスチャンネルから切断しました。');
+ } else {
+ await interaction.reply('ボイスチャンネルに接続していません。');
+ }
+ },
+};
diff --git a/src/commands/join.js b/src/commands/join.js
new file mode 100644
index 0000000..58b1ba2
--- /dev/null
+++ b/src/commands/join.js
@@ -0,0 +1,23 @@
+const { SlashCommandBuilder } = require('discord.js');
+const { joinVoiceChannel } = require('@discordjs/voice');
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('join')
+ .setDescription('Join the voice channel you are in'),
+ async execute(interaction) {
+ const voiceChannel = interaction.member.voice.channel;
+
+ if (!voiceChannel) {
+ return interaction.reply('ボイスチャンネルに接続されていません。');
+ }
+
+ joinVoiceChannel({
+ channelId: voiceChannel.id,
+ guildId: voiceChannel.guild.id,
+ adapterCreator: voiceChannel.guild.voiceAdapterCreator,
+ });
+
+ await interaction.reply('ボイスチャンネルに接続しました。');
+ },
+};
diff --git a/src/events/messageCreate.js b/src/events/messageCreate.js
new file mode 100644
index 0000000..202da56
--- /dev/null
+++ b/src/events/messageCreate.js
@@ -0,0 +1,11 @@
+module.exports = {
+ name: 'messageCreate',
+ execute(message) {
+ if (message.author.bot) return;
+
+ if (message.mentions.has(message.client.user)) {
+ const response = message.content.replace(`<@${message.client.user.id}>`, '').trim();
+ message.channel.send(response || "メッセージを受け取りました!");
+ }
+ },
+};
diff --git a/src/events/ready.js b/src/events/ready.js
new file mode 100644
index 0000000..23b32fb
--- /dev/null
+++ b/src/events/ready.js
@@ -0,0 +1,7 @@
+module.exports = {
+ name: 'ready',
+ once: true,
+ execute(client) {
+ console.log(`Logged in as ${client.user.tag}!`);
+ },
+};
diff --git a/src/index.js b/src/index.js
index 62e110b..8d3de4d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,22 +1,73 @@
-const { Client, GatewayIntentBits } = require('discord.js');
-const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent] });
+require('dotenv').config();
+const { Client, GatewayIntentBits, Collection, REST, Routes } = require('discord.js');
+const fs = require('fs');
+const path = require('path');
-// ボットが起動したときのメッセージ
-client.once('ready', () => {
- console.log(`Logged in as ${client.user.tag}!`);
+const client = new Client({
+ intents: [
+ GatewayIntentBits.Guilds,
+ GatewayIntentBits.GuildMessages,
+ GatewayIntentBits.MessageContent,
+ GatewayIntentBits.GuildVoiceStates
+ ]
});
-// メッセージが送信されたときに呼び出されるイベント
-client.on('messageCreate', message => {
- // 自分のメッセージやBotからのメッセージには反応しない
- if (message.author.bot) return;
+// コマンドの読み込み
+client.commands = new Collection();
+const commands = [];
+const commandsPath = path.join(__dirname, 'commands');
+const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
- // ボットがメンションされた場合に反応
- if (message.mentions.has(client.user)) {
- const response = message.content.replace(`<@${client.user.id}>`, '').trim();
+for (const file of commandFiles) {
+ const filePath = path.join(commandsPath, file);
+ const command = require(filePath);
+ client.commands.set(command.data.name, command);
+ commands.push(command.data.toJSON());
+}
- message.channel.send(response || "メッセージを受け取りました!");
+const rest = new REST({ version: '10' }).setToken(process.env.BOT_TOKEN);
+
+// スラッシュコマンドのグローバル登録
+(async () => {
+ try {
+ console.log('Registering global slash commands...');
+ await rest.put(
+ Routes.applicationCommands(process.env.APPLICATION_ID),
+ { body: commands },
+ );
+ console.log('Global slash commands registered successfully.');
+ } catch (error) {
+ console.error('Error registering global slash commands:', error);
+ }
+})();
+
+// インタラクションの処理
+client.on('interactionCreate', async (interaction) => {
+ if (!interaction.isCommand()) return;
+
+ const command = client.commands.get(interaction.commandName);
+ if (!command) return;
+
+ try {
+ await command.execute(interaction);
+ } catch (error) {
+ console.error(error);
+ await interaction.reply({ content: 'コマンド実行中にエラーが発生しました。', ephemeral: true });
}
});
+// イベントの読み込み
+const eventsPath = path.join(__dirname, 'events');
+const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));
+
+for (const file of eventFiles) {
+ const filePath = path.join(eventsPath, file);
+ const event = require(filePath);
+ if (event.once) {
+ client.once(event.name, (...args) => event.execute(...args, client));
+ } else {
+ client.on(event.name, (...args) => event.execute(...args, client));
+ }
+}
+
client.login(process.env.BOT_TOKEN);