use crate::constants::VoiceOpCode;
use crate::gateway::InterMessage;
use crate::model::{
id::{
ChannelId,
GuildId,
UserId
},
voice::VoiceState
};
use tracing::instrument;
use tokio::sync::Mutex;
use std::sync::Arc;
use futures::channel::mpsc::{
unbounded,
UnboundedSender as Sender,
};
use super::connection_info::ConnectionInfo;
use super::{Audio, AudioReceiver, AudioSource, Bitrate, Status as VoiceStatus, tasks, LockedAudio};
use serde_json::json;
#[derive(Clone)]
pub struct Handler {
pub channel_id: Option<ChannelId>,
pub endpoint: Option<String>,
pub guild_id: GuildId,
pub self_deaf: bool,
pub self_mute: bool,
sender: Sender<VoiceStatus>,
pub session_id: Option<String>,
pub token: Option<String>,
pub user_id: UserId,
ws: Option<Sender<InterMessage>>,
}
impl Handler {
#[inline]
pub(crate) fn new(
guild_id: GuildId,
ws: Sender<InterMessage>,
user_id: UserId,
) -> Self {
Self::new_raw(guild_id, Some(ws), user_id)
}
#[inline]
pub fn standalone(guild_id: GuildId, user_id: UserId) -> Self {
Self::new_raw(guild_id, None, user_id)
}
#[instrument(skip(self))]
pub fn connect(&mut self) -> bool {
if self.endpoint.is_none() || self.session_id.is_none() || self.token.is_none() {
return false;
}
let endpoint = self.endpoint.clone().unwrap();
let guild_id = self.guild_id;
let session_id = self.session_id.clone().unwrap();
let token = self.token.clone().unwrap();
let user_id = self.user_id;
self.send(VoiceStatus::Connect(ConnectionInfo {
endpoint,
guild_id,
session_id,
token,
user_id,
}));
true
}
#[instrument(skip(self))]
pub fn deafen(&mut self, deaf: bool) {
self.self_deaf = deaf;
if self.channel_id.is_some() {
self.update();
}
}
#[instrument(skip(self))]
pub fn join(&mut self, channel_id: ChannelId) {
self.channel_id = Some(channel_id);
self.send_join();
}
#[instrument(skip(self))]
pub fn leave(&mut self) {
if self.channel_id.is_some() {
self.channel_id = None;
self.send(VoiceStatus::Disconnect);
self.update();
}
}
#[instrument(skip(self, receiver))]
pub fn listen(&mut self, receiver: Option<Arc<dyn AudioReceiver>>) {
self.send(VoiceStatus::SetReceiver(receiver))
}
#[instrument(skip(self))]
pub fn mute(&mut self, mute: bool) {
self.self_mute = mute;
if self.channel_id.is_some() {
self.update();
self.send(VoiceStatus::Mute(mute));
}
}
#[instrument(skip(self, source))]
pub fn play(&mut self, source: Box<dyn AudioSource>) {
self.play_returning(source);
}
#[instrument(skip(self, source))]
pub fn play_returning(&mut self, source: Box<dyn AudioSource>) -> LockedAudio {
let player = Arc::new(Mutex::new(Audio::new(source)));
self.send(VoiceStatus::AddSender(player.clone()));
player
}
#[instrument(skip(self, source))]
pub fn play_only(&mut self, source: Box<dyn AudioSource>) -> LockedAudio {
let player = Arc::new(Mutex::new(Audio::new(source)));
self.send(VoiceStatus::SetSender(Some(player.clone())));
player
}
#[instrument(skip(self))]
pub fn set_bitrate(&mut self, bitrate: Bitrate) {
self.send(VoiceStatus::SetBitrate(bitrate))
}
#[instrument(skip(self))]
pub fn stop(&mut self) {
self.send(VoiceStatus::SetSender(None))
}
#[instrument(skip(self))]
pub fn switch_to(&mut self, channel_id: ChannelId) {
match self.channel_id {
Some(current_id) if current_id == channel_id => {
return;
},
_ => {
self.channel_id = Some(channel_id);
self.update();
},
}
}
#[instrument(skip(self, token))]
pub fn update_server(&mut self, endpoint: &Option<String>, token: &str) {
self.token = Some(token.to_string());
if let Some(endpoint) = endpoint.clone() {
self.endpoint = Some(endpoint);
if self.session_id.is_some() {
self.connect();
}
} else {
self.leave();
}
}
#[instrument(skip(self))]
pub fn update_state(&mut self, voice_state: &VoiceState) {
if self.user_id != voice_state.user_id.0 {
return;
}
self.channel_id = voice_state.channel_id;
if voice_state.channel_id.is_some() {
self.session_id = Some(voice_state.session_id.clone());
if self.endpoint.is_some() && self.token.is_some() {
self.connect();
}
} else {
self.leave();
}
}
fn new_raw(
guild_id: GuildId,
ws: Option<Sender<InterMessage>>,
user_id: UserId,
) -> Self {
let (tx, rx) = unbounded();
tasks::start(guild_id, rx);
Handler {
channel_id: None,
endpoint: None,
guild_id,
self_deaf: false,
self_mute: false,
sender: tx,
session_id: None,
token: None,
user_id,
ws,
}
}
#[instrument(skip(self, status))]
fn send(&mut self, status: VoiceStatus) {
if let Err(error) = self.sender.unbounded_send(status) {
let (tx, rx) = unbounded();
self.sender = tx;
self.sender.unbounded_send(error.into_inner()).unwrap();
tasks::start(self.guild_id, rx);
self.update();
}
}
#[instrument(skip(self))]
fn send_join(&self) {
if self.channel_id.is_none() {
return;
}
self.update();
}
#[instrument(skip(self))]
fn update(&self) {
if let Some(ref ws) = self.ws {
let map = json!({
"op": VoiceOpCode::SessionDescription.num(),
"d": {
"channel_id": self.channel_id.map(|c| c.0),
"guild_id": self.guild_id.0,
"self_deaf": self.self_deaf,
"self_mute": self.self_mute,
}
});
let _ = ws.unbounded_send(InterMessage::Json(map));
}
}
}
impl Drop for Handler {
fn drop(&mut self) { self.leave(); }
}