1use anyhow::Context as AnyhowContext;
11use serenity::all::{Client, Context, EventHandler, GatewayIntents, Interaction, Ready};
12use tracing::{error, info};
13
14use crate::config::Config;
15use crate::utils::Utils;
16
17mod commands;
18mod config;
19mod polls;
20mod utils;
21
22struct Bot {
23 config: Config,
24}
25
26#[serenity::async_trait]
27impl EventHandler for Bot {
28 async fn ready(&self, ctx: Context, ready: Ready) {
29 info!("Bot is connected as {}", ready.user.name);
30
31 let commands: Vec<_> = commands::get_commands()
32 .iter()
33 .map(|cmd| cmd.register())
34 .collect();
35
36 Utils::register_commands(&ctx, self.config.guild_id(), commands).await;
37 }
38
39 async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
40 if let Interaction::Command(command) = interaction {
41 let command_name = command.data.name.clone();
42 let config = self.config.clone();
43
44 for cmd in commands::get_commands() {
45 if cmd.name() == command_name.as_str() {
46 cmd.run(ctx, command, config).await;
47 return;
48 }
49 }
50
51 Utils::ephemeral_response(&ctx.http, &command, "Unknown command").await;
52 }
53 }
54}
55
56#[tokio::main]
57async fn main() -> anyhow::Result<()> {
58 dotenvy::dotenv().ok();
59
60 let config = config::init().context("Failed to initialize configuration")?;
61 let intents = GatewayIntents::GUILDS
62 | GatewayIntents::GUILD_VOICE_STATES
63 | GatewayIntents::GUILD_MEMBERS
64 | GatewayIntents::GUILD_MESSAGE_REACTIONS;
65
66 let mut client = Client::builder(&config.discord_token, intents)
67 .event_handler(Bot { config })
68 .await
69 .context("Failed to create Discord client")?;
70
71 info!("Starting Streamocracy Discord Bot...");
72
73 if let Err(e) = client.start().await {
74 error!("Client error: {:?}", e);
75 }
76
77 Ok(())
78}