diff options
| author | Zeyla Hellyer <[email protected]> | 2018-01-18 09:09:06 -0800 |
|---|---|---|
| committer | Zeyla Hellyer <[email protected]> | 2018-01-18 09:09:06 -0800 |
| commit | ba112ccb7d3b1d524b29a999f21624cfa356cffa (patch) | |
| tree | c9d86eb546b12f131532a36d3fad49c6a02501bf /src | |
| parent | Use an InterMessage to communicate over gateway (diff) | |
| download | serenity-ba112ccb7d3b1d524b29a999f21624cfa356cffa.tar.xz serenity-ba112ccb7d3b1d524b29a999f21624cfa356cffa.zip | |
Expose a client voice manager
Diffstat (limited to 'src')
| -rw-r--r-- | src/client/bridge/gateway/mod.rs | 2 | ||||
| -rw-r--r-- | src/client/bridge/gateway/shard_manager.rs | 13 | ||||
| -rw-r--r-- | src/client/bridge/gateway/shard_queuer.rs | 35 | ||||
| -rw-r--r-- | src/client/bridge/gateway/shard_runner.rs | 130 | ||||
| -rw-r--r-- | src/client/bridge/mod.rs | 3 | ||||
| -rw-r--r-- | src/client/bridge/voice/mod.rs | 117 | ||||
| -rw-r--r-- | src/client/mod.rs | 46 | ||||
| -rw-r--r-- | src/gateway/shard.rs | 113 | ||||
| -rw-r--r-- | src/internal/macros.rs | 29 | ||||
| -rw-r--r-- | src/voice/manager.rs | 8 |
10 files changed, 289 insertions, 207 deletions
diff --git a/src/client/bridge/gateway/mod.rs b/src/client/bridge/gateway/mod.rs index 06f5ea4..3cc6dca 100644 --- a/src/client/bridge/gateway/mod.rs +++ b/src/client/bridge/gateway/mod.rs @@ -60,7 +60,7 @@ pub use self::shard_manager::{ShardManager, ShardManagerOptions}; pub use self::shard_manager_monitor::ShardManagerMonitor; pub use self::shard_messenger::ShardMessenger; pub use self::shard_queuer::ShardQueuer; -pub use self::shard_runner::ShardRunner; +pub use self::shard_runner::{ShardRunner, ShardRunnerOptions}; pub use self::shard_runner_message::ShardRunnerMessage; use std::fmt::{Display, Formatter, Result as FmtResult}; diff --git a/src/client/bridge/gateway/shard_manager.rs b/src/client/bridge/gateway/shard_manager.rs index ea3ad1e..b62ff00 100644 --- a/src/client/bridge/gateway/shard_manager.rs +++ b/src/client/bridge/gateway/shard_manager.rs @@ -20,6 +20,8 @@ use typemap::ShareMap; #[cfg(feature = "framework")] use framework::Framework; +#[cfg(feature = "voice")] +use client::bridge::voice::ClientVoiceManager; /// A manager for handling the status of shards by starting them, restarting /// them, and stopping them when required. @@ -40,6 +42,11 @@ use framework::Framework; /// /// # use std::error::Error; /// # +/// # #[cfg(feature = "voice")] +/// # use serenity::client::bridge::voice::ClientVoiceManager; +/// # #[cfg(feature = "voice")] +/// # use serenity::model::id::UserId; +/// # /// # #[cfg(feature = "framework")] /// # fn try_main() -> Result<(), Box<Error>> { /// # @@ -78,6 +85,8 @@ use framework::Framework; /// shard_total: 5, /// token: &token, /// threadpool, +/// # #[cfg(feature = "voice")] +/// # voice_manager: &Arc::new(Mutex::new(ClientVoiceManager::new(0, UserId(0)))), /// ws_url: &gateway_url, /// }); /// # Ok(()) @@ -135,6 +144,8 @@ impl ShardManager { rx: shard_queue_rx, threadpool: opt.threadpool, token: Arc::clone(opt.token), + #[cfg(feature = "voice")] + voice_manager: Arc::clone(opt.voice_manager), ws_url: Arc::clone(opt.ws_url), }; @@ -343,5 +354,7 @@ pub struct ShardManagerOptions<'a, H: EventHandler + Send + Sync + 'static> { pub shard_total: u64, pub threadpool: ThreadPool, pub token: &'a Arc<Mutex<String>>, + #[cfg(feature = "voice")] + pub voice_manager: &'a Arc<Mutex<ClientVoiceManager>>, pub ws_url: &'a Arc<Mutex<String>>, } diff --git a/src/client/bridge/gateway/shard_queuer.rs b/src/client/bridge/gateway/shard_queuer.rs index 461fdd1..afcd4c7 100644 --- a/src/client/bridge/gateway/shard_queuer.rs +++ b/src/client/bridge/gateway/shard_queuer.rs @@ -13,11 +13,14 @@ use super::{ ShardQueuerMessage, ShardRunner, ShardRunnerInfo, + ShardRunnerOptions, }; use threadpool::ThreadPool; use typemap::ShareMap; use ::gateway::ConnectionStage; +#[cfg(feature = "voice")] +use client::bridge::voice::ClientVoiceManager; #[cfg(feature = "framework")] use framework::Framework; @@ -70,6 +73,9 @@ pub struct ShardQueuer<H: EventHandler + Send + Sync + 'static> { pub threadpool: ThreadPool, /// A copy of the token to connect with. pub token: Arc<Mutex<String>>, + /// A copy of the client's voice manager. + #[cfg(feature = "voice")] + pub voice_manager: Arc<Mutex<ClientVoiceManager>>, /// A copy of the URI to use to connect to the gateway. pub ws_url: Arc<Mutex<String>>, } @@ -165,24 +171,17 @@ impl<H: EventHandler + Send + Sync + 'static> ShardQueuer<H> { shard_info, )?; - let mut runner = feature_framework! {{ - ShardRunner::new( - shard, - self.manager_tx.clone(), - Arc::clone(&self.framework), - Arc::clone(&self.data), - Arc::clone(&self.event_handler), - self.threadpool.clone(), - ) - } else { - ShardRunner::new( - shard, - self.manager_tx.clone(), - self.data.clone(), - self.event_handler.clone(), - self.threadpool.clone(), - ) - }}; + let mut runner = ShardRunner::new(ShardRunnerOptions { + data: Arc::clone(&self.data), + event_handler: Arc::clone(&self.event_handler), + #[cfg(feature = "framework")] + framework: Arc::clone(&self.framework), + manager_tx: self.manager_tx.clone(), + threadpool: self.threadpool.clone(), + #[cfg(feature = "voice")] + voice_manager: Arc::clone(&self.voice_manager), + shard, + }); let runner_info = ShardRunnerInfo { latency: None, diff --git a/src/client/bridge/gateway/shard_runner.rs b/src/client/bridge/gateway/shard_runner.rs index ba9e00e..bfe4214 100644 --- a/src/client/bridge/gateway/shard_runner.rs +++ b/src/client/bridge/gateway/shard_runner.rs @@ -17,6 +17,8 @@ use websocket::WebSocketError; #[cfg(feature = "framework")] use framework::Framework; +#[cfg(feature = "voice")] +use super::super::voice::ClientVoiceManager; /// A runner for managing a [`Shard`] and its respective WebSocket client. /// @@ -33,53 +35,27 @@ pub struct ShardRunner<H: EventHandler + Send + Sync + 'static> { runner_tx: Sender<InterMessage>, shard: Shard, threadpool: ThreadPool, + #[cfg(feature = "voice")] + voice_manager: Arc<Mutex<ClientVoiceManager>>, } impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { /// Creates a new runner for a Shard. - #[allow(too_many_arguments)] - #[cfg(feature = "framework")] - pub fn new( - shard: Shard, - manager_tx: Sender<ShardManagerMessage>, - framework: Arc<Mutex<Option<Box<Framework + Send>>>>, - data: Arc<Mutex<ShareMap>>, - event_handler: Arc<H>, - threadpool: ThreadPool, - ) -> Self { - let (tx, rx) = mpsc::channel(); - - Self { - runner_rx: rx, - runner_tx: tx, - data, - event_handler, - framework, - manager_tx, - shard, - threadpool, - } - } - - /// Creates a new runner for a Shard. - #[cfg(not(feature = "framework"))] - pub fn new( - shard: Shard, - manager_tx: Sender<ShardManagerMessage>, - data: Arc<Mutex<ShareMap>>, - event_handler: Arc<H>, - threadpool: ThreadPool, - ) -> Self { + pub fn new(opt: ShardRunnerOptions<H>) -> Self { let (tx, rx) = mpsc::channel(); Self { runner_rx: rx, runner_tx: tx, - data, - event_handler, - manager_tx, - shard, - threadpool, + data: opt.data, + event_handler: opt.event_handler, + #[cfg(feature = "framework")] + framework: opt.framework, + manager_tx: opt.manager_tx, + shard: opt.shard, + threadpool: opt.threadpool, + #[cfg(feature = "voice")] + voice_manager: opt.voice_manager, } } @@ -132,19 +108,8 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { return self.request_restart(); } - #[cfg(feature = "voice")] - { - for message in self.shard.cycle_voice_recv() { - if let Err(why) = self.shard.client.send_json(&message) { - println!("Err sending from voice over WS: {:?}", why); - } - } - } - let pre = self.shard.stage(); - let (event, action, successful) = self.recv_event(); - let post = self.shard.stage(); if post != pre { @@ -325,6 +290,39 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { } } + #[cfg(feature = "voice")] + fn handle_voice_event(&self, event: &Event) { + match *event { + Event::Ready(_) => { + self.voice_manager.lock().set( + self.shard.shard_info()[0], + self.runner_tx.clone(), + ); + }, + Event::VoiceServerUpdate(ref event) => { + if let Some(guild_id) = event.guild_id { + let mut manager = self.voice_manager.lock(); + let mut search = manager.get_mut(guild_id); + + if let Some(handler) = search { + handler.update_server(&event.endpoint, &event.token); + } + } + }, + Event::VoiceStateUpdate(ref event) => { + if let Some(guild_id) = event.guild_id { + let mut manager = self.voice_manager.lock(); + let mut search = manager.get_mut(guild_id); + + if let Some(handler) = search { + handler.update_state(&event.voice_state); + } + } + }, + _ => {}, + } + } + // Receives values over the internal shard runner rx channel and handles // them. // @@ -431,11 +429,15 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { }, }; - match event { - Ok(GatewayEvent::HeartbeatAck) => { - self.update_manager(); - }, - _ => {}, + if let Ok(GatewayEvent::HeartbeatAck) = event { + self.update_manager(); + } + + #[cfg(feature = "voice")] + { + if let Ok(GatewayEvent::Dispatch(_, ref event)) = event { + self.handle_voice_event(&event); + } } let event = match event { @@ -457,6 +459,11 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { let msg = ShardManagerMessage::Restart(shard_id); let _ = self.manager_tx.send(msg); + #[cfg(feature = "voice")] + { + self.voice_manager.lock().manager_remove(&shard_id.0); + } + Ok(()) } @@ -468,3 +475,18 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { }); } } + +/// Options to be passed to [`ShardRunner::new`]. +/// +/// [`ShardRunner::new`]: struct.ShardRunner.html#method.new +pub struct ShardRunnerOptions<H: EventHandler + Send + Sync + 'static> { + pub data: Arc<Mutex<ShareMap>>, + pub event_handler: Arc<H>, + #[cfg(feature = "framework")] + pub framework: Arc<Mutex<Option<Box<Framework + Send>>>>, + pub manager_tx: Sender<ShardManagerMessage>, + pub shard: Shard, + pub threadpool: ThreadPool, + #[cfg(feature = "voice")] + pub voice_manager: Arc<Mutex<ClientVoiceManager>>, +} diff --git a/src/client/bridge/mod.rs b/src/client/bridge/mod.rs index ea18d73..41fcdec 100644 --- a/src/client/bridge/mod.rs +++ b/src/client/bridge/mod.rs @@ -8,3 +8,6 @@ //! [`client`]: ../ pub mod gateway; + +#[cfg(feature = "voice")] +pub mod voice; diff --git a/src/client/bridge/voice/mod.rs b/src/client/bridge/voice/mod.rs new file mode 100644 index 0000000..e67366b --- /dev/null +++ b/src/client/bridge/voice/mod.rs @@ -0,0 +1,117 @@ +use gateway::InterMessage; +use std::collections::HashMap; +use std::sync::mpsc::Sender as MpscSender; +use ::model::id::{ChannelId, GuildId, UserId}; +use ::voice::{Handler, Manager}; +use ::utils; + +pub struct ClientVoiceManager { + managers: HashMap<u64, Manager>, + shard_count: u64, + user_id: UserId, +} + +impl ClientVoiceManager { + pub fn new(shard_count: u64, user_id: UserId) -> Self { + Self { + managers: HashMap::default(), + shard_count, + user_id, + } + } + + pub fn get<G: Into<GuildId>>(&self, guild_id: G) -> Option<&Handler> { + let (gid, sid) = self.manager_info(guild_id); + + self.managers.get(&sid)?.get(gid) + } + + pub fn get_mut<G: Into<GuildId>>(&mut self, guild_id: G) + -> Option<&mut Handler> { + let (gid, sid) = self.manager_info(guild_id); + + self.managers.get_mut(&sid)?.get_mut(gid) + } + + /// Refer to [`Manager::join`]. + /// + /// This is a shortcut to retrieving the inner [`Manager`] and then calling + /// its `join` method. + /// + /// [`Manager`]: ../../../voice/struct.Manager.html + /// [`Manager::join`]: ../../../voice/struct.Manager.html#method.join + pub fn join<C, G>(&mut self, guild_id: G, channel_id: C) + -> Option<&mut Handler> where C: Into<ChannelId>, G: Into<GuildId> { + let (gid, sid) = self.manager_info(guild_id); + + self.managers.get_mut(&sid).map(|manager| manager.join(gid, channel_id)) + } + + /// Refer to [`Manager::leave`]. + /// + /// This is a shortcut to retrieving the inner [`Manager`] and then calling + /// its `leave` method. + /// + /// [`Manager`]: ../../../voice/struct.Manager.html + /// [`Manager::leave`]: ../../../voice/struct.Manager.html#method.leave + pub fn leave<G: Into<GuildId>>(&mut self, guild_id: G) -> Option<()> { + let (gid, sid) = self.manager_info(guild_id); + + self.managers.get_mut(&sid).map(|manager| manager.leave(gid)) + } + + /// Refer to [`Manager::remove`]. + /// + /// This is a shortcut to retrieving the inner [`Manager`] and then calling + /// its `remove` method. + /// + /// [`Manager`]: ../../../voice/struct.Manager.html + /// [`Manager::remove`]: ../../../voice/struct.Manager.html#method.remove + pub fn remove<G: Into<GuildId>>(&mut self, guild_id: G) -> Option<()> { + let (gid, sid) = self.manager_info(guild_id); + + self.managers.get_mut(&sid).map(|manager| manager.leave(gid)) + } + + pub fn set(&mut self, shard_id: u64, sender: MpscSender<InterMessage>) { + self.managers.insert(shard_id, Manager::new(sender, self.user_id)); + } + + /// Sets the number of shards for the voice manager to use when calculating + /// guilds' shard numbers. + /// + /// You probably should not call this. + #[doc(hidden)] + pub fn set_shard_count(&mut self, shard_count: u64) { + self.shard_count = shard_count; + } + + /// Sets the ID of the user for the voice manager. + /// + /// You probably _really_ should not call this. + /// + /// But it's there if you need it. For some reason. + #[doc(hidden)] + pub fn set_user_id(&mut self, user_id: UserId) { + self.user_id = user_id; + } + + pub fn manager_get(&self, shard_id: &u64) -> Option<&Manager> { + self.managers.get(shard_id) + } + + pub fn manager_get_mut(&mut self, shard_id: &u64) -> Option<&mut Manager> { + self.managers.get_mut(shard_id) + } + + pub fn manager_remove(&mut self, shard_id: &u64) -> Option<Manager> { + self.managers.remove(shard_id) + } + + fn manager_info<G: Into<GuildId>>(&self, guild_id: G) -> (GuildId, u64) { + let guild_id = guild_id.into(); + let shard_id = utils::shard_id(guild_id.0, self.shard_count); + + (guild_id, shard_id) + } +} diff --git a/src/client/mod.rs b/src/client/mod.rs index 1219793..9dfaa10 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -47,6 +47,10 @@ use typemap::ShareMap; #[cfg(feature = "framework")] use framework::Framework; +#[cfg(feature = "voice")] +use model::id::UserId; +#[cfg(feature = "voice")] +use self::bridge::voice::ClientVoiceManager; /// The Client is the way to be able to start sending authenticated requests /// over the REST API, as well as initializing a WebSocket connection through @@ -276,6 +280,12 @@ pub struct Client { pub threadpool: ThreadPool, /// The token in use by the client. pub token: Arc<Mutex<String>>, + /// The voice manager for the client. + /// + /// This is an ergonomic structure for interfacing over shards' voice + /// connections. + #[cfg(feature = "voice")] + pub voice_manager: Arc<Mutex<ClientVoiceManager>>, /// URI that the client's shards will use to connect to the gateway. /// /// This is likely not important for production usage and is, at best, used @@ -337,6 +347,12 @@ impl Client { #[cfg(feature = "framework")] let framework = Arc::new(Mutex::new(None)); + #[cfg(feature = "voice")] + let voice_manager = Arc::new(Mutex::new(ClientVoiceManager::new( + 0, + UserId(0), + ))); + let (shard_manager, shard_manager_worker) = { ShardManager::new(ShardManagerOptions { data: &data, @@ -348,6 +364,8 @@ impl Client { shard_total: 0, threadpool: threadpool.clone(), token: &locked, + #[cfg(feature = "voice")] + voice_manager: &voice_manager, ws_url: &url, }) }; @@ -361,6 +379,8 @@ impl Client { shard_manager, shard_manager_worker, threadpool, + #[cfg(feature = "voice")] + voice_manager, }) } @@ -794,15 +814,31 @@ impl Client { // // [`ClientError::Shutdown`]: enum.ClientError.html#variant.Shutdown fn start_connection(&mut self, shard_data: [u64; 3]) -> Result<()> { - // Update the framework's current user if the feature is enabled. + #[cfg(feature = "voice")] + self.voice_manager.lock().set_shard_count(shard_data[2]); + + // This is kind of gross, but oh well. // - // This also acts as a form of check to ensure the token is correct. - #[cfg(all(feature = "standard_framework", feature = "framework"))] + // Both the framework and voice bridge need the user's ID, so we'll only + // retrieve it over REST if at least one of those are enabled. + #[cfg(any(all(feature = "standard_framework", feature = "framework"), + feature = "voice"))] { let user = http::get_current_user()?; - if let Some(ref mut framework) = *self.framework.lock() { - framework.update_current_user(user.id); + // Update the framework's current user if the feature is enabled. + // + // This also acts as a form of check to ensure the token is correct. + #[cfg(all(feature = "standard_framework", feature = "framework"))] + { + if let Some(ref mut framework) = *self.framework.lock() { + framework.update_current_user(user.id); + } + } + + #[cfg(feature = "voice")] + { + self.voice_manager.lock().set_user_id(user.id); } } diff --git a/src/gateway/shard.rs b/src/gateway/shard.rs index af7b76b..22fb186 100644 --- a/src/gateway/shard.rs +++ b/src/gateway/shard.rs @@ -21,17 +21,6 @@ use websocket::stream::sync::AsTcpStream; use websocket::sync::client::ClientBuilder; use websocket::WebSocketError; -#[cfg(feature = "voice")] -use serde_json::Value; -#[cfg(feature = "voice")] -use std::sync::mpsc::{self, Receiver as MpscReceiver}; -#[cfg(feature = "voice")] -use super::InterMessage; -#[cfg(feature = "voice")] -use voice::Manager as VoiceManager; -#[cfg(feature = "voice")] -use http; - /// A Shard is a higher-level handler for a websocket connection to Discord's /// gateway. The shard allows for sending and receiving messages over the /// websocket, such as setting the active game, reconnecting, syncing guilds, @@ -92,12 +81,6 @@ pub struct Shard { stage: ConnectionStage, pub token: Arc<Mutex<String>>, ws_url: Arc<Mutex<String>>, - /// The voice connections that this Shard is responsible for. The Shard will - /// update the voice connections' states. - #[cfg(feature = "voice")] - pub manager: VoiceManager, - #[cfg(feature = "voice")] - manager_rx: MpscReceiver<InterMessage>, } impl Shard { @@ -155,44 +138,19 @@ impl Shard { let stage = ConnectionStage::Handshake; let session_id = None; - Ok(feature_voice! { - { - let (tx, rx) = mpsc::channel(); - - let user = http::get_current_user()?; - - Shard { - manager: VoiceManager::new(tx, user.id), - manager_rx: rx, - shutdown: false, - client, - current_presence, - heartbeat_instants, - heartbeat_interval, - last_heartbeat_acknowledged, - seq, - stage, - token, - shard_info, - session_id, - ws_url, - } - } else { - Shard { - shutdown: false, - client, - current_presence, - heartbeat_instants, - heartbeat_interval, - last_heartbeat_acknowledged, - seq, - stage, - token, - session_id, - shard_info, - ws_url, - } - } + Ok(Shard { + shutdown: false, + client, + current_presence, + heartbeat_instants, + heartbeat_interval, + last_heartbeat_acknowledged, + seq, + stage, + token, + session_id, + shard_info, + ws_url, }) } @@ -407,10 +365,6 @@ impl Shard { self.session_id = Some(ready.ready.session_id.clone()); self.stage = ConnectionStage::Connected; - - /* - set_client_timeout(&mut self.client)?; - */ }, Event::Resumed(_) => { info!("[Shard {:?}] Resumed", self.shard_info); @@ -419,13 +373,7 @@ impl Shard { self.last_heartbeat_acknowledged = true; self.heartbeat_instants = (Some(Instant::now()), None); }, - #[cfg_attr(rustfmt, rustfmt_skip)] - ref _other => { - #[cfg(feature = "voice")] - { - self.voice_dispatch(_other); - } - }, + _ => {}, } self.seq = seq; @@ -664,39 +612,6 @@ impl Shard { None } - #[cfg(feature = "voice")] - fn voice_dispatch(&mut self, event: &Event) { - if let Event::VoiceStateUpdate(ref update) = *event { - if let Some(guild_id) = update.guild_id { - if let Some(handler) = self.manager.get(guild_id) { - handler.update_state(&update.voice_state); - } - } - } - - if let Event::VoiceServerUpdate(ref update) = *event { - if let Some(guild_id) = update.guild_id { - if let Some(handler) = self.manager.get(guild_id) { - handler.update_server(&update.endpoint, &update.token); - } - } - } - } - - #[cfg(feature = "voice")] - pub(crate) fn cycle_voice_recv(&mut self) -> Vec<Value> { - let mut messages = vec![]; - - while let Ok(InterMessage::Json(v)) = self.manager_rx.try_recv() { - messages.push(v); - } - - self.shutdown = true; - debug!("[Shard {:?}] Cleanly shutdown shard", self.shard_info); - - messages - } - /// Performs a deterministic reconnect. /// /// The type of reconnect is deterministic on whether a [`session_id`]. diff --git a/src/internal/macros.rs b/src/internal/macros.rs index 4ee43d3..875326a 100644 --- a/src/internal/macros.rs +++ b/src/internal/macros.rs @@ -89,16 +89,6 @@ macro_rules! feature_cache { } } -// Enable/disable check for framework -#[cfg(all(feature = "client", feature = "framework"))] -macro_rules! feature_framework { - ($enabled:block else $disabled:block) => { - { - $enabled - } - } -} - #[cfg(all(feature = "client", not(feature = "framework")))] macro_rules! feature_framework { ($enabled:block else $disabled:block) => { @@ -108,25 +98,6 @@ macro_rules! feature_framework { } } -// Enable/disable check for voice -#[cfg(all(feature = "gateway", feature = "voice"))] -macro_rules! feature_voice { - ($enabled:block else $disabled:block) => { - { - $enabled - } - } -} - -#[cfg(all(feature = "gateway", not(feature = "voice")))] -macro_rules! feature_voice { - ($enabled:block else $disabled:block) => { - { - $disabled - } - } -} - macro_rules! enum_number { (#[$attr_:meta] $name:ident { $(#[$attr:meta] $variant:ident = $value:expr, )* }) => { #[$attr_] diff --git a/src/voice/manager.rs b/src/voice/manager.rs index a6bcd2f..79f469a 100644 --- a/src/voice/manager.rs +++ b/src/voice/manager.rs @@ -36,8 +36,14 @@ impl Manager { } } + /// Retrieves an immutable handler for the given target, if one exists. + pub fn get<G: Into<GuildId>>(&self, guild_id: G) -> Option<&Handler> { + self.handlers.get(&guild_id.into()) + } + /// Retrieves a mutable handler for the given target, if one exists. - pub fn get<G: Into<GuildId>>(&mut self, guild_id: G) -> Option<&mut Handler> { + pub fn get_mut<G: Into<GuildId>>(&mut self, guild_id: G) + -> Option<&mut Handler> { self.handlers.get_mut(&guild_id.into()) } |