テキスト読み上げを行えるようにした
This commit is contained in:
parent
b968a8f4bc
commit
a5149d5ca1
@ -1,5 +1,7 @@
|
||||
FROM node:23.1.0-alpine
|
||||
|
||||
RUN apk add --no-cache ffmpeg
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
|
@ -5,9 +5,15 @@ services:
|
||||
NODE_ENV: development
|
||||
env_file:
|
||||
- config/development.env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
volumes:
|
||||
- .:/app
|
||||
- /app/node_modules
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
command: ["npm", "run", "dev"]
|
||||
depends_on:
|
||||
- voicevox
|
||||
voicevox:
|
||||
image: voicevox/voicevox_engine:latest
|
||||
ports:
|
||||
- "50021:50021"
|
||||
|
@ -6,8 +6,8 @@ services:
|
||||
NODE_ENV: production
|
||||
env_file:
|
||||
- config/production.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
volumes:
|
||||
- .:/app
|
||||
restart: always
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
|
132
package-lock.json
generated
132
package-lock.json
generated
@ -10,8 +10,11 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@discordjs/voice": "^0.17.0",
|
||||
"axios": "^1.7.7",
|
||||
"discord.js": "^14.16.3",
|
||||
"dotenv": "^16.4.5"
|
||||
"dotenv": "^16.4.5",
|
||||
"libsodium-wrappers": "^0.7.15",
|
||||
"uuid": "^11.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.7"
|
||||
@ -268,6 +271,23 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
|
||||
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@ -337,6 +357,18 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
@ -362,6 +394,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/discord-api-types": {
|
||||
"version": "0.37.100",
|
||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.100.tgz",
|
||||
@ -425,6 +466,40 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
|
||||
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
@ -516,6 +591,21 @@
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/libsodium": {
|
||||
"version": "0.7.15",
|
||||
"resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.15.tgz",
|
||||
"integrity": "sha512-sZwRknt/tUpE2AwzHq3jEyUU5uvIZHtSssktXq7owd++3CSgn8RGrv6UZJJBpP7+iBghBqe7Z06/2M31rI2NKw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/libsodium-wrappers": {
|
||||
"version": "0.7.15",
|
||||
"resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.15.tgz",
|
||||
"integrity": "sha512-E4anqJQwcfiC6+Yrl01C1m8p99wEhLmJSs0VQqST66SbQXXBoaJY0pF4BNjRYa/sOQAxx6lXAaAFIlx+15tXJQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"libsodium": "^0.7.15"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
@ -534,6 +624,27 @@
|
||||
"integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
@ -632,6 +743,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pstree.remy": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
|
||||
@ -748,6 +865,19 @@
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "11.0.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz",
|
||||
"integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/esm/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
|
@ -12,8 +12,11 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@discordjs/voice": "^0.17.0",
|
||||
"axios": "^1.7.7",
|
||||
"discord.js": "^14.16.3",
|
||||
"dotenv": "^16.4.5"
|
||||
"dotenv": "^16.4.5",
|
||||
"libsodium-wrappers": "^0.7.15",
|
||||
"uuid": "^11.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.7"
|
||||
|
@ -4,20 +4,28 @@ const { joinVoiceChannel } = require('@discordjs/voice');
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('join')
|
||||
.setDescription('Join the voice channel you are in'),
|
||||
.setDescription('Join the voice channel you are in and listen to this text channel'),
|
||||
async execute(interaction) {
|
||||
const voiceChannel = interaction.member.voice.channel;
|
||||
const textChannel = interaction.channel;
|
||||
|
||||
if (!voiceChannel) {
|
||||
return interaction.reply('ボイスチャンネルに接続されていません。');
|
||||
}
|
||||
|
||||
// ボイスチャンネルに参加
|
||||
joinVoiceChannel({
|
||||
channelId: voiceChannel.id,
|
||||
guildId: voiceChannel.guild.id,
|
||||
adapterCreator: voiceChannel.guild.voiceAdapterCreator,
|
||||
});
|
||||
|
||||
await interaction.reply('ボイスチャンネルに接続しました。');
|
||||
// サーバーごとにチャンネル情報を保存
|
||||
interaction.client.guildChannels.set(interaction.guild.id, {
|
||||
textChannelId: textChannel.id,
|
||||
voiceChannelId: voiceChannel.id
|
||||
});
|
||||
|
||||
await interaction.reply('ボイスチャンネルに接続し、このテキストチャンネルのメッセージを読み上げます。');
|
||||
},
|
||||
};
|
||||
|
@ -1,11 +1,27 @@
|
||||
const { createAudioPlayer, createAudioResource, getVoiceConnection } = require('@discordjs/voice');
|
||||
const { synthesizeSpeech } = require('../services/tts');
|
||||
|
||||
module.exports = {
|
||||
name: 'messageCreate',
|
||||
execute(message) {
|
||||
async execute(message) {
|
||||
const client = message.client;
|
||||
|
||||
// サーバーごとのチャンネル情報を取得
|
||||
const guildChannels = client.guildChannels.get(message.guild.id);
|
||||
if (!guildChannels) return;
|
||||
|
||||
// 記憶されたテキストチャンネルのメッセージのみ処理
|
||||
if (message.channel.id !== guildChannels.textChannelId) return;
|
||||
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 || "メッセージを受け取りました!");
|
||||
}
|
||||
const connection = getVoiceConnection(message.guild.id);
|
||||
if (!connection) return;
|
||||
|
||||
const audioUrl = await synthesizeSpeech(message.content);
|
||||
const resource = createAudioResource(audioUrl);
|
||||
const player = createAudioPlayer();
|
||||
|
||||
player.play(resource);
|
||||
connection.subscribe(player);
|
||||
},
|
||||
};
|
||||
|
@ -12,6 +12,9 @@ const client = new Client({
|
||||
]
|
||||
});
|
||||
|
||||
// サーバーごとのテキストチャンネルとボイスチャンネルの情報を保持
|
||||
client.guildChannels = new Map();
|
||||
|
||||
// コマンドの読み込み
|
||||
client.commands = new Collection();
|
||||
const commands = [];
|
||||
|
23
src/services/tts.js
Normal file
23
src/services/tts.js
Normal file
@ -0,0 +1,23 @@
|
||||
const axios = require('axios');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
async function synthesizeSpeech(text) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log('[development] Synthesizing speech...');
|
||||
const voicevoxUrl = `http://voicevox:50021/audio_query?text=${encodeURIComponent(text)}&speaker=1`;
|
||||
const { data } = await axios.post(voicevoxUrl);
|
||||
|
||||
const synthesisUrl = `http://voicevox:50021/synthesis?speaker=1`;
|
||||
const response = await axios.post(synthesisUrl, data, { responseType: 'arraybuffer' });
|
||||
const filePath = path.join('/tmp', `${uuidv4()}.mp3`);
|
||||
fs.writeFileSync(filePath, Buffer.from(response.data));
|
||||
return filePath;
|
||||
} else {
|
||||
console.log('[production] Synthesizing speech...');
|
||||
return path.join(__dirname, 'dummy.mp3'); // 本番用の音声生成処理
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { synthesizeSpeech };
|
Loading…
Reference in New Issue
Block a user