251 lines
6.7 KiB
Rust
251 lines
6.7 KiB
Rust
use crate::{command, group, GlobalData};
|
|
use serenity::client::Context;
|
|
use serenity::framework::standard::{Args, CommandResult};
|
|
use serenity::model::channel::{AttachmentType, Message};
|
|
use serenity::utils::MessageBuilder;
|
|
use songbird::driver::Bitrate;
|
|
use songbird::input;
|
|
use songbird::input::cached::Compressed;
|
|
use std::borrow::Cow;
|
|
use std::collections::HashMap;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
#[group]
|
|
#[commands(say, list_words, list_voices)]
|
|
pub struct Voices;
|
|
|
|
async fn get_voice_dictionary(path: &Path) -> Result<HashMap<String, PathBuf>, tokio::io::Error> {
|
|
let mut dir = tokio::fs::read_dir(path).await?;
|
|
let mut dict = HashMap::new();
|
|
|
|
while let Some(word) = &dir.next_entry().await? {
|
|
let word_name = word
|
|
.path()
|
|
.file_stem()
|
|
.unwrap()
|
|
.to_str()
|
|
.unwrap()
|
|
.to_string();
|
|
dict.insert(word_name, word.path());
|
|
}
|
|
|
|
Ok(dict)
|
|
}
|
|
|
|
async fn get_voices(voice_path: &Path) -> Result<Vec<String>, tokio::io::Error> {
|
|
let mut dir = tokio::fs::read_dir(voice_path).await?;
|
|
let mut voices = Vec::new();
|
|
|
|
while let Some(file) = &dir.next_entry().await? {
|
|
if file.path().is_dir() {
|
|
let voice_name = file
|
|
.path()
|
|
.file_name()
|
|
.unwrap()
|
|
.to_str()
|
|
.unwrap()
|
|
.to_string();
|
|
|
|
voices.push(voice_name)
|
|
}
|
|
}
|
|
|
|
Ok(voices)
|
|
}
|
|
|
|
async fn find_voice(voice_path: &Path, name: &str) -> Result<Option<PathBuf>, tokio::io::Error> {
|
|
let mut dir = tokio::fs::read_dir(voice_path).await?;
|
|
|
|
while let Some(file) = &dir.next_entry().await? {
|
|
if file.path().is_dir() {
|
|
let voice_name = file
|
|
.path()
|
|
.file_name()
|
|
.unwrap()
|
|
.to_str()
|
|
.unwrap()
|
|
.to_string();
|
|
|
|
if voice_name.eq_ignore_ascii_case(name) {
|
|
return Ok(Some(file.path()));
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
|
|
#[command]
|
|
#[only_in(guilds)]
|
|
#[min_args(1)]
|
|
async fn say(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
|
let guild = msg.guild(&ctx.cache).unwrap();
|
|
let guild_id = guild.id;
|
|
|
|
let data = ctx.data.read().await;
|
|
let global_data = data.get::<GlobalData>().unwrap();
|
|
|
|
let voice = args.parse::<String>().unwrap();
|
|
|
|
let voice_path = match find_voice(&global_data.cfg.voice_path, &voice).await? {
|
|
None => {
|
|
msg.reply(&ctx.http, format!("No voice found called '{}'", voice))
|
|
.await?;
|
|
return Ok(());
|
|
}
|
|
Some(voice_path) => voice_path,
|
|
};
|
|
|
|
args.advance();
|
|
|
|
let dict = get_voice_dictionary(&voice_path).await?;
|
|
|
|
let mut phrase = Vec::new();
|
|
while let Some(word) = &args.current() {
|
|
let mut add_period = false;
|
|
let mut add_comma = false;
|
|
let word = if word.ends_with(',') {
|
|
add_comma = true;
|
|
word.replace(',', "")
|
|
} else if word.ends_with('.') {
|
|
add_period = true;
|
|
word.replace('.', "")
|
|
} else {
|
|
word.to_string()
|
|
};
|
|
|
|
if dict.contains_key(&word) {
|
|
phrase.push(word.to_string());
|
|
} else {
|
|
msg.reply(
|
|
&ctx.http,
|
|
format!("The word '{}' is not in the dictionary", word),
|
|
)
|
|
.await?;
|
|
return Ok(());
|
|
}
|
|
|
|
if add_comma {
|
|
phrase.push("_comma".to_string());
|
|
}
|
|
|
|
if add_period {
|
|
phrase.push("_period".to_string());
|
|
}
|
|
|
|
args.advance();
|
|
}
|
|
|
|
let channel_id = guild
|
|
.voice_states
|
|
.get(&msg.author.id)
|
|
.and_then(|voice_state| voice_state.channel_id);
|
|
|
|
let connect_to = match channel_id {
|
|
Some(channel) => channel,
|
|
None => {
|
|
msg.reply(ctx, "You are not in a voice channel").await?;
|
|
|
|
return Ok(());
|
|
}
|
|
};
|
|
|
|
let manager = songbird::get(ctx)
|
|
.await
|
|
.expect("Songbird not initialized")
|
|
.clone();
|
|
|
|
let (handler_lock, success_reader) = manager.join(guild_id, connect_to).await;
|
|
|
|
success_reader?;
|
|
|
|
let mut handler = handler_lock.lock().await;
|
|
|
|
for word in phrase {
|
|
let word_path = dict.get(&word).unwrap();
|
|
|
|
let audio_src = Compressed::new(
|
|
input::ffmpeg(word_path.to_str().unwrap())
|
|
.await
|
|
.expect("Bad audio link."),
|
|
Bitrate::BitsPerSecond(128_000),
|
|
)
|
|
.expect("Bad params on message load");
|
|
let _ = audio_src.raw.spawn_loader();
|
|
|
|
let duration = audio_src.metadata.duration.unwrap();
|
|
let voice = handler.play_source(audio_src.into());
|
|
voice.set_volume(0.5)?;
|
|
|
|
tokio::time::sleep(duration).await;
|
|
}
|
|
|
|
handler.leave().await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[only_in(guilds)]
|
|
#[min_args(1)]
|
|
async fn list_words(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|
let data = ctx.data.read().await;
|
|
let global_data = data.get::<GlobalData>().unwrap();
|
|
|
|
let voice = args.parse::<String>().unwrap();
|
|
|
|
match find_voice(&global_data.cfg.voice_path, &voice).await? {
|
|
None => {
|
|
msg.reply(&ctx.http, format!("No voice found called '{}'", voice))
|
|
.await?;
|
|
}
|
|
Some(voice_path) => {
|
|
let dict = get_voice_dictionary(&voice_path).await?;
|
|
let mut words: Vec<String> = dict.keys().cloned().collect();
|
|
|
|
words.sort();
|
|
|
|
let mut list_msg = MessageBuilder::new();
|
|
list_msg.push_line(format!("Here are the words for {}:", voice));
|
|
|
|
for word in words {
|
|
list_msg.push("* ");
|
|
list_msg.push_line(word);
|
|
}
|
|
|
|
let file_data = list_msg.build();
|
|
let file_data = file_data.as_bytes();
|
|
let file_data = Cow::from(file_data);
|
|
|
|
msg.channel_id
|
|
.send_message(&ctx.http, |m| {
|
|
m.add_file(AttachmentType::Bytes {
|
|
data: file_data,
|
|
filename: "words.txt".to_string(),
|
|
})
|
|
})
|
|
.await?;
|
|
}
|
|
};
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[only_in(guilds)]
|
|
async fn list_voices(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
|
let data = ctx.data.read().await;
|
|
let global_data = data.get::<GlobalData>().unwrap();
|
|
|
|
let mut voice_message = MessageBuilder::new();
|
|
|
|
for voice in get_voices(&global_data.cfg.voice_path).await? {
|
|
voice_message.push("* ");
|
|
voice_message.push_line(voice);
|
|
}
|
|
|
|
msg.reply(&ctx.http, voice_message.build()).await?;
|
|
|
|
Ok(())
|
|
}
|