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"
|
tracing-subscriber = "0.3.19"
|
||||||
lru = "0.13.0"
|
lru = "0.13.0"
|
||||||
tracing = "0.1.41"
|
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]
|
[dependencies.uuid]
|
||||||
version = "0.8"
|
version = "0.8"
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use serenity::{
|
use serenity::{
|
||||||
all::{
|
all::{
|
||||||
ButtonStyle, CommandInteraction, CreateActionRow, CreateButton, CreateInteractionResponse, CreateInteractionResponseMessage, CreateSelectMenu, CreateSelectMenuKind, CreateSelectMenuOption
|
ButtonStyle, CommandInteraction, CreateActionRow, CreateButton, CreateInteractionResponse,
|
||||||
|
CreateInteractionResponseMessage, CreateSelectMenu, CreateSelectMenuKind,
|
||||||
|
CreateSelectMenuOption,
|
||||||
},
|
},
|
||||||
prelude::Context,
|
prelude::Context,
|
||||||
};
|
};
|
||||||
@ -10,6 +12,7 @@ use crate::{
|
|||||||
tts::tts_type::TTSType,
|
tts::tts_type::TTSType,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn config_command(
|
pub async fn config_command(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
command: &CommandInteraction,
|
command: &CommandInteraction,
|
||||||
@ -38,53 +41,59 @@ pub async fn config_command(
|
|||||||
let tts_type = config.tts_type.unwrap_or(TTSType::GCP);
|
let tts_type = config.tts_type.unwrap_or(TTSType::GCP);
|
||||||
|
|
||||||
let engine_select = CreateActionRow::SelectMenu(
|
let engine_select = CreateActionRow::SelectMenu(
|
||||||
CreateSelectMenu::new("TTS_CONFIG_ENGINE", CreateSelectMenuKind::String { options: vec![
|
CreateSelectMenu::new(
|
||||||
CreateSelectMenuOption::new("Google TTS", "TTS_CONFIG_ENGINE_SELECTED_GOOGLE")
|
"TTS_CONFIG_ENGINE",
|
||||||
.default_selection(tts_type == TTSType::GCP),
|
CreateSelectMenuKind::String {
|
||||||
CreateSelectMenuOption::new("VOICEVOX", "TTS_CONFIG_ENGINE_SELECTED_VOICEVOX")
|
options: vec![
|
||||||
.default_selection(tts_type == TTSType::VOICEVOX)
|
CreateSelectMenuOption::new("Google TTS", "TTS_CONFIG_ENGINE_SELECTED_GOOGLE")
|
||||||
] }).placeholder("読み上げAPIを選択")
|
.default_selection(tts_type == TTSType::GCP),
|
||||||
|
CreateSelectMenuOption::new("VOICEVOX", "TTS_CONFIG_ENGINE_SELECTED_VOICEVOX")
|
||||||
|
.default_selection(tts_type == TTSType::VOICEVOX),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.placeholder("読み上げAPIを選択"),
|
||||||
);
|
);
|
||||||
|
|
||||||
let server_button = CreateActionRow::Buttons(vec![
|
let server_button = CreateActionRow::Buttons(vec![CreateButton::new("TTS_CONFIG_SERVER")
|
||||||
CreateButton::new("TTS_CONFIG_SERVER")
|
.label("サーバー設定")
|
||||||
.label("サーバー設定")
|
.style(ButtonStyle::Primary)]);
|
||||||
.style(ButtonStyle::Primary)
|
|
||||||
]);
|
|
||||||
|
|
||||||
let mut components = vec![engine_select, server_button];
|
let mut components = vec![engine_select, server_button];
|
||||||
|
|
||||||
for (index, speaker_chunk) in voicevox_speakers[0..24].chunks(25).enumerate() {
|
for (index, speaker_chunk) in voicevox_speakers[0..24].chunks(25).enumerate() {
|
||||||
let mut options = Vec::new();
|
let mut options = Vec::new();
|
||||||
|
|
||||||
for (name, id) in speaker_chunk {
|
for (name, id) in speaker_chunk {
|
||||||
options.push(
|
options.push(
|
||||||
CreateSelectMenuOption::new(
|
CreateSelectMenuOption::new(
|
||||||
name,
|
name,
|
||||||
format!("TTS_CONFIG_VOICEVOX_SPEAKER_SELECTED_{}", id)
|
format!("TTS_CONFIG_VOICEVOX_SPEAKER_SELECTED_{}", id),
|
||||||
).default_selection(*id == voicevox_speaker)
|
)
|
||||||
|
.default_selection(*id == voicevox_speaker),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
components.push(
|
components.push(CreateActionRow::SelectMenu(
|
||||||
CreateActionRow::SelectMenu(
|
CreateSelectMenu::new(
|
||||||
CreateSelectMenu::new(
|
format!("TTS_CONFIG_VOICEVOX_SPEAKER_{}", index),
|
||||||
format!("TTS_CONFIG_VOICEVOX_SPEAKER_{}", index),
|
CreateSelectMenuKind::String { options },
|
||||||
CreateSelectMenuKind::String { options }
|
|
||||||
).placeholder("VOICEVOX Speakerを指定")
|
|
||||||
)
|
)
|
||||||
);
|
.placeholder("VOICEVOX Speakerを指定"),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
command
|
command
|
||||||
.create_response(&ctx.http,
|
.create_response(
|
||||||
|
&ctx.http,
|
||||||
CreateInteractionResponse::Message(
|
CreateInteractionResponse::Message(
|
||||||
CreateInteractionResponseMessage::new()
|
CreateInteractionResponseMessage::new()
|
||||||
.content("読み上げ設定")
|
.content("読み上げ設定")
|
||||||
.components(components)
|
.components(components)
|
||||||
.ephemeral(true)
|
.ephemeral(true),
|
||||||
))
|
),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -5,17 +5,19 @@ use serenity::{
|
|||||||
model::prelude::UserId,
|
model::prelude::UserId,
|
||||||
prelude::Context,
|
prelude::Context,
|
||||||
};
|
};
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
data::{TTSClientData, TTSData},
|
data::{TTSClientData, TTSData},
|
||||||
tts::instance::TTSInstance,
|
tts::instance::TTSInstance,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn setup_command(
|
pub async fn setup_command(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
command: &CommandInteraction,
|
command: &CommandInteraction,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("Received event");
|
info!("Received event");
|
||||||
|
|
||||||
if command.guild_id.is_none() {
|
if command.guild_id.is_none() {
|
||||||
command
|
command
|
||||||
@ -29,7 +31,7 @@ pub async fn setup_command(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Fetching guild cache");
|
info!("Fetching guild cache");
|
||||||
let guild_id = command.guild_id.unwrap();
|
let guild_id = command.guild_id.unwrap();
|
||||||
let guild = guild_id.to_guild_cached(&ctx.cache).unwrap().clone();
|
let guild = guild_id.to_guild_cached(&ctx.cache).unwrap().clone();
|
||||||
|
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
use crate::{
|
use crate::{database::database::Database, tts::tts::TTS};
|
||||||
database::database::Database,
|
|
||||||
tts::tts::TTS,
|
|
||||||
};
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
futures::lock::Mutex,
|
futures::lock::Mutex,
|
||||||
model::id::GuildId,
|
model::id::GuildId,
|
||||||
|
@ -5,6 +5,7 @@ use crate::tts::{
|
|||||||
use super::{dictionary::Dictionary, server_config::ServerConfig, user_config::UserConfig};
|
use super::{dictionary::Dictionary, server_config::ServerConfig, user_config::UserConfig};
|
||||||
use redis::Commands;
|
use redis::Commands;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
pub client: redis::Client,
|
pub client: redis::Client,
|
||||||
}
|
}
|
||||||
@ -14,6 +15,7 @@ impl Database {
|
|||||||
Self { client }
|
Self { client }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn get_server_config(
|
pub async fn get_server_config(
|
||||||
&mut self,
|
&mut self,
|
||||||
server_id: u64,
|
server_id: u64,
|
||||||
@ -32,6 +34,7 @@ impl Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn get_user_config(
|
pub async fn get_user_config(
|
||||||
&mut self,
|
&mut self,
|
||||||
user_id: u64,
|
user_id: u64,
|
||||||
@ -50,6 +53,7 @@ impl Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn set_server_config(
|
pub async fn set_server_config(
|
||||||
&mut self,
|
&mut self,
|
||||||
server_id: u64,
|
server_id: u64,
|
||||||
@ -64,6 +68,7 @@ impl Database {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn set_user_config(
|
pub async fn set_user_config(
|
||||||
&mut self,
|
&mut self,
|
||||||
user_id: u64,
|
user_id: u64,
|
||||||
@ -78,20 +83,25 @@ impl Database {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn set_default_server_config(&mut self, server_id: u64) -> redis::RedisResult<()> {
|
pub async fn set_default_server_config(&mut self, server_id: u64) -> redis::RedisResult<()> {
|
||||||
let config = ServerConfig {
|
let config = ServerConfig {
|
||||||
dictionary: Dictionary::new(),
|
dictionary: Dictionary::new(),
|
||||||
autostart_channel_id: None,
|
autostart_channel_id: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.client.get_connection().unwrap().set::<String, String, ()>(
|
self.client
|
||||||
format!("discord_server:{}", server_id),
|
.get_connection()
|
||||||
serde_json::to_string(&config).unwrap(),
|
.unwrap()
|
||||||
)?;
|
.set::<String, String, ()>(
|
||||||
|
format!("discord_server:{}", server_id),
|
||||||
|
serde_json::to_string(&config).unwrap(),
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn set_default_user_config(&mut self, user_id: u64) -> redis::RedisResult<()> {
|
pub async fn set_default_user_config(&mut self, user_id: u64) -> redis::RedisResult<()> {
|
||||||
let voice_selection = VoiceSelectionParams {
|
let voice_selection = VoiceSelectionParams {
|
||||||
languageCode: String::from("ja-JP"),
|
languageCode: String::from("ja-JP"),
|
||||||
@ -107,14 +117,18 @@ impl Database {
|
|||||||
voicevox_speaker: Some(1),
|
voicevox_speaker: Some(1),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.client.get_connection().unwrap().set::<String, String, ()>(
|
self.client
|
||||||
format!("discord_user:{}", user_id),
|
.get_connection()
|
||||||
serde_json::to_string(&config).unwrap(),
|
.unwrap()
|
||||||
)?;
|
.set::<String, String, ()>(
|
||||||
|
format!("discord_user:{}", user_id),
|
||||||
|
serde_json::to_string(&config).unwrap(),
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn get_server_config_or_default(
|
pub async fn get_server_config_or_default(
|
||||||
&mut self,
|
&mut self,
|
||||||
server_id: u64,
|
server_id: u64,
|
||||||
@ -129,6 +143,7 @@ impl Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn get_user_config_or_default(
|
pub async fn get_user_config_or_default(
|
||||||
&mut self,
|
&mut self,
|
||||||
user_id: u64,
|
user_id: u64,
|
||||||
|
@ -8,19 +8,31 @@ use crate::{
|
|||||||
tts::tts_type::TTSType,
|
tts::tts_type::TTSType,
|
||||||
};
|
};
|
||||||
use serenity::{
|
use serenity::{
|
||||||
all::{ActionRowComponent, ButtonStyle, ComponentInteractionDataKind, CreateActionRow, CreateButton, CreateEmbed, CreateInputText, CreateInteractionResponse, CreateInteractionResponseMessage, CreateModal, CreateSelectMenu, CreateSelectMenuKind, CreateSelectMenuOption, InputTextStyle}, async_trait, client::{Context, EventHandler}, model::{
|
all::{
|
||||||
application::Interaction, channel::Message, gateway::Ready, prelude::ChannelType, voice::VoiceState
|
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;
|
pub struct Handler;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl EventHandler for Handler {
|
impl EventHandler for Handler {
|
||||||
|
#[tracing::instrument]
|
||||||
async fn message(&self, ctx: Context, message: Message) {
|
async fn message(&self, ctx: Context, message: Message) {
|
||||||
events::message_receive::message(ctx, message).await
|
events::message_receive::message(ctx, message).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
async fn ready(&self, ctx: Context, ready: Ready) {
|
async fn ready(&self, ctx: Context, ready: Ready) {
|
||||||
events::ready::ready(ctx, ready).await
|
events::ready::ready(ctx, ready).await
|
||||||
}
|
}
|
||||||
@ -95,10 +107,15 @@ impl EventHandler for Handler {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
modal
|
modal
|
||||||
.create_response(&ctx.http, CreateInteractionResponse::UpdateMessage(CreateInteractionResponseMessage::new().content(format!(
|
.create_response(
|
||||||
"辞書を追加しました\n名前: {}\n変換元: {}\n変換後: {}",
|
&ctx.http,
|
||||||
rule_name, from, to
|
CreateInteractionResponse::UpdateMessage(
|
||||||
))))
|
CreateInteractionResponseMessage::new().content(format!(
|
||||||
|
"辞書を追加しました\n名前: {}\n変換元: {}\n変換後: {}",
|
||||||
|
rule_name, from, to
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -106,12 +123,16 @@ impl EventHandler for Handler {
|
|||||||
if let Some(message_component) = interaction.message_component() {
|
if let Some(message_component) = interaction.message_component() {
|
||||||
match &*message_component.data.custom_id {
|
match &*message_component.data.custom_id {
|
||||||
"TTS_CONFIG_SERVER_REMOVE_DICTIONARY_MENU" => {
|
"TTS_CONFIG_SERVER_REMOVE_DICTIONARY_MENU" => {
|
||||||
let i = usize::from_str_radix(&match message_component.data.kind {
|
let i = usize::from_str_radix(
|
||||||
ComponentInteractionDataKind::StringSelect { ref values, .. } => {
|
&match message_component.data.kind {
|
||||||
values[0].clone()
|
ComponentInteractionDataKind::StringSelect { ref values, .. } => {
|
||||||
}
|
values[0].clone()
|
||||||
_ => panic!("Cannot get index"),
|
}
|
||||||
}, 10).unwrap();
|
_ => panic!("Cannot get index"),
|
||||||
|
},
|
||||||
|
10,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let data_read = ctx.data.read().await;
|
let data_read = ctx.data.read().await;
|
||||||
|
|
||||||
let mut config = {
|
let mut config = {
|
||||||
@ -141,8 +162,13 @@ impl EventHandler for Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message_component
|
message_component
|
||||||
.create_response(&ctx,
|
.create_response(
|
||||||
CreateInteractionResponse::UpdateMessage(CreateInteractionResponseMessage::new().content("辞書を削除しました")))
|
&ctx,
|
||||||
|
CreateInteractionResponse::UpdateMessage(
|
||||||
|
CreateInteractionResponseMessage::new()
|
||||||
|
.content("辞書を削除しました"),
|
||||||
|
),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -163,38 +189,40 @@ impl EventHandler for Handler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
message_component
|
message_component
|
||||||
.create_response(&ctx.http,
|
.create_response(
|
||||||
|
&ctx.http,
|
||||||
CreateInteractionResponse::UpdateMessage(
|
CreateInteractionResponse::UpdateMessage(
|
||||||
CreateInteractionResponseMessage::new()
|
CreateInteractionResponseMessage::new()
|
||||||
.content("削除する辞書内容を選択してください")
|
.content("削除する辞書内容を選択してください")
|
||||||
.components(vec![
|
.components(vec![CreateActionRow::SelectMenu(
|
||||||
CreateActionRow::SelectMenu(
|
CreateSelectMenu::new(
|
||||||
CreateSelectMenu::new(
|
"TTS_CONFIG_SERVER_REMOVE_DICTIONARY_MENU",
|
||||||
"TTS_CONFIG_SERVER_REMOVE_DICTIONARY_MENU",
|
CreateSelectMenuKind::String {
|
||||||
CreateSelectMenuKind::String {
|
options: {
|
||||||
options: {
|
let mut options = vec![];
|
||||||
let mut options = vec![];
|
for (i, rule) in
|
||||||
for (i, rule) in config
|
config.dictionary.rules.iter().enumerate()
|
||||||
.dictionary
|
{
|
||||||
.rules
|
let option = CreateSelectMenuOption::new(
|
||||||
.iter()
|
rule.id.clone(),
|
||||||
.enumerate()
|
i.to_string(),
|
||||||
{
|
)
|
||||||
let option = CreateSelectMenuOption::new(rule.id.clone(), i.to_string()).description(format!(
|
.description(format!(
|
||||||
"{} -> {}",
|
"{} -> {}",
|
||||||
rule.rule.clone(),
|
rule.rule.clone(),
|
||||||
rule.to.clone()
|
rule.to.clone()
|
||||||
));
|
));
|
||||||
options.push(option);
|
options.push(option);
|
||||||
}
|
|
||||||
options
|
|
||||||
}
|
}
|
||||||
})
|
options
|
||||||
.max_values(1)
|
},
|
||||||
.min_values(0)
|
},
|
||||||
)
|
)
|
||||||
])
|
.max_values(1)
|
||||||
))
|
.min_values(0),
|
||||||
|
)]),
|
||||||
|
),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -214,12 +242,11 @@ impl EventHandler for Handler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
message_component
|
message_component
|
||||||
.create_response(&ctx.http, CreateInteractionResponse::UpdateMessage(
|
.create_response(
|
||||||
CreateInteractionResponseMessage::new()
|
&ctx.http,
|
||||||
.content("")
|
CreateInteractionResponse::UpdateMessage(
|
||||||
.embed(CreateEmbed::new()
|
CreateInteractionResponseMessage::new().content("").embed(
|
||||||
.title("辞書一覧")
|
CreateEmbed::new().title("辞書一覧").fields({
|
||||||
.fields({
|
|
||||||
let mut fields = vec![];
|
let mut fields = vec![];
|
||||||
for rule in config.dictionary.rules {
|
for rule in config.dictionary.rules {
|
||||||
let field = (
|
let field = (
|
||||||
@ -230,14 +257,17 @@ impl EventHandler for Handler {
|
|||||||
fields.push(field);
|
fields.push(field);
|
||||||
}
|
}
|
||||||
fields
|
fields
|
||||||
}))
|
}),
|
||||||
))
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
"TTS_CONFIG_SERVER_ADD_DICTIONARY_BUTTON" => {
|
"TTS_CONFIG_SERVER_ADD_DICTIONARY_BUTTON" => {
|
||||||
message_component
|
message_component
|
||||||
.create_response(&ctx.http,
|
.create_response(
|
||||||
|
&ctx.http,
|
||||||
CreateInteractionResponse::Modal(
|
CreateInteractionResponse::Modal(
|
||||||
CreateModal::new("TTS_CONFIG_SERVER_ADD_DICTIONARY", "辞書追加")
|
CreateModal::new("TTS_CONFIG_SERVER_ADD_DICTIONARY", "辞書追加")
|
||||||
.components({
|
.components({
|
||||||
@ -246,29 +276,30 @@ impl EventHandler for Handler {
|
|||||||
CreateInputText::new(
|
CreateInputText::new(
|
||||||
InputTextStyle::Short,
|
InputTextStyle::Short,
|
||||||
"rule_name",
|
"rule_name",
|
||||||
"辞書名"
|
"辞書名",
|
||||||
)
|
)
|
||||||
.required(true)
|
.required(true),
|
||||||
),
|
),
|
||||||
CreateActionRow::InputText(
|
CreateActionRow::InputText(
|
||||||
CreateInputText::new(
|
CreateInputText::new(
|
||||||
InputTextStyle::Paragraph,
|
InputTextStyle::Paragraph,
|
||||||
"from",
|
"from",
|
||||||
"変換元(正規表現)"
|
"変換元(正規表現)",
|
||||||
)
|
)
|
||||||
.required(true)
|
.required(true),
|
||||||
),
|
),
|
||||||
CreateActionRow::InputText(
|
CreateActionRow::InputText(
|
||||||
CreateInputText::new(
|
CreateInputText::new(
|
||||||
InputTextStyle::Short,
|
InputTextStyle::Short,
|
||||||
"to",
|
"to",
|
||||||
"変換先"
|
"変換先",
|
||||||
)
|
)
|
||||||
.required(true)
|
.required(true),
|
||||||
)
|
),
|
||||||
]
|
]
|
||||||
})
|
}),
|
||||||
))
|
),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -278,7 +309,13 @@ impl EventHandler for Handler {
|
|||||||
if values.len() == 0 {
|
if values.len() == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} 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"),
|
_ => panic!("Cannot get index"),
|
||||||
@ -303,7 +340,13 @@ impl EventHandler for Handler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
message_component
|
message_component
|
||||||
.create_response(&ctx.http, CreateInteractionResponse::UpdateMessage(CreateInteractionResponseMessage::new().content("自動参加チャンネルを設定しました。")))
|
.create_response(
|
||||||
|
&ctx.http,
|
||||||
|
CreateInteractionResponse::UpdateMessage(
|
||||||
|
CreateInteractionResponseMessage::new()
|
||||||
|
.content("自動参加チャンネルを設定しました。"),
|
||||||
|
),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -336,70 +379,82 @@ impl EventHandler for Handler {
|
|||||||
if channel.kind != ChannelType::Voice {
|
if channel.kind != ChannelType::Voice {
|
||||||
continue;
|
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(
|
let option = CreateSelectMenuOption::new(
|
||||||
&channel.name,
|
&channel.name,
|
||||||
format!("SET_AUTOSTART_CHANNEL_{}", id.get())
|
format!("SET_AUTOSTART_CHANNEL_{}", id.get()),
|
||||||
)
|
)
|
||||||
.description(description)
|
.description(description)
|
||||||
.default_selection(channel.id.get() == autostart_channel_id);
|
.default_selection(channel.id.get() == autostart_channel_id);
|
||||||
|
|
||||||
options.push(option);
|
options.push(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
message_component
|
message_component
|
||||||
.create_response(&ctx.http,
|
.create_response(
|
||||||
|
&ctx.http,
|
||||||
CreateInteractionResponse::UpdateMessage(
|
CreateInteractionResponse::UpdateMessage(
|
||||||
CreateInteractionResponseMessage::new()
|
CreateInteractionResponseMessage::new()
|
||||||
.content("自動参加チャンネル設定")
|
.content("自動参加チャンネル設定")
|
||||||
.components(vec![
|
.components(vec![CreateActionRow::SelectMenu(
|
||||||
CreateActionRow::SelectMenu(
|
CreateSelectMenu::new(
|
||||||
CreateSelectMenu::new(
|
"SET_AUTOSTART_CHANNEL",
|
||||||
"SET_AUTOSTART_CHANNEL",
|
CreateSelectMenuKind::String { options },
|
||||||
CreateSelectMenuKind::String { options }
|
|
||||||
)
|
|
||||||
.min_values(0)
|
|
||||||
.max_values(1)
|
|
||||||
)
|
)
|
||||||
])
|
.min_values(0)
|
||||||
))
|
.max_values(1),
|
||||||
|
)]),
|
||||||
|
),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
},
|
}
|
||||||
"TTS_CONFIG_SERVER" => {
|
"TTS_CONFIG_SERVER" => {
|
||||||
message_component
|
message_component
|
||||||
.create_response(&ctx.http,
|
.create_response(
|
||||||
|
&ctx.http,
|
||||||
CreateInteractionResponse::UpdateMessage(
|
CreateInteractionResponse::UpdateMessage(
|
||||||
CreateInteractionResponseMessage::new()
|
CreateInteractionResponseMessage::new()
|
||||||
.content("サーバー設定")
|
.content("サーバー設定")
|
||||||
.components(vec![
|
.components(vec![CreateActionRow::Buttons(vec![
|
||||||
CreateActionRow::Buttons(vec![
|
CreateButton::new(
|
||||||
CreateButton::new("TTS_CONFIG_SERVER_ADD_DICTIONARY_BUTTON")
|
"TTS_CONFIG_SERVER_ADD_DICTIONARY_BUTTON",
|
||||||
.label("辞書を追加")
|
)
|
||||||
.style(ButtonStyle::Primary),
|
.label("辞書を追加")
|
||||||
CreateButton::new("TTS_CONFIG_SERVER_REMOVE_DICTIONARY_BUTTON")
|
.style(ButtonStyle::Primary),
|
||||||
.label("辞書を削除")
|
CreateButton::new(
|
||||||
.style(ButtonStyle::Danger),
|
"TTS_CONFIG_SERVER_REMOVE_DICTIONARY_BUTTON",
|
||||||
CreateButton::new("TTS_CONFIG_SERVER_SHOW_DICTIONARY_BUTTON")
|
)
|
||||||
.label("辞書一覧")
|
.label("辞書を削除")
|
||||||
.style(ButtonStyle::Primary),
|
.style(ButtonStyle::Danger),
|
||||||
CreateButton::new("TTS_CONFIG_SERVER_SET_AUTOSTART_CHANNEL")
|
CreateButton::new(
|
||||||
.label("自動参加チャンネル")
|
"TTS_CONFIG_SERVER_SHOW_DICTIONARY_BUTTON",
|
||||||
.style(ButtonStyle::Primary)
|
)
|
||||||
])
|
.label("辞書一覧")
|
||||||
])
|
.style(ButtonStyle::Primary),
|
||||||
))
|
CreateButton::new(
|
||||||
|
"TTS_CONFIG_SERVER_SET_AUTOSTART_CHANNEL",
|
||||||
|
)
|
||||||
|
.label("自動参加チャンネル")
|
||||||
|
.style(ButtonStyle::Primary),
|
||||||
|
])]),
|
||||||
|
),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
match message_component.data.kind {
|
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 res = &values[0].clone();
|
||||||
let data_read = ctx.data.read().await;
|
let data_read = ctx.data.read().await;
|
||||||
|
|
||||||
let mut config = {
|
let mut config = {
|
||||||
let database = data_read
|
let database = data_read
|
||||||
.get::<DatabaseClientData>()
|
.get::<DatabaseClientData>()
|
||||||
@ -412,10 +467,10 @@ impl EventHandler for Handler {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut config_changed = false;
|
let mut config_changed = false;
|
||||||
let mut voicevox_changed = false;
|
let mut voicevox_changed = false;
|
||||||
|
|
||||||
match res.as_str() {
|
match res.as_str() {
|
||||||
"TTS_CONFIG_ENGINE_SELECTED_GOOGLE" => {
|
"TTS_CONFIG_ENGINE_SELECTED_GOOGLE" => {
|
||||||
config.tts_type = Some(TTSType::GCP);
|
config.tts_type = Some(TTSType::GCP);
|
||||||
@ -431,14 +486,14 @@ impl EventHandler for Handler {
|
|||||||
.strip_prefix("TTS_CONFIG_VOICEVOX_SPEAKER_SELECTED_")
|
.strip_prefix("TTS_CONFIG_VOICEVOX_SPEAKER_SELECTED_")
|
||||||
.and_then(|id_str| id_str.parse::<i64>().ok())
|
.and_then(|id_str| id_str.parse::<i64>().ok())
|
||||||
.expect("Invalid speaker ID format");
|
.expect("Invalid speaker ID format");
|
||||||
|
|
||||||
config.voicevox_speaker = Some(speaker_id);
|
config.voicevox_speaker = Some(speaker_id);
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
voicevox_changed = true;
|
voicevox_changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config_changed {
|
if config_changed {
|
||||||
let database = data_read
|
let database = data_read
|
||||||
.get::<DatabaseClientData>()
|
.get::<DatabaseClientData>()
|
||||||
@ -449,26 +504,29 @@ impl EventHandler for Handler {
|
|||||||
.set_user_config(message_component.user.id.get(), config.clone())
|
.set_user_config(message_component.user.id.get(), config.clone())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.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に変更する必要があります。"
|
"設定しました\nこの音声を使うにはAPIをGoogleからVOICEVOXに変更する必要があります。"
|
||||||
} else {
|
} else {
|
||||||
"設定しました"
|
"設定しました"
|
||||||
};
|
};
|
||||||
|
|
||||||
message_component
|
message_component
|
||||||
.create_response(&ctx.http,
|
.create_response(
|
||||||
|
&ctx.http,
|
||||||
CreateInteractionResponse::Message(
|
CreateInteractionResponse::Message(
|
||||||
CreateInteractionResponseMessage::new()
|
CreateInteractionResponseMessage::new()
|
||||||
.content(response_content)
|
.content(response_content)
|
||||||
.ephemeral(true)
|
.ephemeral(true),
|
||||||
))
|
),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,33 @@
|
|||||||
use serenity::{
|
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) {
|
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(
|
||||||
CreateCommand::new("stop").description("Stop tts"),
|
&ctx.http,
|
||||||
CreateCommand::new("setup").description("Setup tts").set_options(vec![
|
vec![
|
||||||
CreateCommandOption::new(CommandOptionType::String, "mode", "TTS channel")
|
CreateCommand::new("stop").description("Stop tts"),
|
||||||
.add_string_choice("Text Channel", "TEXT_CHANNEL")
|
CreateCommand::new("setup")
|
||||||
.add_string_choice("New Thread", "NEW_THREAD")
|
.description("Setup tts")
|
||||||
.add_string_choice("Voice Channel", "VOICE_CHANNEL")
|
.set_options(vec![CreateCommandOption::new(
|
||||||
.required(false)
|
CommandOptionType::String,
|
||||||
]),
|
"mode",
|
||||||
CreateCommand::new("config").description("Config"),
|
"TTS channel",
|
||||||
CreateCommand::new("skip").description("skip tts message"),
|
)
|
||||||
]).await.unwrap();
|
.add_string_choice("Text Channel", "TEXT_CHANNEL")
|
||||||
|
.add_string_choice("New Thread", "NEW_THREAD")
|
||||||
|
.add_string_choice("Voice Channel", "VOICE_CHANNEL")
|
||||||
|
.required(false)]),
|
||||||
|
CreateCommand::new("config").description("Config"),
|
||||||
|
CreateCommand::new("skip").description("skip tts message"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use serenity::model::{
|
use serenity::model::{
|
||||||
|
guild::{Member, PartialMember},
|
||||||
user::User,
|
user::User,
|
||||||
guild::{Member, PartialMember}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait ReadName {
|
pub trait ReadName {
|
||||||
@ -15,7 +15,9 @@ impl ReadName for Member {
|
|||||||
|
|
||||||
impl ReadName for PartialMember {
|
impl ReadName for PartialMember {
|
||||||
fn read_name(&self) -> String {
|
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,4 +25,4 @@ impl ReadName for User {
|
|||||||
fn read_name(&self) -> String {
|
fn read_name(&self) -> String {
|
||||||
self.display_name().to_string()
|
self.display_name().to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,9 @@ use serenity::{model::prelude::Message, prelude::Context};
|
|||||||
use songbird::input::cached::Compressed;
|
use songbird::input::cached::Compressed;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
data::{DatabaseClientData, TTSClientData}, implement::member_name::ReadName, tts::{
|
data::{DatabaseClientData, TTSClientData},
|
||||||
|
implement::member_name::ReadName,
|
||||||
|
tts::{
|
||||||
gcp_tts::structs::{
|
gcp_tts::structs::{
|
||||||
audio_config::AudioConfig, synthesis_input::SynthesisInput,
|
audio_config::AudioConfig, synthesis_input::SynthesisInput,
|
||||||
synthesize_request::SynthesizeRequest,
|
synthesize_request::SynthesizeRequest,
|
||||||
@ -12,7 +14,7 @@ use crate::{
|
|||||||
instance::TTSInstance,
|
instance::TTSInstance,
|
||||||
message::TTSMessage,
|
message::TTSMessage,
|
||||||
tts_type::TTSType,
|
tts_type::TTSType,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@ -48,7 +50,11 @@ impl TTSMessage for Message {
|
|||||||
let member = self.member.clone();
|
let member = self.member.clone();
|
||||||
let name = if let Some(_) = member {
|
let name = if let Some(_) = member {
|
||||||
let guild = ctx.cache.guild(self.guild_id.unwrap()).unwrap().clone();
|
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 {
|
} else {
|
||||||
self.author.read_name()
|
self.author.read_name()
|
||||||
};
|
};
|
||||||
@ -58,7 +64,11 @@ impl TTSMessage for Message {
|
|||||||
let member = self.member.clone();
|
let member = self.member.clone();
|
||||||
let name = if let Some(_) = member {
|
let name = if let Some(_) = member {
|
||||||
let guild = ctx.cache.guild(self.guild_id.unwrap()).unwrap().clone();
|
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 {
|
} else {
|
||||||
self.author.read_name()
|
self.author.read_name()
|
||||||
};
|
};
|
||||||
|
14
src/main.rs
14
src/main.rs
@ -5,6 +5,7 @@ mod database;
|
|||||||
mod event_handler;
|
mod event_handler;
|
||||||
mod events;
|
mod events;
|
||||||
mod implement;
|
mod implement;
|
||||||
|
mod trace;
|
||||||
mod tts;
|
mod tts;
|
||||||
|
|
||||||
use std::{collections::HashMap, env, sync::Arc};
|
use std::{collections::HashMap, env, sync::Arc};
|
||||||
@ -14,9 +15,14 @@ use data::{DatabaseClientData, TTSClientData, TTSData};
|
|||||||
use database::database::Database;
|
use database::database::Database;
|
||||||
use event_handler::Handler;
|
use event_handler::Handler;
|
||||||
use serenity::{
|
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 tts::{gcp_tts::gcp_tts::GCPTTS, tts::TTS, voicevox::voicevox::VOICEVOX};
|
||||||
|
|
||||||
use songbird::SerenityInit;
|
use songbird::SerenityInit;
|
||||||
@ -43,7 +49,7 @@ async fn create_client(prefix: &str, token: &str, id: u64) -> Result<Client, ser
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
tracing_subscriber::fmt().with_max_level(Level::INFO).init();
|
let _guard = init_tracing_subscriber();
|
||||||
// Load config
|
// Load config
|
||||||
let config = {
|
let config = {
|
||||||
let config = std::fs::read_to_string("./config.toml");
|
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)));
|
data.insert::<DatabaseClientData>(Arc::new(Mutex::new(database_client)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info!("Bot initialized.");
|
||||||
|
|
||||||
// Run client
|
// Run client
|
||||||
if let Err(why) = client.start().await {
|
if let Err(why) = client.start().await {
|
||||||
println!("Client error: {:?}", why);
|
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::{
|
use crate::tts::gcp_tts::structs::{
|
||||||
synthesize_request::SynthesizeRequest, synthesize_response::SynthesizeResponse,
|
synthesize_request::SynthesizeRequest, synthesize_response::SynthesizeResponse,
|
||||||
};
|
};
|
||||||
use gcp_auth::Token;
|
use gcp_auth::Token;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GCPTTS {
|
pub struct GCPTTS {
|
||||||
pub token: Arc<RwLock<Token>>,
|
pub token: Arc<RwLock<Token>>,
|
||||||
pub credentials_path: String,
|
pub credentials_path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GCPTTS {
|
impl GCPTTS {
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn update_token(&self) -> Result<(), gcp_auth::Error> {
|
pub async fn update_token(&self) -> Result<(), gcp_auth::Error> {
|
||||||
let mut token = self.token.write().await;
|
let mut token = self.token.write().await;
|
||||||
if token.has_expired() {
|
if token.has_expired() {
|
||||||
@ -26,6 +27,7 @@ impl GCPTTS {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn new(credentials_path: String) -> Result<Self, gcp_auth::Error> {
|
pub async fn new(credentials_path: String) -> Result<Self, gcp_auth::Error> {
|
||||||
let authenticator = gcp_auth::from_credentials_file(credentials_path.clone()).await?;
|
let authenticator = gcp_auth::from_credentials_file(credentials_path.clone()).await?;
|
||||||
let token = authenticator
|
let token = authenticator
|
||||||
@ -59,18 +61,19 @@ impl GCPTTS {
|
|||||||
/// }
|
/// }
|
||||||
/// }).await.unwrap();
|
/// }).await.unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn synthesize(
|
pub async fn synthesize(
|
||||||
&self,
|
&self,
|
||||||
request: SynthesizeRequest,
|
request: SynthesizeRequest,
|
||||||
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
||||||
self.update_token().await.unwrap();
|
self.update_token().await.unwrap();
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
let token_string = {
|
let token_string = {
|
||||||
let token = self.token.read().await;
|
let token = self.token.read().await;
|
||||||
token.as_str().to_string()
|
token.as_str().to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
match client
|
match client
|
||||||
.post("https://texttospeech.googleapis.com/v1/text:synthesize")
|
.post("https://texttospeech.googleapis.com/v1/text:synthesize")
|
||||||
.header(reqwest::header::CONTENT_TYPE, "application/json")
|
.header(reqwest::header::CONTENT_TYPE, "application/json")
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
model::{
|
model::{
|
||||||
channel::Message,
|
channel::Message,
|
||||||
@ -8,6 +10,7 @@ use serenity::{
|
|||||||
|
|
||||||
use crate::tts::message::TTSMessage;
|
use crate::tts::message::TTSMessage;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct TTSInstance {
|
pub struct TTSInstance {
|
||||||
pub before_message: Option<Message>,
|
pub before_message: Option<Message>,
|
||||||
pub text_channel: ChannelId,
|
pub text_channel: ChannelId,
|
||||||
@ -22,9 +25,10 @@ impl TTSInstance {
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// instance.read(message, &ctx).await;
|
/// instance.read(message, &ctx).await;
|
||||||
/// ```
|
/// ```
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn read<T>(&mut self, message: T, ctx: &Context)
|
pub async fn read<T>(&mut self, message: T, ctx: &Context)
|
||||||
where
|
where
|
||||||
T: TTSMessage,
|
T: TTSMessage + Debug,
|
||||||
{
|
{
|
||||||
let audio = message.synthesize(self, ctx).await;
|
let audio = message.synthesize(self, ctx).await;
|
||||||
|
|
||||||
@ -38,6 +42,7 @@ impl TTSInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn skip(&mut self, ctx: &Context) {
|
pub async fn skip(&mut self, ctx: &Context) {
|
||||||
let manager = songbird::get(&ctx).await.unwrap();
|
let manager = songbird::get(&ctx).await.unwrap();
|
||||||
let call = manager.get(self.guild).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>;
|
async fn synthesize(&self, instance: &mut TTSInstance, ctx: &Context) -> Vec<Compressed>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct AnnounceMessage {
|
pub struct AnnounceMessage {
|
||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
pub mod gcp_tts;
|
pub mod gcp_tts;
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
|
pub mod tts;
|
||||||
pub mod tts_type;
|
pub mod tts_type;
|
||||||
pub mod voicevox;
|
pub mod voicevox;
|
||||||
pub mod tts;
|
|
@ -5,8 +5,18 @@ use lru::LruCache;
|
|||||||
use songbird::{driver::Bitrate, input::cached::Compressed};
|
use songbird::{driver::Bitrate, input::cached::Compressed};
|
||||||
use tracing::info;
|
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 struct TTS {
|
||||||
pub voicevox_client: VOICEVOX,
|
pub voicevox_client: VOICEVOX,
|
||||||
gcp_tts_client: GCPTTS,
|
gcp_tts_client: GCPTTS,
|
||||||
@ -20,10 +30,7 @@ pub enum CacheKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TTS {
|
impl TTS {
|
||||||
pub fn new(
|
pub fn new(voicevox_client: VOICEVOX, gcp_tts_client: GCPTTS) -> Self {
|
||||||
voicevox_client: VOICEVOX,
|
|
||||||
gcp_tts_client: GCPTTS,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
voicevox_client,
|
voicevox_client,
|
||||||
gcp_tts_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 cache_key = CacheKey::Voicevox(text.to_string(), speaker);
|
||||||
|
|
||||||
let cached_audio = {
|
let cached_audio = {
|
||||||
@ -43,15 +55,16 @@ impl TTS {
|
|||||||
info!("Cache hit for VOICEVOX TTS");
|
info!("Cache hit for VOICEVOX TTS");
|
||||||
return Ok(audio);
|
return Ok(audio);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Cache miss for VOICEVOX TTS");
|
info!("Cache miss for VOICEVOX TTS");
|
||||||
|
|
||||||
let audio = self.voicevox_client
|
let audio = self
|
||||||
|
.voicevox_client
|
||||||
.synthesize(text.to_string(), speaker)
|
.synthesize(text.to_string(), speaker)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let compressed = Compressed::new(audio.into(), Bitrate::Auto).await?;
|
let compressed = Compressed::new(audio.into(), Bitrate::Auto).await?;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut cache_guard = self.cache.write().unwrap();
|
let mut cache_guard = self.cache.write().unwrap();
|
||||||
cache_guard.put(cache_key, compressed.clone());
|
cache_guard.put(cache_key, compressed.clone());
|
||||||
@ -60,7 +73,11 @@ impl TTS {
|
|||||||
Ok(compressed)
|
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(
|
let cache_key = CacheKey::GCP(
|
||||||
synthesize_request.input.clone(),
|
synthesize_request.input.clone(),
|
||||||
synthesize_request.voice.clone(),
|
synthesize_request.voice.clone(),
|
||||||
@ -75,15 +92,13 @@ impl TTS {
|
|||||||
info!("Cache hit for GCP TTS");
|
info!("Cache hit for GCP TTS");
|
||||||
return Ok(audio);
|
return Ok(audio);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Cache miss for GCP TTS");
|
info!("Cache miss for GCP TTS");
|
||||||
|
|
||||||
let audio = self.gcp_tts_client
|
let audio = self.gcp_tts_client.synthesize(synthesize_request).await?;
|
||||||
.synthesize(synthesize_request)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let compressed = Compressed::new(audio.into(), Bitrate::Auto).await?;
|
let compressed = Compressed::new(audio.into(), Bitrate::Auto).await?;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut cache_guard = self.cache.write().unwrap();
|
let mut cache_guard = self.cache.write().unwrap();
|
||||||
cache_guard.put(cache_key, compressed.clone());
|
cache_guard.put(cache_key, compressed.clone());
|
||||||
|
@ -2,12 +2,13 @@ use super::structs::speaker::Speaker;
|
|||||||
|
|
||||||
const BASE_API_URL: &str = "https://deprecatedapis.tts.quest/v2/";
|
const BASE_API_URL: &str = "https://deprecatedapis.tts.quest/v2/";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct VOICEVOX {
|
pub struct VOICEVOX {
|
||||||
pub key: String,
|
pub key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VOICEVOX {
|
impl VOICEVOX {
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn get_styles(&self) -> Vec<(String, i64)> {
|
pub async fn get_styles(&self) -> Vec<(String, i64)> {
|
||||||
let speakers = self.get_speaker_list().await;
|
let speakers = self.get_speaker_list().await;
|
||||||
let mut speaker_list = vec![];
|
let mut speaker_list = vec![];
|
||||||
@ -20,6 +21,7 @@ impl VOICEVOX {
|
|||||||
speaker_list
|
speaker_list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn get_speakers(&self) -> Vec<String> {
|
pub async fn get_speakers(&self) -> Vec<String> {
|
||||||
let speakers = self.get_speaker_list().await;
|
let speakers = self.get_speaker_list().await;
|
||||||
let mut speaker_list = vec![];
|
let mut speaker_list = vec![];
|
||||||
@ -34,6 +36,7 @@ impl VOICEVOX {
|
|||||||
Self { key }
|
Self { key }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
async fn get_speaker_list(&self) -> Vec<Speaker> {
|
async fn get_speaker_list(&self) -> Vec<Speaker> {
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
match client
|
match client
|
||||||
@ -49,6 +52,7 @@ impl VOICEVOX {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
pub async fn synthesize(
|
pub async fn synthesize(
|
||||||
&self,
|
&self,
|
||||||
text: String,
|
text: String,
|
||||||
|
Reference in New Issue
Block a user