use std::future::Future; use std::pin::Pin; use std::sync::Arc; use std::time::Duration; use std::error; use std::env; use serenity::async_trait; use serenity::prelude::GatewayIntents; use serenity::client::Context; use serenity::model::gateway::Ready; use serenity::all::{EventHandler, Message}; use serenity::Client; use dotenv::dotenv; use songbird::SerenityInit; use tokio_cron_scheduler::{JobScheduler, Job}; mod message_handler; use message_handler::handle; mod commands; mod util; mod other; use other::notice; mod types; use types::Error; struct Handler; async fn on_error(error: poise::FrameworkError<'_, (), Error>) { match error { poise::FrameworkError::Setup { error, .. } => panic!("Failed to start bot: {:?}", error), poise::FrameworkError::Command { error, ctx, .. } => { println!("Error in command `{}`: {:?}", ctx.command().name, error,); } error => { if let Err(e) = poise::builtins::on_error(error).await { println!("Error while handling error: {}", e) } } } } #[async_trait] impl EventHandler for Handler { async fn message(&self, ctx: Context, msg: Message) { handle(ctx, msg).await; } async fn ready(&self, ctx: Context, ready: Ready) { println!("{} v0.3 is connected!", ready.user.name); #[cfg(feature="RELEASE")] { use util::debug::hello; let _ = hello(ctx.http.clone()).await; } let sched = JobScheduler::new().await.unwrap(); let job_closure = move |_, _| -> Pin + Send>> { let ctx_clone = ctx.clone(); Box::pin( async move { notice::notice_wrapper(ctx_clone).await; }) }; sched.add(Job::new_async("0 0 13 * * *", job_closure).expect("Cron job not set up correctly")).await.unwrap(); sched.start().await.unwrap(); } } #[tokio::main] async fn main() -> anyhow::Result<()> { use anyhow::Context; dotenv().ok(); // create poise framework for registering commands let options: poise::FrameworkOptions<(), Box> = poise::FrameworkOptions { commands: vec![ commands::say(), commands::hug(), commands::player::play_local(), commands::player::connect(), commands::player::disconnect(), ], prefix_options: poise::PrefixFrameworkOptions { prefix: Some("/".into()), edit_tracker: Some(Arc::new(poise::EditTracker::for_timespan( Duration::from_secs(3600), ))), ..Default::default() }, on_error: |err| Box::pin(on_error(err)), command_check: Some(|ctx| { Box::pin(async move { return Ok(!ctx.author().bot) }) }), ..Default::default() }; let framework = poise::Framework::builder() .setup(move |ctx, _ready, framework| { Box::pin(async move { #[cfg(feature="GUILD_COMMAND")] { use poise::samples::register_in_guild; use serenity::all::GuildId; let debug_guild_id = env::var("DEBUG_GUILD_ID") .context("DEBUG_GUILD_ID not found in env")? .parse::().unwrap(); register_in_guild(ctx, &framework.options().commands, GuildId::new(debug_guild_id)).await?; } #[cfg(not(feature="GUILD_COMMAND"))] { use poise::samples::register_globally; register_globally(ctx, &framework.options().commands).await?; } Ok(()) }) }) .options(options) .build(); let token_str = "TOKEN"; #[cfg(feature="DEBUG")] let token_str = "DEBUGTOKEN"; let token = env::var(token_str).context("TOKEN not found in env")?; let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_VOICE_STATES | GatewayIntents::GUILD_MESSAGE_REACTIONS | GatewayIntents::GUILD_MESSAGE_TYPING; let mut client = Client::builder(&token, intents) .event_handler(Handler) .framework(framework) .register_songbird() .await .context("Failed to build client")?; client.start().await?; Ok(()) }