Skip to content

Commit

Permalink
Merge pull request #25 from Chipsnet/Chipsnet/issue3
Browse files Browse the repository at this point in the history
  • Loading branch information
Chipsnet authored Feb 6, 2022
2 parents 2e4eda8 + 8d6da6b commit 6b9f9bb
Show file tree
Hide file tree
Showing 38 changed files with 7,723 additions and 80 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ yarn-error.log
softalk
index.js
config.yml
voice.wav
voice.wav
bep-eng.dic
input.txt
12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#docker build -t damare .

FROM node:lts-buster

RUN apt update && apt install -y vim wget open-jtalk
RUN git clone https://github.com/Chipsnet/damare.git
WORKDIR /damare
RUN yarn install

COPY config.yml .
ENTRYPOINT ["yarn", "start"]

514 changes: 503 additions & 11 deletions README.md

Large diffs are not rendered by default.

172 changes: 111 additions & 61 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,65 @@
const bunyan = require('bunyan');
const { exec } = require('child_process');
const Encoding = require('encoding-japanese');
const packageJson = require('./package.json');
const fs = require('fs');
const { exit } = require('process');
const OpenJtalk = require('./openjtalk')
const Softalk = require('./softalk')
const update = require('./update')
const Discord = require('discord.js');
const chokidar = require('chokidar');
const yaml = require("js-yaml");

const log = bunyan.createLogger({name: 'damare', level: 'debug'});
let useVoiceClient;
let voiceClient;

log.info("Damare reading bot v" + packageJson.version);
log.info("Damare 読み上げBot v" + packageJson.version);
log.info("開発者: 巳波みなと https://minato86.me")
log.info("このソフトウェアが気に入ったらサポートをお願いします: https://ko-fi.com/minato86")

log.info('Checking softalk...');

if (fs.existsSync('./softalk/SofTalk.exe')) {
log.info('Softalk found.');
} else {
log.error('Softalk not found. Can\'t Start damare. Please put softalk to current dir. If you want more info, visit https://github.com/Chipsnet/damare.');
exit()
}
update(log, packageJson);

if (fs.existsSync('./voice.wav')) {
log.debug('Voice file found. Deleted.')
log.debug('⚠️ voice.wavが見つかりました、削除します')
fs.unlinkSync('./voice.wav');
log.debug('✅ voice.wavが削除されました')
}

try {
log.debug("🔄 設定ファイルを読み込みます")
config = yaml.load(
fs.readFileSync("./config.yml", "utf-8")
);
} catch (error) {
log.fatal('Config file not found. Please make config file. More information: https://github.com/Chipsnet/warbot-js.')
log.fatal('💥 設定ファイルが見つかりませんでした. 起動には設定ファイルが必要です. 詳しくは公式サイトをご覧ください: https://damare.m86.work/')
log.error(error);
process.exit(0)
process.exit(1);
}

if (!config.voiceclient) {
log.warn("⚠️ 設定ファイルにvoiceclientが設定されていません. デフォルト設定のSoftalkを使用します.")
useVoiceClient = 1;
} else {
useVoiceClient = config.voiceclient;
}

function toString (bytes) {
return Encoding.convert(bytes, {
from: 'SJIS',
to: 'UNICODE',
type: 'string',
});
if (useVoiceClient == 1) {
voiceClient = new Softalk(log);
} else if (useVoiceClient == 2) {
voiceClient = new OpenJtalk(log);
}


log.debug('✅ 設定ファイルを読み込みました')

const client = new Discord.Client();
const broadcast = client.voice.createBroadcast();
let connection = null;
let readMessages = [];
let canReadMessage = true;
let readChannel = null;
let dispatcher;
let prefix = config.prefix;

client.on('ready', () => {
log.info('Discord login success! Logged in as : ' + client.user.tag);
log.info('✨ Discordログイン完了!あなたのお名前:' + client.user.tag);
});

client.on('message', async message => {
Expand All @@ -65,19 +71,27 @@ client.on('message', async message => {
if (message.member.voice.channel) {
readChannel = message.channel.id
connection = await message.member.voice.channel.join();
connection.play(broadcast, {volume: 0.3});

message.reply('✨ VCに接続しました!');

log.info(`💫 ボイスチャンネルに接続しました!チャンネル名: ${message.member.voice.channel.name}`);
log.debug(`ℹ️ 接続先チャンネル: ${message.member.voice.channel.name}, 実行ユーザ: ${message.author.tag}`)
} else {
message.reply('⚠️ まずはボイスチャンネルに接続してください!');
log.debug(`🚫 ユーザーがVCにいないため、接続できませんでした. 実行ユーザ: ${message.author.tag}`);
}
}

if (message.content === `${prefix}stop`) {
if (connection === null) {
message.reply('⚠ ボイスチャンネルに接続されていないので、切断ができませんでした。');
message.reply('⚠ ボイスチャンネルに接続されていないので、切断ができませんでした。');
} else {
connection.disconnect();
message.reply('👍 無事切断できました')
connection = null;
readChannel = null;

message.reply('👍 無事切断できました')
log.info(`🛠️ VCから切断しました. 実行ユーザ: ${message.author.tag}`);
}
}

Expand All @@ -87,6 +101,12 @@ client.on('message', async message => {
message.reply('💥 読み上げ状態をリセットしました');
}

if (message.content === `${prefix}skip` || message.content === `${prefix}damare`) {
dispatcher.end();
message.react('🤫');
log.debug(`ℹ️ ユーザーがスキップしました. 実行ユーザ: ${message.author.tag}`);
}

if (message.content === `${prefix}help`) {
message.reply('```\n'+
'Damare 読み上げBot コマンドリスト\n' +
Expand All @@ -96,27 +116,36 @@ client.on('message', async message => {
`${prefix}stop : 再生を停止してVCから切断します。\n` +
`${prefix}reset : 読み上げ状態や内部のキューをリセットします。問題が発生した場合にのみ使用してください。\n` +
`${prefix}help : ヘルプを表示します。\n` +
`${prefix}skip: 読み上げをスキップします。\n` +
'```'
);
}

if (message.channel.id === readChannel && message.content != `${prefix}talk` && message.author.bot == false && message.content.startsWith(prefix) == false) {
if (canReadMessage) {
log.debug(`Message recived. canReadMessage: ${canReadMessage}`)
log.debug(`ℹ️ テキストを受信しました. canReadMessage: ${canReadMessage}`)
readMessages.push(message.content);
softalk();
createVoice();
} else {
log.debug(`Message recived. canReadMessage: ${canReadMessage}`)
log.debug(`ℹ️ テキストを受信しました. canReadMessage: ${canReadMessage}`)
readMessages.push(message.content);
}
}
});

async function softalk() {
canReadMessage = false;
log.debug(`canReadMessage set to ${canReadMessage} on softalk.`);
let mes = readMessages.shift();
client.on("voiceStateUpdate", () => {
if (connection === null) return;

if (connection.channel.members.size <= 1) {
connection.disconnect();
connection = null;
readChannel = null;

log.info("🛠️ 誰もいなくなったため, VCから切断しました.")
}
})

function replaceString(mes) {
mes = mes.replace(/<.*?>/g, "")
mes = mes.replace(/:.*?:/g, "")
mes = mes.replace(/\|\|.*?\|\|/g, "伏せ字")
Expand All @@ -141,38 +170,59 @@ async function softalk() {
mes = mes.split('!').join('')
mes = mes.split('`').join('')

log.debug('Softalk talk message: ' + mes);
log.debug('In queue' + readMessages);

exec('"./softalk/SofTalk.exe" /NM:女性01 /R:' + __dirname + '\\voice.wav /T:0 /X:1 /V:100 /W:' + mes, { encoding: 'Shift_JIS' }, (error, stdout, stderr) => {
if (error) {
log.error("An error occurred while running Softalk.\n" + toString(stderr));
if (readMessages.length) {
canReadMessage = true;
} else {
softalk();
}
return;
}
})
return mes;
}

chokidar.watch("./voice.wav").on('add', () => {
log.debug('New file found.');
function nextMessage() {
if (!readMessages.length) {
canReadMessage = true;
log.debug(`ℹ️ 再生終了によりcanReadMessageが ${canReadMessage} に設定されました`);
} else {
createVoice();
}
}

let dispatcher = broadcast.play('./voice.wav');
async function createVoice() {
canReadMessage = false;
log.debug(`ℹ️ 音声生成を開始するためcanReadMessageが ${canReadMessage} に設定されました`);
let mes = readMessages.shift();

log.debug("ℹ️ キューにあるメッセージ:", readMessages)
log.debug(`📝 変換前のテキスト: ${mes}`);

mes = replaceString(mes);

if (mes === "") {
log.debug("ℹ️ 読み上げるテキストが空なので、読み上げをスキップします")
nextMessage();
return;
}

try {
await voiceClient.createVoice(mes)
} catch (error) {
log.error("🚫 音声の生成中にエラーが発生しました", error)
nextMessage();
return;
}

playVoice();
}

function playVoice() {
log.debug('📢 再生処理を開始しします');
dispatcher = connection.play('./voice.wav', { volume: 1 });

dispatcher.on('finish', () => {
fs.unlinkSync('./voice.wav');
if (!readMessages.length) {
canReadMessage = true;
log.debug(`canReadMessage set to ${canReadMessage} by chokidar due to finish.`);
} else {
softalk();
}
})
})
setTimeout(() => {
log.debug("✅ 再生が完了しました")

fs.unlinkSync('./voice.wav');

nextMessage();
}, 1000)
})
}

client.login(config.token);
log.info('Trying Login to discord...');
log.info('🚀 Discordにログインを試みています...');
32 changes: 32 additions & 0 deletions openjtalk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const { execFileSync } = require('child_process');
const iconv = require('iconv-lite');
const fs = require('fs');

module.exports = class {
constructor(log) {
this.log = log.child({ module: 'openjtalk' });
//this.htsvoice = "./openjtalk/voice/hts_voice_nitech_jp_atr503_m001-1.05/nitech_jp_atr503_m001.htsvoice";
this.htsvoice = "./openjtalk/voice/mei/mei_normal.htsvoice";

if (process.platform === 'win32') {
this.dic = "./openjtalk/dic/open_jtalk_dic_shift_jis-1.11"
} else {
this.dic = "./openjtalk/dic/open_jtalk_dic_utf_8-1.11"
}
}

async createVoice(message) {
this.log.debug("📝 input.txtを生成します:", message)

if (process.platform === 'win32') {
fs.writeFileSync('./input.txt', iconv.encode(message, 'shift_jis'));
} else {
fs.writeFileSync('./input.txt', message);
}

execFileSync("open_jtalk", ["-m", this.htsvoice, "-x", this.dic, , "-g", "9", "-ow", "voice.wav", "./input.txt"]);
this.log.debug("🎤 voice.wavを生成しました")

return;
}
}
Loading

0 comments on commit 6b9f9bb

Please sign in to comment.