profile
viewpoint

fkaa/flu 10

Lua framework for Rust

fkaa/cl-demo 2

particles

fkaa/editor 2

Particle Editor

fkaa/AdventOfCode15 0

Repository for solutions to http://adventofcode.com/ for 2015

fkaa/AdventOfCode17 0

:christmas_tree:

startedstjepang/smol

started time in 7 hours

Pull request review commentMavulp/pokebot

Use anyhow to return teamspeak errors

 pub enum AudioPlayerError {     SeekError, } +impl std::fmt::Display for AudioPlayerError {+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {+        use AudioPlayerError::*;+        match self {+            MissingPlugin(name) => write!(+                f,+                "The '{}' plugin was not found, make sure it is installed",

s/plugin/GStreamer plugin/

no need for "Make sure it is installed"

Jokler

comment created time in 8 hours

PullRequestReviewEvent

Pull request review commentMavulp/pokebot

Use anyhow to return teamspeak errors

 impl TeamSpeakConnection {                     Ok(ConEvents(events)) => {                         for event in &events {                             if let Some(msg) = get_message(event) {+                                // FIXME Errors are just getting dropped

?

Jokler

comment created time in 8 hours

Pull request review commentMavulp/pokebot

Use anyhow to return teamspeak errors

 impl MasterBot {     async fn bot_args_for_client(         &mut self,         user_id: ClientId,-    ) -> Result<MusicBotArgs, BotCreationError> {-        let channel = match self.teamspeak.channel_of_user(user_id).await {+    ) -> std::result::Result<MusicBotArgs, BotCreationError> {

why normal result?

Jokler

comment created time in 8 hours

PullRequestReviewEvent

startedunicode-org/icu4x

started time in 4 days

push eventMavulp/pokebot

kilmanio

commit sha bde3c019a597fec1834be7a58fc20fb720f185de

Added config for default volume

view details

Felix Kaaman

commit sha d0a3a0a1f4dd3d477e4c5ec323f45ccb32d9350e

Merge pull request #77 from kilmanio/volume-config Added config for default volume

view details

push time in 4 days

PR merged Mavulp/pokebot

Added config for default volume

resolves #71

+11 -1

0 comment

4 changed files

kilmanio

pr closed time in 4 days

issue closedMavulp/pokebot

Add a config option for the default music bot volume

Is your feature request related to a problem? Please describe. When a music bot it has a default volume preset, this volume is often too loud.

Describe the solution you'd like A config option would allow the default volume to be changed per server.

Describe alternatives you've considered Lower the default volume for everyone but since this is subjective I am against it.

Relevant line https://github.com/Mavulp/pokebot/blob/67c11897f164ff825a499f8c1296bb5c90658f06/src/bot/music.rs#L94

closed time in 4 days

Jokler

issue closedMavulp/pokebot

Upload a teamspeak avatar containing more info

Upload an avatar of maybe the current playlist and other information that cant fit in the nickname or description. Can probably have some fun with gif (/dʒɪf/) or even SVG as well..

Probably a good idea to make it an optional feature (in rust terms) since it's a very standalone feature.

Relevant crates:

  • Drawing - https://lib.rs/crates/raqote https://lib.rs/crates/piet
  • Image encoding - https://lib.rs/crates/image

closed time in 13 days

fkaa

issue commentMavulp/pokebot

Upload a teamspeak avatar containing more info

Closing because lack of interest :=)

fkaa

comment created time in 13 days

PullRequestReviewEvent

issue openedMavulp/pokebot

Pipe GStreamer logging to rust

Is your feature request related to a problem? Please describe. Pipe output of GStreamer logging to rust so we can put it through whatever logging framework we have to avoid GStreamer log output competing with rust stdout.

Describe the solution you'd like See https://gstreamer.freedesktop.org/documentation/gstreamer/gstinfo.html?gi-language=c#gst_debug_add_log_function

created time in 14 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl MasterBot {             con_config = con_config.channel(channel);         } -        let connection = TeamSpeakConnection::new(tx.clone(), con_config)-            .await-            .unwrap();+        let connection = TeamSpeakConnection::new(logger.clone()).await.unwrap();+        trace!(logger, "Created teamspeak connection"); -        let config = Arc::new(MasterConfig {+        let config = MasterConfig {             master_name: args.master_name,             address: args.address,-            names: args.names,-            ids: args.ids.expect("identies should exists"),-            local: args.local,             verbose: args.verbose,-        });--        let name_count = config.names.len();-        let id_count = config.ids.len();+        }; -        let music_bots = Arc::new(RwLock::new(MusicBots {+        let bot_addr = Self {+            config,+            my_addr: None,+            teamspeak: connection,+            logger: logger.clone(),             rng: SmallRng::from_entropy(),-            available_names: (0..name_count).collect(),-            available_ids: (0..id_count).collect(),+            available_names: args.names,+            available_ids: args.ids.expect("identities"),             connected_bots: HashMap::new(),-        }));+        }+        .create(None)+        .spawn(&mut Tokio::Global); -        let bot = Arc::new(Self {-            config,-            music_bots,-            teamspeak: connection,-            sender: tx.clone(),-        });--        let cbot = bot.clone();-        let msg_loop = async move {-            'outer: loop {-                while let Some(msg) = rx.recv().await {-                    match msg {-                        MusicBotMessage::Quit(reason) => {-                            let mut cteamspeak = cbot.teamspeak.clone();-                            cteamspeak.disconnect(&reason).await;-                            break 'outer;-                        }-                        MusicBotMessage::ClientDisconnected { id, .. } => {-                            if id == cbot.my_id().await {-                                // TODO Reconnect since quit was not called-                                break 'outer;-                            }-                        }-                        _ => cbot.on_message(msg).await.unwrap(),-                    }-                }-            }-        };+        bot_addr.send(Connect(con_config)).await.unwrap().unwrap();+        trace!(logger, "Spawned master bot actor"); -        (bot, msg_loop)+        bot_addr     } -    async fn build_bot_args_for(&self, id: ClientId) -> Result<MusicBotArgs, BotCreationError> {-        let mut cteamspeak = self.teamspeak.clone();-        let channel = match cteamspeak.channel_of_user(id).await {+    async fn build_bot_args_for(+        &mut self,+        user_id: ClientId,+    ) -> Result<MusicBotArgs, BotCreationError> {+        let channel = match self.teamspeak.channel_of_user(user_id).await {             Some(channel) => channel,             None => return Err(BotCreationError::UnfoundUser),         }; -        if channel == cteamspeak.my_channel().await {+        if channel == self.teamspeak.my_channel().await.unwrap() {             return Err(BotCreationError::MasterChannel(                 self.config.master_name.clone(),             ));         } -        let MusicBots {-            ref mut rng,-            ref mut available_names,-            ref mut available_ids,-            ref connected_bots,-        } = &mut *self.music_bots.write().expect("RwLock was not poisoned");--        for bot in connected_bots.values() {-            if bot.my_channel().await == channel {-                return Err(BotCreationError::MultipleBots(bot.name().to_owned()));+        for bot in self.connected_bots.values() {+            if bot.send(GetChannel).await.unwrap() == Some(channel) {+                return Err(BotCreationError::MultipleBots(+                    bot.send(GetName).await.unwrap(),+                ));             }         } -        let channel_path = cteamspeak-            .channel_path_of_user(id)+        let channel_path = self+            .teamspeak+            .channel_path_of_user(user_id)             .await             .expect("can find poke sender"); -        available_names.shuffle(rng);-        let name_index = match available_names.pop() {+        self.available_names.shuffle(&mut self.rng);+        let name = match self.available_names.pop() {             Some(v) => v,             None => {                 return Err(BotCreationError::OutOfNames);             }         };-        let name = self.config.names[name_index].clone(); -        available_ids.shuffle(rng);-        let id_index = match available_ids.pop() {+        self.available_ids.shuffle(&mut self.rng);+        let identity = match self.available_ids.pop() {             Some(v) => v,             None => {                 return Err(BotCreationError::OutOfIdentities);             }         }; -        let id = self.config.ids[id_index].clone();--        let cmusic_bots = self.music_bots.clone();-        let disconnect_cb = Box::new(move |n, name_index, id_index| {-            let mut music_bots = cmusic_bots.write().expect("RwLock was not poisoned");-            music_bots.connected_bots.remove(&n);-            music_bots.available_names.push(name_index);-            music_bots.available_ids.push(id_index);-        });--        info!("Connecting to {} on {}", channel_path, self.config.address);+        info!(+            self.logger,+            "Connecting"; "channel" => &channel_path, "address" => &self.config.address+        );          Ok(MusicBotArgs {-            name,-            name_index,-            id_index,-            local: self.config.local,+            name: name.clone(),+            master: self.my_addr.clone(),             address: self.config.address.clone(),-            id,+            identity,+            local: false,             channel: channel_path,             verbose: self.config.verbose,-            disconnect_cb,+            logger: self.logger.new(o!("musicbot" => name)),         })     } -    async fn spawn_bot_for(&self, id: ClientId) {+    async fn spawn_bot_for(&mut self, id: ClientId) {

then do s/spawn_bot_for/spawn_bot_for_client/?

Jokler

comment created time in 14 days

PullRequestReviewEvent

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl MusicBot {         Ok(())     } -    async fn on_client_left_channel(&self, old_channel: ChannelId) {-        let my_channel = self.my_channel().await;+    // TODO logs an error if this music bot is the one leaving

TODO now or later?

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 async fn run() -> Result<(), Box<dyn std::error::Error>> {     }      if config.id.is_none() || config.ids.is_none() {-        error!("Failed to find required identites, try running with `-g`");+        error!(+            root_logger,+            "Failed to find required identites, try running with `-g`"+        );         return Ok(());     } +    let local = args.local;     let bot_args = config.merge(args); -    info!("Starting PokeBot!");-    debug!("Received CLI arguments: {:?}", std::env::args());+    info!(root_logger, "Starting PokeBot!");+    debug!(root_logger, "Received CLI arguments"; "args" => ?std::env::args()); -    if bot_args.local {+    if local {         let name = bot_args.names[0].clone();-        let id = bot_args.ids.expect("identies should exists")[0].clone();--        let disconnect_cb = Box::new(move |_, _, _| {});+        let identity = bot_args.ids.expect("identies should exists")[0].clone();          let bot_args = MusicBotArgs {             name,-            name_index: 0,-            id_index: 0,+            master: None,             local: true,             address: bot_args.address.clone(),-            id,+            identity,             channel: String::from("local"),             verbose: bot_args.verbose,-            disconnect_cb,+            logger: root_logger,         };-        MusicBot::new(bot_args).await.1.await;+        MusicBot::new(bot_args).await;++        ctrl_c.await??;     } else {         let domain = bot_args.domain.clone();         let bind_address = bot_args.bind_address.clone();-        let (bot, fut) = MasterBot::new(bot_args).await;+        let bot_name = bot_args.master_name.clone();+        let bot_logger = root_logger.new(o!("master" => bot_name.clone()));+        let bot = MasterBot::new(bot_args, bot_logger).await;++        let weak_bot = bot.downgrade(); -        thread::spawn(|| {+        let clogger = root_logger.clone();+        thread::spawn(move || {             let web_args = web_server::WebServerArgs {                 domain,                 bind_address,-                bot,+                bot: weak_bot,             };-            if let Err(e) = web_server::start(web_args) {-                error!("Error in web server: {}", e);+            if let Err(e) = web_server::start(web_args, clogger.new(o!("webserver" => bot_name))) {+                error!(clogger, "Error in web server"; "error" => %e);             }         }); -        fut.await;-        // Keep tokio running while the bot disconnects-        tokio::time::delay_for(Duration::from_secs(1)).await;+        #[cfg(unix)]+        tokio::select! {+            res = ctrl_c => {+                res??;+                info!(root_logger, "Received signal, shutting down"; "signal" => "SIGINT");+            }+            _ = sigterm => {+                info!(root_logger, "Received signal, shutting down"; "signal" => "SIGTERM");+            }+            _ = sighup => {+                info!(root_logger, "Received signal, shutting down"; "signal" => "SIGHUP");+            }+            _ = sigquit => {+                info!(root_logger, "Received signal, shutting down"; "signal" => "SIGQUIT");+            }+        };++        #[cfg(windows)]+        ctrl_c.await??;++        bot.send(Quit(String::from("Stopping")))+            .await+            .unwrap()+            .unwrap();

We should look into anyhow or something so we can ?? from everywhere :-)

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl MasterBot {         Ok(())     } -    async fn my_id(&self) -> ClientId {-        let mut cteamspeak = self.teamspeak.clone();+    pub async fn bot_data(&self, name: String) -> Option<crate::web_server::BotData> {+        let bot = self.connected_bots.get(&name)?; -        cteamspeak.my_id().await+        bot.send(GetBotData).await.ok()     } -    pub fn bot_data(&self, name: String) -> Option<crate::web_server::BotData> {-        let music_bots = self.music_bots.read().unwrap();-        let bot = music_bots.connected_bots.get(&name)?;--        Some(crate::web_server::BotData {-            name,-            state: bot.state(),-            volume: bot.volume(),-            position: bot.position(),-            currently_playing: bot.currently_playing(),-            playlist: bot.playlist_to_vec(),-        })-    }--    pub fn bot_datas(&self) -> Vec<crate::web_server::BotData> {-        let music_bots = self.music_bots.read().unwrap();--        let len = music_bots.connected_bots.len();+    pub async fn bot_datas(&self) -> Vec<crate::web_server::BotData> {+        let len = self.connected_bots.len();         let mut result = Vec::with_capacity(len);-        for (name, bot) in &music_bots.connected_bots {-            let bot_data = crate::web_server::BotData {-                name: name.clone(),-                state: bot.state(),-                volume: bot.volume(),-                position: bot.position(),-                currently_playing: bot.currently_playing(),-                playlist: bot.playlist_to_vec(),-            };-+        for bot in self.connected_bots.values() {+            let bot_data = bot.send(GetBotData).await.unwrap();             result.push(bot_data);         }          result     }      pub fn bot_names(&self) -> Vec<String> {-        let music_bots = self.music_bots.read().unwrap();--        let len = music_bots.connected_bots.len();+        let len = self.connected_bots.len();         let mut result = Vec::with_capacity(len);-        for name in music_bots.connected_bots.keys() {+        for name in self.connected_bots.keys() {             result.push(name.clone());         }          result     } -    pub fn quit(&self, reason: String) {-        let music_bots = self.music_bots.read().unwrap();-        for bot in music_bots.connected_bots.values() {-            bot.quit(reason.clone())+    fn on_bot_disconnect(&mut self, name: String, id: Identity) {+        self.connected_bots.remove(&name);+        self.available_names.push(name);+        self.available_ids.push(id);+    }++    pub async fn quit(&mut self, reason: String) -> Result<(), tsclientlib::Error> {+        let futures = self+            .connected_bots+            .values()+            .map(|b| b.send(Quit(reason.clone())));+        for res in futures::future::join_all(futures).await {

use futures::future

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 async fn run() -> Result<(), Box<dyn std::error::Error>> {     }      if config.id.is_none() || config.ids.is_none() {-        error!("Failed to find required identites, try running with `-g`");+        error!(+            root_logger,+            "Failed to find required identites, try running with `-g`"+        );         return Ok(());     } +    let local = args.local;     let bot_args = config.merge(args); -    info!("Starting PokeBot!");-    debug!("Received CLI arguments: {:?}", std::env::args());+    info!(root_logger, "Starting PokeBot!");+    debug!(root_logger, "Received CLI arguments"; "args" => ?std::env::args()); -    if bot_args.local {+    if local {         let name = bot_args.names[0].clone();-        let id = bot_args.ids.expect("identies should exists")[0].clone();--        let disconnect_cb = Box::new(move |_, _, _| {});+        let identity = bot_args.ids.expect("identies should exists")[0].clone();          let bot_args = MusicBotArgs {             name,-            name_index: 0,-            id_index: 0,+            master: None,             local: true,             address: bot_args.address.clone(),-            id,+            identity,             channel: String::from("local"),             verbose: bot_args.verbose,-            disconnect_cb,+            logger: root_logger,         };-        MusicBot::new(bot_args).await.1.await;+        MusicBot::new(bot_args).await;++        ctrl_c.await??;     } else {         let domain = bot_args.domain.clone();         let bind_address = bot_args.bind_address.clone();-        let (bot, fut) = MasterBot::new(bot_args).await;+        let bot_name = bot_args.master_name.clone();+        let bot_logger = root_logger.new(o!("master" => bot_name.clone()));+        let bot = MasterBot::new(bot_args, bot_logger).await;++        let weak_bot = bot.downgrade(); -        thread::spawn(|| {+        let clogger = root_logger.clone();+        thread::spawn(move || {             let web_args = web_server::WebServerArgs {                 domain,                 bind_address,-                bot,+                bot: weak_bot,             };-            if let Err(e) = web_server::start(web_args) {-                error!("Error in web server: {}", e);+            if let Err(e) = web_server::start(web_args, clogger.new(o!("webserver" => bot_name))) {+                error!(clogger, "Error in web server"; "error" => %e);             }         }); -        fut.await;-        // Keep tokio running while the bot disconnects-        tokio::time::delay_for(Duration::from_secs(1)).await;+        #[cfg(unix)]+        tokio::select! {+            res = ctrl_c => {+                res??;+                info!(root_logger, "Received signal, shutting down"; "signal" => "SIGINT");+            }+            _ = sigterm => {+                info!(root_logger, "Received signal, shutting down"; "signal" => "SIGTERM");+            }+            _ = sighup => {+                info!(root_logger, "Received signal, shutting down"; "signal" => "SIGHUP");+            }+            _ = sigquit => {+                info!(root_logger, "Received signal, shutting down"; "signal" => "SIGQUIT");+            }+        };++        #[cfg(windows)]+        ctrl_c.await??;++        bot.send(Quit(String::from("Stopping")))+            .await+            .unwrap()+            .unwrap();     }      Ok(()) }++pub async fn terminate() -> std::io::Result<()> {+    signal(SignalKind::terminate())?.recv().await;+    Ok(())+}++pub async fn hangup() -> std::io::Result<()> {+    signal(SignalKind::hangup())?.recv().await;+    Ok(())+}++pub async fn quit() -> std::io::Result<()> {+    signal(SignalKind::quit())?.recv().await;+    Ok(())+}

Shouldn't these be #[cfg(unix)] as well?

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 async fn run() -> Result<(), Box<dyn std::error::Error>> {     }      if config.id.is_none() || config.ids.is_none() {-        error!("Failed to find required identites, try running with `-g`");+        error!(+            root_logger,+            "Failed to find required identites, try running with `-g`"+        );         return Ok(());     } +    let local = args.local;     let bot_args = config.merge(args); -    info!("Starting PokeBot!");-    debug!("Received CLI arguments: {:?}", std::env::args());+    info!(root_logger, "Starting PokeBot!");+    debug!(root_logger, "Received CLI arguments"; "args" => ?std::env::args()); -    if bot_args.local {+    if local {         let name = bot_args.names[0].clone();-        let id = bot_args.ids.expect("identies should exists")[0].clone();--        let disconnect_cb = Box::new(move |_, _, _| {});+        let identity = bot_args.ids.expect("identies should exists")[0].clone();          let bot_args = MusicBotArgs {             name,-            name_index: 0,-            id_index: 0,+            master: None,             local: true,             address: bot_args.address.clone(),-            id,+            identity,             channel: String::from("local"),             verbose: bot_args.verbose,-            disconnect_cb,+            logger: root_logger,         };-        MusicBot::new(bot_args).await.1.await;+        MusicBot::new(bot_args).await;++        ctrl_c.await??;

??!

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 fn get_message(event: &Event) -> Option<MusicBotMessage> { }  impl TeamSpeakConnection {-    pub async fn new(-        tx: Arc<RwLock<UnboundedSender<MusicBotMessage>>>,+    pub async fn new(logger: Logger) -> Result<TeamSpeakConnection, tsclientlib::Error> {+        Ok(TeamSpeakConnection {+            handle: None,+            logger,+        })+    }++    pub fn connect_for_bot<T: Actor + Handler<MusicBotMessage>>(+        &mut self,         options: ConnectOptions,-    ) -> Result<TeamSpeakConnection, tsclientlib::Error> {+        bot: WeakAddress<T>,+    ) -> Result<(), tsclientlib::Error> {+        info!(self.logger, "Starting TeamSpeak connection");+         let conn = options.connect()?;-        let conn = SyncConnection::from(conn);-        let mut handle = conn.get_handle();--        tokio::spawn(conn.for_each(move |i| {-            let tx = tx.clone();-            async move {-                match i {-                    Ok(SyncStreamItem::ConEvents(events)) => {+        let mut conn = SyncConnection::from(conn);+        let handle = conn.get_handle();+        self.handle = Some(handle);++        let ev_logger = self.logger.clone();+        tokio::spawn(async move {+            while let Some(item) = conn.next().await {+                use SyncStreamItem::*;++                match item {+                    Ok(ConEvents(events)) => {                         for event in &events {                             if let Some(msg) = get_message(event) {-                                let tx = tx.read().expect("RwLock was not poisoned");-                                // Ignore the result because the receiver might get dropped first.-                                let _ = tx.send(msg);+                                tokio::spawn(bot.send(msg));                             }                         }                     }-                    Err(e) => error!("Error occured during event reading: {}", e),-                    Ok(SyncStreamItem::DisconnectedTemporarily) => debug!("Temporary disconnect!"),-                    _ => (),+                    Err(e) => error!(ev_logger, "Error occured during event reading: {}", e),+                    Ok(DisconnectedTemporarily(r)) => {+                        debug!(ev_logger, "Temporary disconnect"; "reason" => ?r)+                    }+                    Ok(Audio(_)) => {+                        trace!(ev_logger, "Audio received");+                    }+                    Ok(IdentityLevelIncreasing(_)) => {+                        trace!(ev_logger, "Identity level increasing");+                    }+                    Ok(IdentityLevelIncreased) => {+                        trace!(ev_logger, "Identity level increased");+                    }+                    Ok(NetworkStatsUpdated) => {+                        trace!(ev_logger, "Network stats updated");+                    }                 }             }-        }));--        handle.wait_until_connected().await?;--        let mut chandle = handle.clone();-        chandle-            .with_connection(|mut conn| {-                conn.get_state()-                    .expect("is connected")-                    .server-                    .set_subscribed(true)-                    .send(&mut conn)-                    .unwrap()-            })-            .await-            .unwrap();--        Ok(TeamSpeakConnection { handle })+        });++        let mut handle = self.handle.clone();+        tokio::spawn(async move {+            handle+                .as_mut()+                .expect("connect_for_bot was called")+                .wait_until_connected()+                .await+                .unwrap();+            handle+                .as_mut()+                .expect("connect_for_bot was called")+                .with_connection(|mut conn| {+                    conn.get_state()+                        .expect("can get state")+                        .server+                        .set_subscribed(true)+                        .send(&mut conn)+                })+                .await+                .and_then(|v| v)

isn't this a no-op?

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 where     Ok(dur.map(Duration::from_secs_f64)) } -pub async fn get_audio_download_from_url(uri: String) -> Result<AudioMetadata, String> {+pub async fn get_audio_download_from_url(

get_audio_metadata

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl TeamSpeakConnection {     }      pub async fn set_nickname(&mut self, name: String) {-        self.handle+        if let Err(e) = self+            .handle+            .as_mut()+            .expect("connect_for_bot was called")             .with_connection(move |mut conn| {                 conn.get_state()                     .expect("can get state")                     .client_update()                     .set_name(&name)                     .send(&mut conn)-                    .map_err(|e| error!("Failed to set nickname: {}", e))             })             .await-            .unwrap()-            .unwrap();+            .and_then(|v| v)+        {+            error!(self.logger, "Failed to set nickname: {}", e);+        }     }      pub async fn set_description(&mut self, desc: String) {-        self.handle+        if let Err(e) = self+            .handle+            .as_mut()+            .expect("connect_for_bot was called")             .with_connection(move |mut conn| {                 let state = conn.get_state().expect("can get state");-                let _ = state+                state                     .clients                     .get(&state.own_client)                     .expect("can get myself")                     .edit()                     .set_description(&desc)                     .send(&mut conn)-                    .map_err(|e| error!("Failed to change description: {}", e));             })             .await-            .unwrap()+            .and_then(|v| v)+        {+            error!(self.logger, "Failed to change description: {}", e);+        }     }      pub async fn send_message_to_channel(&mut self, text: String) {-        self.handle+        if let Err(e) = self+            .handle+            .as_mut()+            .expect("connect_for_bot was called")             .with_connection(move |mut conn| {-                let _ = conn-                    .get_state()+                conn.get_state()                     .expect("can get state")                     .send_message(MessageTarget::Channel, &text)                     .send(&mut conn)-                    .map_err(|e| error!("Failed to send message: {}", e));             })             .await-            .unwrap()+            .and_then(|v| v)+        {+            error!(self.logger, "Failed to send message: {}", e);+        }     }      pub async fn send_message_to_user(&mut self, client: ClientId, text: String) {-        self.handle+        if let Err(e) = self+            .handle+            .as_mut()+            .expect("connect_for_bot was called")             .with_connection(move |mut conn| {-                let _ = conn-                    .get_state()+                conn.get_state()                     .expect("can get state")                     .send_message(MessageTarget::Client(client), &text)                     .send(&mut conn)-                    .map_err(|e| error!("Failed to send message: {}", e));             })             .await-            .unwrap()+            .and_then(|v| v)+        {+            error!(self.logger, "Failed to send message: {}", e);+        }     } -    pub async fn subscribe(&mut self, id: ChannelId) {-        self.handle-            .with_connection(move |mut conn| {-                let channel = match conn.get_state().expect("can get state").channels.get(&id) {-                    Some(c) => c,-                    None => {-                        error!("Failed to find channel to subscribe to");-                        return;-                    }-                };--                if let Err(e) = channel.set_subscribed(true).send(&mut conn) {-                    error!("Failed to send subscribe packet: {}", e);-                }-            })-            .await-            .unwrap()-    }--    pub async fn disconnect(&mut self, reason: &str) {+    pub async fn disconnect(&mut self, reason: &str) -> Result<(), tsclientlib::Error> {         let opt = DisconnectOptions::new()             .reason(Reason::Clientdisconnect)             .message(reason);-        self.handle.disconnect(opt).await.unwrap();+        self.handle+            .as_mut()+            .expect("connect_for_bot was called")

wrong expect message?

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl MusicBot {                 }             }             Command::Next => {-                let playlist = self.playlist.read().expect("RwLock was not poisoned");-                if !playlist.is_empty() {-                    info!("Skipping to next track");+                if !self.playlist.is_empty() {+                    info!(self.logger, "Skipping to next track");                     self.player.stop_current()?;                 } else {-                    info!("Playlist empty, cannot skip");+                    info!(self.logger, "Playlist empty, cannot skip");                     self.player.reset()?;                 }             }             Command::Clear => {-                self.playlist-                    .write()-                    .expect("RwLock was not poisoned")-                    .clear();+                self.send_message(String::from("Cleared playlist")).await;+                self.playlist.clear();             }             Command::Volume { volume } => {                 self.player.change_volume(volume)?;                 self.update_name(self.state()).await;             }             Command::Leave => {-                self.quit(String::from("Leaving"));+                self.quit(String::from("Leaving"), true).await.unwrap();             }         }          Ok(())     } -    async fn update_name(&self, state: State) {-        let volume = (self.volume() * 100.0).round();+    async fn update_name(&mut self, state: State) {+        let volume = (self.volume().await * 100.0).round();         let name = match state {             State::EndOfStream => format!("🎵 {} ({}%)", self.name, volume),             _ => format!("🎵 {} - {} ({}%)", self.name, state, volume),         };         self.set_nickname(name).await;     } -    async fn on_state(&self, state: State) -> Result<(), AudioPlayerError> {-        let current_state = *self.state.read().unwrap();-        if current_state != state {+    async fn on_state(&mut self, state: State) -> Result<(), AudioPlayerError> {

s/state/new_state/g to make code clearer

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 where     Ok(dur.map(Duration::from_secs_f64)) } -pub async fn get_audio_download_from_url(uri: String) -> Result<AudioMetadata, String> {+pub async fn get_audio_download_from_url(+    uri: String,+    logger: &Logger,+) -> Result<AudioMetadata, String> {     //youtube-dl sometimes just fails, so we give it a second try

fuckywucky 😠

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 async fn run() -> Result<(), Box<dyn std::error::Error>> {     }      if config.id.is_none() || config.ids.is_none() {-        error!("Failed to find required identites, try running with `-g`");+        error!(+            root_logger,+            "Failed to find required identites, try running with `-g`"+        );         return Ok(());     } +    let local = args.local;     let bot_args = config.merge(args); -    info!("Starting PokeBot!");-    debug!("Received CLI arguments: {:?}", std::env::args());+    info!(root_logger, "Starting PokeBot!");+    debug!(root_logger, "Received CLI arguments"; "args" => ?std::env::args()); -    if bot_args.local {+    if local {         let name = bot_args.names[0].clone();-        let id = bot_args.ids.expect("identies should exists")[0].clone();--        let disconnect_cb = Box::new(move |_, _, _| {});+        let identity = bot_args.ids.expect("identies should exists")[0].clone();          let bot_args = MusicBotArgs {             name,-            name_index: 0,-            id_index: 0,+            master: None,             local: true,             address: bot_args.address.clone(),-            id,+            identity,             channel: String::from("local"),             verbose: bot_args.verbose,-            disconnect_cb,+            logger: root_logger,         };-        MusicBot::new(bot_args).await.1.await;+        MusicBot::new(bot_args).await;++        ctrl_c.await??;     } else {         let domain = bot_args.domain.clone();         let bind_address = bot_args.bind_address.clone();-        let (bot, fut) = MasterBot::new(bot_args).await;+        let bot_name = bot_args.master_name.clone();+        let bot_logger = root_logger.new(o!("master" => bot_name.clone()));+        let bot = MasterBot::new(bot_args, bot_logger).await;++        let weak_bot = bot.downgrade(); -        thread::spawn(|| {+        let clogger = root_logger.clone();

lol

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl TeamSpeakConnection {     }      pub async fn set_nickname(&mut self, name: String) {-        self.handle+        if let Err(e) = self+            .handle+            .as_mut()+            .expect("connect_for_bot was called")             .with_connection(move |mut conn| {                 conn.get_state()                     .expect("can get state")                     .client_update()                     .set_name(&name)                     .send(&mut conn)-                    .map_err(|e| error!("Failed to set nickname: {}", e))             })             .await-            .unwrap()-            .unwrap();+            .and_then(|v| v)+        {+            error!(self.logger, "Failed to set nickname: {}", e);+        }     }      pub async fn set_description(&mut self, desc: String) {-        self.handle+        if let Err(e) = self+            .handle+            .as_mut()+            .expect("connect_for_bot was called")             .with_connection(move |mut conn| {                 let state = conn.get_state().expect("can get state");-                let _ = state+                state                     .clients                     .get(&state.own_client)                     .expect("can get myself")                     .edit()                     .set_description(&desc)                     .send(&mut conn)-                    .map_err(|e| error!("Failed to change description: {}", e));             })             .await-            .unwrap()+            .and_then(|v| v)

big if, not sure if we can make these better, maybe get better at propagating errors?

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 async fn run() -> Result<(), Box<dyn std::error::Error>> {     }      if config.id.is_none() || config.ids.is_none() {-        error!("Failed to find required identites, try running with `-g`");+        error!(+            root_logger,+            "Failed to find required identites, try running with `-g`"+        );         return Ok(());     } +    let local = args.local;     let bot_args = config.merge(args); -    info!("Starting PokeBot!");-    debug!("Received CLI arguments: {:?}", std::env::args());+    info!(root_logger, "Starting PokeBot!");+    debug!(root_logger, "Received CLI arguments"; "args" => ?std::env::args()); -    if bot_args.local {+    if local {         let name = bot_args.names[0].clone();-        let id = bot_args.ids.expect("identies should exists")[0].clone();--        let disconnect_cb = Box::new(move |_, _, _| {});+        let identity = bot_args.ids.expect("identies should exists")[0].clone();          let bot_args = MusicBotArgs {             name,-            name_index: 0,-            id_index: 0,+            master: None,             local: true,             address: bot_args.address.clone(),-            id,+            identity,             channel: String::from("local"),             verbose: bot_args.verbose,-            disconnect_cb,+            logger: root_logger,         };-        MusicBot::new(bot_args).await.1.await;+        MusicBot::new(bot_args).await;++        ctrl_c.await??;     } else {         let domain = bot_args.domain.clone();         let bind_address = bot_args.bind_address.clone();-        let (bot, fut) = MasterBot::new(bot_args).await;+        let bot_name = bot_args.master_name.clone();+        let bot_logger = root_logger.new(o!("master" => bot_name.clone()));+        let bot = MasterBot::new(bot_args, bot_logger).await;++        let weak_bot = bot.downgrade(); -        thread::spawn(|| {+        let clogger = root_logger.clone();+        thread::spawn(move || {

should probably move into a function spawn_web_server

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl MusicBot {         self.player.play().unwrap();     } -    pub async fn add_audio(&self, url: String, user: String) {-        match crate::youtube_dl::get_audio_download_from_url(url).await {+    pub async fn add_audio(&mut self, url: String, user: String) {+        match crate::youtube_dl::get_audio_download_from_url(url, &self.logger).await {

use namespace

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 pub enum MusicBotMessage {         client: Box<data::Client>,     },     StateChange(State),-    Quit(String),+}++impl Message for MusicBotMessage {+    type Result = Result<(), AudioPlayerError>; }  pub struct MusicBot {     name: String,-    player: Arc<AudioPlayer>,+    identity: Identity,+    player: AudioPlayer,     teamspeak: Option<TeamSpeakConnection>,-    playlist: Arc<RwLock<Playlist>>,-    state: Arc<RwLock<State>>,+    master: Option<WeakAddress<MasterBot>>,+    playlist: Playlist,+    state: State,+    logger: Logger, }  pub struct MusicBotArgs {     pub name: String,-    pub name_index: usize,-    pub id_index: usize,+    pub master: Option<WeakAddress<MasterBot>>,     pub local: bool,     pub address: String,-    pub id: Identity,+    pub identity: Identity,     pub channel: String,     pub verbose: u8,-    pub disconnect_cb: Box<dyn FnMut(String, usize, usize) + Send + Sync>,+    pub logger: Logger, }  impl MusicBot {-    pub async fn new(args: MusicBotArgs) -> (Arc<Self>, impl Future<Output = ()>) {-        let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();-        let tx = Arc::new(RwLock::new(tx));-        let (player, connection) = if args.local {-            info!("Starting in CLI mode");-            let audio_player = AudioPlayer::new(tx.clone(), None).unwrap();--            (audio_player, None)-        } else {-            info!("Starting in TeamSpeak mode");--            let con_config = Connection::build(args.address)-                .version(tsclientlib::Version::Linux_3_3_2)-                .name(format!("🎵 {}", args.name))-                .identity(args.id)-                .log_commands(args.verbose >= 1)-                .log_packets(args.verbose >= 2)-                .log_udp_packets(args.verbose >= 3)-                .channel(args.channel);--            let connection = TeamSpeakConnection::new(tx.clone(), con_config)-                .await-                .unwrap();-            let mut cconnection = connection.clone();-            let audio_player = AudioPlayer::new(-                tx.clone(),-                Some(Box::new(move |samples| {-                    let mut rt = tokio::runtime::Runtime::new().unwrap();-                    rt.block_on(cconnection.send_audio_packet(samples));-                })),-            )-            .unwrap();--            (audio_player, Some(connection))-        };-+    pub async fn new(args: MusicBotArgs) -> Address<Self> {+        let mut player = AudioPlayer::new(args.logger.clone()).unwrap();         player.change_volume(VolumeChange::Absolute(0.5)).unwrap();-        let player = Arc::new(player);-        let playlist = Arc::new(RwLock::new(Playlist::new())); -        spawn_gstreamer_thread(player.clone(), tx.clone());+        let playlist = Playlist::new(args.logger.clone()); -        if args.local {-            spawn_stdin_reader(tx);-        }+        let teamspeak = if args.local {+            info!(args.logger, "Starting in CLI mode");+            debug!(args.logger, "Spawning stdin reader thread");

no we're not :-o

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl MasterBot {             con_config = con_config.channel(channel);         } -        let connection = TeamSpeakConnection::new(tx.clone(), con_config)-            .await-            .unwrap();+        let connection = TeamSpeakConnection::new(logger.clone()).await.unwrap();+        trace!(logger, "Created teamspeak connection"); -        let config = Arc::new(MasterConfig {+        let config = MasterConfig {             master_name: args.master_name,             address: args.address,-            names: args.names,-            ids: args.ids.expect("identies should exists"),-            local: args.local,             verbose: args.verbose,-        });--        let name_count = config.names.len();-        let id_count = config.ids.len();+        }; -        let music_bots = Arc::new(RwLock::new(MusicBots {+        let bot_addr = Self {+            config,+            my_addr: None,+            teamspeak: connection,+            logger: logger.clone(),             rng: SmallRng::from_entropy(),-            available_names: (0..name_count).collect(),-            available_ids: (0..id_count).collect(),+            available_names: args.names,+            available_ids: args.ids.expect("identities"),             connected_bots: HashMap::new(),-        }));+        }+        .create(None)+        .spawn(&mut Tokio::Global); -        let bot = Arc::new(Self {-            config,-            music_bots,-            teamspeak: connection,-            sender: tx.clone(),-        });--        let cbot = bot.clone();-        let msg_loop = async move {-            'outer: loop {-                while let Some(msg) = rx.recv().await {-                    match msg {-                        MusicBotMessage::Quit(reason) => {-                            let mut cteamspeak = cbot.teamspeak.clone();-                            cteamspeak.disconnect(&reason).await;-                            break 'outer;-                        }-                        MusicBotMessage::ClientDisconnected { id, .. } => {-                            if id == cbot.my_id().await {-                                // TODO Reconnect since quit was not called-                                break 'outer;-                            }-                        }-                        _ => cbot.on_message(msg).await.unwrap(),-                    }-                }-            }-        };+        bot_addr.send(Connect(con_config)).await.unwrap().unwrap();+        trace!(logger, "Spawned master bot actor"); -        (bot, msg_loop)+        bot_addr     } -    async fn build_bot_args_for(&self, id: ClientId) -> Result<MusicBotArgs, BotCreationError> {-        let mut cteamspeak = self.teamspeak.clone();-        let channel = match cteamspeak.channel_of_user(id).await {+    async fn build_bot_args_for(

s/build/get/?

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 where     Ok(dur.map(Duration::from_secs_f64)) } -pub async fn get_audio_download_from_url(uri: String) -> Result<AudioMetadata, String> {+pub async fn get_audio_download_from_url(

I'll just make a PR after this one 🦺

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl TeamSpeakConnection {                 Some(path)             })             .await-            .unwrap()+            .map_err(|e| error!(self.logger, "Failed to get channel path of user"; "error" => %e))+            .ok()+            .and_then(|v| v)     } -    pub async fn my_channel(&mut self) -> ChannelId {+    pub async fn my_channel(&mut self) -> Option<ChannelId> {

current_channel?

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 pub enum MusicBotMessage {         client: Box<data::Client>,     },     StateChange(State),-    Quit(String),+}++impl Message for MusicBotMessage {+    type Result = Result<(), AudioPlayerError>; }  pub struct MusicBot {     name: String,-    player: Arc<AudioPlayer>,+    identity: Identity,+    player: AudioPlayer,     teamspeak: Option<TeamSpeakConnection>,-    playlist: Arc<RwLock<Playlist>>,-    state: Arc<RwLock<State>>,+    master: Option<WeakAddress<MasterBot>>,+    playlist: Playlist,+    state: State,+    logger: Logger, }  pub struct MusicBotArgs {     pub name: String,-    pub name_index: usize,-    pub id_index: usize,+    pub master: Option<WeakAddress<MasterBot>>,     pub local: bool,     pub address: String,-    pub id: Identity,+    pub identity: Identity,     pub channel: String,     pub verbose: u8,-    pub disconnect_cb: Box<dyn FnMut(String, usize, usize) + Send + Sync>,+    pub logger: Logger, }  impl MusicBot {-    pub async fn new(args: MusicBotArgs) -> (Arc<Self>, impl Future<Output = ()>) {-        let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();-        let tx = Arc::new(RwLock::new(tx));-        let (player, connection) = if args.local {-            info!("Starting in CLI mode");-            let audio_player = AudioPlayer::new(tx.clone(), None).unwrap();--            (audio_player, None)-        } else {-            info!("Starting in TeamSpeak mode");--            let con_config = Connection::build(args.address)-                .version(tsclientlib::Version::Linux_3_3_2)-                .name(format!("🎵 {}", args.name))-                .identity(args.id)-                .log_commands(args.verbose >= 1)-                .log_packets(args.verbose >= 2)-                .log_udp_packets(args.verbose >= 3)-                .channel(args.channel);--            let connection = TeamSpeakConnection::new(tx.clone(), con_config)-                .await-                .unwrap();-            let mut cconnection = connection.clone();-            let audio_player = AudioPlayer::new(-                tx.clone(),-                Some(Box::new(move |samples| {-                    let mut rt = tokio::runtime::Runtime::new().unwrap();-                    rt.block_on(cconnection.send_audio_packet(samples));-                })),-            )-            .unwrap();--            (audio_player, Some(connection))-        };-+    pub async fn new(args: MusicBotArgs) -> Address<Self> {

do we change to MusicBot::spawn(args) maybe? since we return the spawned actor's address now

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl MasterBot {         Ok(())     } -    async fn my_id(&self) -> ClientId {-        let mut cteamspeak = self.teamspeak.clone();+    pub async fn bot_data(&self, name: String) -> Option<crate::web_server::BotData> {+        let bot = self.connected_bots.get(&name)?; -        cteamspeak.my_id().await+        bot.send(GetBotData).await.ok()     } -    pub fn bot_data(&self, name: String) -> Option<crate::web_server::BotData> {-        let music_bots = self.music_bots.read().unwrap();-        let bot = music_bots.connected_bots.get(&name)?;--        Some(crate::web_server::BotData {-            name,-            state: bot.state(),-            volume: bot.volume(),-            position: bot.position(),-            currently_playing: bot.currently_playing(),-            playlist: bot.playlist_to_vec(),-        })-    }--    pub fn bot_datas(&self) -> Vec<crate::web_server::BotData> {-        let music_bots = self.music_bots.read().unwrap();--        let len = music_bots.connected_bots.len();+    pub async fn bot_datas(&self) -> Vec<crate::web_server::BotData> {+        let len = self.connected_bots.len();         let mut result = Vec::with_capacity(len);-        for (name, bot) in &music_bots.connected_bots {-            let bot_data = crate::web_server::BotData {-                name: name.clone(),-                state: bot.state(),-                volume: bot.volume(),-                position: bot.position(),-                currently_playing: bot.currently_playing(),-                playlist: bot.playlist_to_vec(),-            };-+        for bot in self.connected_bots.values() {+            let bot_data = bot.send(GetBotData).await.unwrap();             result.push(bot_data);         }          result     }      pub fn bot_names(&self) -> Vec<String> {

get

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl MasterBot {         Ok(())     } -    async fn my_id(&self) -> ClientId {-        let mut cteamspeak = self.teamspeak.clone();+    pub async fn bot_data(&self, name: String) -> Option<crate::web_server::BotData> {+        let bot = self.connected_bots.get(&name)?; -        cteamspeak.my_id().await+        bot.send(GetBotData).await.ok()     } -    pub fn bot_data(&self, name: String) -> Option<crate::web_server::BotData> {-        let music_bots = self.music_bots.read().unwrap();-        let bot = music_bots.connected_bots.get(&name)?;--        Some(crate::web_server::BotData {-            name,-            state: bot.state(),-            volume: bot.volume(),-            position: bot.position(),-            currently_playing: bot.currently_playing(),-            playlist: bot.playlist_to_vec(),-        })-    }--    pub fn bot_datas(&self) -> Vec<crate::web_server::BotData> {-        let music_bots = self.music_bots.read().unwrap();--        let len = music_bots.connected_bots.len();+    pub async fn bot_datas(&self) -> Vec<crate::web_server::BotData> {

get

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl MasterBot {         Ok(())     } -    async fn my_id(&self) -> ClientId {-        let mut cteamspeak = self.teamspeak.clone();+    pub async fn bot_data(&self, name: String) -> Option<crate::web_server::BotData> {

get_bot_data

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl MasterBot {             con_config = con_config.channel(channel);         } -        let connection = TeamSpeakConnection::new(tx.clone(), con_config)-            .await-            .unwrap();+        let connection = TeamSpeakConnection::new(logger.clone()).await.unwrap();+        trace!(logger, "Created teamspeak connection"); -        let config = Arc::new(MasterConfig {+        let config = MasterConfig {             master_name: args.master_name,             address: args.address,-            names: args.names,-            ids: args.ids.expect("identies should exists"),-            local: args.local,             verbose: args.verbose,-        });--        let name_count = config.names.len();-        let id_count = config.ids.len();+        }; -        let music_bots = Arc::new(RwLock::new(MusicBots {+        let bot_addr = Self {+            config,+            my_addr: None,+            teamspeak: connection,+            logger: logger.clone(),             rng: SmallRng::from_entropy(),-            available_names: (0..name_count).collect(),-            available_ids: (0..id_count).collect(),+            available_names: args.names,+            available_ids: args.ids.expect("identities"),             connected_bots: HashMap::new(),-        }));+        }+        .create(None)+        .spawn(&mut Tokio::Global); -        let bot = Arc::new(Self {-            config,-            music_bots,-            teamspeak: connection,-            sender: tx.clone(),-        });--        let cbot = bot.clone();-        let msg_loop = async move {-            'outer: loop {-                while let Some(msg) = rx.recv().await {-                    match msg {-                        MusicBotMessage::Quit(reason) => {-                            let mut cteamspeak = cbot.teamspeak.clone();-                            cteamspeak.disconnect(&reason).await;-                            break 'outer;-                        }-                        MusicBotMessage::ClientDisconnected { id, .. } => {-                            if id == cbot.my_id().await {-                                // TODO Reconnect since quit was not called-                                break 'outer;-                            }-                        }-                        _ => cbot.on_message(msg).await.unwrap(),-                    }-                }-            }-        };+        bot_addr.send(Connect(con_config)).await.unwrap().unwrap();+        trace!(logger, "Spawned master bot actor"); -        (bot, msg_loop)+        bot_addr     } -    async fn build_bot_args_for(&self, id: ClientId) -> Result<MusicBotArgs, BotCreationError> {-        let mut cteamspeak = self.teamspeak.clone();-        let channel = match cteamspeak.channel_of_user(id).await {+    async fn build_bot_args_for(+        &mut self,+        user_id: ClientId,+    ) -> Result<MusicBotArgs, BotCreationError> {+        let channel = match self.teamspeak.channel_of_user(user_id).await {             Some(channel) => channel,             None => return Err(BotCreationError::UnfoundUser),         }; -        if channel == cteamspeak.my_channel().await {+        if channel == self.teamspeak.my_channel().await.unwrap() {             return Err(BotCreationError::MasterChannel(                 self.config.master_name.clone(),             ));         } -        let MusicBots {-            ref mut rng,-            ref mut available_names,-            ref mut available_ids,-            ref connected_bots,-        } = &mut *self.music_bots.write().expect("RwLock was not poisoned");--        for bot in connected_bots.values() {-            if bot.my_channel().await == channel {-                return Err(BotCreationError::MultipleBots(bot.name().to_owned()));+        for bot in self.connected_bots.values() {+            if bot.send(GetChannel).await.unwrap() == Some(channel) {+                return Err(BotCreationError::MultipleBots(+                    bot.send(GetName).await.unwrap(),+                ));             }         } -        let channel_path = cteamspeak-            .channel_path_of_user(id)+        let channel_path = self+            .teamspeak+            .channel_path_of_user(user_id)             .await             .expect("can find poke sender"); -        available_names.shuffle(rng);-        let name_index = match available_names.pop() {+        self.available_names.shuffle(&mut self.rng);+        let name = match self.available_names.pop() {             Some(v) => v,             None => {                 return Err(BotCreationError::OutOfNames);             }         };-        let name = self.config.names[name_index].clone(); -        available_ids.shuffle(rng);-        let id_index = match available_ids.pop() {+        self.available_ids.shuffle(&mut self.rng);+        let identity = match self.available_ids.pop() {             Some(v) => v,             None => {                 return Err(BotCreationError::OutOfIdentities);             }         }; -        let id = self.config.ids[id_index].clone();--        let cmusic_bots = self.music_bots.clone();-        let disconnect_cb = Box::new(move |n, name_index, id_index| {-            let mut music_bots = cmusic_bots.write().expect("RwLock was not poisoned");-            music_bots.connected_bots.remove(&n);-            music_bots.available_names.push(name_index);-            music_bots.available_ids.push(id_index);-        });--        info!("Connecting to {} on {}", channel_path, self.config.address);+        info!(+            self.logger,+            "Connecting"; "channel" => &channel_path, "address" => &self.config.address+        );          Ok(MusicBotArgs {-            name,-            name_index,-            id_index,-            local: self.config.local,+            name: name.clone(),+            master: self.my_addr.clone(),             address: self.config.address.clone(),-            id,+            identity,+            local: false,             channel: channel_path,             verbose: self.config.verbose,-            disconnect_cb,+            logger: self.logger.new(o!("musicbot" => name)),         })     } -    async fn spawn_bot_for(&self, id: ClientId) {+    async fn spawn_bot_for(&mut self, id: ClientId) {

for who?

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl MasterBot {             con_config = con_config.channel(channel);         } -        let connection = TeamSpeakConnection::new(tx.clone(), con_config)-            .await-            .unwrap();+        let connection = TeamSpeakConnection::new(logger.clone()).await.unwrap();+        trace!(logger, "Created teamspeak connection"); -        let config = Arc::new(MasterConfig {+        let config = MasterConfig {             master_name: args.master_name,             address: args.address,-            names: args.names,-            ids: args.ids.expect("identies should exists"),-            local: args.local,             verbose: args.verbose,-        });--        let name_count = config.names.len();-        let id_count = config.ids.len();+        }; -        let music_bots = Arc::new(RwLock::new(MusicBots {+        let bot_addr = Self {+            config,+            my_addr: None,+            teamspeak: connection,+            logger: logger.clone(),             rng: SmallRng::from_entropy(),-            available_names: (0..name_count).collect(),-            available_ids: (0..id_count).collect(),+            available_names: args.names,+            available_ids: args.ids.expect("identities"),             connected_bots: HashMap::new(),-        }));+        }+        .create(None)+        .spawn(&mut Tokio::Global); -        let bot = Arc::new(Self {-            config,-            music_bots,-            teamspeak: connection,-            sender: tx.clone(),-        });--        let cbot = bot.clone();-        let msg_loop = async move {-            'outer: loop {-                while let Some(msg) = rx.recv().await {-                    match msg {-                        MusicBotMessage::Quit(reason) => {-                            let mut cteamspeak = cbot.teamspeak.clone();-                            cteamspeak.disconnect(&reason).await;-                            break 'outer;-                        }-                        MusicBotMessage::ClientDisconnected { id, .. } => {-                            if id == cbot.my_id().await {-                                // TODO Reconnect since quit was not called-                                break 'outer;-                            }-                        }-                        _ => cbot.on_message(msg).await.unwrap(),-                    }-                }-            }-        };+        bot_addr.send(Connect(con_config)).await.unwrap().unwrap();+        trace!(logger, "Spawned master bot actor"); -        (bot, msg_loop)+        bot_addr     } -    async fn build_bot_args_for(&self, id: ClientId) -> Result<MusicBotArgs, BotCreationError> {-        let mut cteamspeak = self.teamspeak.clone();-        let channel = match cteamspeak.channel_of_user(id).await {+    async fn build_bot_args_for(+        &mut self,+        user_id: ClientId,+    ) -> Result<MusicBotArgs, BotCreationError> {+        let channel = match self.teamspeak.channel_of_user(user_id).await {             Some(channel) => channel,             None => return Err(BotCreationError::UnfoundUser),         }; -        if channel == cteamspeak.my_channel().await {+        if channel == self.teamspeak.my_channel().await.unwrap() {             return Err(BotCreationError::MasterChannel(                 self.config.master_name.clone(),             ));         } -        let MusicBots {-            ref mut rng,-            ref mut available_names,-            ref mut available_ids,-            ref connected_bots,-        } = &mut *self.music_bots.write().expect("RwLock was not poisoned");--        for bot in connected_bots.values() {-            if bot.my_channel().await == channel {-                return Err(BotCreationError::MultipleBots(bot.name().to_owned()));+        for bot in self.connected_bots.values() {+            if bot.send(GetChannel).await.unwrap() == Some(channel) {+                return Err(BotCreationError::MultipleBots(+                    bot.send(GetName).await.unwrap(),+                ));             }         } -        let channel_path = cteamspeak-            .channel_path_of_user(id)+        let channel_path = self+            .teamspeak+            .channel_path_of_user(user_id)             .await             .expect("can find poke sender"); -        available_names.shuffle(rng);-        let name_index = match available_names.pop() {+        self.available_names.shuffle(&mut self.rng);+        let name = match self.available_names.pop() {             Some(v) => v,             None => {                 return Err(BotCreationError::OutOfNames);             }         };-        let name = self.config.names[name_index].clone(); -        available_ids.shuffle(rng);-        let id_index = match available_ids.pop() {+        self.available_ids.shuffle(&mut self.rng);+        let identity = match self.available_ids.pop() {             Some(v) => v,             None => {                 return Err(BotCreationError::OutOfIdentities);             }         }; -        let id = self.config.ids[id_index].clone();--        let cmusic_bots = self.music_bots.clone();-        let disconnect_cb = Box::new(move |n, name_index, id_index| {-            let mut music_bots = cmusic_bots.write().expect("RwLock was not poisoned");-            music_bots.connected_bots.remove(&n);-            music_bots.available_names.push(name_index);-            music_bots.available_ids.push(id_index);-        });--        info!("Connecting to {} on {}", channel_path, self.config.address);+        info!(+            self.logger,+            "Connecting"; "channel" => &channel_path, "address" => &self.config.address+        );

Shouldn't this be before connecting, not returning the config?

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl MasterBot {             con_config = con_config.channel(channel);         } -        let connection = TeamSpeakConnection::new(tx.clone(), con_config)-            .await-            .unwrap();+        let connection = TeamSpeakConnection::new(logger.clone()).await.unwrap();+        trace!(logger, "Created teamspeak connection"); -        let config = Arc::new(MasterConfig {+        let config = MasterConfig {             master_name: args.master_name,             address: args.address,-            names: args.names,-            ids: args.ids.expect("identies should exists"),-            local: args.local,             verbose: args.verbose,-        });--        let name_count = config.names.len();-        let id_count = config.ids.len();+        }; -        let music_bots = Arc::new(RwLock::new(MusicBots {+        let bot_addr = Self {+            config,+            my_addr: None,+            teamspeak: connection,+            logger: logger.clone(),             rng: SmallRng::from_entropy(),-            available_names: (0..name_count).collect(),-            available_ids: (0..id_count).collect(),+            available_names: args.names,+            available_ids: args.ids.expect("identities"),             connected_bots: HashMap::new(),-        }));+        }+        .create(None)+        .spawn(&mut Tokio::Global); -        let bot = Arc::new(Self {-            config,-            music_bots,-            teamspeak: connection,-            sender: tx.clone(),-        });--        let cbot = bot.clone();-        let msg_loop = async move {-            'outer: loop {-                while let Some(msg) = rx.recv().await {-                    match msg {-                        MusicBotMessage::Quit(reason) => {-                            let mut cteamspeak = cbot.teamspeak.clone();-                            cteamspeak.disconnect(&reason).await;-                            break 'outer;-                        }-                        MusicBotMessage::ClientDisconnected { id, .. } => {-                            if id == cbot.my_id().await {-                                // TODO Reconnect since quit was not called-                                break 'outer;-                            }-                        }-                        _ => cbot.on_message(msg).await.unwrap(),-                    }-                }-            }-        };+        bot_addr.send(Connect(con_config)).await.unwrap().unwrap();+        trace!(logger, "Spawned master bot actor"); -        (bot, msg_loop)+        bot_addr     } -    async fn build_bot_args_for(&self, id: ClientId) -> Result<MusicBotArgs, BotCreationError> {-        let mut cteamspeak = self.teamspeak.clone();-        let channel = match cteamspeak.channel_of_user(id).await {+    async fn build_bot_args_for(

for who?

Jokler

comment created time in 15 days

PullRequestReviewEvent

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl AudioPlayer {              audio_bin.add_many(&[&opus_enc, &sink])?; -            gst::Element::link_many(&[&queue, &convert, &volume, &resample, &opus_enc, &sink])?;+            gst::Element::link_many(&[+                &queue,+                &convert,+                &self.volume,+                &resample,+                &opus_enc,+                &sink,+            ])?;         } else {             let sink = make_element("autoaudiosink", "auto audio sink")?;              audio_bin.add_many(&[&sink])?; -            gst::Element::link_many(&[&queue, &convert, &volume, &resample, &sink])?;+            gst::Element::link_many(&[&queue, &convert, &self.volume, &resample, &sink])?;         };          let ghost_pad = GhostPad::with_target(Some("audio bin sink"), queue_sink_pad).unwrap();         ghost_pad.set_active(true)?;         audio_bin.add_pad(&ghost_pad)?; -        Ok((audio_bin, volume, ghost_pad))+        add_decode_bin_new_pad_callback(+            &decode_bin,+            audio_bin.clone(),+            ghost_pad,+            self.logger.clone(),+        );++        self.pipeline.add(&audio_bin)?;++        Ok(())     } -    pub fn set_metadata(&self, data: AudioMetadata) -> Result<(), AudioPlayerError> {+    pub fn set_metadata(&mut self, data: AudioMetadata) -> Result<(), AudioPlayerError> {         self.set_source_url(data.url.clone())?; -        let mut currently_playing = self.currently_playing.write().unwrap();-        *currently_playing = Some(data);+        self.currently_playing = Some(data);          Ok(())     }      fn set_source_url(&self, location: String) -> Result<(), AudioPlayerError> {-        info!("Setting location URI: {}", location);+        info!(self.logger, "Setting source"; "uri" => &location);

uri or url? :-)

Jokler

comment created time in 15 days

Pull request review commentMavulp/pokebot

Replace channels&locks with actors & log with slog

 impl AudioPlayer {             pipeline,             bus,             http_src,+            logger, -            volume_f64: RwLock::new(0.0),+            volume_f64: 0.0,             volume,-            sender,-            currently_playing: RwLock::new(None),+            currently_playing: None,         })     } -    fn create_audio_bin(+    pub fn setup_with_callback(

Setup what with callback?

Jokler

comment created time in 15 days

PullRequestReviewEvent

startedRestioson/xtra

started time in 19 days

issue openedMavulp/pokebot

Investigate structuring the bot(s) using actors

Is your feature request related to a problem? Please describe. Currently the TeamSpeak/GStreamer parts of the bot is a bit of a mess with Mutexes/RwLock mixed with async functions

Describe the solution you'd like See if we can move the concept of the main bot and the music bots into their own actors to decouple them more.

Describe alternatives you've considered Try to clean up the current code?

created time in 25 days

Pull request review commentMavulp/pokebot

Added retry attempt when youtube-dl fails

 where     Ok(dur.map(Duration::from_secs_f64)) } -pub async fn get_audio_download_url(uri: String) -> Result<AudioMetadata, String> {-    let ytdl_args = ["--no-playlist", "-f", "bestaudio/best", "-j", &uri];+async fn run_youtube_dl(url: &str) -> Result<String, String> {+    let ytdl_args = ["--no-playlist", "-f", "bestaudio/best", "-j", &url];      let mut cmd = Command::new("youtube-dl");     cmd.args(&ytdl_args);     cmd.stdin(Stdio::null());-+         debug!("yt-dl command: {:?}", cmd);-     let ytdl_output = cmd.output().await.unwrap();      if !ytdl_output.status.success() {-        return Err(String::from_utf8(ytdl_output.stderr).unwrap());+        let s = String::from_utf8(ytdl_output.stderr).unwrap();+        return Err(s);     }      let output_str = String::from_utf8(ytdl_output.stdout).unwrap();-    let output = serde_json::from_str(&output_str).map_err(|e| e.to_string())?;++    Ok(output_str)+}

Move below get_audio_download_url

kilmanio

comment created time in 25 days

Pull request review commentMavulp/pokebot

Added retry attempt when youtube-dl fails

 where     Ok(dur.map(Duration::from_secs_f64)) } -pub async fn get_audio_download_url(uri: String) -> Result<AudioMetadata, String> {-    let ytdl_args = ["--no-playlist", "-f", "bestaudio/best", "-j", &uri];+async fn run_youtube_dl(url: &str) -> Result<String, String> {+    let ytdl_args = ["--no-playlist", "-f", "bestaudio/best", "-j", &url];      let mut cmd = Command::new("youtube-dl");     cmd.args(&ytdl_args);     cmd.stdin(Stdio::null());-+    

kilmanio

comment created time in 25 days

PullRequestReviewEvent

Pull request review commentMavulp/pokebot

Added retry attempt when youtube-dl fails

 where     Ok(dur.map(Duration::from_secs_f64)) } -pub async fn get_audio_download_url(uri: String) -> Result<AudioMetadata, String> {-    let ytdl_args = ["--no-playlist", "-f", "bestaudio/best", "-j", &uri];+async fn run_youtube_dl(url: &str) -> Result<String, String> {+    let ytdl_args = ["--no-playlist", "-f", "bestaudio/best", "-j", &url];      let mut cmd = Command::new("youtube-dl");     cmd.args(&ytdl_args);     cmd.stdin(Stdio::null());-+         debug!("yt-dl command: {:?}", cmd);-     let ytdl_output = cmd.output().await.unwrap();      if !ytdl_output.status.success() {-        return Err(String::from_utf8(ytdl_output.stderr).unwrap());+        let s = String::from_utf8(ytdl_output.stderr).unwrap();+        return Err(s);     }      let output_str = String::from_utf8(ytdl_output.stdout).unwrap();-    let output = serde_json::from_str(&output_str).map_err(|e| e.to_string())?;++    Ok(output_str)+}++pub async fn get_audio_download_url(uri: String) -> Result<AudioMetadata, String> {
pub async fn get_audio_metadata_from_url(uri: String) -> Result<AudioMetadata, String> {
kilmanio

comment created time in 25 days

Pull request review commentMavulp/pokebot

Added retry attempt when youtube-dl fails

 where     Ok(dur.map(Duration::from_secs_f64)) } -pub async fn get_audio_download_url(uri: String) -> Result<AudioMetadata, String> {-    let ytdl_args = ["--no-playlist", "-f", "bestaudio/best", "-j", &uri];+async fn run_youtube_dl(url: &str) -> Result<String, String> {+    let ytdl_args = ["--no-playlist", "-f", "bestaudio/best", "-j", &url];      let mut cmd = Command::new("youtube-dl");     cmd.args(&ytdl_args);     cmd.stdin(Stdio::null());-+         debug!("yt-dl command: {:?}", cmd);-     let ytdl_output = cmd.output().await.unwrap();      if !ytdl_output.status.success() {-        return Err(String::from_utf8(ytdl_output.stderr).unwrap());+        let s = String::from_utf8(ytdl_output.stderr).unwrap();+        return Err(s);     }      let output_str = String::from_utf8(ytdl_output.stdout).unwrap();-    let output = serde_json::from_str(&output_str).map_err(|e| e.to_string())?;++    Ok(output_str)+}++pub async fn get_audio_download_url(uri: String) -> Result<AudioMetadata, String> {+    let ytdl_output = match run_youtube_dl(&uri).await {+        Ok(o) => o,+        Err(e) => {+            if e.contains("Unable to extract video data") {+                run_youtube_dl(&uri).await?+            } else {+                return Err(e)+            }+        }+    };++    let output = serde_json::from_str(&ytdl_output).map_err(|e| e.to_string())?;      Ok(output)

Maybe something like this?

    match run_youtube_dl(&uri).await {
        Ok(o) => Ok(serde_json::from_str(&o).map_err(|e| e.to_string())?),
        Err(e) => {
            if e.contains("Unable to extract video data") {
                run_youtube_dl(&uri).await?
            } else {
                Err(e)
            }
        }
    }
kilmanio

comment created time in 25 days

PullRequestReviewEvent
PublicEvent

push eventfkaa/iup-rust

Felix Kaaman

commit sha 4c0eebd7b2ee38d954c3f9e156c104fd36777740

Reorganize structure of repo

view details

push time in a month

push eventfkaa/catlinman

Felix Kaaman

commit sha 731db0ee60a3967492ea6bcf8c993878a20b2a0f

improve README

view details

push time in a month

PullRequestReviewEvent
PullRequestReviewEvent

startedrevcx/revc

started time in 2 months

more