Skip to main content

voice/
voice.rs

1use fluxer::prelude::*;
2use async_trait::async_trait;
3use tokio::sync::Mutex;
4use tokio::task::AbortHandle;
5
6const PREFIX: &str = "!";
7const AUDIO_FILE: &str = "audio/audio.mp3";
8
9struct Handler {
10    playback: Mutex<Option<AbortHandle>>,
11    voice: Mutex<Option<FluxerVoiceConnection>>,
12}
13
14fn parse_command(content: &str) -> Option<(&str, &str)> {
15    let trimmed = content.strip_prefix(PREFIX)?;
16    match trimmed.find(' ') {
17        Some(pos) => Some((&trimmed[..pos], trimmed[pos + 1..].trim())),
18        None => Some((trimmed, "")),
19    }
20}
21
22#[async_trait]
23impl EventHandler for Handler {
24    async fn on_ready(&self, _ctx: Context, ready: Ready) {
25        println!("Logged in as {}", ready.user.username);
26    }
27
28    async fn on_message(&self, ctx: Context, msg: Message) {
29        if msg.author.bot.unwrap_or(false) {
30            return;
31        }
32
33        let content = match msg.content.as_deref() {
34            Some(c) => c,
35            None => return,
36        };
37
38        let channel_id = msg.channel_id.as_deref().unwrap_or_default();
39        let guild_id = match msg.guild_id.as_deref() {
40            Some(id) => id,
41            None => return,
42        };
43
44        let (cmd, args) = match parse_command(content) {
45            Some(v) => v,
46            None => return,
47        };
48
49        match cmd {
50            "join" => {
51                if args.is_empty() {
52                    let _ = ctx.http.send_message(channel_id, "`!join <voice_channel_id>`").await;
53                    return;
54                }
55
56                match ctx.join_voice(guild_id, args).await {
57                    Ok(conn) => {
58                        *self.voice.lock().await = Some(conn);
59                        let _ = ctx.http.send_message(channel_id, "Joined.").await;
60                    }
61                    Err(e) => {
62                        let _ = ctx.http.send_message(channel_id, &format!("Failed: {}", e)).await;
63                    }
64                }
65            }
66
67            "leave" => {
68                if let Some(handle) = self.playback.lock().await.take() {
69                    handle.abort();
70                }
71                *self.voice.lock().await = None;
72                let _ = ctx.leave_voice(guild_id).await;
73                let _ = ctx.http.send_message(channel_id, "Left.").await;
74            }
75
76            "play" => {
77                let conn = self.voice.lock().await;
78                let conn = match conn.as_ref() {
79                    Some(c) => c,
80                    None => {
81                        let _ = ctx.http.send_message(channel_id, "Not in a voice channel.").await;
82                        return;
83                    }
84                };
85
86                if let Some(handle) = self.playback.lock().await.take() {
87                    handle.abort();
88                }
89
90                match conn.play_music(AUDIO_FILE, ctx.http.clone(), channel_id.to_string()).await {
91                    Ok(handle) => {
92                        *self.playback.lock().await = Some(handle);
93                        let _ = ctx.http.send_message(channel_id, &format!("Playing `{}`.", AUDIO_FILE)).await;
94                    }
95                    Err(e) => {
96                        let _ = ctx.http.send_message(channel_id, &format!("Failed: {}", e)).await;
97                    }
98                }
99            }
100
101            "stop" => {
102                if let Some(handle) = self.playback.lock().await.take() {
103                    handle.abort();
104                    let _ = ctx.http.send_message(channel_id, "Stopped.").await;
105                } else {
106                    let _ = ctx.http.send_message(channel_id, "Nothing is playing.").await;
107                }
108            }
109
110            _ => {}
111        }
112    }
113}
114
115#[tokio::main]
116async fn main() {
117    rustls::crypto::ring::default_provider()
118        .install_default()
119        .expect("Failed to install rustls crypto provider");
120
121    let token = std::env::var("FLUXER_TOKEN")
122        .expect("Set FLUXER_TOKEN to your bot token");
123
124    let handler = Handler {
125        playback: Mutex::new(None),
126        voice: Mutex::new(None),
127    };
128
129    let mut client = Client::builder(&token)
130        // .api_url("http://localhost:48763/api/v1") this is for self hosted instances
131        .event_handler(handler)
132        .build();
133
134    if let Err(e) = client.start().await {
135        eprintln!("Error: {}", e);
136    }
137}