diff --git a/Cargo.toml b/Cargo.toml index 7253fd9..1e4d712 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,4 +31,4 @@ features = ["builder", "cache", "client", "gateway", "model", "utils", "unstable [dependencies.tokio] version = "1.0" -features = ["macros", "rt-multi-thread"] \ No newline at end of file +features = ["macros", "rt-multi-thread"] diff --git a/src/commands/config.rs b/src/commands/config.rs index e59a5ba..7b46779 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,56 +1,89 @@ -use serenity::{prelude::Context, model::prelude::{application_command::ApplicationCommandInteraction, InteractionApplicationCommandCallbackDataFlags}}; +use serenity::{ + model::prelude::{ + application_command::ApplicationCommandInteraction, + InteractionApplicationCommandCallbackDataFlags, + }, + prelude::Context, +}; -use crate::{tts::{voicevox::voicevox::VOICEVOX, tts_type::TTSType}, data::DatabaseClientData}; +use crate::{ + data::DatabaseClientData, + tts::{tts_type::TTSType, voicevox::voicevox::VOICEVOX}, +}; -pub async fn config_command(ctx: &Context, command: &ApplicationCommandInteraction) -> Result<(), Box> { +pub async fn config_command( + ctx: &Context, + command: &ApplicationCommandInteraction, +) -> Result<(), Box> { let data_read = ctx.data.read().await; let config = { - let database = data_read.get::().expect("Cannot get DatabaseClientData").clone(); + let database = data_read + .get::() + .expect("Cannot get DatabaseClientData") + .clone(); let mut database = database.lock().await; - database.get_user_config_or_default(command.user.id.0).await.unwrap().unwrap() + database + .get_user_config_or_default(command.user.id.0) + .await + .unwrap() + .unwrap() }; let voicevox_speaker = config.voicevox_speaker.unwrap_or(1); let tts_type = config.tts_type.unwrap_or(TTSType::GCP); - command.create_interaction_response(&ctx.http, |f| { - f.interaction_response_data(|d| { - d.content("読み上げ設定") - .components(|c| { - c.create_action_row(|a| { - a.create_select_menu(|m| { - m.custom_id("TTS_CONFIG_ENGINE") - .options(|o| { - o.create_option(|co| { - co.label("Google TTS") - .value("TTS_CONFIG_ENGINE_SELECTED_GOOGLE") - .default_selection(tts_type == TTSType::GCP) - }).create_option(|co| { - co.label("VOICEVOX") - .value("TTS_CONFIG_ENGINE_SELECTED_VOICEVOX") - .default_selection(tts_type == TTSType::VOICEVOX) - }) - }).placeholder("読み上げAPIを選択") - }) - }).create_action_row(|a| { - a.create_select_menu(|m| { - m.custom_id("TTS_CONFIG_VOICEVOX_SPEAKER") - .options(|o| { - let mut o = o; - for (name, value) in VOICEVOX::get_speakers() { - o = o.create_option(|co| { - co.label(name) - .value(format!("TTS_CONFIG_VOICEVOX_SPEAKER_SELECTED_{}", value)) - .default_selection(value == voicevox_speaker) + command + .create_interaction_response(&ctx.http, |f| { + f.interaction_response_data(|d| { + d.content("読み上げ設定") + .components(|c| { + c.create_action_row(|a| { + a.create_select_menu(|m| { + m.custom_id("TTS_CONFIG_ENGINE") + .options(|o| { + o.create_option(|co| { + co.label("Google TTS") + .value("TTS_CONFIG_ENGINE_SELECTED_GOOGLE") + .default_selection(tts_type == TTSType::GCP) }) - } - o - }).placeholder("VOICEVOX Speakerを指定") + .create_option( + |co| { + co.label("VOICEVOX") + .value("TTS_CONFIG_ENGINE_SELECTED_VOICEVOX") + .default_selection( + tts_type == TTSType::VOICEVOX, + ) + }, + ) + }) + .placeholder("読み上げAPIを選択") + }) + }) + .create_action_row(|a| { + a.create_select_menu(|m| { + m.custom_id("TTS_CONFIG_VOICEVOX_SPEAKER") + .options(|o| { + let mut o = o; + for (name, value) in VOICEVOX::get_speakers() { + o = o.create_option(|co| { + co.label(name) + .value(format!( + "TTS_CONFIG_VOICEVOX_SPEAKER_SELECTED_{}", + value + )) + .default_selection(value == voicevox_speaker) + }) + } + o + }) + .placeholder("VOICEVOX Speakerを指定") + }) }) }) - }).flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + }) }) - }).await?; + .await?; Ok(()) } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 2e7f2f6..ab78f39 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,3 +1,3 @@ +pub mod config; pub mod setup; pub mod stop; -pub mod config; \ No newline at end of file diff --git a/src/commands/setup.rs b/src/commands/setup.rs index 4b1a2f8..71c7488 100644 --- a/src/commands/setup.rs +++ b/src/commands/setup.rs @@ -1,24 +1,39 @@ -use serenity::{prelude::Context, model::prelude::{application_command::ApplicationCommandInteraction, InteractionApplicationCommandCallbackDataFlags, UserId}}; +use serenity::{ + model::prelude::{ + application_command::ApplicationCommandInteraction, + InteractionApplicationCommandCallbackDataFlags, UserId, + }, + prelude::Context, +}; use crate::{data::TTSData, tts::instance::TTSInstance}; -pub async fn setup_command(ctx: &Context, command: &ApplicationCommandInteraction) -> Result<(), Box> { +pub async fn setup_command( + ctx: &Context, + command: &ApplicationCommandInteraction, +) -> Result<(), Box> { if let None = command.guild_id { - command.create_interaction_response(&ctx.http, |f| { - f.interaction_response_data(|d| { - d.content("このコマンドはサーバーでのみ使用可能です.").flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + command + .create_interaction_response(&ctx.http, |f| { + f.interaction_response_data(|d| { + d.content("このコマンドはサーバーでのみ使用可能です.") + .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + }) }) - }).await?; + .await?; return Ok(()); } let guild = command.guild_id.unwrap().to_guild_cached(&ctx.cache).await; if let None = guild { - command.create_interaction_response(&ctx.http, |f| { - f.interaction_response_data(|d| { - d.content("ギルドキャッシュを取得できませんでした.").flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + command + .create_interaction_response(&ctx.http, |f| { + f.interaction_response_data(|d| { + d.content("ギルドキャッシュを取得できませんでした.") + .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + }) }) - }).await?; + .await?; return Ok(()); } let guild = guild.unwrap(); @@ -29,40 +44,55 @@ pub async fn setup_command(ctx: &Context, command: &ApplicationCommandInteractio .and_then(|state| state.channel_id); if let None = channel_id { - command.create_interaction_response(&ctx.http, |f| { - f.interaction_response_data(|d| { - d.content("ボイスチャンネルに参加してから実行してください.").flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + command + .create_interaction_response(&ctx.http, |f| { + f.interaction_response_data(|d| { + d.content("ボイスチャンネルに参加してから実行してください.") + .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + }) }) - }).await?; + .await?; return Ok(()); } let channel_id = channel_id.unwrap(); - let manager = songbird::get(ctx).await.expect("Cannot get songbird client.").clone(); + let manager = songbird::get(ctx) + .await + .expect("Cannot get songbird client.") + .clone(); let storage_lock = { let data_read = ctx.data.read().await; - data_read.get::().expect("Cannot get TTSStorage").clone() + data_read + .get::() + .expect("Cannot get TTSStorage") + .clone() }; { let mut storage = storage_lock.write().await; if storage.contains_key(&guild.id) { - command.create_interaction_response(&ctx.http, |f| { - f.interaction_response_data(|d| { - d.content("すでにセットアップしています.").flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + command + .create_interaction_response(&ctx.http, |f| { + f.interaction_response_data(|d| { + d.content("すでにセットアップしています.") + .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + }) }) - }).await?; + .await?; return Ok(()); } - storage.insert(guild.id, TTSInstance { - before_message: None, - guild: guild.id, - text_channel: command.channel_id, - voice_channel: channel_id - }); + storage.insert( + guild.id, + TTSInstance { + before_message: None, + guild: guild.id, + text_channel: command.channel_id, + voice_channel: channel_id, + }, + ); } let _handler = manager.join(guild.id.0, channel_id.0).await; @@ -78,4 +108,4 @@ pub async fn setup_command(ctx: &Context, command: &ApplicationCommandInteractio }).await?; Ok(()) -} \ No newline at end of file +} diff --git a/src/commands/stop.rs b/src/commands/stop.rs index e16f5fa..5b31a71 100644 --- a/src/commands/stop.rs +++ b/src/commands/stop.rs @@ -1,24 +1,39 @@ -use serenity::{prelude::Context, model::prelude::{application_command::ApplicationCommandInteraction, InteractionApplicationCommandCallbackDataFlags, UserId}}; +use serenity::{ + model::prelude::{ + application_command::ApplicationCommandInteraction, + InteractionApplicationCommandCallbackDataFlags, UserId, + }, + prelude::Context, +}; use crate::data::TTSData; -pub async fn stop_command(ctx: &Context, command: &ApplicationCommandInteraction) -> Result<(), Box> { +pub async fn stop_command( + ctx: &Context, + command: &ApplicationCommandInteraction, +) -> Result<(), Box> { if let None = command.guild_id { - command.create_interaction_response(&ctx.http, |f| { - f.interaction_response_data(|d| { - d.content("このコマンドはサーバーでのみ使用可能です.").flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + command + .create_interaction_response(&ctx.http, |f| { + f.interaction_response_data(|d| { + d.content("このコマンドはサーバーでのみ使用可能です.") + .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + }) }) - }).await?; + .await?; return Ok(()); } let guild = command.guild_id.unwrap().to_guild_cached(&ctx.cache).await; if let None = guild { - command.create_interaction_response(&ctx.http, |f| { - f.interaction_response_data(|d| { - d.content("ギルドキャッシュを取得できませんでした.").flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + command + .create_interaction_response(&ctx.http, |f| { + f.interaction_response_data(|d| { + d.content("ギルドキャッシュを取得できませんでした.") + .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + }) }) - }).await?; + .await?; return Ok(()); } let guild = guild.unwrap(); @@ -29,29 +44,41 @@ pub async fn stop_command(ctx: &Context, command: &ApplicationCommandInteraction .and_then(|state| state.channel_id); if let None = channel_id { - command.create_interaction_response(&ctx.http, |f| { - f.interaction_response_data(|d| { - d.content("ボイスチャンネルに参加してから実行してください.").flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + command + .create_interaction_response(&ctx.http, |f| { + f.interaction_response_data(|d| { + d.content("ボイスチャンネルに参加してから実行してください.") + .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + }) }) - }).await?; + .await?; return Ok(()); } - let manager = songbird::get(ctx).await.expect("Cannot get songbird client.").clone(); + let manager = songbird::get(ctx) + .await + .expect("Cannot get songbird client.") + .clone(); let storage_lock = { let data_read = ctx.data.read().await; - data_read.get::().expect("Cannot get TTSStorage").clone() + data_read + .get::() + .expect("Cannot get TTSStorage") + .clone() }; { let mut storage = storage_lock.write().await; if !storage.contains_key(&guild.id) { - command.create_interaction_response(&ctx.http, |f| { - f.interaction_response_data(|d| { - d.content("すでに停止しています").flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + command + .create_interaction_response(&ctx.http, |f| { + f.interaction_response_data(|d| { + d.content("すでに停止しています") + .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + }) }) - }).await?; + .await?; return Ok(()); } @@ -60,11 +87,11 @@ pub async fn stop_command(ctx: &Context, command: &ApplicationCommandInteraction let _handler = manager.remove(guild.id.0).await; - command.create_interaction_response(&ctx.http, |f| { - f.interaction_response_data(|d| { - d.content("停止しました") + command + .create_interaction_response(&ctx.http, |f| { + f.interaction_response_data(|d| d.content("停止しました")) }) - }).await?; + .await?; Ok(()) } diff --git a/src/config.rs b/src/config.rs index 29b665e..63f03be 100644 --- a/src/config.rs +++ b/src/config.rs @@ -6,5 +6,5 @@ pub struct Config { pub token: String, pub application_id: u64, pub redis_url: String, - pub voicevox_key: String -} \ No newline at end of file + pub voicevox_key: String, +} diff --git a/src/data.rs b/src/data.rs index 172e671..7ecbc86 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,8 +1,15 @@ -use crate::{tts::{gcp_tts::gcp_tts::TTS, voicevox::voicevox::VOICEVOX}, database::database::Database}; -use serenity::{prelude::{TypeMapKey, RwLock}, model::id::GuildId, futures::lock::Mutex}; +use crate::{ + database::database::Database, + tts::{gcp_tts::gcp_tts::TTS, voicevox::voicevox::VOICEVOX}, +}; +use serenity::{ + futures::lock::Mutex, + model::id::GuildId, + prelude::{RwLock, TypeMapKey}, +}; use crate::tts::instance::TTSInstance; -use std::{sync::Arc, collections::HashMap}; +use std::{collections::HashMap, sync::Arc}; /// TTSInstance data pub struct TTSData; diff --git a/src/database/database.rs b/src/database/database.rs index f68cd1b..3720426 100644 --- a/src/database/database.rs +++ b/src/database/database.rs @@ -1,10 +1,12 @@ -use crate::tts::{gcp_tts::structs::voice_selection_params::VoiceSelectionParams, tts_type::TTSType}; +use crate::tts::{ + gcp_tts::structs::voice_selection_params::VoiceSelectionParams, tts_type::TTSType, +}; use super::user_config::UserConfig; use redis::Commands; pub struct Database { - pub client: redis::Client + pub client: redis::Client, } impl Database { @@ -12,22 +14,35 @@ impl Database { Self { client } } - pub async fn get_user_config(&mut self, user_id: u64) -> redis::RedisResult> { + pub async fn get_user_config( + &mut self, + user_id: u64, + ) -> redis::RedisResult> { if let Ok(connection) = self.client.get_connection() { - let config: String = connection.get(format!("discord_user:{}", user_id)).unwrap_or_default(); + let config: String = connection + .get(format!("discord_user:{}", user_id)) + .unwrap_or_default(); match serde_json::from_str(&config) { Ok(config) => Ok(Some(config)), - Err(_) => Ok(None) + Err(_) => Ok(None), } } else { Ok(None) } } - pub async fn set_user_config(&mut self, user_id: u64, config: UserConfig) -> redis::RedisResult<()> { + pub async fn set_user_config( + &mut self, + user_id: u64, + config: UserConfig, + ) -> redis::RedisResult<()> { let config = serde_json::to_string(&config).unwrap(); - self.client.get_connection().unwrap().set::(format!("discord_user:{}", user_id), config).unwrap(); + self.client + .get_connection() + .unwrap() + .set::(format!("discord_user:{}", user_id), config) + .unwrap(); Ok(()) } @@ -35,7 +50,7 @@ impl Database { let voice_selection = VoiceSelectionParams { languageCode: String::from("ja-JP"), name: String::from("ja-JP-Wavenet-B"), - ssmlGender: String::from("neutral") + ssmlGender: String::from("neutral"), }; let voice_type = TTSType::GCP; @@ -43,15 +58,21 @@ impl Database { let config = UserConfig { tts_type: Some(voice_type), gcp_tts_voice: Some(voice_selection), - voicevox_speaker: Some(1) + voicevox_speaker: Some(1), }; - self.client.get_connection().unwrap().set(format!("discord_user:{}", user_id), serde_json::to_string(&config).unwrap())?; + self.client.get_connection().unwrap().set( + format!("discord_user:{}", user_id), + serde_json::to_string(&config).unwrap(), + )?; Ok(()) } - pub async fn get_user_config_or_default(&mut self, user_id: u64) -> redis::RedisResult> { + pub async fn get_user_config_or_default( + &mut self, + user_id: u64, + ) -> redis::RedisResult> { let config = self.get_user_config(user_id).await?; match config { Some(_) => Ok(config), diff --git a/src/database/mod.rs b/src/database/mod.rs index 895f6ac..46510b8 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -1,2 +1,2 @@ +pub mod database; pub mod user_config; -pub mod database; \ No newline at end of file diff --git a/src/database/user_config.rs b/src/database/user_config.rs index fc9b57d..f0ab66a 100644 --- a/src/database/user_config.rs +++ b/src/database/user_config.rs @@ -1,10 +1,12 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; -use crate::tts::{gcp_tts::structs::voice_selection_params::VoiceSelectionParams, tts_type::TTSType}; +use crate::tts::{ + gcp_tts::structs::voice_selection_params::VoiceSelectionParams, tts_type::TTSType, +}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct UserConfig { pub tts_type: Option, pub gcp_tts_voice: Option, - pub voicevox_speaker: Option + pub voicevox_speaker: Option, } diff --git a/src/event_handler.rs b/src/event_handler.rs index d8af6a2..7f352bf 100644 --- a/src/event_handler.rs +++ b/src/event_handler.rs @@ -1,11 +1,22 @@ -use serenity::{client::{EventHandler, Context}, async_trait, model::{gateway::Ready, interactions::Interaction, id::GuildId, channel::Message, voice::VoiceState, prelude::InteractionApplicationCommandCallbackDataFlags}}; -use crate::{events, commands::{setup::setup_command, stop::stop_command, config::config_command}, data::DatabaseClientData, tts::tts_type::TTSType}; +use crate::{ + commands::{config::config_command, setup::setup_command, stop::stop_command}, + data::DatabaseClientData, + events, + tts::tts_type::TTSType, +}; +use serenity::{ + async_trait, + client::{Context, EventHandler}, + model::{ + channel::Message, gateway::Ready, id::GuildId, interactions::Interaction, + prelude::InteractionApplicationCommandCallbackDataFlags, voice::VoiceState, + }, +}; pub struct Handler; #[async_trait] impl EventHandler for Handler { - async fn message(&self, ctx: Context, message: Message) { events::message_receive::message(ctx, message).await } @@ -29,9 +40,16 @@ impl EventHandler for Handler { let data_read = ctx.data.read().await; let mut config = { - let database = data_read.get::().expect("Cannot get DatabaseClientData").clone(); + let database = data_read + .get::() + .expect("Cannot get DatabaseClientData") + .clone(); let mut database = database.lock().await; - database.get_user_config_or_default(message_component.user.id.0).await.unwrap().unwrap() + database + .get_user_config_or_default(message_component.user.id.0) + .await + .unwrap() + .unwrap() }; let res = (*v).clone(); @@ -48,7 +66,13 @@ impl EventHandler for Handler { } _ => { if res.starts_with("TTS_CONFIG_VOICEVOX_SPEAKER_SELECTED_") { - config.voicevox_speaker = Some(i64::from_str_radix(&res.replace("TTS_CONFIG_VOICEVOX_SPEAKER_SELECTED_", ""), 10).unwrap()); + config.voicevox_speaker = Some( + i64::from_str_radix( + &res.replace("TTS_CONFIG_VOICEVOX_SPEAKER_SELECTED_", ""), + 10, + ) + .unwrap(), + ); config_changed = true; voicevox_changed = true; } @@ -56,11 +80,17 @@ impl EventHandler for Handler { } if config_changed { - let database = data_read.get::().expect("Cannot get DatabaseClientData").clone(); + let database = data_read + .get::() + .expect("Cannot get DatabaseClientData") + .clone(); let mut database = database.lock().await; - database.set_user_config(message_component.user.id.0, config.clone()).await.unwrap(); + database + .set_user_config(message_component.user.id.0, config.clone()) + .await + .unwrap(); - if voicevox_changed && config.tts_type.unwrap_or(TTSType::GCP) == TTSType::GCP { + if voicevox_changed && config.tts_type.unwrap_or(TTSType::GCP) == TTSType::GCP { message_component.create_interaction_response(&ctx.http, |f| { f.interaction_response_data(|d| { d.content("設定しました\nこの音声を使うにはAPIをGoogleからVOICEVOXに変更する必要があります。") @@ -68,12 +98,16 @@ impl EventHandler for Handler { }) }).await.unwrap(); } else { - message_component.create_interaction_response(&ctx.http, |f| { - f.interaction_response_data(|d| { - d.content("設定しました") - .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + message_component + .create_interaction_response(&ctx.http, |f| { + f.interaction_response_data(|d| { + d.content("設定しました").flags( + InteractionApplicationCommandCallbackDataFlags::EPHEMERAL, + ) + }) }) - }).await.unwrap(); + .await + .unwrap(); } } } diff --git a/src/events/message_receive.rs b/src/events/message_receive.rs index 20f2f21..62f558c 100644 --- a/src/events/message_receive.rs +++ b/src/events/message_receive.rs @@ -1,4 +1,4 @@ -use serenity::{prelude::Context, model::prelude::Message}; +use serenity::{model::prelude::Message, prelude::Context}; use crate::data::TTSData; @@ -17,7 +17,10 @@ pub async fn message(ctx: Context, message: Message) { let storage_lock = { let data_read = ctx.data.read().await; - data_read.get::().expect("Cannot get TTSStorage").clone() + data_read + .get::() + .expect("Cannot get TTSStorage") + .clone() }; { @@ -34,4 +37,4 @@ pub async fn message(ctx: Context, message: Message) { instance.read(message, &ctx).await; } -} \ No newline at end of file +} diff --git a/src/events/mod.rs b/src/events/mod.rs index 236a826..da1ccea 100644 --- a/src/events/mod.rs +++ b/src/events/mod.rs @@ -1,3 +1,3 @@ -pub mod ready; pub mod message_receive; -pub mod voice_state_update; \ No newline at end of file +pub mod ready; +pub mod voice_state_update; diff --git a/src/events/ready.rs b/src/events/ready.rs index 4fa0c4d..f3a31fb 100644 --- a/src/events/ready.rs +++ b/src/events/ready.rs @@ -1,20 +1,16 @@ -use serenity::{prelude::Context, model::prelude::{Ready, application_command::ApplicationCommand}}; +use serenity::{ + model::prelude::{application_command::ApplicationCommand, Ready}, + prelude::Context, +}; pub async fn ready(ctx: Context, ready: Ready) { println!("{} is connected!", ready.user.name); let _ = ApplicationCommand::set_global_application_commands(&ctx.http, |commands| { - commands.create_application_command(|command| { - command.name("stop") - .description("Stop tts") - }); - commands.create_application_command(|command| { - command.name("setup") - .description("Setup tts") - }); - commands.create_application_command(|command| { - command.name("config") - .description("Config") - }) - }).await; -} \ No newline at end of file + commands.create_application_command(|command| command.name("stop").description("Stop tts")); + commands + .create_application_command(|command| command.name("setup").description("Setup tts")); + commands.create_application_command(|command| command.name("config").description("Config")) + }) + .await; +} diff --git a/src/events/voice_state_update.rs b/src/events/voice_state_update.rs index a9e5656..53e128f 100644 --- a/src/events/voice_state_update.rs +++ b/src/events/voice_state_update.rs @@ -1,6 +1,16 @@ -use serenity::{prelude::Context, model::{prelude::GuildId, voice::VoiceState}}; +use serenity::{ + model::{prelude::GuildId, voice::VoiceState}, + prelude::Context, +}; -use crate::{data::TTSData, implement::{voice_move_state::{VoiceMoveStateTrait, VoiceMoveState}, member_name::ReadName}, tts::message::AnnounceMessage}; +use crate::{ + data::TTSData, + implement::{ + member_name::ReadName, + voice_move_state::{VoiceMoveState, VoiceMoveStateTrait}, + }, + tts::message::AnnounceMessage, +}; pub async fn voice_state_update( ctx: Context, @@ -15,7 +25,10 @@ pub async fn voice_state_update( let storage_lock = { let data_read = ctx.data.read().await; - data_read.get::().expect("Cannot get TTSStorage").clone() + data_read + .get::() + .expect("Cannot get TTSStorage") + .clone() }; { @@ -29,15 +42,19 @@ pub async fn voice_state_update( let voice_move_state = new.move_state(&old, instance.voice_channel); let message: Option = match voice_move_state { - VoiceMoveState::JOIN => Some(format!("{} さんが通話に参加しました", new.member.unwrap().read_name())), - VoiceMoveState::LEAVE => Some(format!("{} さんが通話から退出しました", new.member.unwrap().read_name())), + VoiceMoveState::JOIN => Some(format!( + "{} さんが通話に参加しました", + new.member.unwrap().read_name() + )), + VoiceMoveState::LEAVE => Some(format!( + "{} さんが通話から退出しました", + new.member.unwrap().read_name() + )), _ => None, }; if let Some(message) = message { - instance.read(AnnounceMessage { - message - }, &ctx).await; + instance.read(AnnounceMessage { message }, &ctx).await; } if voice_move_state == VoiceMoveState::LEAVE { @@ -51,10 +68,13 @@ pub async fn voice_state_update( if del_flag { storage.remove(&guild_id); - let manager = songbird::get(&ctx).await.expect("Cannot get songbird client.").clone(); + let manager = songbird::get(&ctx) + .await + .expect("Cannot get songbird client.") + .clone(); manager.remove(guild_id.0).await.unwrap(); } } } -} \ No newline at end of file +} diff --git a/src/implement/member_name.rs b/src/implement/member_name.rs index 6a10779..e41b937 100644 --- a/src/implement/member_name.rs +++ b/src/implement/member_name.rs @@ -8,4 +8,4 @@ impl ReadName for Member { fn read_name(&self) -> String { self.nick.clone().unwrap_or(self.user.name.clone()) } -} \ No newline at end of file +} diff --git a/src/implement/message.rs b/src/implement/message.rs index c467bb7..3fd5b9b 100644 --- a/src/implement/message.rs +++ b/src/implement/message.rs @@ -1,17 +1,19 @@ -use std::{fs::File, io::Write, env}; +use std::{env, fs::File, io::Write}; use async_trait::async_trait; -use serenity::{prelude::Context, model::prelude::Message}; +use serenity::{model::prelude::Message, prelude::Context}; use crate::{ - data::{TTSClientData, DatabaseClientData}, + data::{DatabaseClientData, TTSClientData}, tts::{ + gcp_tts::structs::{ + audio_config::AudioConfig, synthesis_input::SynthesisInput, + synthesize_request::SynthesizeRequest, + }, instance::TTSInstance, message::TTSMessage, tts_type::TTSType, - gcp_tts::structs::{ - audio_config::AudioConfig, synthesis_input::SynthesisInput, synthesize_request::SynthesizeRequest - }, validator + validator, }, }; @@ -42,7 +44,11 @@ impl TTSMessage for Message { }; if self.attachments.len() > 0 { - res = format!("{}{}個の添付ファイル", res, self.attachments.len()); + res = format!( + "{}{}個の添付ファイル", + res, + self.attachments.len() + ); } instance.before_message = Some(self.clone()); @@ -54,34 +60,51 @@ impl TTSMessage for Message { let text = self.parse(instance, ctx).await; let data_read = ctx.data.read().await; - let storage = data_read.get::().expect("Cannot get GCP TTSClientStorage").clone(); + let storage = data_read + .get::() + .expect("Cannot get GCP TTSClientStorage") + .clone(); let mut tts = storage.lock().await; let config = { - let database = data_read.get::().expect("Cannot get DatabaseClientData").clone(); + let database = data_read + .get::() + .expect("Cannot get DatabaseClientData") + .clone(); let mut database = database.lock().await; - database.get_user_config_or_default(self.author.id.0).await.unwrap().unwrap() + database + .get_user_config_or_default(self.author.id.0) + .await + .unwrap() + .unwrap() }; let audio = match config.tts_type.unwrap_or(TTSType::GCP) { - TTSType::GCP => { - tts.0.synthesize(SynthesizeRequest { + TTSType::GCP => tts + .0 + .synthesize(SynthesizeRequest { input: SynthesisInput { text: None, - ssml: Some(format!("{}", text)) + ssml: Some(format!("{}", text)), }, voice: config.gcp_tts_voice.unwrap(), audioConfig: AudioConfig { audioEncoding: String::from("mp3"), speakingRate: 1.2f32, - pitch: 1.0f32 - } - }).await.unwrap() - } + pitch: 1.0f32, + }, + }) + .await + .unwrap(), - TTSType::VOICEVOX => { - tts.1.synthesize(text.replace("", "、"), config.voicevox_speaker.unwrap_or(1)).await.unwrap() - } + TTSType::VOICEVOX => tts + .1 + .synthesize( + text.replace("", "、"), + config.voicevox_speaker.unwrap_or(1), + ) + .await + .unwrap(), }; let uuid = uuid::Uuid::new_v4().to_string(); diff --git a/src/implement/mod.rs b/src/implement/mod.rs index f4e04b8..172d6e2 100644 --- a/src/implement/mod.rs +++ b/src/implement/mod.rs @@ -1,3 +1,3 @@ -pub mod message; pub mod member_name; -pub mod voice_move_state; \ No newline at end of file +pub mod message; +pub mod voice_move_state; diff --git a/src/implement/voice_move_state.rs b/src/implement/voice_move_state.rs index 87cdb5a..2b65f51 100644 --- a/src/implement/voice_move_state.rs +++ b/src/implement/voice_move_state.rs @@ -1,4 +1,4 @@ -use serenity::model::{voice::VoiceState, prelude::ChannelId}; +use serenity::model::{prelude::ChannelId, voice::VoiceState}; pub trait VoiceMoveStateTrait { fn move_state(&self, old: &Option, target_channel: ChannelId) -> VoiceMoveState; @@ -8,7 +8,7 @@ pub trait VoiceMoveStateTrait { pub enum VoiceMoveState { JOIN, LEAVE, - NONE + NONE, } impl VoiceMoveStateTrait for VoiceState { @@ -17,10 +17,10 @@ impl VoiceMoveStateTrait for VoiceState { if let None = old.clone() { return if target_channel == new.channel_id.unwrap() { - VoiceMoveState::JOIN - } else { - VoiceMoveState::NONE - } + VoiceMoveState::JOIN + } else { + VoiceMoveState::NONE + }; } let old = (*old).clone().unwrap(); @@ -46,9 +46,7 @@ impl VoiceMoveStateTrait for VoiceState { VoiceMoveState::NONE } } - _ => { - VoiceMoveState::NONE - } + _ => VoiceMoveState::NONE, } } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 59c4760..ba1d65d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,25 @@ +mod commands; mod config; -mod event_handler; -mod tts; -mod implement; mod data; mod database; +mod event_handler; mod events; -mod commands; +mod implement; +mod tts; -use std::{sync::Arc, collections::HashMap}; +use std::{collections::HashMap, sync::Arc}; use config::Config; -use data::{TTSData, TTSClientData, DatabaseClientData}; +use data::{DatabaseClientData, TTSClientData, TTSData}; use database::database::Database; use event_handler::Handler; -use tts::{gcp_tts::gcp_tts::TTS, voicevox::voicevox::VOICEVOX}; use serenity::{ - client::{Client, bridge::gateway::GatewayIntents}, - framework::StandardFramework, prelude::RwLock, futures::lock::Mutex + client::{bridge::gateway::GatewayIntents, Client}, + framework::StandardFramework, + futures::lock::Mutex, + prelude::RwLock, }; +use tts::{gcp_tts::gcp_tts::TTS, voicevox::voicevox::VOICEVOX}; use songbird::SerenityInit; @@ -30,10 +32,7 @@ use songbird::SerenityInit; /// client.start().await; /// ``` async fn create_client(prefix: &str, token: &str, id: u64) -> Result { - let framework = StandardFramework::new() - .configure(|c| c - .with_whitespace(true) - .prefix(prefix)); + let framework = StandardFramework::new().configure(|c| c.with_whitespace(true).prefix(prefix)); Client::builder(token) .event_handler(Handler) @@ -51,12 +50,14 @@ async fn main() { let config: Config = toml::from_str(&config).expect("Cannot load config file."); // Create discord client - let mut client = create_client(&config.prefix, &config.token, config.application_id).await.expect("Err creating client"); + let mut client = create_client(&config.prefix, &config.token, config.application_id) + .await + .expect("Err creating client"); // Create GCP TTS client let tts = match TTS::new("./credentials.json".to_string()).await { Ok(tts) => tts, - Err(err) => panic!("{}", err) + Err(err) => panic!("{}", err), }; let voicevox = VOICEVOX::new(config.voicevox_key); diff --git a/src/tts/gcp_tts/gcp_tts.rs b/src/tts/gcp_tts/gcp_tts.rs index 8cfc12b..acf9d33 100644 --- a/src/tts/gcp_tts/gcp_tts.rs +++ b/src/tts/gcp_tts/gcp_tts.rs @@ -1,21 +1,22 @@ -use gcp_auth::Token; use crate::tts::gcp_tts::structs::{ - synthesize_request::SynthesizeRequest, - synthesize_response::SynthesizeResponse, + synthesize_request::SynthesizeRequest, synthesize_response::SynthesizeResponse, }; +use gcp_auth::Token; #[derive(Clone)] pub struct TTS { pub token: Token, - pub credentials_path: String + pub credentials_path: String, } impl TTS { - pub async fn update_token(&mut self) -> Result<(), gcp_auth::Error> { if self.token.has_expired() { - let authenticator = gcp_auth::from_credentials_file(self.credentials_path.clone()).await?; - let token = authenticator.get_token(&["https://www.googleapis.com/auth/cloud-platform"]).await?; + let authenticator = + gcp_auth::from_credentials_file(self.credentials_path.clone()).await?; + let token = authenticator + .get_token(&["https://www.googleapis.com/auth/cloud-platform"]) + .await?; self.token = token; } @@ -24,11 +25,13 @@ impl TTS { pub async fn new(credentials_path: String) -> Result { let authenticator = gcp_auth::from_credentials_file(credentials_path.clone()).await?; - let token = authenticator.get_token(&["https://www.googleapis.com/auth/cloud-platform"]).await?; + let token = authenticator + .get_token(&["https://www.googleapis.com/auth/cloud-platform"]) + .await?; Ok(TTS { token, - credentials_path + credentials_path, }) } @@ -53,19 +56,29 @@ impl TTS { /// } /// }).await.unwrap(); /// ``` - pub async fn synthesize(&mut self, request: SynthesizeRequest) -> Result, Box> { + pub async fn synthesize( + &mut self, + request: SynthesizeRequest, + ) -> Result, Box> { self.update_token().await.unwrap(); let client = reqwest::Client::new(); - match client.post("https://texttospeech.googleapis.com/v1/text:synthesize") + match client + .post("https://texttospeech.googleapis.com/v1/text:synthesize") .header(reqwest::header::CONTENT_TYPE, "application/json") - .header(reqwest::header::AUTHORIZATION, format!("Bearer {}", self.token.as_str())) + .header( + reqwest::header::AUTHORIZATION, + format!("Bearer {}", self.token.as_str()), + ) .body(serde_json::to_string(&request).unwrap()) - .send().await { - Ok(ok) => { - let response: SynthesizeResponse = serde_json::from_str(&ok.text().await.expect("")).unwrap(); - Ok(base64::decode(response.audioContent).unwrap()[..].to_vec()) - }, - Err(err) => Err(Box::new(err)) + .send() + .await + { + Ok(ok) => { + let response: SynthesizeResponse = + serde_json::from_str(&ok.text().await.expect("")).unwrap(); + Ok(base64::decode(response.audioContent).unwrap()[..].to_vec()) + } + Err(err) => Err(Box::new(err)), } } } diff --git a/src/tts/gcp_tts/mod.rs b/src/tts/gcp_tts/mod.rs index 69fde75..8548096 100644 --- a/src/tts/gcp_tts/mod.rs +++ b/src/tts/gcp_tts/mod.rs @@ -1,2 +1,2 @@ pub mod gcp_tts; -pub mod structs; \ No newline at end of file +pub mod structs; diff --git a/src/tts/gcp_tts/structs/audio_config.rs b/src/tts/gcp_tts/structs/audio_config.rs index 1f5aa75..076df85 100644 --- a/src/tts/gcp_tts/structs/audio_config.rs +++ b/src/tts/gcp_tts/structs/audio_config.rs @@ -1,4 +1,4 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; /// Example: /// ```rust @@ -13,5 +13,5 @@ use serde::{Serialize, Deserialize}; pub struct AudioConfig { pub audioEncoding: String, pub speakingRate: f32, - pub pitch: f32 -} \ No newline at end of file + pub pitch: f32, +} diff --git a/src/tts/gcp_tts/structs/mod.rs b/src/tts/gcp_tts/structs/mod.rs index 62dcbee..60e4ec8 100644 --- a/src/tts/gcp_tts/structs/mod.rs +++ b/src/tts/gcp_tts/structs/mod.rs @@ -1,5 +1,5 @@ pub mod audio_config; pub mod synthesis_input; pub mod synthesize_request; +pub mod synthesize_response; pub mod voice_selection_params; -pub mod synthesize_response; \ No newline at end of file diff --git a/src/tts/gcp_tts/structs/synthesis_input.rs b/src/tts/gcp_tts/structs/synthesis_input.rs index e4deccb..d7a1464 100644 --- a/src/tts/gcp_tts/structs/synthesis_input.rs +++ b/src/tts/gcp_tts/structs/synthesis_input.rs @@ -1,4 +1,4 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; /// Example: /// ```rust @@ -10,5 +10,5 @@ use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize, Debug)] pub struct SynthesisInput { pub text: Option, - pub ssml: Option -} \ No newline at end of file + pub ssml: Option, +} diff --git a/src/tts/gcp_tts/structs/synthesize_request.rs b/src/tts/gcp_tts/structs/synthesize_request.rs index 96aaae6..540fdcb 100644 --- a/src/tts/gcp_tts/structs/synthesize_request.rs +++ b/src/tts/gcp_tts/structs/synthesize_request.rs @@ -1,9 +1,8 @@ -use serde::{Serialize, Deserialize}; use crate::tts::gcp_tts::structs::{ - synthesis_input::SynthesisInput, - audio_config::AudioConfig, + audio_config::AudioConfig, synthesis_input::SynthesisInput, voice_selection_params::VoiceSelectionParams, }; +use serde::{Deserialize, Serialize}; /// Example: /// ```rust @@ -30,4 +29,4 @@ pub struct SynthesizeRequest { pub input: SynthesisInput, pub voice: VoiceSelectionParams, pub audioConfig: AudioConfig, -} \ No newline at end of file +} diff --git a/src/tts/gcp_tts/structs/synthesize_response.rs b/src/tts/gcp_tts/structs/synthesize_response.rs index 8ca495c..71985cb 100644 --- a/src/tts/gcp_tts/structs/synthesize_response.rs +++ b/src/tts/gcp_tts/structs/synthesize_response.rs @@ -1,7 +1,7 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] #[allow(non_snake_case)] pub struct SynthesizeResponse { - pub audioContent: String -} \ No newline at end of file + pub audioContent: String, +} diff --git a/src/tts/gcp_tts/structs/voice_selection_params.rs b/src/tts/gcp_tts/structs/voice_selection_params.rs index 625272f..37c78bd 100644 --- a/src/tts/gcp_tts/structs/voice_selection_params.rs +++ b/src/tts/gcp_tts/structs/voice_selection_params.rs @@ -1,4 +1,4 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; /// Example: /// ```rust @@ -13,5 +13,5 @@ use serde::{Serialize, Deserialize}; pub struct VoiceSelectionParams { pub languageCode: String, pub name: String, - pub ssmlGender: String -} \ No newline at end of file + pub ssmlGender: String, +} diff --git a/src/tts/instance.rs b/src/tts/instance.rs index 707661f..1968991 100644 --- a/src/tts/instance.rs +++ b/src/tts/instance.rs @@ -1,12 +1,18 @@ -use serenity::{model::{channel::Message, id::{ChannelId, GuildId}}, prelude::Context}; +use serenity::{ + model::{ + channel::Message, + id::{ChannelId, GuildId}, + }, + prelude::Context, +}; -use crate::{tts::message::TTSMessage}; +use crate::tts::message::TTSMessage; pub struct TTSInstance { pub before_message: Option, pub text_channel: ChannelId, pub voice_channel: ChannelId, - pub guild: GuildId + pub guild: GuildId, } impl TTSInstance { @@ -17,7 +23,8 @@ impl TTSInstance { /// instance.read(message, &ctx).await; /// ``` pub async fn read(&mut self, message: T, ctx: &Context) - where T: TTSMessage + where + T: TTSMessage, { let path = message.synthesize(self, ctx).await; @@ -25,7 +32,9 @@ impl TTSInstance { let manager = songbird::get(&ctx).await.unwrap(); let call = manager.get(self.guild).unwrap(); let mut call = call.lock().await; - let input = songbird::input::ffmpeg(path).await.expect("File not found."); + let input = songbird::input::ffmpeg(path) + .await + .expect("File not found."); call.enqueue_source(input); } } diff --git a/src/tts/message.rs b/src/tts/message.rs index b93b915..59b4fcd 100644 --- a/src/tts/message.rs +++ b/src/tts/message.rs @@ -1,16 +1,18 @@ -use std::{fs::File, io::Write, env}; +use std::{env, fs::File, io::Write}; use async_trait::async_trait; use serenity::prelude::Context; -use crate::{tts::instance::TTSInstance, data::TTSClientData}; +use crate::{data::TTSClientData, tts::instance::TTSInstance}; -use super::gcp_tts::structs::{synthesize_request::SynthesizeRequest, synthesis_input::SynthesisInput, audio_config::AudioConfig, voice_selection_params::VoiceSelectionParams}; +use super::gcp_tts::structs::{ + audio_config::AudioConfig, synthesis_input::SynthesisInput, + synthesize_request::SynthesizeRequest, voice_selection_params::VoiceSelectionParams, +}; /// Message trait that can be used to synthesize text to speech. #[async_trait] pub trait TTSMessage { - /// Parse the message for synthesis. /// /// Example: @@ -36,31 +38,41 @@ pub struct AnnounceMessage { impl TTSMessage for AnnounceMessage { async fn parse(&self, instance: &mut TTSInstance, _ctx: &Context) -> String { instance.before_message = None; - format!(r#"アナウンス{}"#, self.message) + format!( + r#"アナウンス{}"#, + self.message + ) } async fn synthesize(&self, instance: &mut TTSInstance, ctx: &Context) -> String { let text = self.parse(instance, ctx).await; let data_read = ctx.data.read().await; - let storage = data_read.get::().expect("Cannot get TTSClientStorage").clone(); + let storage = data_read + .get::() + .expect("Cannot get TTSClientStorage") + .clone(); let mut storage = storage.lock().await; - let audio = storage.0.synthesize(SynthesizeRequest { - input: SynthesisInput { - text: None, - ssml: Some(text) - }, - voice: VoiceSelectionParams { - languageCode: String::from("ja-JP"), - name: String::from("ja-JP-Wavenet-B"), - ssmlGender: String::from("neutral") - }, - audioConfig: AudioConfig { - audioEncoding: String::from("mp3"), - speakingRate: 1.2f32, - pitch: 1.0f32 - } - }).await.unwrap(); + let audio = storage + .0 + .synthesize(SynthesizeRequest { + input: SynthesisInput { + text: None, + ssml: Some(text), + }, + voice: VoiceSelectionParams { + languageCode: String::from("ja-JP"), + name: String::from("ja-JP-Wavenet-B"), + ssmlGender: String::from("neutral"), + }, + audioConfig: AudioConfig { + audioEncoding: String::from("mp3"), + speakingRate: 1.2f32, + pitch: 1.0f32, + }, + }) + .await + .unwrap(); let uuid = uuid::Uuid::new_v4().to_string(); @@ -72,4 +84,4 @@ impl TTSMessage for AnnounceMessage { file_path.into_os_string().into_string().unwrap() } -} \ No newline at end of file +} diff --git a/src/tts/mod.rs b/src/tts/mod.rs index 8199dc4..bd92226 100644 --- a/src/tts/mod.rs +++ b/src/tts/mod.rs @@ -1,6 +1,6 @@ pub mod gcp_tts; -pub mod voicevox; +pub mod instance; pub mod message; pub mod tts_type; -pub mod instance; -pub mod validator; \ No newline at end of file +pub mod validator; +pub mod voicevox; diff --git a/src/tts/tts_type.rs b/src/tts/tts_type.rs index 1dcaf6f..20c90f0 100644 --- a/src/tts/tts_type.rs +++ b/src/tts/tts_type.rs @@ -3,5 +3,5 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum TTSType { GCP, - VOICEVOX -} \ No newline at end of file + VOICEVOX, +} diff --git a/src/tts/voicevox/mod.rs b/src/tts/voicevox/mod.rs index 67ae331..a57a59a 100644 --- a/src/tts/voicevox/mod.rs +++ b/src/tts/voicevox/mod.rs @@ -1,2 +1,2 @@ pub mod structs; -pub mod voicevox; \ No newline at end of file +pub mod voicevox; diff --git a/src/tts/voicevox/structs/accent_phrase.rs b/src/tts/voicevox/structs/accent_phrase.rs index cf9846b..c99ff29 100644 --- a/src/tts/voicevox/structs/accent_phrase.rs +++ b/src/tts/voicevox/structs/accent_phrase.rs @@ -1,4 +1,4 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use super::mora::Mora; @@ -7,5 +7,5 @@ pub struct AccentPhrase { pub moras: Vec, pub accent: f64, pub pause_mora: Option, - pub is_interrogative: bool -} \ No newline at end of file + pub is_interrogative: bool, +} diff --git a/src/tts/voicevox/structs/audio_query.rs b/src/tts/voicevox/structs/audio_query.rs index d059c49..90c1680 100644 --- a/src/tts/voicevox/structs/audio_query.rs +++ b/src/tts/voicevox/structs/audio_query.rs @@ -1,4 +1,4 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use super::accent_phrase::AccentPhrase; @@ -14,5 +14,5 @@ pub struct AudioQuery { pub postPhonemeLength: f64, pub outputSamplingRate: f64, pub outputStereo: bool, - pub kana: Option -} \ No newline at end of file + pub kana: Option, +} diff --git a/src/tts/voicevox/structs/mod.rs b/src/tts/voicevox/structs/mod.rs index b229b2f..3688db7 100644 --- a/src/tts/voicevox/structs/mod.rs +++ b/src/tts/voicevox/structs/mod.rs @@ -1,3 +1,3 @@ -pub mod mora; +pub mod accent_phrase; pub mod audio_query; -pub mod accent_phrase; \ No newline at end of file +pub mod mora; diff --git a/src/tts/voicevox/structs/mora.rs b/src/tts/voicevox/structs/mora.rs index c7582f8..1810c19 100644 --- a/src/tts/voicevox/structs/mora.rs +++ b/src/tts/voicevox/structs/mora.rs @@ -1,4 +1,4 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Mora { @@ -7,5 +7,5 @@ pub struct Mora { pub consonant_length: Option, pub vowel: String, pub vowel_length: f64, - pub pitch: f64 -} \ No newline at end of file + pub pitch: f64, +} diff --git a/src/tts/voicevox/voicevox.rs b/src/tts/voicevox/voicevox.rs index fd64fac..0847de7 100644 --- a/src/tts/voicevox/voicevox.rs +++ b/src/tts/voicevox/voicevox.rs @@ -2,7 +2,7 @@ const API_URL: &str = "https://api.su-shiki.com/v2/voicevox/audio"; #[derive(Clone)] pub struct VOICEVOX { - pub key: String + pub key: String, } impl VOICEVOX { @@ -33,21 +33,30 @@ impl VOICEVOX { } pub fn new(key: String) -> Self { - Self { - key - } + Self { key } } - pub async fn synthesize(&self, text: String, speaker: i64) -> Result, Box> { + pub async fn synthesize( + &self, + text: String, + speaker: i64, + ) -> Result, Box> { let client = reqwest::Client::new(); - match client.post(API_URL).query(&[("speaker", speaker.to_string()), ("text", text), ("key", self.key.clone())]).send().await { + match client + .post(API_URL) + .query(&[ + ("speaker", speaker.to_string()), + ("text", text), + ("key", self.key.clone()), + ]) + .send() + .await + { Ok(response) => { let body = response.bytes().await?; Ok(body.to_vec()) } - Err(err) => { - Err(Box::new(err)) - } + Err(err) => Err(Box::new(err)), } } -} \ No newline at end of file +}