2022-10-18 22:59:32 +02:00
|
|
|
use chrono::Datelike;
|
|
|
|
use dotenv::dotenv;
|
|
|
|
|
|
|
|
use log::*;
|
|
|
|
use serenity::{framework::StandardFramework, prelude::GatewayIntents, Client};
|
|
|
|
use songbird::SerenityInit;
|
2022-11-05 18:15:23 +01:00
|
|
|
use std::{any::Any, env, process::exit};
|
2022-10-18 22:59:32 +02:00
|
|
|
|
2022-10-31 22:46:34 +01:00
|
|
|
use crate::{
|
|
|
|
bot::commands::CommandManager, database::Database, session::manager::SessionManager,
|
|
|
|
stats::StatsManager,
|
|
|
|
};
|
2022-10-18 22:59:32 +02:00
|
|
|
|
2022-11-05 18:15:23 +01:00
|
|
|
#[cfg(unix)]
|
|
|
|
use tokio::signal::unix::SignalKind;
|
|
|
|
|
2022-10-18 22:59:32 +02:00
|
|
|
mod audio;
|
|
|
|
mod bot;
|
2022-11-01 09:36:25 +01:00
|
|
|
mod consts;
|
2022-10-18 22:59:32 +02:00
|
|
|
mod database;
|
|
|
|
mod ipc;
|
|
|
|
mod librespot_ext;
|
|
|
|
mod player;
|
|
|
|
mod session;
|
2022-10-31 22:46:34 +01:00
|
|
|
mod stats;
|
2022-10-18 22:59:32 +02:00
|
|
|
mod utils;
|
|
|
|
|
2022-11-05 15:39:19 +01:00
|
|
|
#[tokio::main]
|
2022-10-18 22:59:32 +02:00
|
|
|
async fn main() {
|
2022-10-31 22:46:34 +01:00
|
|
|
if std::env::var("RUST_LOG").is_err() {
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
{
|
|
|
|
std::env::set_var("RUST_LOG", "spoticord");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(debug_assertions))]
|
|
|
|
{
|
|
|
|
std::env::set_var("RUST_LOG", "spoticord=info");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-18 22:59:32 +02:00
|
|
|
env_logger::init();
|
|
|
|
|
|
|
|
let args: Vec<String> = env::args().collect();
|
|
|
|
|
|
|
|
if args.len() > 2 {
|
|
|
|
if &args[1] == "--player" {
|
|
|
|
// Woah! We're running in player mode!
|
|
|
|
|
|
|
|
debug!("Starting Spoticord player");
|
|
|
|
|
|
|
|
player::main().await;
|
|
|
|
|
2022-10-31 22:46:34 +01:00
|
|
|
debug!("Player exited, shutting down");
|
|
|
|
|
2022-10-18 22:59:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
info!("It's a good day");
|
|
|
|
info!(" - Spoticord {}", chrono::Utc::now().year());
|
|
|
|
|
|
|
|
let result = dotenv();
|
|
|
|
|
|
|
|
if let Ok(path) = result {
|
|
|
|
debug!("Loaded environment file: {}", path.to_str().unwrap());
|
|
|
|
} else {
|
|
|
|
warn!("No .env file found, expecting all necessary environment variables");
|
|
|
|
}
|
|
|
|
|
|
|
|
let token = env::var("TOKEN").expect("a token in the environment");
|
|
|
|
let db_url = env::var("DATABASE_URL").expect("a database URL in the environment");
|
2022-10-31 22:46:34 +01:00
|
|
|
let kv_url = env::var("KV_URL").expect("a redis URL in the environment");
|
|
|
|
|
|
|
|
let stats_manager = StatsManager::new(kv_url).expect("Failed to connect to redis");
|
|
|
|
let session_manager = SessionManager::new();
|
2022-10-18 22:59:32 +02:00
|
|
|
|
|
|
|
// Create client
|
|
|
|
let mut client = Client::builder(
|
|
|
|
token,
|
|
|
|
GatewayIntents::GUILDS | GatewayIntents::GUILD_VOICE_STATES,
|
|
|
|
)
|
|
|
|
.event_handler(crate::bot::events::Handler)
|
|
|
|
.framework(StandardFramework::new())
|
|
|
|
.register_songbird()
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
let mut data = client.data.write().await;
|
|
|
|
|
|
|
|
data.insert::<Database>(Database::new(db_url, None));
|
|
|
|
data.insert::<CommandManager>(CommandManager::new());
|
2022-10-31 22:46:34 +01:00
|
|
|
data.insert::<SessionManager>(session_manager.clone());
|
2022-10-18 22:59:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
let shard_manager = client.shard_manager.clone();
|
2022-10-31 22:46:34 +01:00
|
|
|
let cache = client.cache_and_http.cache.clone();
|
|
|
|
|
2022-11-05 18:15:23 +01:00
|
|
|
let mut term: Option<Box<dyn Any + Send>>;
|
|
|
|
|
2022-11-05 15:39:19 +01:00
|
|
|
#[cfg(unix)]
|
2022-11-05 18:15:23 +01:00
|
|
|
{
|
|
|
|
term = Some(Box::new(
|
|
|
|
tokio::signal::unix::signal(SignalKind::terminate()).unwrap(),
|
|
|
|
));
|
|
|
|
}
|
2022-10-18 22:59:32 +02:00
|
|
|
|
2022-10-31 22:46:34 +01:00
|
|
|
// Background tasks
|
2022-10-18 22:59:32 +02:00
|
|
|
tokio::spawn(async move {
|
2022-10-31 22:46:34 +01:00
|
|
|
loop {
|
|
|
|
tokio::select! {
|
|
|
|
_ = tokio::time::sleep(std::time::Duration::from_secs(60)) => {
|
|
|
|
let guild_count = cache.guilds().len();
|
|
|
|
let active_count = session_manager.get_active_session_count().await;
|
2022-10-18 22:59:32 +02:00
|
|
|
|
2022-10-31 22:46:34 +01:00
|
|
|
if let Err(why) = stats_manager.set_server_count(guild_count) {
|
|
|
|
error!("Failed to update server count: {}", why);
|
|
|
|
}
|
2022-10-18 22:59:32 +02:00
|
|
|
|
2022-10-31 22:46:34 +01:00
|
|
|
if let Err(why) = stats_manager.set_active_count(active_count) {
|
|
|
|
error!("Failed to update active count: {}", why);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Yes, I like to handle my s's when I'm working with amounts
|
|
|
|
debug!("Updated stats: {} guild{}, {} active session{}", guild_count, if guild_count == 1 { "" } else { "s" }, active_count, if active_count == 1 { "" } else { "s" });
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = tokio::signal::ctrl_c() => {
|
|
|
|
info!("Received interrupt signal, shutting down...");
|
|
|
|
|
|
|
|
shard_manager.lock().await.shutdown_all().await;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-11-05 18:15:23 +01:00
|
|
|
_ = async {
|
2022-11-07 12:58:22 +01:00
|
|
|
#[cfg(unix)]
|
2022-11-05 18:15:23 +01:00
|
|
|
match term {
|
|
|
|
Some(ref mut term) => {
|
|
|
|
let term = term.downcast_mut::<tokio::signal::unix::Signal>().unwrap();
|
|
|
|
|
|
|
|
term.recv().await
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}, if term.is_some() => {
|
2022-10-31 22:46:34 +01:00
|
|
|
info!("Received terminate signal, shutting down...");
|
|
|
|
|
|
|
|
shard_manager.lock().await.shutdown_all().await;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-10-18 22:59:32 +02:00
|
|
|
});
|
|
|
|
|
2022-10-31 22:46:34 +01:00
|
|
|
// Start the bot
|
2022-10-18 22:59:32 +02:00
|
|
|
if let Err(why) = client.start_autosharded().await {
|
2022-10-31 22:46:34 +01:00
|
|
|
error!("FATAL Error in bot: {:?}", why);
|
|
|
|
exit(1);
|
2022-10-18 22:59:32 +02:00
|
|
|
}
|
|
|
|
}
|