// src/main.rs // Declare the modules used in this crate mod handler; mod state; mod commands; mod lavalink_handler; mod utils; // Import necessary types from our modules use handler::Handler; use state::BotState; use lavalink_handler::MyLavalinkEventHandlerRs; // Import necessary types from external crates use serenity::client::Client; use serenity::prelude::GatewayIntents; // Needed to subscribe to events use lavalink_rs::LavalinkClient; use tokio::sync::Mutex; use tracing::{error, info, instrument}; use std::sync::Arc; // For shared ownership use std::env; // To read environment variables // The entry point of the application #[tokio::main] #[instrument] // Adds tracing to the main function async fn main() { // Initialize tracing for logging output tracing_subscriber::fmt() .with_max_level(tracing::Level::INFO) // Set logging level .init(); // Load environment variables from .env file dotenv::dotenv().ok(); // Get credentials from environment variables let token = env::var("DISCORD_TOKEN").expect("DISCORD_TOKEN not set"); let lavalink_host = env::var("LAVALINK_HOST") .expect("LAVALINK_HOST not set"); let lavalink_port = env::var("LAVALINK_PORT") .expect("LAVALINK_PORT not set") .parse::() .expect("Invalid LAVALINK_PORT"); let lavalink_password = env::var("LAVALINK_PASSWORD") .expect("LAVALINK_PASSWORD not set"); // Define the gateway intents needed for the bot // GUILDS: To receive guild information for commands and cache // GUILD_VOICE_STATES: Crucial for receiving voice state updates let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_VOICE_STATES; // Build the Serenity client. Need the await here because of the async builder. let mut serenity_client = Client::builder(&token, intents) .await .expect("Error creating serenity client"); // Clone the Arc client from Serenity. We need this to be able to send // HTTP requests (like sending messages) from places that don't have the full Context, // such as the Lavalink event handler. let http = serenity_client.cache_and_http.http.clone(); // Get the bot's user ID from the cache. This is needed to initialize the Lavalink client. let bot_user_id = serenity_client .cache_and_http .cache .current_user_id() .await .expect("Failed to get bot user ID from cache"); // Create the Lavalink client builder let mut lavalink_client_builder = LavalinkClient::builder(bot_user_id); // Set Lavalink node details lavalink_client_builder .set_host(lavalink_host) .set_port(lavalink_port) .set_password(lavalink_password); // Create and set the custom Lavalink event handler, passing the http client let lavalink_event_handler = Arc::new(MyLavalinkEventHandlerRs { http: http.clone() // Clone Http again for the lavalink event handler }); lavalink_client_builder.set_event_handler(lavalink_event_handler); // Build the Lavalink client let lavalink_client = lavalink_client_builder.build(); // Create and store the shared state (BotState) let bot_state = Arc::new(Mutex::new(BotState { lavalink: lavalink_client.clone(), // Clone lavalink client for the handler struct http: http, // Store the Http client clone in the state // announcement_channels: Mutex::new(HashMap::new()), // Optional: for announcements })); // Set the Serenity event handler, passing the shared state serenity_client.event_handler(Handler { state: bot_state }); // Spawn the lavalink-rs client's main run task. This task will manage the // WebSocket connection to the Lavalink server and process its events. let lavalink_task = tokio::spawn(async move { info!("Starting lavalink-rs client task..."); if let Err(why) = lavalink_client.run().await { error!("Lavalink client task error: {:?}", why); } info!("Lavalink client task stopped."); }); // Start the Serenity client gateway. This connects the bot to Discord // and begins receiving events. This call is blocking until the client stops. info!("Starting serenity client gateway..."); if let Err(why) = serenity_client.start().await { error!("Serenity client gateway error: {:?}", why); } info!("Serenity client gateway stopped."); // Wait for the lavalink task to finish. // In a real bot, this task should ideally never finish unless there's a critical error. let _ = lavalink_task.await; info!("Bot shut down."); }