Jelajahi Sumber

WIP: Update serenity to 0.12

Frans Bergman 1 bulan lalu
induk
melakukan
22185b9328
7 mengubah file dengan 660 tambahan dan 109 penghapusan
  1. 552 60
      Cargo.lock
  2. 3 3
      Cargo.toml
  3. 19 13
      src/audio/audio.rs
  4. 66 9
      src/audio/audio_state.rs
  5. 7 6
      src/audio/subprocess.rs
  6. 4 2
      src/main.rs
  7. 9 16
      src/util/util.rs

File diff ditekan karena terlalu besar
+ 552 - 60
Cargo.lock


+ 3 - 3
Cargo.toml

@@ -10,8 +10,8 @@ edition = "2018"
 futures = "0.3.5"
 lazy_static = "1.4.0"
 rand = "0.8.3"
-serenity = {version = "0.11", features = ["standard_framework", "voice", "rustls_backend", "cache", "framework"] }
-songbird = "0.3.2"
-symphonia-core = "0.5.2"
+serenity = {version = "0.12", features = ["standard_framework", "voice", "rustls_backend", "cache", "framework"] }
+songbird = {version = "0.4.3", features = ["serenity", "driver"]}
+symphonia-core = "0.5.4"
 tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "time", "sync"] }
 serde_json = "1.0.81"

+ 19 - 13
src/audio/audio.rs

@@ -8,9 +8,8 @@ use serenity::{
         macros::{command, group},
         Args, CommandResult,
     },
-    model::{channel::Message, id::GuildId},
+    model::{channel::Message, id::GuildId, prelude::*},
 };
-use songbird::tracks::TrackCommand;
 use std::{collections::HashMap, sync::Arc};
 use tokio::sync::Mutex;
 
@@ -37,12 +36,11 @@ lazy_static! {
 }
 
 async fn get_audio_state(ctx: &Context, msg: &Message) -> Option<Arc<Mutex<AudioState>>> {
-    let guild = msg.guild(&ctx.cache).unwrap();
-    let guild_id = guild.id;
-
     let mut audio_states = AUDIO_STATES.lock().await;
+
     let manager = songbird::get(ctx).await.unwrap();
-    let channel_id = guild
+
+    let channel_id = msg.guild(&ctx.cache).unwrap()
         .voice_states
         .get(&msg.author.id)
         .and_then(|voice_state| voice_state.channel_id);
@@ -55,6 +53,8 @@ async fn get_audio_state(ctx: &Context, msg: &Message) -> Option<Arc<Mutex<Audio
         }
     };
 
+    let guild_id = msg.guild_id.unwrap();
+
     if let Some(call_locked) = manager.get(guild_id) {
         let call = call_locked.lock().await;
 
@@ -76,8 +76,8 @@ async fn get_audio_state(ctx: &Context, msg: &Message) -> Option<Arc<Mutex<Audio
         None => {
             audio_states.remove(&guild_id);
             match manager.join(guild_id, channel_id).await {
-                (call, Ok(_)) => call,
-                (_, Err(err)) => {
+                Ok(call) => call,
+                Err(err) => {
                     println!("Error joining call: {:?}", err);
                     return None;
                 }
@@ -108,8 +108,7 @@ async fn get_audio_state(ctx: &Context, msg: &Message) -> Option<Arc<Mutex<Audio
 }
 
 async fn remove_audio_state(ctx: &Context, msg: &Message) -> Result<(), String> {
-    let guild = msg.guild(&ctx.cache).unwrap();
-    let guild_id = guild.id;
+    let guild_id = msg.guild_id.unwrap();
 
     let mut audio_states = AUDIO_STATES.lock().await;
     let manager = songbird::get(ctx).await.unwrap();
@@ -189,7 +188,14 @@ async fn skip(ctx: &Context, msg: &Message) -> CommandResult {
         None => return Ok(()),
     };
 
-    if let Err(why) = AudioState::send_track_command(audio_state, TrackCommand::Stop).await {
+    let total_users = {
+        let guild = msg.guild(&ctx.cache).ok_or("aaa".to_string())?;
+        let state = guild.voice_states.get(&msg.author.id).ok_or("aaa")?;
+        let channel = guild.channels.get(&state.channel_id.unwrap());
+        channel.unwrap().members(&ctx.cache).unwrap().len()
+    };
+
+    if let Err(why) = AudioState::vote_skip(audio_state, msg.author.clone(), total_users).await {
         send_embed(ctx, msg, &format!("Error: {}", why)).await;
     } else {
         message_react(ctx, msg, "↪").await;
@@ -205,7 +211,7 @@ async fn pause(ctx: &Context, msg: &Message) -> CommandResult {
         None => return Ok(()),
     };
 
-    if let Err(why) = AudioState::send_track_command(audio_state, TrackCommand::Pause).await {
+    if let Err(why) = AudioState::pause(audio_state).await {
         send_embed(ctx, msg, &format!("Error: {}", why)).await;
     } else {
         message_react(ctx, msg, "⏸").await;
@@ -221,7 +227,7 @@ async fn resume(ctx: &Context, msg: &Message) -> CommandResult {
         None => return Ok(()),
     };
 
-    if let Err(why) = AudioState::send_track_command(audio_state, TrackCommand::Play).await {
+    if let Err(why) = AudioState::play(audio_state).await {
         send_embed(ctx, msg, &format!("Error: {}", why)).await;
     } else {
         message_react(ctx, msg, "▶").await;

+ 66 - 9
src/audio/audio_state.rs

@@ -4,12 +4,12 @@ use serenity::{
     async_trait,
     client::Context,
     http::Http,
-    model::{channel::Message, id::ChannelId},
+    model::{channel::Message, id::ChannelId, user::User},
     prelude::Mutex as SerenityMutex,
 };
 use songbird::{
-    input::{self, reader::Reader},
-    tracks::{TrackCommand, TrackHandle},
+    input::{Input, RawAdapter},
+    tracks::TrackHandle,
     Call, Event, EventContext, EventHandler as VoiceEventHandler, TrackEvent,
 };
 use std::time::Duration;
@@ -17,6 +17,7 @@ use std::sync::Arc;
 use tokio::sync::Mutex;
 use tokio::time::sleep;
 use tokio::task::JoinHandle;
+use std::collections::HashSet;
 
 pub struct AudioState {
     queue: SongQueue,
@@ -26,6 +27,7 @@ pub struct AudioState {
     track_handle: Option<TrackHandle>,
     is_looping: bool,
     volume: f32,
+    skip_votes: HashSet<User>,
 
     channel_id: ChannelId,
     http: Arc<Http>,
@@ -41,6 +43,7 @@ impl AudioState {
             track_handle: None,
             is_looping: false,
             volume: 1.0,
+            skip_votes: HashSet::new(),
 
             channel_id: msg.channel_id,
             http: ctx.http.clone(),
@@ -98,13 +101,14 @@ impl AudioState {
                 return;
             }
         };
-        let reader = Reader::Extension(source);
-        let source = input::Input::float_pcm(true, reader);
+        let source = RawAdapter::new(source, 48000, 2);
+        let input: Input = source.into();
+        let input = input.make_live_async().await.expect("Should work?");
 
         let handler = state.handler.clone();
         let mut handler = handler.lock().await;
 
-        let handle = handler.play_source(source);
+        let handle = handler.play_input(input);
         if let Err(e) = handle.set_volume(state.volume) {
             println!("{}", e);
         }
@@ -119,6 +123,7 @@ impl AudioState {
         }
         {
             let text = song.get_string();
+            state.skip_votes.clear();
             send_embed_http(
                 state.channel_id,
                 state.http.clone(),
@@ -153,13 +158,12 @@ impl AudioState {
         }
     }
 
-    pub async fn send_track_command(
+    pub async fn skip(
         audio_state: Arc<Mutex<AudioState>>,
-        cmd: TrackCommand,
     ) -> Result<(), String> {
         let state = audio_state.lock().await;
         match state.track_handle.as_ref() {
-            Some(track_handle) => match track_handle.send(cmd) {
+            Some(track_handle) => match track_handle.stop() {
                 Ok(()) => Ok(()),
                 Err(why) => Err(format!("{:?}", why)),
             },
@@ -167,6 +171,59 @@ impl AudioState {
         }
     }
 
+    pub async fn pause(
+        audio_state: Arc<Mutex<AudioState>>,
+    ) -> Result<(), String> {
+        let state = audio_state.lock().await;
+        match state.track_handle.as_ref() {
+            Some(track_handle) => match track_handle.pause() {
+                Ok(()) => Ok(()),
+                Err(why) => Err(format!("{:?}", why)),
+            },
+            None => Err("no song currently playing".to_string()),
+        }
+    }
+
+    pub async fn play(
+        audio_state: Arc<Mutex<AudioState>>,
+    ) -> Result<(), String> {
+        let state = audio_state.lock().await;
+        match state.track_handle.as_ref() {
+            Some(track_handle) => match track_handle.play() {
+                Ok(()) => Ok(()),
+                Err(why) => Err(format!("{:?}", why)),
+            },
+            None => Err("no song currently playing".to_string()),
+        }
+    }
+
+    pub async fn vote_skip(audio_state: Arc<Mutex<AudioState>>, user: User, total_users: usize) -> Result<(), String> {
+        let audio_state_tmp = audio_state.clone();
+        let mut state = audio_state_tmp.lock().await;
+        if state.skip_votes.contains(&user) {
+            Err("You already voted to skip".to_string())
+        } else {
+            state.skip_votes.insert(user);
+
+            if state.skip_votes.len() > total_users / 2 {
+                let text = match &state.current_song {
+                    Some(song) => song.get_string(),
+                    None => "".to_string(),
+                };
+                send_embed_http(
+                    state.channel_id,
+                    state.http.clone(),
+                    &format!("Skipping:\n\n {}", text),
+                )
+                .await;
+                drop(state);
+                AudioState::skip(audio_state).await
+            } else {
+                Ok(())
+            }
+        }
+    }
+
     pub async fn shuffle(audio_state: Arc<Mutex<AudioState>>) -> Result<(), String> {
         let mut state = audio_state.lock().await;
         state.queue.shuffle()

+ 7 - 6
src/audio/subprocess.rs

@@ -1,5 +1,5 @@
 use serde_json::Value;
-use songbird::input::reader::MediaSource;
+use symphonia_core::io::{MediaSourceStream, MediaSourceStreamOptions};
 use std::{
     io::BufReader,
     process::{Command, Stdio},
@@ -33,7 +33,7 @@ pub async fn ytdl(query: &str) -> Result<Value, std::io::Error> {
     }
 }
 
-pub async fn ffmpeg_pcm(url: &str) -> Result<Box<dyn MediaSource + Send>, String> {
+pub async fn ffmpeg_pcm(url: &str) -> Result<MediaSourceStream, String> {
     let mut cmd = Command::new("ffmpeg");
     let cmd = cmd
         .arg("-reconnect")
@@ -54,7 +54,8 @@ pub async fn ffmpeg_pcm(url: &str) -> Result<Box<dyn MediaSource + Send>, String
         .arg("pcm_f32le")
         .arg("pipe:1")
         .stdout(Stdio::piped())
-        .stderr(Stdio::null());
+        .stderr(Stdio::inherit());
+    println!("{:?}", cmd.get_args());
     let child = match cmd.spawn() {
         Ok(child) => child,
         Err(error) => {
@@ -65,7 +66,7 @@ pub async fn ffmpeg_pcm(url: &str) -> Result<Box<dyn MediaSource + Send>, String
         Some(out) => out,
         None => return Err("subprocess::ffmpeg_pcm: failed to get child stdout".to_string()),
     };
-    let buf = BufReader::with_capacity(16384 * 8, out);
-    let buf: Box<dyn MediaSource + Send> = Box::new(ReadOnlySource::new(buf));
-    Ok(buf)
+    let buffer_len = 16384 * 8;
+    let source = MediaSourceStream::new(Box::new(ReadOnlySource::new(out)), MediaSourceStreamOptions {buffer_len});
+    Ok(source)
 }

+ 4 - 2
src/main.rs

@@ -1,7 +1,7 @@
 use serenity::{
     async_trait,
     client::{Client, Context, EventHandler},
-    framework::StandardFramework,
+    framework::standard::{StandardFramework, Configuration},
     model::gateway::{GatewayIntents, Ready},
 };
 use std::env;
@@ -24,9 +24,11 @@ impl EventHandler for Handler {
 async fn main() {
     let token = env::var("OCTAVE_BOT_TOKEN").expect("Error: token not found");
     let framework = StandardFramework::new()
-        .configure(|c| c.prefix("!"))
         .group(&audio::AUDIO_GROUP);
 
+    framework
+        .configure(Configuration::new().prefix("!"));
+
     let intents = GatewayIntents::non_privileged() | GatewayIntents::MESSAGE_CONTENT;
 
     let mut client = Client::builder(token, intents)

+ 9 - 16
src/util/util.rs

@@ -2,21 +2,18 @@ use serenity::{
     client::Context,
     http::Http,
     model::{channel::Message, id::ChannelId, prelude::ReactionType},
+    builder::{CreateEmbed, CreateMessage}
 };
 
 use std::sync::Arc;
 
 pub async fn send_embed(ctx: &Context, msg: &Message, text: &str) {
+    let embed = CreateEmbed::new().description(text).color(0xf542bf);
+    let builder = CreateMessage::new().embed(embed);
+
     let res = msg
         .channel_id
-        .send_message(&ctx.http, |m| {
-            m.embed(|e| {
-                e.colour(0xf542bf);
-                e.description(text);
-                e
-            });
-            m
-        })
+        .send_message(&ctx.http, builder)
         .await;
     if let Err(why) = res {
         println!("Error sending embed: {:?}", why);
@@ -24,15 +21,11 @@ pub async fn send_embed(ctx: &Context, msg: &Message, text: &str) {
 }
 
 pub async fn send_embed_http(channel_id: ChannelId, http: Arc<Http>, text: &str) {
+    let embed = CreateEmbed::new().description(text).color(0xf542bf);
+    let builder = CreateMessage::new().embed(embed);
+
     let res = channel_id
-        .send_message(http, |m| {
-            m.embed(|e| {
-                e.colour(0xf542bf);
-                e.description(text);
-                e
-            });
-            m
-        })
+        .send_message(http, builder)
         .await;
 
     if let Err(why) = res {

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini