diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..4fa5bde --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1,2 @@ +pub mod setup; +pub mod stop; \ No newline at end of file diff --git a/src/commands/setup.rs b/src/commands/setup.rs new file mode 100644 index 0000000..d7a2303 --- /dev/null +++ b/src/commands/setup.rs @@ -0,0 +1,77 @@ +use serenity::{prelude::Context, model::prelude::{application_command::ApplicationCommandInteraction, InteractionApplicationCommandCallbackDataFlags, UserId}}; + +use crate::{data::TTSData, tts::instance::TTSInstance}; + +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) + }) + }).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) + }) + }).await?; + return Ok(()); + } + let guild = guild.unwrap(); + + let channel_id = guild + .voice_states + .get(&UserId(command.user.id.0)) + .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) + }) + }).await?; + return Ok(()); + } + + let channel_id = channel_id.unwrap(); + + 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() + }; + + { + 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) + }) + }).await?; + return Ok(()); + } + + 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; + + command.create_interaction_response(&ctx.http, |f| { + f.interaction_response_data(|d| { + d.content("セットアップ完了").flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + }) + }).await?; + + Ok(()) +} \ No newline at end of file diff --git a/src/commands/stop.rs b/src/commands/stop.rs new file mode 100644 index 0000000..2a50ffc --- /dev/null +++ b/src/commands/stop.rs @@ -0,0 +1,70 @@ +use serenity::{prelude::Context, model::prelude::{application_command::ApplicationCommandInteraction, InteractionApplicationCommandCallbackDataFlags, UserId}}; + +use crate::data::TTSData; + +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) + }) + }).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) + }) + }).await?; + return Ok(()); + } + let guild = guild.unwrap(); + + let channel_id = guild + .voice_states + .get(&UserId(command.user.id.0)) + .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) + }) + }).await?; + return Ok(()); + } + + 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() + }; + + { + 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) + }) + }).await?; + return Ok(()); + } + + storage.remove(&guild.id); + } + + let _handler = manager.leave(guild.id.0).await; + + command.create_interaction_response(&ctx.http, |f| { + f.interaction_response_data(|d| { + d.content("停止しました").flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) + }) + }).await?; + + Ok(()) +} diff --git a/src/event_handler.rs b/src/event_handler.rs index 5b940f6..cd9ae0c 100644 --- a/src/event_handler.rs +++ b/src/event_handler.rs @@ -1,149 +1,8 @@ -use serenity::{client::{EventHandler, Context}, async_trait, model::{gateway::Ready, interactions::{Interaction, application_command::ApplicationCommandInteraction, InteractionApplicationCommandCallbackDataFlags}, id::{GuildId, UserId}, channel::Message, voice::VoiceState}}; -use crate::{data::TTSData, tts::{instance::TTSInstance, message::AnnounceMessage}, implement::{member_name::ReadName, voice_move_state::{VoiceMoveStateTrait, VoiceMoveState}}, events}; +use serenity::{client::{EventHandler, Context}, async_trait, model::{gateway::Ready, interactions::Interaction, id::GuildId, channel::Message, voice::VoiceState}}; +use crate::{events, commands::{setup::setup_command, stop::stop_command}}; pub struct Handler; -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) - }) - }).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) - }) - }).await?; - return Ok(()); - } - let guild = guild.unwrap(); - - let channel_id = guild - .voice_states - .get(&UserId(command.user.id.0)) - .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) - }) - }).await?; - return Ok(()); - } - - 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() - }; - - { - 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) - }) - }).await?; - return Ok(()); - } - - storage.remove(&guild.id); - } - - let _handler = manager.leave(guild.id.0).await; - - command.create_interaction_response(&ctx.http, |f| { - f.interaction_response_data(|d| { - d.content("停止しました").flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) - }) - }).await?; - - Ok(()) -} - -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) - }) - }).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) - }) - }).await?; - return Ok(()); - } - let guild = guild.unwrap(); - - let channel_id = guild - .voice_states - .get(&UserId(command.user.id.0)) - .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) - }) - }).await?; - return Ok(()); - } - - let channel_id = channel_id.unwrap(); - - 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() - }; - - { - 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) - }) - }).await?; - return Ok(()); - } - - 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; - - command.create_interaction_response(&ctx.http, |f| { - f.interaction_response_data(|d| { - d.content("セットアップ完了").flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) - }) - }).await?; - - Ok(()) -} - #[async_trait] impl EventHandler for Handler { @@ -173,34 +32,6 @@ impl EventHandler for Handler { old: Option, new: VoiceState, ) { - let guild_id = guild_id.unwrap(); - - let storage_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Cannot get TTSStorage").clone() - }; - - { - let mut storage = storage_lock.write().await; - if !storage.contains_key(&guild_id) { - return; - } - - let instance = storage.get_mut(&guild_id).unwrap(); - - 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())), - _ => None, - }; - - if let Some(message) = message { - instance.read(AnnounceMessage { - message - }, &ctx).await; - } - } + events::voice_state_update::voice_state_update(ctx, guild_id, old, new).await } } diff --git a/src/events/mod.rs b/src/events/mod.rs index fe32279..236a826 100644 --- a/src/events/mod.rs +++ b/src/events/mod.rs @@ -1,2 +1,3 @@ pub mod ready; -pub mod message_receive; \ No newline at end of file +pub mod message_receive; +pub mod voice_state_update; \ No newline at end of file diff --git a/src/events/voice_state_update.rs b/src/events/voice_state_update.rs new file mode 100644 index 0000000..4b06859 --- /dev/null +++ b/src/events/voice_state_update.rs @@ -0,0 +1,40 @@ +use serenity::{prelude::Context, model::{prelude::GuildId, voice::VoiceState}}; + +use crate::{data::TTSData, implement::{voice_move_state::{VoiceMoveStateTrait, VoiceMoveState}, member_name::ReadName}, tts::message::AnnounceMessage}; + +pub async fn voice_state_update( + ctx: Context, + guild_id: Option, + old: Option, + new: VoiceState, +) { + let guild_id = guild_id.unwrap(); + + let storage_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Cannot get TTSStorage").clone() + }; + + { + let mut storage = storage_lock.write().await; + if !storage.contains_key(&guild_id) { + return; + } + + let instance = storage.get_mut(&guild_id).unwrap(); + + 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())), + _ => None, + }; + + if let Some(message) = message { + instance.read(AnnounceMessage { + message + }, &ctx).await; + } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index abef0d6..59c4760 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod implement; mod data; mod database; mod events; +mod commands; use std::{sync::Arc, collections::HashMap};