diff --git a/voice.go b/voice.go index 26c8078..601d2b9 100644 --- a/voice.go +++ b/voice.go @@ -181,11 +181,24 @@ func (v *VoiceConnection) Close() { } if v.wsConn != nil { - v.log(LogInformational, "closing wsConn") - err := v.wsConn.Close() + v.log(LogInformational, "sending close frame") + + // To cleanly close a connection, a client should send a close + // frame and wait for the server to close the connection. + err := v.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) if err != nil { - log.Println("error closing websocket connection: ", err) + v.log(LogError, "error closing websocket, %s", err) } + + // TODO: Wait for Discord to actually close the connection. + time.Sleep(1 * time.Second) + + v.log(LogInformational, "closing websocket") + err = v.wsConn.Close() + if err != nil { + v.log(LogError, "error closing websocket, %s", err) + } + v.wsConn = nil } } @@ -799,42 +812,6 @@ func (v *VoiceConnection) reconnect() { defer func() { v.reconnecting = false }() - /* - if v.session == nil { - v.log(LogInformational, "cannot reconnect with nil session") - return - } - - // check that the gateway session is Ready - // this needs more smarts - but the issue is that well the session - // could be not ready and in that case the disconnect below will panic - // one cause for that is if the gw disconnects and starts the reconnect - // processes right before the voice disconnects. - // NOTE: this will probably change but it's a safety net for now - i := 0 - for v.session.DataReady == false || v.session.wsConn == nil { - if i > 20 { - v.log(LogInformational, "timeout waiting for ready session, I give up.") - return - } - time.Sleep(1 * time.Second) - i++ - } - - // Send a OP4 with a nil channel to disconnect - // this may not be required, but is here as a safety for now. - if v.sessionID != "" { - data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}} - v.session.wsMutex.Lock() - err := v.session.wsConn.WriteJSON(data) - v.session.wsMutex.Unlock() - if err != nil { - v.log(LogError, "error sending disconnect packet, %s", err) - } - v.sessionID = "" - } - */ - // Close any currently open connections v.Close() @@ -867,6 +844,17 @@ func (v *VoiceConnection) reconnect() { return } + // if the quick reconnect above didn't work lets just send a disconnect + // packet to reset things. + // Send a OP4 with a nil channel to disconnect + data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}} + v.session.wsMutex.Lock() + err = v.session.wsConn.WriteJSON(data) + v.session.wsMutex.Unlock() + if err != nil { + v.log(LogError, "error sending disconnect packet, %s", err) + } + v.log(LogInformational, "error reconnecting to channel %s, %s", v.ChannelID, err) } } diff --git a/wsapi.go b/wsapi.go index 2e5ca0d..a19c384 100644 --- a/wsapi.go +++ b/wsapi.go @@ -427,19 +427,19 @@ func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *Voi s.log(LogInformational, "called") - // If a voice connection already exists for this guild then - // return that connection. If the channel differs, also change channels. - var ok bool - if voice, ok = s.VoiceConnections[gID]; ok && voice.GuildID != "" { - //TODO: consider a better variable than GuildID in the above check - // to verify if this connection is valid or not. + voice, _ = s.VoiceConnections[gID] - if voice.ChannelID != cID { - err = voice.ChangeChannel(cID, mute, deaf) - } - return + if voice == nil { + voice = &VoiceConnection{} + s.VoiceConnections[gID] = voice } + voice.GuildID = gID + voice.ChannelID = cID + voice.deaf = deaf + voice.mute = mute + voice.session = s + // Send the request to Discord that we want to join the voice channel data := voiceChannelJoinOp{4, voiceChannelJoinData{&gID, &cID, mute, deaf}} s.wsMutex.Lock() @@ -457,17 +457,6 @@ func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *Voi return } - if voice == nil { - voice = &VoiceConnection{} - s.VoiceConnections[gID] = voice - } - - voice.GuildID = gID - voice.ChannelID = cID - voice.deaf = deaf - voice.mute = mute - voice.session = s - return } @@ -656,9 +645,28 @@ func (s *Session) Close() (err error) { s.listening = nil } + // TODO: Close all active Voice Connections too + // this should force stop any reconnecting voice channels too + if s.wsConn != nil { + + s.log(LogInformational, "sending close frame") + // To cleanly close a connection, a client should send a close + // frame and wait for the server to close the connection. + err := s.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) + if err != nil { + s.log(LogError, "error closing websocket, %s", err) + } + + // TODO: Wait for Discord to actually close the connection. + time.Sleep(1 * time.Second) + s.log(LogInformational, "closing gateway websocket") err = s.wsConn.Close() + if err != nil { + s.log(LogError, "error closing websocket, %s", err) + } + s.wsConn = nil }