add tracing opentelemetry

This commit is contained in:
mii443
2025-04-10 13:40:29 +09:00
parent 24c609aaf2
commit 8a1fa22074
17 changed files with 464 additions and 200 deletions

View File

@ -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"

View File

@ -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(())

View File

@ -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();

View File

@ -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,

View File

@ -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,

View File

@ -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();
}
},
_ => {
}
_ => {}
}
}
}

View File

@ -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();
}

View File

@ -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())
}
}

View File

@ -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()
};

View File

@ -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
View 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,
}

View File

@ -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,

View File

@ -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();

View File

@ -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,
}

View File

@ -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;

View File

@ -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?;

View File

@ -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,