use clap::Parser; use fomo_reducer::{VCs, initialize_vcs, update_vcs}; use opendal::{IntoOperatorUri, Operator, OperatorUri}; use secrecy::{ExposeSecret, SecretString}; use snafu::Snafu; use std::{fmt::Debug, str::FromStr}; use tracing_subscriber::fmt::format::FmtSpan; use twilight_gateway::{ Event, EventTypeFlags, Intents, Shard, ShardId, StreamExt, error::ReceiveMessageErrorType, }; use twilight_model::id::{Id, marker::UserMarker}; #[derive(Clone)] struct OpendalOperator { uri: OperatorUri, operator: Operator, } impl FromStr for OpendalOperator { type Err = opendal::Error; fn from_str(s: &str) -> Result { let uri = s.into_operator_uri()?; let operator = Operator::from_uri(&uri)?; Ok(Self { uri, operator }) } } impl OpendalOperator { fn into_inner(self) -> Operator { self.operator } } impl From for Operator { fn from(wrapper: OpendalOperator) -> Self { wrapper.into_inner() } } impl Debug for OpendalOperator { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Debug::fmt(&self.uri, f) } } #[derive(Debug, Parser)] struct Args { #[arg(long, env)] discord_token: SecretString, #[arg(long, env)] storage: OpendalOperator, #[arg(long, env)] bot_owner: Id, } #[derive(Debug, Snafu)] enum MainError {} #[snafu::report] #[tokio::main] async fn main() -> Result<(), MainError> { tracing_subscriber::fmt() .pretty() .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE) .init(); let args = Parser::parse(); tracing::debug!(?args, "using"); let Args { discord_token, storage, bot_owner, } = args; rustls::crypto::aws_lc_rs::default_provider() .install_default() .unwrap(); let shard_id = ShardId::new(0, 1); let intents = Intents::GUILD_VOICE_STATES; let mut shard = Shard::new(shard_id, discord_token.expose_secret().to_owned(), intents); let vc_event_types = EventTypeFlags::GUILD_VOICE_STATES; let mut next_event = shard.next_event(vc_event_types); let discord_client = twilight_http::Client::new(discord_token.expose_secret().to_owned()); let mut voice_status = initialize_vcs(&discord_client).await; while let Some(event_res) = next_event.await { match event_res { Ok(event) => { tracing::debug!(?voice_status, "before handling"); handle_event(event, &mut voice_status).await; tracing::debug!(?voice_status, "after handling"); } Err(error) => { tracing::error!(?error); } } next_event = shard.next_event(vc_event_types); } Ok(()) } #[tracing::instrument(skip(vcs))] async fn handle_event(event: Event, vcs: &mut VCs) { match event { Event::VoiceStateUpdate(voice_state_update) => { update_vcs(&voice_state_update, vcs); } other => { tracing::warn!(?other, "wasn't expected"); } } }