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 .event_handler(handler)
132 .build();
133
134 if let Err(e) = client.start().await {
135 eprintln!("Error: {}", e);
136 }
137}