diff --git a/voice.go b/voice.go index 89b01a6..faf2847 100644 --- a/voice.go +++ b/voice.go @@ -29,16 +29,18 @@ import ( // A VoiceConnectionConnection struct holds all the data and functions related to a Discord Voice Connection. type VoiceConnection struct { - sync.Mutex // future use - Ready bool // If true, voice is ready to send/receive audio - Debug bool // If true, print extra logging - Receive bool // If false, don't try to receive packets - OP2 *voiceOP2 // exported for dgvoice, may change. - OpusSend chan []byte // Chan for sending opus audio - OpusRecv chan *Packet // Chan for receiving opus audio - GuildID string - ChannelID string - UserID string + sync.Mutex + Debug bool // If true, print extra logging + Ready bool // If true, voice is ready to send/receive audio + GuildID string + ChannelID string + UserID string + + OpusSend chan []byte // Chan for sending opus audio + OpusRecv chan *Packet // Chan for receiving opus audio + + Receive bool // If false, don't try to receive packets + OP2 *voiceOP2 // exported for dgvoice, may change. // FrameRate int // This can be used to set the FrameRate of Opus data // FrameSize int // This can be used to set the FrameSize of Opus data @@ -94,9 +96,10 @@ type voiceHandshakeOp struct { } // Open opens a voice connection. This should be called -// after VoiceConnectionChannelJoin is used and the data VOICE websocket events +// after VoiceChannelJoin is used and the data VOICE websocket events // are captured. func (v *VoiceConnection) Open() (err error) { + v.Lock() defer v.Unlock() @@ -131,19 +134,20 @@ func (v *VoiceConnection) Open() (err error) { } // WaitUntilConnected is a TEMP FUNCTION -// And this is going to be removed to changed entirely. +// And this is going to be removed or changed entirely. // I know it looks hacky, but I needed to get it working quickly. func (v *VoiceConnection) WaitUntilConnected() error { i := 0 for { + if v.Ready { + return nil + } + if i > 10 { return fmt.Errorf("Timeout waiting for voice.") } - if v.Ready { - return nil - } time.Sleep(1 * time.Second) i++ } @@ -151,6 +155,100 @@ func (v *VoiceConnection) WaitUntilConnected() error { return nil } +type voiceSpeakingData struct { + Speaking bool `json:"speaking"` + Delay int `json:"delay"` +} + +type voiceSpeakingOp struct { + Op int `json:"op"` // Always 5 + Data voiceSpeakingData `json:"d"` +} + +// Speaking sends a speaking notification to Discord over the voice websocket. +// This must be sent as true prior to sending audio and should be set to false +// once finished sending audio. +// b : Send true if speaking, false if not. +func (v *VoiceConnection) Speaking(b bool) (err error) { + + if v.wsConn == nil { + return fmt.Errorf("No VoiceConnection websocket.") + } + + data := voiceSpeakingOp{5, voiceSpeakingData{b, 0}} + err = v.wsConn.WriteJSON(data) + if err != nil { + fmt.Println("Speaking() write json error:", err) + return + } + + return +} + +// ChangeChannel sends Discord a request to change channels within a Guild +// !!! NOTE !!! This function may be removed in favour of just using ChannelVoiceJoin +func (v *VoiceConnection) ChangeChannel(channelID string, mute, deaf bool) (err error) { + + data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, &channelID, mute, deaf}} + err = v.session.wsConn.WriteJSON(data) + + return +} + +// Disconnect disconnects from this voice channel and closes the websocket +// and udp connections to Discord. +// !!! NOTE !!! this function may be removed in favour of ChannelVoiceLeave +func (v *VoiceConnection) Disconnect() (err error) { + + // Send a OP4 with a nil channel to disconnect + if v.sessionID != "" { + data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}} + err = v.session.wsConn.WriteJSON(data) + v.sessionID = "" + } + + // Close websocket and udp connections + v.Close() + + delete(v.session.VoiceConnections, v.GuildID) + + return +} + +// Close closes the voice ws and udp connections +func (v *VoiceConnection) Close() { + + v.Lock() + defer v.Unlock() + + v.Ready = false + + if v.close != nil { + close(v.close) + v.close = nil + } + + if v.UDPConn != nil { + err := v.UDPConn.Close() + if err != nil { + fmt.Println("error closing udp connection: ", err) + } + v.UDPConn = nil + } + + if v.wsConn != nil { + err := v.wsConn.Close() + if err != nil { + fmt.Println("error closing websocket connection: ", err) + } + v.wsConn = nil + } +} + +// ------------------------------------------------------------------------------------------------ +// Unexported Internal Functions Below. +// ------------------------------------------------------------------------------------------------ + // wsListen listens on the voice websocket for messages and passes them // to the voice event handler. This is automatically called by the Open func func (v *VoiceConnection) wsListen(wsConn *websocket.Conn, close <-chan struct{}) { @@ -298,36 +396,6 @@ func (v *VoiceConnection) wsHeartbeat(wsConn *websocket.Conn, close <-chan struc } } -type voiceSpeakingData struct { - Speaking bool `json:"speaking"` - Delay int `json:"delay"` -} - -type voiceSpeakingOp struct { - Op int `json:"op"` // Always 5 - Data voiceSpeakingData `json:"d"` -} - -// Speaking sends a speaking notification to Discord over the voice websocket. -// This must be sent as true prior to sending audio and should be set to false -// once finished sending audio. -// b : Send true if speaking, false if not. -func (v *VoiceConnection) Speaking(b bool) (err error) { - - if v.wsConn == nil { - return fmt.Errorf("No VoiceConnection websocket.") - } - - data := voiceSpeakingOp{5, voiceSpeakingData{b, 0}} - err = v.wsConn.WriteJSON(data) - if err != nil { - fmt.Println("Speaking() write json error:", err) - return - } - - return -} - // ------------------------------------------------------------------------------------------------ // Code related to the VoiceConnection UDP connection // ------------------------------------------------------------------------------------------------ @@ -613,63 +681,3 @@ func (v *VoiceConnection) opusReceiver(UDPConn *net.UDPConn, close <-chan struct } } } - -// Close closes the voice ws and udp connections -func (v *VoiceConnection) Close() { - - v.Lock() - defer v.Unlock() - - v.Ready = false - - if v.close != nil { - close(v.close) - v.close = nil - } - - if v.UDPConn != nil { - err := v.UDPConn.Close() - if err != nil { - fmt.Println("error closing udp connection: ", err) - } - v.UDPConn = nil - } - - if v.wsConn != nil { - err := v.wsConn.Close() - if err != nil { - fmt.Println("error closing websocket connection: ", err) - } - v.wsConn = nil - } -} - -// ChangeChannel sends Discord a request to change channels within a Guild -// !!! NOTE !!! This function may be removed in favour of just using ChannelVoiceJoin -func (v *VoiceConnection) ChangeChannel(channelID string, mute, deaf bool) (err error) { - - data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, &channelID, mute, deaf}} - err = v.session.wsConn.WriteJSON(data) - - return -} - -// Disconnect disconnects from this voice channel and closes the websocket -// and udp connections to Discord. -// !!! NOTE !!! this function may be removed in favour of ChannelVoiceLeave -func (v *VoiceConnection) Disconnect() (err error) { - - // Send a OP4 with a nil channel to disconnect - if v.sessionID != "" { - data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}} - err = v.session.wsConn.WriteJSON(data) - v.sessionID = "" - } - - // Close websocket and udp connections - v.Close() - - delete(v.session.VoiceConnections, v.GuildID) - - return -}