aboutsummaryrefslogtreecommitdiffstats
path: root/mumd/src/state.rs
diff options
context:
space:
mode:
authorKapten Z∅∅m <55669224+default-username-852@users.noreply.github.com>2021-06-06 23:19:05 +0200
committerGitHub <noreply@github.com>2021-06-06 23:19:05 +0200
commit360b232de29f0104a8beb0c57e8defd9e54c9e6c (patch)
tree3595d6ae9dbe293ef0403ce581edd4742569147c /mumd/src/state.rs
parentea8b1906e14c3b319d3ad184b6d7cfc507c23b4f (diff)
parent55a12fbdfb435886b2f211fe1fb00daafb32b6a7 (diff)
downloadmum-360b232de29f0104a8beb0c57e8defd9e54c9e6c.tar.gz
Merge pull request #92 from mum-rs/text-message
Text message
Diffstat (limited to 'mumd/src/state.rs')
-rw-r--r--mumd/src/state.rs781
1 files changed, 424 insertions, 357 deletions
diff --git a/mumd/src/state.rs b/mumd/src/state.rs
index 46df421..a553e18 100644
--- a/mumd/src/state.rs
+++ b/mumd/src/state.rs
@@ -14,33 +14,38 @@ use mumble_protocol::control::msgs;
use mumble_protocol::control::ControlPacket;
use mumble_protocol::ping::PongPacket;
use mumble_protocol::voice::Serverbound;
-use mumlib::command::{Command, CommandResponse};
+use mumlib::command::{Command, CommandResponse, MessageTarget};
use mumlib::config::Config;
-use mumlib::error::ChannelIdentifierError;
use mumlib::Error;
use crate::state::user::UserDiff;
-use std::net::{SocketAddr, ToSocketAddrs};
+use std::{iter, net::{SocketAddr, ToSocketAddrs}, sync::{Arc, RwLock}};
use tokio::sync::{mpsc, watch};
macro_rules! at {
($event:expr, $generator:expr) => {
- ExecutionContext::TcpEvent($event, Box::new($generator))
+ ExecutionContext::TcpEventCallback($event, Box::new($generator))
};
}
macro_rules! now {
($data:expr) => {
- ExecutionContext::Now(Box::new(move || $data))
+ ExecutionContext::Now(Box::new(move || Box::new(iter::once($data))))
};
}
+type Responses = Box<dyn Iterator<Item = mumlib::error::Result<Option<CommandResponse>>>>;
+
//TODO give me a better name
pub enum ExecutionContext {
- TcpEvent(
+ TcpEventCallback(
+ TcpEvent,
+ Box<dyn FnOnce(TcpEventData) -> Responses>,
+ ),
+ TcpEventSubscriber(
TcpEvent,
- Box<dyn FnOnce(TcpEventData) -> mumlib::error::Result<Option<CommandResponse>>>,
+ Box<dyn FnMut(TcpEventData, &mut mpsc::UnboundedSender<mumlib::error::Result<Option<CommandResponse>>>) -> bool>,
),
- Now(Box<dyn FnOnce() -> mumlib::error::Result<Option<CommandResponse>>>),
+ Now(Box<dyn FnOnce() -> Responses>),
Ping(
Box<dyn FnOnce() -> mumlib::error::Result<SocketAddr>>,
Box<dyn FnOnce(Option<PongPacket>) -> mumlib::error::Result<Option<CommandResponse>> + Send>,
@@ -59,6 +64,7 @@ pub struct State {
server: Option<Server>,
audio_input: AudioInput,
audio_output: AudioOutput,
+ message_buffer: Vec<(String, u32)>,
phase_watcher: (watch::Sender<StatePhase>, watch::Receiver<StatePhase>),
}
@@ -79,355 +85,13 @@ impl State {
server: None,
audio_input,
audio_output,
+ message_buffer: Vec::new(),
phase_watcher,
};
state.reload_config();
Ok(state)
}
- pub fn handle_command(
- &mut self,
- command: Command,
- packet_sender: &mut mpsc::UnboundedSender<ControlPacket<Serverbound>>,
- connection_info_sender: &mut watch::Sender<Option<ConnectionInfo>>,
- ) -> ExecutionContext {
- match command {
- Command::ChannelJoin { channel_identifier } => {
- if !matches!(*self.phase_receiver().borrow(), StatePhase::Connected(_)) {
- return now!(Err(Error::Disconnected));
- }
-
- let channels = self.server().unwrap().channels();
-
- let matches = channels
- .iter()
- .map(|e| (e.0, e.1.path(channels)))
- .filter(|e| e.1.ends_with(&channel_identifier))
- .collect::<Vec<_>>();
- let id = match matches.len() {
- 0 => {
- let soft_matches = channels
- .iter()
- .map(|e| (e.0, e.1.path(channels).to_lowercase()))
- .filter(|e| e.1.ends_with(&channel_identifier.to_lowercase()))
- .collect::<Vec<_>>();
- match soft_matches.len() {
- 0 => {
- return now!(Err(Error::ChannelIdentifierError(
- channel_identifier,
- ChannelIdentifierError::Invalid
- )))
- }
- 1 => *soft_matches.get(0).unwrap().0,
- _ => {
- return now!(Err(Error::ChannelIdentifierError(
- channel_identifier,
- ChannelIdentifierError::Invalid
- )))
- }
- }
- }
- 1 => *matches.get(0).unwrap().0,
- _ => {
- return now!(Err(Error::ChannelIdentifierError(
- channel_identifier,
- ChannelIdentifierError::Ambiguous
- )))
- }
- };
-
- let mut msg = msgs::UserState::new();
- msg.set_session(self.server.as_ref().unwrap().session_id().unwrap());
- msg.set_channel_id(id);
- packet_sender.send(msg.into()).unwrap();
- now!(Ok(None))
- }
- Command::ChannelList => {
- if !matches!(*self.phase_receiver().borrow(), StatePhase::Connected(_)) {
- return now!(Err(Error::Disconnected));
- }
- let list = channel::into_channel(
- self.server.as_ref().unwrap().channels(),
- self.server.as_ref().unwrap().users(),
- );
- now!(Ok(Some(CommandResponse::ChannelList { channels: list })))
- }
- Command::ConfigReload => {
- self.reload_config();
- now!(Ok(None))
- }
- Command::DeafenSelf(toggle) => {
- if !matches!(*self.phase_receiver().borrow(), StatePhase::Connected(_)) {
- return now!(Err(Error::Disconnected));
- }
-
- let server = self.server().unwrap();
- let action = match (toggle, server.muted(), server.deafened()) {
- (Some(false), false, false) => None,
- (Some(false), false, true) => Some((false, false)),
- (Some(false), true, false) => None,
- (Some(false), true, true) => Some((true, false)),
- (Some(true), false, false) => Some((false, true)),
- (Some(true), false, true) => None,
- (Some(true), true, false) => Some((true, true)),
- (Some(true), true, true) => None,
- (None, false, false) => Some((false, true)),
- (None, false, true) => Some((false, false)),
- (None, true, false) => Some((true, true)),
- (None, true, true) => Some((true, false)),
- };
-
- let mut new_deaf = None;
- if let Some((mute, deafen)) = action {
- if server.deafened() != deafen {
- self.audio_output.play_effect(if deafen {
- NotificationEvents::Deafen
- } else {
- NotificationEvents::Undeafen
- });
- } else if server.muted() != mute {
- self.audio_output.play_effect(if mute {
- NotificationEvents::Mute
- } else {
- NotificationEvents::Unmute
- });
- }
- let mut msg = msgs::UserState::new();
- if server.muted() != mute {
- msg.set_self_mute(mute);
- } else if !mute && !deafen && server.deafened() {
- msg.set_self_mute(false);
- }
- if server.deafened() != deafen {
- msg.set_self_deaf(deafen);
- new_deaf = Some(deafen);
- }
- let server = self.server_mut().unwrap();
- server.set_muted(mute);
- server.set_deafened(deafen);
- packet_sender.send(msg.into()).unwrap();
- }
-
- now!(Ok(new_deaf.map(|b| CommandResponse::DeafenStatus { is_deafened: b })))
- }
- Command::InputVolumeSet(volume) => {
- self.audio_input.set_volume(volume);
- now!(Ok(None))
- }
- Command::MuteOther(string, toggle) => {
- if !matches!(*self.phase_receiver().borrow(), StatePhase::Connected(_)) {
- return now!(Err(Error::Disconnected));
- }
-
- let id = self
- .server_mut()
- .unwrap()
- .users_mut()
- .iter_mut()
- .find(|(_, user)| user.name() == string);
-
- let (id, user) = match id {
- Some(id) => (*id.0, id.1),
- None => return now!(Err(Error::InvalidUsername(string))),
- };
-
- let action = match toggle {
- Some(state) => {
- if user.suppressed() != state {
- Some(state)
- } else {
- None
- }
- }
- None => Some(!user.suppressed()),
- };
-
- if let Some(action) = action {
- user.set_suppressed(action);
- self.audio_output.set_mute(id, action);
- }
-
- return now!(Ok(None));
- }
- Command::MuteSelf(toggle) => {
- if !matches!(*self.phase_receiver().borrow(), StatePhase::Connected(_)) {
- return now!(Err(Error::Disconnected));
- }
-
- let server = self.server().unwrap();
- let action = match (toggle, server.muted(), server.deafened()) {
- (Some(false), false, false) => None,
- (Some(false), false, true) => Some((false, false)),
- (Some(false), true, false) => Some((false, false)),
- (Some(false), true, true) => Some((false, false)),
- (Some(true), false, false) => Some((true, false)),
- (Some(true), false, true) => None,
- (Some(true), true, false) => None,
- (Some(true), true, true) => None,
- (None, false, false) => Some((true, false)),
- (None, false, true) => Some((false, false)),
- (None, true, false) => Some((false, false)),
- (None, true, true) => Some((false, false)),
- };
-
- let mut new_mute = None;
- if let Some((mute, deafen)) = action {
- if server.deafened() != deafen {
- self.audio_output.play_effect(if deafen {
- NotificationEvents::Deafen
- } else {
- NotificationEvents::Undeafen
- });
- } else if server.muted() != mute {
- self.audio_output.play_effect(if mute {
- NotificationEvents::Mute
- } else {
- NotificationEvents::Unmute
- });
- }
- let mut msg = msgs::UserState::new();
- if server.muted() != mute {
- msg.set_self_mute(mute);
- new_mute = Some(mute)
- } else if !mute && !deafen && server.deafened() {
- msg.set_self_mute(false);
- new_mute = Some(false)
- }
- if server.deafened() != deafen {
- msg.set_self_deaf(deafen);
- }
- let server = self.server_mut().unwrap();
- server.set_muted(mute);
- server.set_deafened(deafen);
- packet_sender.send(msg.into()).unwrap();
- }
-
- now!(Ok(new_mute.map(|b| CommandResponse::MuteStatus { is_muted: b })))
- }
- Command::OutputVolumeSet(volume) => {
- self.audio_output.set_volume(volume);
- now!(Ok(None))
- }
- Command::Ping => {
- now!(Ok(Some(CommandResponse::Pong)))
- }
- Command::ServerConnect {
- host,
- port,
- username,
- password,
- accept_invalid_cert,
- } => {
- if !matches!(*self.phase_receiver().borrow(), StatePhase::Disconnected) {
- return now!(Err(Error::AlreadyConnected));
- }
- let mut server = Server::new();
- *server.username_mut() = Some(username);
- *server.password_mut() = password;
- *server.host_mut() = Some(format!("{}:{}", host, port));
- self.server = Some(server);
- self.phase_watcher
- .0
- .send(StatePhase::Connecting)
- .unwrap();
-
- let socket_addr = match (host.as_ref(), port)
- .to_socket_addrs()
- .map(|mut e| e.next())
- {
- Ok(Some(v)) => v,
- _ => {
- warn!("Error parsing server addr");
- return now!(Err(Error::InvalidServerAddr(host, port)));
- }
- };
- connection_info_sender
- .send(Some(ConnectionInfo::new(
- socket_addr,
- host,
- accept_invalid_cert,
- )))
- .unwrap();
- at!(TcpEvent::Connected, |res| {
- //runs the closure when the client is connected
- if let TcpEventData::Connected(res) = res {
- res.map(|msg| {
- Some(CommandResponse::ServerConnect {
- welcome_message: if msg.has_welcome_text() {
- Some(msg.get_welcome_text().to_string())
- } else {
- None
- },
- })
- })
- } else {
- unreachable!("callback should be provided with a TcpEventData::Connected");
- }
- })
- }
- Command::ServerDisconnect => {
- if !matches!(*self.phase_receiver().borrow(), StatePhase::Connected(_)) {
- return now!(Err(Error::Disconnected));
- }
-
- self.server = None;
-
- self.phase_watcher
- .0
- .send(StatePhase::Disconnected)
- .unwrap();
- self.audio_output.play_effect(NotificationEvents::ServerDisconnect);
- now!(Ok(None))
- }
- Command::ServerStatus { host, port } => ExecutionContext::Ping(
- Box::new(move || {
- match (host.as_str(), port)
- .to_socket_addrs()
- .map(|mut e| e.next())
- {
- Ok(Some(v)) => Ok(v),
- _ => Err(Error::InvalidServerAddr(host, port)),
- }
- }),
- Box::new(move |pong| {
- Ok(pong.map(|pong| CommandResponse::ServerStatus {
- version: pong.version,
- users: pong.users,
- max_users: pong.max_users,
- bandwidth: pong.bandwidth,
- }))
- }),
- ),
- Command::Status => {
- if !matches!(*self.phase_receiver().borrow(), StatePhase::Connected(_)) {
- return now!(Err(Error::Disconnected));
- }
- let state = self.server.as_ref().unwrap().into();
- now!(Ok(Some(CommandResponse::Status {
- server_state: state, //guaranteed not to panic because if we are connected, server is guaranteed to be Some
- })))
- }
- Command::UserVolumeSet(string, volume) => {
- if !matches!(*self.phase_receiver().borrow(), StatePhase::Connected(_)) {
- return now!(Err(Error::Disconnected));
- }
- let user_id = match self
- .server()
- .unwrap()
- .users()
- .iter()
- .find(|e| e.1.name() == string)
- .map(|e| *e.0)
- {
- None => return now!(Err(Error::InvalidUsername(string))),
- Some(v) => v,
- };
-
- self.audio_output.set_user_volume(user_id, volume);
- now!(Ok(None))
- }
- }
- }
pub fn parse_user_state(&mut self, msg: msgs::UserState) {
if !msg.has_session() {
@@ -590,6 +254,10 @@ impl State {
self.audio_output.load_sound_effects(sound_effects);
}
}
+
+ pub fn register_message(&mut self, msg: (String, u32)) {
+ self.message_buffer.push(msg);
+ }
pub fn broadcast_phase(&self, phase: StatePhase) {
self.phase_watcher
@@ -609,12 +277,6 @@ impl State {
pub fn audio_output(&self) -> &AudioOutput {
&self.audio_output
}
- pub fn audio_input_mut(&mut self) -> &mut AudioInput {
- &mut self.audio_input
- }
- pub fn audio_output_mut(&mut self) -> &mut AudioOutput {
- &mut self.audio_output
- }
pub fn phase_receiver(&self) -> watch::Receiver<StatePhase> {
self.phase_watcher.1.clone()
}
@@ -640,4 +302,409 @@ impl State {
.1
.channel()
}
+
+ /// Gets the username of a user with id `user` connected to the same server that we are connected to.
+ /// If we are connected to the server but the user with the id doesn't exist, the string "Unknown user {id}"
+ /// is returned instead. If we aren't connected to a server, None is returned instead.
+ fn get_user_name(&self, user: u32) -> Option<String> {
+ self.server()
+ .map(|e| e.users()
+ .get(&user).map(|e| e.name().to_string())
+ .unwrap_or(format!("Unknown user {}", user)))
+ }
+}
+
+pub fn handle_command(
+ og_state: Arc<RwLock<State>>,
+ command: Command,
+ packet_sender: &mut mpsc::UnboundedSender<ControlPacket<Serverbound>>,
+ connection_info_sender: &mut watch::Sender<Option<ConnectionInfo>>,
+) -> ExecutionContext {
+ let mut state = og_state.write().unwrap();
+ match command {
+ Command::ChannelJoin { channel_identifier } => {
+ if !matches!(*state.phase_receiver().borrow(), StatePhase::Connected(_)) {
+ return now!(Err(Error::Disconnected));
+ }
+
+ let id = match state.server().unwrap().channel_name(&channel_identifier) {
+ Ok((id, _)) => id,
+ Err(e) => return now!(Err(Error::ChannelIdentifierError(channel_identifier, e))),
+ };
+
+ let mut msg = msgs::UserState::new();
+ msg.set_session(state.server.as_ref().unwrap().session_id().unwrap());
+ msg.set_channel_id(id);
+ packet_sender.send(msg.into()).unwrap();
+ now!(Ok(None))
+ }
+ Command::ChannelList => {
+ if !matches!(*state.phase_receiver().borrow(), StatePhase::Connected(_)) {
+ return now!(Err(Error::Disconnected));
+ }
+ let list = channel::into_channel(
+ state.server.as_ref().unwrap().channels(),
+ state.server.as_ref().unwrap().users(),
+ );
+ now!(Ok(Some(CommandResponse::ChannelList { channels: list })))
+ }
+ Command::ConfigReload => {
+ state.reload_config();
+ now!(Ok(None))
+ }
+ Command::DeafenSelf(toggle) => {
+ if !matches!(*state.phase_receiver().borrow(), StatePhase::Connected(_)) {
+ return now!(Err(Error::Disconnected));
+ }
+
+ let server = state.server().unwrap();
+ let action = match (toggle, server.muted(), server.deafened()) {
+ (Some(false), false, false) => None,
+ (Some(false), false, true) => Some((false, false)),
+ (Some(false), true, false) => None,
+ (Some(false), true, true) => Some((true, false)),
+ (Some(true), false, false) => Some((false, true)),
+ (Some(true), false, true) => None,
+ (Some(true), true, false) => Some((true, true)),
+ (Some(true), true, true) => None,
+ (None, false, false) => Some((false, true)),
+ (None, false, true) => Some((false, false)),
+ (None, true, false) => Some((true, true)),
+ (None, true, true) => Some((true, false)),
+ };
+
+ let mut new_deaf = None;
+ if let Some((mute, deafen)) = action {
+ if server.deafened() != deafen {
+ state.audio_output.play_effect(if deafen {
+ NotificationEvents::Deafen
+ } else {
+ NotificationEvents::Undeafen
+ });
+ } else if server.muted() != mute {
+ state.audio_output.play_effect(if mute {
+ NotificationEvents::Mute
+ } else {
+ NotificationEvents::Unmute
+ });
+ }
+ let mut msg = msgs::UserState::new();
+ if server.muted() != mute {
+ msg.set_self_mute(mute);
+ } else if !mute && !deafen && server.deafened() {
+ msg.set_self_mute(false);
+ }
+ if server.deafened() != deafen {
+ msg.set_self_deaf(deafen);
+ new_deaf = Some(deafen);
+ }
+ let server = state.server_mut().unwrap();
+ server.set_muted(mute);
+ server.set_deafened(deafen);
+ packet_sender.send(msg.into()).unwrap();
+ }
+
+ now!(Ok(new_deaf.map(|b| CommandResponse::DeafenStatus { is_deafened: b })))
+ }
+ Command::InputVolumeSet(volume) => {
+ state.audio_input.set_volume(volume);
+ now!(Ok(None))
+ }
+ Command::MuteOther(string, toggle) => {
+ if !matches!(*state.phase_receiver().borrow(), StatePhase::Connected(_)) {
+ return now!(Err(Error::Disconnected));
+ }
+
+ let id = state
+ .server_mut()
+ .unwrap()
+ .users_mut()
+ .iter_mut()
+ .find(|(_, user)| user.name() == string);
+
+ let (id, user) = match id {
+ Some(id) => (*id.0, id.1),
+ None => return now!(Err(Error::InvalidUsername(string))),
+ };
+
+ let action = match toggle {
+ Some(state) => {
+ if user.suppressed() != state {
+ Some(state)
+ } else {
+ None
+ }
+ }
+ None => Some(!user.suppressed()),
+ };
+
+ if let Some(action) = action {
+ user.set_suppressed(action);
+ state.audio_output.set_mute(id, action);
+ }
+
+ return now!(Ok(None));
+ }
+ Command::MuteSelf(toggle) => {
+ if !matches!(*state.phase_receiver().borrow(), StatePhase::Connected(_)) {
+ return now!(Err(Error::Disconnected));
+ }
+
+ let server = state.server().unwrap();
+ let action = match (toggle, server.muted(), server.deafened()) {
+ (Some(false), false, false) => None,
+ (Some(false), false, true) => Some((false, false)),
+ (Some(false), true, false) => Some((false, false)),
+ (Some(false), true, true) => Some((false, false)),
+ (Some(true), false, false) => Some((true, false)),
+ (Some(true), false, true) => None,
+ (Some(true), true, false) => None,
+ (Some(true), true, true) => None,
+ (None, false, false) => Some((true, false)),
+ (None, false, true) => Some((false, false)),
+ (None, true, false) => Some((false, false)),
+ (None, true, true) => Some((false, false)),
+ };
+
+ let mut new_mute = None;
+ if let Some((mute, deafen)) = action {
+ if server.deafened() != deafen {
+ state.audio_output.play_effect(if deafen {
+ NotificationEvents::Deafen
+ } else {
+ NotificationEvents::Undeafen
+ });
+ } else if server.muted() != mute {
+ state.audio_output.play_effect(if mute {
+ NotificationEvents::Mute
+ } else {
+ NotificationEvents::Unmute
+ });
+ }
+ let mut msg = msgs::UserState::new();
+ if server.muted() != mute {
+ msg.set_self_mute(mute);
+ new_mute = Some(mute)
+ } else if !mute && !deafen && server.deafened() {
+ msg.set_self_mute(false);
+ new_mute = Some(false)
+ }
+ if server.deafened() != deafen {
+ msg.set_self_deaf(deafen);
+ }
+ let server = state.server_mut().unwrap();
+ server.set_muted(mute);
+ server.set_deafened(deafen);
+ packet_sender.send(msg.into()).unwrap();
+ }
+
+ now!(Ok(new_mute.map(|b| CommandResponse::MuteStatus { is_muted: b })))
+ }
+ Command::OutputVolumeSet(volume) => {
+ state.audio_output.set_volume(volume);
+ now!(Ok(None))
+ }
+ Command::Ping => {
+ now!(Ok(Some(CommandResponse::Pong)))
+ }
+ Command::ServerConnect {
+ host,
+ port,
+ username,
+ password,
+ accept_invalid_cert,
+ } => {
+ if !matches!(*state.phase_receiver().borrow(), StatePhase::Disconnected) {
+ return now!(Err(Error::AlreadyConnected));
+ }
+ let mut server = Server::new();
+ *server.username_mut() = Some(username);
+ *server.password_mut() = password;
+ *server.host_mut() = Some(format!("{}:{}", host, port));
+ state.server = Some(server);
+ state.phase_watcher
+ .0
+ .send(StatePhase::Connecting)
+ .unwrap();
+
+ let socket_addr = match (host.as_ref(), port)
+ .to_socket_addrs()
+ .map(|mut e| e.next())
+ {
+ Ok(Some(v)) => v,
+ _ => {
+ warn!("Error parsing server addr");
+ return now!(Err(Error::InvalidServerAddr(host, port)));
+ }
+ };
+ connection_info_sender
+ .send(Some(ConnectionInfo::new(
+ socket_addr,
+ host,
+ accept_invalid_cert,
+ )))
+ .unwrap();
+ at!(TcpEvent::Connected, |res| {
+ //runs the closure when the client is connected
+ if let TcpEventData::Connected(res) = res {
+ Box::new(iter::once(res.map(|msg| {
+ Some(CommandResponse::ServerConnect {
+ welcome_message: if msg.has_welcome_text() {
+ Some(msg.get_welcome_text().to_string())
+ } else {
+ None
+ },
+ })
+ })))
+ } else {
+ unreachable!("callback should be provided with a TcpEventData::Connected");
+ }
+ })
+ }
+ Command::ServerDisconnect => {
+ if !matches!(*state.phase_receiver().borrow(), StatePhase::Connected(_)) {
+ return now!(Err(Error::Disconnected));
+ }
+
+ state.server = None;
+
+ state.phase_watcher
+ .0
+ .send(StatePhase::Disconnected)
+ .unwrap();
+ state.audio_output.play_effect(NotificationEvents::ServerDisconnect);
+ now!(Ok(None))
+ }
+ Command::ServerStatus { host, port } => ExecutionContext::Ping(
+ Box::new(move || {
+ match (host.as_str(), port)
+ .to_socket_addrs()
+ .map(|mut e| e.next())
+ {
+ Ok(Some(v)) => Ok(v),
+ _ => Err(Error::InvalidServerAddr(host, port)),
+ }
+ }),
+ Box::new(move |pong| {
+ Ok(pong.map(|pong| (CommandResponse::ServerStatus {
+ version: pong.version,
+ users: pong.users,
+ max_users: pong.max_users,
+ bandwidth: pong.bandwidth,
+ })))
+ }),
+ ),
+ Command::Status => {
+ if !matches!(*state.phase_receiver().borrow(), StatePhase::Connected(_)) {
+ return now!(Err(Error::Disconnected));
+ }
+ let state = state.server.as_ref().unwrap().into();
+ now!(Ok(Some(CommandResponse::Status {
+ server_state: state, //guaranteed not to panic because if we are connected, server is guaranteed to be Some
+ })))
+ }
+ Command::UserVolumeSet(string, volume) => {
+ if !matches!(*state.phase_receiver().borrow(), StatePhase::Connected(_)) {
+ return now!(Err(Error::Disconnected));
+ }
+ let user_id = match state
+ .server()
+ .unwrap()
+ .users()
+ .iter()
+ .find(|e| e.1.name() == string)
+ .map(|e| *e.0)
+ {
+ None => return now!(Err(Error::InvalidUsername(string))),
+ Some(v) => v,
+ };
+
+ state.audio_output.set_user_volume(user_id, volume);
+ now!(Ok(None))
+ }
+ Command::PastMessages { block } => {
+ //does it make sense to wait for messages while not connected?
+ if !matches!(*state.phase_receiver().borrow(), StatePhase::Connected(_)) {
+ return now!(Err(Error::Disconnected));
+ }
+ if block {
+ let ref_state = Arc::clone(&og_state);
+ ExecutionContext::TcpEventSubscriber(
+ TcpEvent::TextMessage,
+ Box::new(move |data, sender| {
+ if let TcpEventData::TextMessage(a) = data {
+ let message = (
+ a.get_message().to_owned(),
+ ref_state.read().unwrap().get_user_name(a.get_actor()).unwrap()
+ );
+ sender.send(Ok(Some(CommandResponse::PastMessage { message }))).is_ok()
+ } else {
+ unreachable!("Should only receive a TextMessage data when listening to TextMessage events");
+ }
+ }),
+ )
+ } else {
+ let messages = std::mem::take(&mut state.message_buffer);
+ let messages: Vec<_> = messages.into_iter()
+ .map(|(msg, user)| (msg, state.get_user_name(user).unwrap()))
+ .map(|e| Ok(Some(CommandResponse::PastMessage { message: e })))
+ .collect();
+
+ ExecutionContext::Now(Box::new(move || {
+ Box::new(messages.into_iter())
+ }))
+ }
+ }
+ Command::SendMessage { message, targets } => {
+ if !matches!(*state.phase_receiver().borrow(), StatePhase::Connected(_)) {
+ return now!(Err(Error::Disconnected));
+ }
+
+ let mut msg = msgs::TextMessage::new();
+
+ msg.set_message(message);
+
+ for target in targets {
+ match target {
+ MessageTarget::Channel { recursive, name } => {
+ let channel_id = state
+ .server()
+ .unwrap()
+ .channel_name(&name);
+
+ let channel_id = match channel_id {
+ Ok(id) => id,
+ Err(e) => return now!(Err(Error::ChannelIdentifierError(name, e))),
+ }.0;
+
+ if recursive {
+ msg.mut_tree_id()
+ } else {
+ msg.mut_channel_id()
+ }.push(channel_id);
+ }
+ MessageTarget::User { name } => {
+ let id = state
+ .server()
+ .unwrap()
+ .users()
+ .iter()
+ .find(|(_, user)| user.name() == &name)
+ .map(|(e, _)| *e);
+
+ let id = match id {
+ Some(id) => id,
+ None => return now!(Err(Error::InvalidUsername(name))),
+ };
+
+ msg.mut_session().push(id);
+ }
+ }
+ }
+
+ packet_sender.send(msg.into()).unwrap();
+
+ now!(Ok(None))
+ }
+ }
}