mirror of
https://github.com/mii443/ncb-tts-r2.git
synced 2025-08-22 16:15:29 +00:00
add tracing opentelemetry
This commit is contained in:
@ -18,6 +18,12 @@ regex = "1"
|
||||
tracing-subscriber = "0.3.19"
|
||||
lru = "0.13.0"
|
||||
tracing = "0.1.41"
|
||||
opentelemetry_sdk = { version = "0.29.0", features = ["trace"] }
|
||||
opentelemetry = "0.29.1"
|
||||
opentelemetry-semantic-conventions = "0.29.0"
|
||||
opentelemetry-otlp = { version = "0.29.0", features = ["grpc-tonic"] }
|
||||
opentelemetry-stdout = "0.29.0"
|
||||
tracing-opentelemetry = "0.30.0"
|
||||
|
||||
[dependencies.uuid]
|
||||
version = "0.8"
|
||||
|
@ -1,6 +1,8 @@
|
||||
use serenity::{
|
||||
all::{
|
||||
ButtonStyle, CommandInteraction, CreateActionRow, CreateButton, CreateInteractionResponse, CreateInteractionResponseMessage, CreateSelectMenu, CreateSelectMenuKind, CreateSelectMenuOption
|
||||
ButtonStyle, CommandInteraction, CreateActionRow, CreateButton, CreateInteractionResponse,
|
||||
CreateInteractionResponseMessage, CreateSelectMenu, CreateSelectMenuKind,
|
||||
CreateSelectMenuOption,
|
||||
},
|
||||
prelude::Context,
|
||||
};
|
||||
@ -10,6 +12,7 @@ use crate::{
|
||||
tts::tts_type::TTSType,
|
||||
};
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn config_command(
|
||||
ctx: &Context,
|
||||
command: &CommandInteraction,
|
||||
@ -38,19 +41,23 @@ pub async fn config_command(
|
||||
let tts_type = config.tts_type.unwrap_or(TTSType::GCP);
|
||||
|
||||
let engine_select = CreateActionRow::SelectMenu(
|
||||
CreateSelectMenu::new("TTS_CONFIG_ENGINE", CreateSelectMenuKind::String { options: vec![
|
||||
CreateSelectMenu::new(
|
||||
"TTS_CONFIG_ENGINE",
|
||||
CreateSelectMenuKind::String {
|
||||
options: vec![
|
||||
CreateSelectMenuOption::new("Google TTS", "TTS_CONFIG_ENGINE_SELECTED_GOOGLE")
|
||||
.default_selection(tts_type == TTSType::GCP),
|
||||
CreateSelectMenuOption::new("VOICEVOX", "TTS_CONFIG_ENGINE_SELECTED_VOICEVOX")
|
||||
.default_selection(tts_type == TTSType::VOICEVOX)
|
||||
] }).placeholder("読み上げAPIを選択")
|
||||
.default_selection(tts_type == TTSType::VOICEVOX),
|
||||
],
|
||||
},
|
||||
)
|
||||
.placeholder("読み上げAPIを選択"),
|
||||
);
|
||||
|
||||
let server_button = CreateActionRow::Buttons(vec![
|
||||
CreateButton::new("TTS_CONFIG_SERVER")
|
||||
let server_button = CreateActionRow::Buttons(vec![CreateButton::new("TTS_CONFIG_SERVER")
|
||||
.label("サーバー設定")
|
||||
.style(ButtonStyle::Primary)
|
||||
]);
|
||||
.style(ButtonStyle::Primary)]);
|
||||
|
||||
let mut components = vec![engine_select, server_button];
|
||||
|
||||
@ -61,29 +68,31 @@ pub async fn config_command(
|
||||
options.push(
|
||||
CreateSelectMenuOption::new(
|
||||
name,
|
||||
format!("TTS_CONFIG_VOICEVOX_SPEAKER_SELECTED_{}", id)
|
||||
).default_selection(*id == voicevox_speaker)
|
||||
format!("TTS_CONFIG_VOICEVOX_SPEAKER_SELECTED_{}", id),
|
||||
)
|
||||
.default_selection(*id == voicevox_speaker),
|
||||
);
|
||||
}
|
||||
|
||||
components.push(
|
||||
CreateActionRow::SelectMenu(
|
||||
components.push(CreateActionRow::SelectMenu(
|
||||
CreateSelectMenu::new(
|
||||
format!("TTS_CONFIG_VOICEVOX_SPEAKER_{}", index),
|
||||
CreateSelectMenuKind::String { options }
|
||||
).placeholder("VOICEVOX Speakerを指定")
|
||||
CreateSelectMenuKind::String { options },
|
||||
)
|
||||
);
|
||||
.placeholder("VOICEVOX Speakerを指定"),
|
||||
));
|
||||
}
|
||||
|
||||
command
|
||||
.create_response(&ctx.http,
|
||||
.create_response(
|
||||
&ctx.http,
|
||||
CreateInteractionResponse::Message(
|
||||
CreateInteractionResponseMessage::new()
|
||||
.content("読み上げ設定")
|
||||
.components(components)
|
||||
.ephemeral(true)
|
||||
))
|
||||
.ephemeral(true),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
@ -5,17 +5,19 @@ use serenity::{
|
||||
model::prelude::UserId,
|
||||
prelude::Context,
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
data::{TTSClientData, TTSData},
|
||||
tts::instance::TTSInstance,
|
||||
};
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn setup_command(
|
||||
ctx: &Context,
|
||||
command: &CommandInteraction,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Received event");
|
||||
info!("Received event");
|
||||
|
||||
if command.guild_id.is_none() {
|
||||
command
|
||||
@ -29,7 +31,7 @@ pub async fn setup_command(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("Fetching guild cache");
|
||||
info!("Fetching guild cache");
|
||||
let guild_id = command.guild_id.unwrap();
|
||||
let guild = guild_id.to_guild_cached(&ctx.cache).unwrap().clone();
|
||||
|
||||
|
@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
database::database::Database,
|
||||
tts::tts::TTS,
|
||||
};
|
||||
use crate::{database::database::Database, tts::tts::TTS};
|
||||
use serenity::{
|
||||
futures::lock::Mutex,
|
||||
model::id::GuildId,
|
||||
|
@ -5,6 +5,7 @@ use crate::tts::{
|
||||
use super::{dictionary::Dictionary, server_config::ServerConfig, user_config::UserConfig};
|
||||
use redis::Commands;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Database {
|
||||
pub client: redis::Client,
|
||||
}
|
||||
@ -14,6 +15,7 @@ impl Database {
|
||||
Self { client }
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn get_server_config(
|
||||
&mut self,
|
||||
server_id: u64,
|
||||
@ -32,6 +34,7 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn get_user_config(
|
||||
&mut self,
|
||||
user_id: u64,
|
||||
@ -50,6 +53,7 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn set_server_config(
|
||||
&mut self,
|
||||
server_id: u64,
|
||||
@ -64,6 +68,7 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn set_user_config(
|
||||
&mut self,
|
||||
user_id: u64,
|
||||
@ -78,13 +83,17 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn set_default_server_config(&mut self, server_id: u64) -> redis::RedisResult<()> {
|
||||
let config = ServerConfig {
|
||||
dictionary: Dictionary::new(),
|
||||
autostart_channel_id: None,
|
||||
};
|
||||
|
||||
self.client.get_connection().unwrap().set::<String, String, ()>(
|
||||
self.client
|
||||
.get_connection()
|
||||
.unwrap()
|
||||
.set::<String, String, ()>(
|
||||
format!("discord_server:{}", server_id),
|
||||
serde_json::to_string(&config).unwrap(),
|
||||
)?;
|
||||
@ -92,6 +101,7 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn set_default_user_config(&mut self, user_id: u64) -> redis::RedisResult<()> {
|
||||
let voice_selection = VoiceSelectionParams {
|
||||
languageCode: String::from("ja-JP"),
|
||||
@ -107,7 +117,10 @@ impl Database {
|
||||
voicevox_speaker: Some(1),
|
||||
};
|
||||
|
||||
self.client.get_connection().unwrap().set::<String, String, ()>(
|
||||
self.client
|
||||
.get_connection()
|
||||
.unwrap()
|
||||
.set::<String, String, ()>(
|
||||
format!("discord_user:{}", user_id),
|
||||
serde_json::to_string(&config).unwrap(),
|
||||
)?;
|
||||
@ -115,6 +128,7 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn get_server_config_or_default(
|
||||
&mut self,
|
||||
server_id: u64,
|
||||
@ -129,6 +143,7 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn get_user_config_or_default(
|
||||
&mut self,
|
||||
user_id: u64,
|
||||
|
@ -8,19 +8,31 @@ use crate::{
|
||||
tts::tts_type::TTSType,
|
||||
};
|
||||
use serenity::{
|
||||
all::{ActionRowComponent, ButtonStyle, ComponentInteractionDataKind, CreateActionRow, CreateButton, CreateEmbed, CreateInputText, CreateInteractionResponse, CreateInteractionResponseMessage, CreateModal, CreateSelectMenu, CreateSelectMenuKind, CreateSelectMenuOption, InputTextStyle}, async_trait, client::{Context, EventHandler}, model::{
|
||||
application::Interaction, channel::Message, gateway::Ready, prelude::ChannelType, voice::VoiceState
|
||||
}
|
||||
all::{
|
||||
ActionRowComponent, ButtonStyle, ComponentInteractionDataKind, CreateActionRow,
|
||||
CreateButton, CreateEmbed, CreateInputText, CreateInteractionResponse,
|
||||
CreateInteractionResponseMessage, CreateModal, CreateSelectMenu, CreateSelectMenuKind,
|
||||
CreateSelectMenuOption, InputTextStyle,
|
||||
},
|
||||
async_trait,
|
||||
client::{Context, EventHandler},
|
||||
model::{
|
||||
application::Interaction, channel::Message, gateway::Ready, prelude::ChannelType,
|
||||
voice::VoiceState,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Handler;
|
||||
|
||||
#[async_trait]
|
||||
impl EventHandler for Handler {
|
||||
#[tracing::instrument]
|
||||
async fn message(&self, ctx: Context, message: Message) {
|
||||
events::message_receive::message(ctx, message).await
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
async fn ready(&self, ctx: Context, ready: Ready) {
|
||||
events::ready::ready(ctx, ready).await
|
||||
}
|
||||
@ -95,10 +107,15 @@ impl EventHandler for Handler {
|
||||
.await
|
||||
.unwrap();
|
||||
modal
|
||||
.create_response(&ctx.http, CreateInteractionResponse::UpdateMessage(CreateInteractionResponseMessage::new().content(format!(
|
||||
.create_response(
|
||||
&ctx.http,
|
||||
CreateInteractionResponse::UpdateMessage(
|
||||
CreateInteractionResponseMessage::new().content(format!(
|
||||
"辞書を追加しました\n名前: {}\n変換元: {}\n変換後: {}",
|
||||
rule_name, from, to
|
||||
))))
|
||||
)),
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@ -106,12 +123,16 @@ impl EventHandler for Handler {
|
||||
if let Some(message_component) = interaction.message_component() {
|
||||
match &*message_component.data.custom_id {
|
||||
"TTS_CONFIG_SERVER_REMOVE_DICTIONARY_MENU" => {
|
||||
let i = usize::from_str_radix(&match message_component.data.kind {
|
||||
let i = usize::from_str_radix(
|
||||
&match message_component.data.kind {
|
||||
ComponentInteractionDataKind::StringSelect { ref values, .. } => {
|
||||
values[0].clone()
|
||||
}
|
||||
_ => panic!("Cannot get index"),
|
||||
}, 10).unwrap();
|
||||
},
|
||||
10,
|
||||
)
|
||||
.unwrap();
|
||||
let data_read = ctx.data.read().await;
|
||||
|
||||
let mut config = {
|
||||
@ -141,8 +162,13 @@ impl EventHandler for Handler {
|
||||
}
|
||||
|
||||
message_component
|
||||
.create_response(&ctx,
|
||||
CreateInteractionResponse::UpdateMessage(CreateInteractionResponseMessage::new().content("辞書を削除しました")))
|
||||
.create_response(
|
||||
&ctx,
|
||||
CreateInteractionResponse::UpdateMessage(
|
||||
CreateInteractionResponseMessage::new()
|
||||
.content("辞書を削除しました"),
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@ -163,24 +189,25 @@ impl EventHandler for Handler {
|
||||
};
|
||||
|
||||
message_component
|
||||
.create_response(&ctx.http,
|
||||
.create_response(
|
||||
&ctx.http,
|
||||
CreateInteractionResponse::UpdateMessage(
|
||||
CreateInteractionResponseMessage::new()
|
||||
.content("削除する辞書内容を選択してください")
|
||||
.components(vec![
|
||||
CreateActionRow::SelectMenu(
|
||||
.components(vec![CreateActionRow::SelectMenu(
|
||||
CreateSelectMenu::new(
|
||||
"TTS_CONFIG_SERVER_REMOVE_DICTIONARY_MENU",
|
||||
CreateSelectMenuKind::String {
|
||||
options: {
|
||||
let mut options = vec![];
|
||||
for (i, rule) in config
|
||||
.dictionary
|
||||
.rules
|
||||
.iter()
|
||||
.enumerate()
|
||||
for (i, rule) in
|
||||
config.dictionary.rules.iter().enumerate()
|
||||
{
|
||||
let option = CreateSelectMenuOption::new(rule.id.clone(), i.to_string()).description(format!(
|
||||
let option = CreateSelectMenuOption::new(
|
||||
rule.id.clone(),
|
||||
i.to_string(),
|
||||
)
|
||||
.description(format!(
|
||||
"{} -> {}",
|
||||
rule.rule.clone(),
|
||||
rule.to.clone()
|
||||
@ -188,13 +215,14 @@ impl EventHandler for Handler {
|
||||
options.push(option);
|
||||
}
|
||||
options
|
||||
}
|
||||
})
|
||||
.max_values(1)
|
||||
.min_values(0)
|
||||
},
|
||||
},
|
||||
)
|
||||
.max_values(1)
|
||||
.min_values(0),
|
||||
)]),
|
||||
),
|
||||
)
|
||||
])
|
||||
))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@ -214,12 +242,11 @@ impl EventHandler for Handler {
|
||||
};
|
||||
|
||||
message_component
|
||||
.create_response(&ctx.http, CreateInteractionResponse::UpdateMessage(
|
||||
CreateInteractionResponseMessage::new()
|
||||
.content("")
|
||||
.embed(CreateEmbed::new()
|
||||
.title("辞書一覧")
|
||||
.fields({
|
||||
.create_response(
|
||||
&ctx.http,
|
||||
CreateInteractionResponse::UpdateMessage(
|
||||
CreateInteractionResponseMessage::new().content("").embed(
|
||||
CreateEmbed::new().title("辞書一覧").fields({
|
||||
let mut fields = vec![];
|
||||
for rule in config.dictionary.rules {
|
||||
let field = (
|
||||
@ -230,14 +257,17 @@ impl EventHandler for Handler {
|
||||
fields.push(field);
|
||||
}
|
||||
fields
|
||||
}))
|
||||
))
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
"TTS_CONFIG_SERVER_ADD_DICTIONARY_BUTTON" => {
|
||||
message_component
|
||||
.create_response(&ctx.http,
|
||||
.create_response(
|
||||
&ctx.http,
|
||||
CreateInteractionResponse::Modal(
|
||||
CreateModal::new("TTS_CONFIG_SERVER_ADD_DICTIONARY", "辞書追加")
|
||||
.components({
|
||||
@ -246,29 +276,30 @@ impl EventHandler for Handler {
|
||||
CreateInputText::new(
|
||||
InputTextStyle::Short,
|
||||
"rule_name",
|
||||
"辞書名"
|
||||
"辞書名",
|
||||
)
|
||||
.required(true)
|
||||
.required(true),
|
||||
),
|
||||
CreateActionRow::InputText(
|
||||
CreateInputText::new(
|
||||
InputTextStyle::Paragraph,
|
||||
"from",
|
||||
"変換元(正規表現)"
|
||||
"変換元(正規表現)",
|
||||
)
|
||||
.required(true)
|
||||
.required(true),
|
||||
),
|
||||
CreateActionRow::InputText(
|
||||
CreateInputText::new(
|
||||
InputTextStyle::Short,
|
||||
"to",
|
||||
"変換先"
|
||||
)
|
||||
.required(true)
|
||||
"変換先",
|
||||
)
|
||||
.required(true),
|
||||
),
|
||||
]
|
||||
})
|
||||
))
|
||||
}),
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@ -278,7 +309,13 @@ impl EventHandler for Handler {
|
||||
if values.len() == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(u64::from_str_radix(&values[0].strip_prefix("SET_AUTOSTART_CHANNEL_").unwrap(), 10).unwrap())
|
||||
Some(
|
||||
u64::from_str_radix(
|
||||
&values[0].strip_prefix("SET_AUTOSTART_CHANNEL_").unwrap(),
|
||||
10,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
_ => panic!("Cannot get index"),
|
||||
@ -303,7 +340,13 @@ impl EventHandler for Handler {
|
||||
};
|
||||
|
||||
message_component
|
||||
.create_response(&ctx.http, CreateInteractionResponse::UpdateMessage(CreateInteractionResponseMessage::new().content("自動参加チャンネルを設定しました。")))
|
||||
.create_response(
|
||||
&ctx.http,
|
||||
CreateInteractionResponse::UpdateMessage(
|
||||
CreateInteractionResponseMessage::new()
|
||||
.content("自動参加チャンネルを設定しました。"),
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@ -337,10 +380,12 @@ impl EventHandler for Handler {
|
||||
continue;
|
||||
}
|
||||
|
||||
let description = channel.topic.unwrap_or_else(|| String::from("No topic provided."));
|
||||
let description = channel
|
||||
.topic
|
||||
.unwrap_or_else(|| String::from("No topic provided."));
|
||||
let option = CreateSelectMenuOption::new(
|
||||
&channel.name,
|
||||
format!("SET_AUTOSTART_CHANNEL_{}", id.get())
|
||||
format!("SET_AUTOSTART_CHANNEL_{}", id.get()),
|
||||
)
|
||||
.description(description)
|
||||
.default_selection(channel.id.get() == autostart_channel_id);
|
||||
@ -349,54 +394,64 @@ impl EventHandler for Handler {
|
||||
}
|
||||
|
||||
message_component
|
||||
.create_response(&ctx.http,
|
||||
.create_response(
|
||||
&ctx.http,
|
||||
CreateInteractionResponse::UpdateMessage(
|
||||
CreateInteractionResponseMessage::new()
|
||||
.content("自動参加チャンネル設定")
|
||||
.components(vec![
|
||||
CreateActionRow::SelectMenu(
|
||||
.components(vec![CreateActionRow::SelectMenu(
|
||||
CreateSelectMenu::new(
|
||||
"SET_AUTOSTART_CHANNEL",
|
||||
CreateSelectMenuKind::String { options }
|
||||
CreateSelectMenuKind::String { options },
|
||||
)
|
||||
.min_values(0)
|
||||
.max_values(1)
|
||||
.max_values(1),
|
||||
)]),
|
||||
),
|
||||
)
|
||||
])
|
||||
))
|
||||
.await
|
||||
.unwrap();
|
||||
},
|
||||
}
|
||||
"TTS_CONFIG_SERVER" => {
|
||||
message_component
|
||||
.create_response(&ctx.http,
|
||||
.create_response(
|
||||
&ctx.http,
|
||||
CreateInteractionResponse::UpdateMessage(
|
||||
CreateInteractionResponseMessage::new()
|
||||
.content("サーバー設定")
|
||||
.components(vec![
|
||||
CreateActionRow::Buttons(vec![
|
||||
CreateButton::new("TTS_CONFIG_SERVER_ADD_DICTIONARY_BUTTON")
|
||||
.components(vec![CreateActionRow::Buttons(vec![
|
||||
CreateButton::new(
|
||||
"TTS_CONFIG_SERVER_ADD_DICTIONARY_BUTTON",
|
||||
)
|
||||
.label("辞書を追加")
|
||||
.style(ButtonStyle::Primary),
|
||||
CreateButton::new("TTS_CONFIG_SERVER_REMOVE_DICTIONARY_BUTTON")
|
||||
CreateButton::new(
|
||||
"TTS_CONFIG_SERVER_REMOVE_DICTIONARY_BUTTON",
|
||||
)
|
||||
.label("辞書を削除")
|
||||
.style(ButtonStyle::Danger),
|
||||
CreateButton::new("TTS_CONFIG_SERVER_SHOW_DICTIONARY_BUTTON")
|
||||
CreateButton::new(
|
||||
"TTS_CONFIG_SERVER_SHOW_DICTIONARY_BUTTON",
|
||||
)
|
||||
.label("辞書一覧")
|
||||
.style(ButtonStyle::Primary),
|
||||
CreateButton::new("TTS_CONFIG_SERVER_SET_AUTOSTART_CHANNEL")
|
||||
CreateButton::new(
|
||||
"TTS_CONFIG_SERVER_SET_AUTOSTART_CHANNEL",
|
||||
)
|
||||
.label("自動参加チャンネル")
|
||||
.style(ButtonStyle::Primary)
|
||||
])
|
||||
])
|
||||
))
|
||||
.style(ButtonStyle::Primary),
|
||||
])]),
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
match message_component.data.kind {
|
||||
ComponentInteractionDataKind::StringSelect { ref values, .. } if !values.is_empty() => {
|
||||
ComponentInteractionDataKind::StringSelect { ref values, .. }
|
||||
if !values.is_empty() =>
|
||||
{
|
||||
let res = &values[0].clone();
|
||||
let data_read = ctx.data.read().await;
|
||||
|
||||
@ -450,25 +505,28 @@ impl EventHandler for Handler {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let response_content = if voicevox_changed && config.tts_type.unwrap_or(TTSType::GCP) == TTSType::GCP {
|
||||
let response_content = if voicevox_changed
|
||||
&& config.tts_type.unwrap_or(TTSType::GCP) == TTSType::GCP
|
||||
{
|
||||
"設定しました\nこの音声を使うにはAPIをGoogleからVOICEVOXに変更する必要があります。"
|
||||
} else {
|
||||
"設定しました"
|
||||
};
|
||||
|
||||
message_component
|
||||
.create_response(&ctx.http,
|
||||
.create_response(
|
||||
&ctx.http,
|
||||
CreateInteractionResponse::Message(
|
||||
CreateInteractionResponseMessage::new()
|
||||
.content(response_content)
|
||||
.ephemeral(true)
|
||||
))
|
||||
.ephemeral(true),
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,33 @@
|
||||
use serenity::{
|
||||
all::{Command, CommandOptionType, CreateCommand, CreateCommandOption}, model::prelude::Ready, prelude::Context
|
||||
all::{Command, CommandOptionType, CreateCommand, CreateCommandOption},
|
||||
model::prelude::Ready,
|
||||
prelude::Context,
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn ready(ctx: Context, ready: Ready) {
|
||||
println!("{} is connected!", ready.user.name);
|
||||
info!("{} is connected!", ready.user.name);
|
||||
|
||||
Command::set_global_commands(&ctx.http, vec![
|
||||
Command::set_global_commands(
|
||||
&ctx.http,
|
||||
vec![
|
||||
CreateCommand::new("stop").description("Stop tts"),
|
||||
CreateCommand::new("setup").description("Setup tts").set_options(vec![
|
||||
CreateCommandOption::new(CommandOptionType::String, "mode", "TTS channel")
|
||||
CreateCommand::new("setup")
|
||||
.description("Setup tts")
|
||||
.set_options(vec![CreateCommandOption::new(
|
||||
CommandOptionType::String,
|
||||
"mode",
|
||||
"TTS channel",
|
||||
)
|
||||
.add_string_choice("Text Channel", "TEXT_CHANNEL")
|
||||
.add_string_choice("New Thread", "NEW_THREAD")
|
||||
.add_string_choice("Voice Channel", "VOICE_CHANNEL")
|
||||
.required(false)
|
||||
]),
|
||||
.required(false)]),
|
||||
CreateCommand::new("config").description("Config"),
|
||||
CreateCommand::new("skip").description("skip tts message"),
|
||||
]).await.unwrap();
|
||||
],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use serenity::model::{
|
||||
guild::{Member, PartialMember},
|
||||
user::User,
|
||||
guild::{Member, PartialMember}
|
||||
};
|
||||
|
||||
pub trait ReadName {
|
||||
@ -15,7 +15,9 @@ impl ReadName for Member {
|
||||
|
||||
impl ReadName for PartialMember {
|
||||
fn read_name(&self) -> String {
|
||||
self.nick.clone().unwrap_or(self.user.as_ref().unwrap().display_name().to_string())
|
||||
self.nick
|
||||
.clone()
|
||||
.unwrap_or(self.user.as_ref().unwrap().display_name().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,9 @@ use serenity::{model::prelude::Message, prelude::Context};
|
||||
use songbird::input::cached::Compressed;
|
||||
|
||||
use crate::{
|
||||
data::{DatabaseClientData, TTSClientData}, implement::member_name::ReadName, tts::{
|
||||
data::{DatabaseClientData, TTSClientData},
|
||||
implement::member_name::ReadName,
|
||||
tts::{
|
||||
gcp_tts::structs::{
|
||||
audio_config::AudioConfig, synthesis_input::SynthesisInput,
|
||||
synthesize_request::SynthesizeRequest,
|
||||
@ -12,7 +14,7 @@ use crate::{
|
||||
instance::TTSInstance,
|
||||
message::TTSMessage,
|
||||
tts_type::TTSType,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
@ -48,7 +50,11 @@ impl TTSMessage for Message {
|
||||
let member = self.member.clone();
|
||||
let name = if let Some(_) = member {
|
||||
let guild = ctx.cache.guild(self.guild_id.unwrap()).unwrap().clone();
|
||||
guild.member(&ctx.http, self.author.id).await.unwrap().read_name()
|
||||
guild
|
||||
.member(&ctx.http, self.author.id)
|
||||
.await
|
||||
.unwrap()
|
||||
.read_name()
|
||||
} else {
|
||||
self.author.read_name()
|
||||
};
|
||||
@ -58,7 +64,11 @@ impl TTSMessage for Message {
|
||||
let member = self.member.clone();
|
||||
let name = if let Some(_) = member {
|
||||
let guild = ctx.cache.guild(self.guild_id.unwrap()).unwrap().clone();
|
||||
guild.member(&ctx.http, self.author.id).await.unwrap().read_name()
|
||||
guild
|
||||
.member(&ctx.http, self.author.id)
|
||||
.await
|
||||
.unwrap()
|
||||
.read_name()
|
||||
} else {
|
||||
self.author.read_name()
|
||||
};
|
||||
|
14
src/main.rs
14
src/main.rs
@ -5,6 +5,7 @@ mod database;
|
||||
mod event_handler;
|
||||
mod events;
|
||||
mod implement;
|
||||
mod trace;
|
||||
mod tts;
|
||||
|
||||
use std::{collections::HashMap, env, sync::Arc};
|
||||
@ -14,9 +15,14 @@ use data::{DatabaseClientData, TTSClientData, TTSData};
|
||||
use database::database::Database;
|
||||
use event_handler::Handler;
|
||||
use serenity::{
|
||||
all::{standard::Configuration, ApplicationId}, client::Client, framework::StandardFramework, futures::lock::Mutex, prelude::{GatewayIntents, RwLock}
|
||||
all::{standard::Configuration, ApplicationId},
|
||||
client::Client,
|
||||
framework::StandardFramework,
|
||||
futures::lock::Mutex,
|
||||
prelude::{GatewayIntents, RwLock},
|
||||
};
|
||||
use tracing::Level;
|
||||
use trace::init_tracing_subscriber;
|
||||
use tracing::info;
|
||||
use tts::{gcp_tts::gcp_tts::GCPTTS, tts::TTS, voicevox::voicevox::VOICEVOX};
|
||||
|
||||
use songbird::SerenityInit;
|
||||
@ -43,7 +49,7 @@ async fn create_client(prefix: &str, token: &str, id: u64) -> Result<Client, ser
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_subscriber::fmt().with_max_level(Level::INFO).init();
|
||||
let _guard = init_tracing_subscriber();
|
||||
// Load config
|
||||
let config = {
|
||||
let config = std::fs::read_to_string("./config.toml");
|
||||
@ -92,6 +98,8 @@ async fn main() {
|
||||
data.insert::<DatabaseClientData>(Arc::new(Mutex::new(database_client)));
|
||||
}
|
||||
|
||||
info!("Bot initialized.");
|
||||
|
||||
// Run client
|
||||
if let Err(why) = client.start().await {
|
||||
println!("Client error: {:?}", why);
|
||||
|
116
src/trace.rs
Normal file
116
src/trace.rs
Normal file
@ -0,0 +1,116 @@
|
||||
use opentelemetry::{
|
||||
global,
|
||||
trace::{SamplingDecision, SamplingResult, TraceContextExt, TraceState, TracerProvider as _},
|
||||
KeyValue,
|
||||
};
|
||||
use opentelemetry_sdk::{
|
||||
metrics::{MeterProviderBuilder, PeriodicReader, SdkMeterProvider},
|
||||
trace::{RandomIdGenerator, SdkTracerProvider, ShouldSample},
|
||||
Resource,
|
||||
};
|
||||
use opentelemetry_semantic_conventions::{
|
||||
resource::{SERVICE_NAME, SERVICE_VERSION},
|
||||
SCHEMA_URL,
|
||||
};
|
||||
use tracing::Level;
|
||||
use tracing_opentelemetry::{MetricsLayer, OpenTelemetryLayer};
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct FilterSampler;
|
||||
|
||||
impl ShouldSample for FilterSampler {
|
||||
fn should_sample(
|
||||
&self,
|
||||
parent_context: Option<&opentelemetry::Context>,
|
||||
_trace_id: opentelemetry::TraceId,
|
||||
name: &str,
|
||||
_span_kind: &opentelemetry::trace::SpanKind,
|
||||
_attributes: &[KeyValue],
|
||||
_links: &[opentelemetry::trace::Link],
|
||||
) -> opentelemetry::trace::SamplingResult {
|
||||
let decision = if name == "dispatch" || name == "recv_event" {
|
||||
SamplingDecision::Drop
|
||||
} else {
|
||||
SamplingDecision::RecordAndSample
|
||||
};
|
||||
|
||||
SamplingResult {
|
||||
decision,
|
||||
attributes: vec![],
|
||||
trace_state: match parent_context {
|
||||
Some(ctx) => ctx.span().span_context().trace_state().clone(),
|
||||
None => TraceState::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resource() -> Resource {
|
||||
Resource::builder().with_service_name("ncb-tts-r2").build()
|
||||
}
|
||||
|
||||
fn init_meter_provider() -> SdkMeterProvider {
|
||||
let exporter = opentelemetry_otlp::MetricExporter::builder()
|
||||
.with_http()
|
||||
.with_temporality(opentelemetry_sdk::metrics::Temporality::default())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let reader = PeriodicReader::builder(exporter)
|
||||
.with_interval(std::time::Duration::from_secs(5))
|
||||
.build();
|
||||
|
||||
let stdout_reader =
|
||||
PeriodicReader::builder(opentelemetry_stdout::MetricExporter::default()).build();
|
||||
|
||||
let meter_provider = MeterProviderBuilder::default()
|
||||
.with_resource(resource())
|
||||
.with_reader(reader)
|
||||
.with_reader(stdout_reader)
|
||||
.build();
|
||||
|
||||
global::set_meter_provider(meter_provider.clone());
|
||||
|
||||
meter_provider
|
||||
}
|
||||
|
||||
fn init_tracer_provider() -> SdkTracerProvider {
|
||||
let exporter = opentelemetry_otlp::SpanExporter::builder()
|
||||
.with_http()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
SdkTracerProvider::builder()
|
||||
.with_sampler(FilterSampler)
|
||||
.with_id_generator(RandomIdGenerator::default())
|
||||
.with_resource(resource())
|
||||
.with_batch_exporter(exporter)
|
||||
.build()
|
||||
}
|
||||
|
||||
pub fn init_tracing_subscriber() -> OtelGuard {
|
||||
let tracer_provider = init_tracer_provider();
|
||||
let meter_provider = init_meter_provider();
|
||||
|
||||
let tracer = tracer_provider.tracer("ncb-tts-r2");
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(tracing_subscriber::filter::LevelFilter::from_level(
|
||||
Level::INFO,
|
||||
))
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.with(MetricsLayer::new(meter_provider.clone()))
|
||||
.with(OpenTelemetryLayer::new(tracer))
|
||||
.init();
|
||||
|
||||
OtelGuard {
|
||||
_tracer_provider: tracer_provider,
|
||||
_meter_provider: meter_provider,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OtelGuard {
|
||||
_tracer_provider: SdkTracerProvider,
|
||||
_meter_provider: SdkMeterProvider,
|
||||
}
|
@ -1,17 +1,18 @@
|
||||
use tokio::sync::RwLock;
|
||||
use std::sync::Arc;
|
||||
use crate::tts::gcp_tts::structs::{
|
||||
synthesize_request::SynthesizeRequest, synthesize_response::SynthesizeResponse,
|
||||
};
|
||||
use gcp_auth::Token;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GCPTTS {
|
||||
pub token: Arc<RwLock<Token>>,
|
||||
pub credentials_path: String,
|
||||
}
|
||||
|
||||
impl GCPTTS {
|
||||
#[tracing::instrument]
|
||||
pub async fn update_token(&self) -> Result<(), gcp_auth::Error> {
|
||||
let mut token = self.token.write().await;
|
||||
if token.has_expired() {
|
||||
@ -26,6 +27,7 @@ impl GCPTTS {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn new(credentials_path: String) -> Result<Self, gcp_auth::Error> {
|
||||
let authenticator = gcp_auth::from_credentials_file(credentials_path.clone()).await?;
|
||||
let token = authenticator
|
||||
@ -59,6 +61,7 @@ impl GCPTTS {
|
||||
/// }
|
||||
/// }).await.unwrap();
|
||||
/// ```
|
||||
#[tracing::instrument]
|
||||
pub async fn synthesize(
|
||||
&self,
|
||||
request: SynthesizeRequest,
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use serenity::{
|
||||
model::{
|
||||
channel::Message,
|
||||
@ -8,6 +10,7 @@ use serenity::{
|
||||
|
||||
use crate::tts::message::TTSMessage;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TTSInstance {
|
||||
pub before_message: Option<Message>,
|
||||
pub text_channel: ChannelId,
|
||||
@ -22,9 +25,10 @@ impl TTSInstance {
|
||||
/// ```rust
|
||||
/// instance.read(message, &ctx).await;
|
||||
/// ```
|
||||
#[tracing::instrument]
|
||||
pub async fn read<T>(&mut self, message: T, ctx: &Context)
|
||||
where
|
||||
T: TTSMessage,
|
||||
T: TTSMessage + Debug,
|
||||
{
|
||||
let audio = message.synthesize(self, ctx).await;
|
||||
|
||||
@ -38,6 +42,7 @@ impl TTSInstance {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn skip(&mut self, ctx: &Context) {
|
||||
let manager = songbird::get(&ctx).await.unwrap();
|
||||
let call = manager.get(self.guild).unwrap();
|
||||
|
@ -29,6 +29,7 @@ pub trait TTSMessage {
|
||||
async fn synthesize(&self, instance: &mut TTSInstance, ctx: &Context) -> Vec<Compressed>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AnnounceMessage {
|
||||
pub message: String,
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
pub mod gcp_tts;
|
||||
pub mod instance;
|
||||
pub mod message;
|
||||
pub mod tts;
|
||||
pub mod tts_type;
|
||||
pub mod voicevox;
|
||||
pub mod tts;
|
@ -5,8 +5,18 @@ use lru::LruCache;
|
||||
use songbird::{driver::Bitrate, input::cached::Compressed};
|
||||
use tracing::info;
|
||||
|
||||
use super::{gcp_tts::{gcp_tts::GCPTTS, structs::{synthesis_input::SynthesisInput, synthesize_request::SynthesizeRequest, voice_selection_params::VoiceSelectionParams}}, voicevox::voicevox::VOICEVOX};
|
||||
use super::{
|
||||
gcp_tts::{
|
||||
gcp_tts::GCPTTS,
|
||||
structs::{
|
||||
synthesis_input::SynthesisInput, synthesize_request::SynthesizeRequest,
|
||||
voice_selection_params::VoiceSelectionParams,
|
||||
},
|
||||
},
|
||||
voicevox::voicevox::VOICEVOX,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TTS {
|
||||
pub voicevox_client: VOICEVOX,
|
||||
gcp_tts_client: GCPTTS,
|
||||
@ -20,10 +30,7 @@ pub enum CacheKey {
|
||||
}
|
||||
|
||||
impl TTS {
|
||||
pub fn new(
|
||||
voicevox_client: VOICEVOX,
|
||||
gcp_tts_client: GCPTTS,
|
||||
) -> Self {
|
||||
pub fn new(voicevox_client: VOICEVOX, gcp_tts_client: GCPTTS) -> Self {
|
||||
Self {
|
||||
voicevox_client,
|
||||
gcp_tts_client,
|
||||
@ -31,7 +38,12 @@ impl TTS {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn synthesize_voicevox(&self, text: &str, speaker: i64) -> Result<Compressed, Box<dyn std::error::Error>> {
|
||||
#[tracing::instrument]
|
||||
pub async fn synthesize_voicevox(
|
||||
&self,
|
||||
text: &str,
|
||||
speaker: i64,
|
||||
) -> Result<Compressed, Box<dyn std::error::Error>> {
|
||||
let cache_key = CacheKey::Voicevox(text.to_string(), speaker);
|
||||
|
||||
let cached_audio = {
|
||||
@ -46,7 +58,8 @@ impl TTS {
|
||||
|
||||
info!("Cache miss for VOICEVOX TTS");
|
||||
|
||||
let audio = self.voicevox_client
|
||||
let audio = self
|
||||
.voicevox_client
|
||||
.synthesize(text.to_string(), speaker)
|
||||
.await?;
|
||||
|
||||
@ -60,7 +73,11 @@ impl TTS {
|
||||
Ok(compressed)
|
||||
}
|
||||
|
||||
pub async fn synthesize_gcp(&self, synthesize_request: SynthesizeRequest) -> Result<Compressed, Box<dyn std::error::Error>> {
|
||||
#[tracing::instrument]
|
||||
pub async fn synthesize_gcp(
|
||||
&self,
|
||||
synthesize_request: SynthesizeRequest,
|
||||
) -> Result<Compressed, Box<dyn std::error::Error>> {
|
||||
let cache_key = CacheKey::GCP(
|
||||
synthesize_request.input.clone(),
|
||||
synthesize_request.voice.clone(),
|
||||
@ -78,9 +95,7 @@ impl TTS {
|
||||
|
||||
info!("Cache miss for GCP TTS");
|
||||
|
||||
let audio = self.gcp_tts_client
|
||||
.synthesize(synthesize_request)
|
||||
.await?;
|
||||
let audio = self.gcp_tts_client.synthesize(synthesize_request).await?;
|
||||
|
||||
let compressed = Compressed::new(audio.into(), Bitrate::Auto).await?;
|
||||
|
||||
|
@ -2,12 +2,13 @@ use super::structs::speaker::Speaker;
|
||||
|
||||
const BASE_API_URL: &str = "https://deprecatedapis.tts.quest/v2/";
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VOICEVOX {
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
impl VOICEVOX {
|
||||
#[tracing::instrument]
|
||||
pub async fn get_styles(&self) -> Vec<(String, i64)> {
|
||||
let speakers = self.get_speaker_list().await;
|
||||
let mut speaker_list = vec![];
|
||||
@ -20,6 +21,7 @@ impl VOICEVOX {
|
||||
speaker_list
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn get_speakers(&self) -> Vec<String> {
|
||||
let speakers = self.get_speaker_list().await;
|
||||
let mut speaker_list = vec![];
|
||||
@ -34,6 +36,7 @@ impl VOICEVOX {
|
||||
Self { key }
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
async fn get_speaker_list(&self) -> Vec<Speaker> {
|
||||
let client = reqwest::Client::new();
|
||||
match client
|
||||
@ -49,6 +52,7 @@ impl VOICEVOX {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn synthesize(
|
||||
&self,
|
||||
text: String,
|
||||
|
Reference in New Issue
Block a user