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