From 31bfe90f38134321ff62dac48289f346e29dab11 Mon Sep 17 00:00:00 2001 From: TopiSenpai Date: Tue, 13 Apr 2021 16:22:57 +0200 Subject: [PATCH] reenabled logging for heartbeat, fixed reconnect & updated guild create/update/delete handler --- api/audio_controller.go | 8 +++ api/events/dm_message_event.go | 1 - api/events/guild_message_events.go | 1 - api/gateway.go | 2 +- api/gateway_events.go | 12 ++-- internal/audio_controller_impl.go | 29 +++++++- internal/disgo_impl.go | 2 +- internal/gateway_impl.go | 81 +++++++++++------------ internal/handlers/guild_create_handler.go | 34 ++++------ internal/handlers/guild_delete_handler.go | 2 +- internal/handlers/guild_update_handler.go | 2 +- internal/handlers/ready_handler.go | 12 +--- 12 files changed, 96 insertions(+), 90 deletions(-) diff --git a/api/audio_controller.go b/api/audio_controller.go index 695a4a24..4819ca7a 100644 --- a/api/audio_controller.go +++ b/api/audio_controller.go @@ -1,5 +1,13 @@ package api +import "errors" + +// errors returned when no gateway or ws conn exists +var ( + ErrNoGateway = errors.New("no gateway initialized") + ErrNoGatewayConn = errors.New("no active gateway connection found") +) + // AudioController lets you Connect / Disconnect from a VoiceChannel type AudioController interface { Disgo() Disgo diff --git a/api/events/dm_message_event.go b/api/events/dm_message_event.go index ee5aebc3..39b012be 100644 --- a/api/events/dm_message_event.go +++ b/api/events/dm_message_event.go @@ -7,7 +7,6 @@ import ( // GenericDMMessageEvent is called upon receiving DMMessageCreateEvent, DMMessageUpdateEvent, DMMessageDeleteEvent, GenericDMMessageReactionEvent, DMMessageReactionAddEvent, DMMessageReactionRemoveEvent, DMMessageReactionRemoveEmoteEvent or DMMessageReactionRemoveAllEvent(requires api.IntentsDirectMessages) type GenericDMMessageEvent struct { GenericMessageEvent - Message *api.Message } // DMChannel returns the api.DMChannel where the GenericDMMessageEvent happened diff --git a/api/events/guild_message_events.go b/api/events/guild_message_events.go index 4fd3ba57..15407c44 100644 --- a/api/events/guild_message_events.go +++ b/api/events/guild_message_events.go @@ -8,7 +8,6 @@ import ( type GenericGuildMessageEvent struct { GenericMessageEvent GuildID api.Snowflake - Message *api.Message } // Guild returns the api.Guild the GenericGuildMessageEvent happened in diff --git a/api/gateway.go b/api/gateway.go index 7c4fb17c..6b47acaa 100644 --- a/api/gateway.go +++ b/api/gateway.go @@ -28,7 +28,7 @@ type Gateway interface { Disgo() Disgo Open() error Status() GatewayStatus - Close(bool) + Close() Conn() *websocket.Conn Latency() time.Duration } diff --git a/api/gateway_events.go b/api/gateway_events.go index 2b687f13..85565b65 100644 --- a/api/gateway_events.go +++ b/api/gateway_events.go @@ -20,13 +20,11 @@ type RawGatewayEvent struct { // ReadyGatewayEvent is the event sent by discord when you successfully Identify type ReadyGatewayEvent struct { - GatewayPacket - D struct { - User User `json:"user"` - Guilds []Guild `json:"guild_events"` - SessionID string `json:"session_id"` - Shard [2]int `json:"shard,omitempty"` - } `json:"d"` + Version int `json:"v"` + SelfUser User `json:"user"` + Guilds []*Guild `json:"guilds"` + SessionID string `json:"session_id"` + Shard *[2]int `json:"shard,omitempty"` } // HelloGatewayEventData is sent when we connect to the gateway diff --git a/internal/audio_controller_impl.go b/internal/audio_controller_impl.go index a25b915b..6b201ff5 100644 --- a/internal/audio_controller_impl.go +++ b/internal/audio_controller_impl.go @@ -1,6 +1,9 @@ package internal -import "github.com/DisgoOrg/disgo/api" +import ( + "github.com/DisgoOrg/disgo/api" + "github.com/gorilla/websocket" +) func newAudioControllerImpl(disgo api.Disgo) api.AudioController { return &AudioControllerImpl{disgo: disgo} @@ -18,7 +21,11 @@ func (c *AudioControllerImpl) Disgo() api.Disgo { // Connect sends a api.GatewayCommand to connect to a api.VoiceChannel func (c *AudioControllerImpl) Connect(guildID api.Snowflake, channelID api.Snowflake) error { - return c.Disgo().Gateway().Conn().WriteJSON(api.NewGatewayCommand(api.OpVoiceStateUpdate, api.UpdateVoiceStateCommand{ + conn, err := c.getConn() + if err != nil { + return err + } + return conn.WriteJSON(api.NewGatewayCommand(api.OpVoiceStateUpdate, api.UpdateVoiceStateCommand{ GuildID: guildID, ChannelID: &channelID, })) @@ -26,8 +33,24 @@ func (c *AudioControllerImpl) Connect(guildID api.Snowflake, channelID api.Snowf // Disconnect sends a api.GatewayCommand to disconnect from a api.VoiceChannel func (c *AudioControllerImpl) Disconnect(guildID api.Snowflake) error { - return c.Disgo().Gateway().Conn().WriteJSON(api.NewGatewayCommand(api.OpVoiceStateUpdate, api.UpdateVoiceStateCommand{ + conn, err := c.getConn() + if err != nil { + return err + } + return conn.WriteJSON(api.NewGatewayCommand(api.OpVoiceStateUpdate, api.UpdateVoiceStateCommand{ GuildID: guildID, ChannelID: nil, })) } + +func (c *AudioControllerImpl) getConn() (*websocket.Conn, error) { + gateway := c.Disgo().Gateway() + if gateway == nil { + return nil, api.ErrNoGateway + } + conn := gateway.Conn() + if conn == nil { + return nil, api.ErrNoGatewayConn + } + return conn, nil +} diff --git a/internal/disgo_impl.go b/internal/disgo_impl.go index 2a9a4686..6b4dfc4f 100644 --- a/internal/disgo_impl.go +++ b/internal/disgo_impl.go @@ -98,7 +98,7 @@ func (d *DisgoImpl) Close() { d.WebhookServer().Close() } if d.Gateway() != nil { - d.Gateway().Close(false) + d.Gateway().Close() } if d.EventManager() != nil { d.EventManager().Close() diff --git a/internal/gateway_impl.go b/internal/gateway_impl.go index 9c988fe1..f0856e80 100644 --- a/internal/gateway_impl.go +++ b/internal/gateway_impl.go @@ -86,7 +86,7 @@ func (g *GatewayImpl) Open() error { gatewayURL := *g.url + "?v=" + endpoints.APIVersion + "&encoding=json" wsConn, rs, err := websocket.DefaultDialer.Dial(gatewayURL, nil) if err != nil { - g.Close(false) + g.Close() var body string if rs != nil && rs.Body != nil { rawBody, err := ioutil.ReadAll(rs.Body) @@ -152,15 +152,15 @@ func (g *GatewayImpl) Open() error { } g.status = api.WaitingForReady } else { - g.Disgo().Logger().Infof("sending Resuming command...") g.status = api.Resuming - if err = wsConn.WriteJSON( - api.NewGatewayCommand(api.OpResume, api.ResumeCommand{ - Token: g.Disgo().Token(), - SessionID: *g.sessionID, - Seq: *g.lastSequenceReceived, - }), - ); err != nil { + cmd := api.NewGatewayCommand(api.OpResume, api.ResumeCommand{ + Token: g.Disgo().Token(), + SessionID: *g.sessionID, + Seq: *g.lastSequenceReceived, + }) + g.Disgo().Logger().Infof("sending Resuming command...") + + if err = wsConn.WriteJSON(cmd); err != nil { return err } } @@ -184,8 +184,11 @@ func (g *GatewayImpl) Latency() time.Duration { } // Close cleans up the gateway internals -func (g *GatewayImpl) Close(toReconnect bool) { - g.status = api.Disconnected +func (g *GatewayImpl) Close() { + g.closeWithCode(websocket.CloseNormalClosure) +} + +func (g *GatewayImpl) closeWithCode(code int) { if g.quit != nil { g.Disgo().Logger().Info("closing gateway goroutines...") close(g.quit) @@ -193,38 +196,20 @@ func (g *GatewayImpl) Close(toReconnect bool) { g.Disgo().Logger().Info("closed gateway goroutines") } if g.conn != nil { - var err error - if toReconnect { - err = g.conn.Close() - g.conn = nil - } else { - err = g.closeWithCode(websocket.CloseNormalClosure) - } - if err != nil { - g.Disgo().Logger().Errorf("error while closing wsconn: %s", err) - } - } -} - -func (g *GatewayImpl) closeWithCode(code int) error { - if g.conn != nil { - err := g.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(code, "")) if err != nil { - return err + g.Disgo().Logger().Errorf("error writing close code: %s", err) } // TODO: Wait for Discord to actually close the connection. time.Sleep(1 * time.Second) err = g.conn.Close() - g.conn = nil if err != nil { - return err + g.Disgo().Logger().Errorf("error closing conn: %s", err) } - + g.conn = nil } - return nil } // Conn returns the underlying websocket.Conn of this api.Gateway @@ -256,7 +241,7 @@ func (g *GatewayImpl) heartbeat() { } func (g *GatewayImpl) sendHeartbeat() { - //g.Disgo().Logger().Debug("sending heartbeat...") + g.Disgo().Logger().Debug("sending heartbeat...") heartbeatEvent := events.HeartbeatEvent{ GenericEvent: events.NewEvent(g.Disgo(), 0), @@ -265,7 +250,7 @@ func (g *GatewayImpl) sendHeartbeat() { if err := g.conn.WriteJSON(api.NewGatewayCommand(api.OpHeartbeat, g.lastSequenceReceived)); err != nil { g.Disgo().Logger().Errorf("failed to send heartbeat with error: %s", err) - g.Close(true) + g.closeWithCode(websocket.CloseServiceRestart) g.reconnect(1 * time.Second) } g.lastHeartbeatSent = time.Now().UTC() @@ -296,7 +281,7 @@ func (g *GatewayImpl) listen() { mt, data, err := g.conn.ReadMessage() if err != nil { g.Disgo().Logger().Errorf("error while reading from ws. error: %s", err) - g.Close(true) + g.closeWithCode(websocket.CloseServiceRestart) g.reconnect(1 * time.Second) return } @@ -326,7 +311,7 @@ func (g *GatewayImpl) listen() { g.Disgo().Logger().Errorf("Error parsing ready event: %s", err) continue } - g.sessionID = &readyEvent.D.SessionID + g.sessionID = &readyEvent.SessionID g.Disgo().Logger().Info("ready event received") } @@ -349,24 +334,32 @@ func (g *GatewayImpl) listen() { e.Handle(*event.T, nil, *event.S, event.D) case api.OpHeartbeat: - //g.Disgo().Logger().Debugf("received: OpHeartbeat") + g.Disgo().Logger().Debugf("received: OpHeartbeat") g.sendHeartbeat() case api.OpReconnect: g.Disgo().Logger().Debugf("received: OpReconnect") - g.Close(true) + g.closeWithCode(websocket.CloseServiceRestart) g.reconnect(1 * time.Second) case api.OpInvalidSession: - g.Disgo().Logger().Debugf("received: OpInvalidSession") - g.Close(false) - // clear reconnect info - g.sessionID = nil - g.lastSequenceReceived = nil + var resumable bool + if err = g.parseEventToStruct(event, &resumable); err != nil { + g.Disgo().Logger().Errorf("Error parsing invalid session data: %s", err) + } + g.Disgo().Logger().Debugf("received: OpInvalidSession, resumable: %b", resumable) + if resumable { + g.closeWithCode(websocket.CloseServiceRestart) + } else { + g.Close() + // clear reconnect info + g.sessionID = nil + g.lastSequenceReceived = nil + } g.reconnect(5 * time.Second) case api.OpHeartbeatACK: - //g.Disgo().Logger().Debugf("received: OpHeartbeatACK") + g.Disgo().Logger().Debugf("received: OpHeartbeatACK") g.lastHeartbeatReceived = time.Now().UTC() } diff --git a/internal/handlers/guild_create_handler.go b/internal/handlers/guild_create_handler.go index b52d6eaf..b0405f3e 100644 --- a/internal/handlers/guild_create_handler.go +++ b/internal/handlers/guild_create_handler.go @@ -25,20 +25,15 @@ func (h GuildCreateHandler) HandleGatewayEvent(disgo api.Disgo, eventManager api return } - guild := fullGuild.Guild - - guild.Disgo = disgo - oldGuild := disgo.Cache().Guild(guild.ID) - var wasUnavailable bool - if oldGuild == nil { - wasUnavailable = true - } else { + oldGuild := disgo.Cache().Guild(fullGuild.ID) + wasUnavailable := true + if oldGuild != nil { + oldGuild = &*oldGuild wasUnavailable = oldGuild.Unavailable } + guild := disgo.EntityBuilder().CreateGuild(fullGuild.Guild, api.CacheStrategyYes) - disgo.Cache().CacheGuild(guild) - for i := range fullGuild.Channels { - channel := fullGuild.Channels[i] + for _, channel := range fullGuild.Channels { channel.GuildID = &guild.ID switch channel.Type { case api.ChannelTypeText, api.ChannelTypeNews: @@ -52,20 +47,20 @@ func (h GuildCreateHandler) HandleGatewayEvent(disgo api.Disgo, eventManager api } } - for i := range fullGuild.Roles { - disgo.EntityBuilder().CreateRole(guild.ID, fullGuild.Roles[i], api.CacheStrategyYes) + for _, role := range fullGuild.Roles { + disgo.EntityBuilder().CreateRole(guild.ID, role, api.CacheStrategyYes) } - for i := range fullGuild.Members { - disgo.EntityBuilder().CreateMember(guild.ID, fullGuild.Members[i], api.CacheStrategyYes) + for _, member := range fullGuild.Members { + disgo.EntityBuilder().CreateMember(guild.ID, member, api.CacheStrategyYes) } - for i := range fullGuild.VoiceStates { - disgo.EntityBuilder().CreateVoiceState(fullGuild.VoiceStates[i], api.CacheStrategyYes) + for _, voiceState := range fullGuild.VoiceStates { + disgo.EntityBuilder().CreateVoiceState(voiceState, api.CacheStrategyYes) } - for i := range fullGuild.Emotes { - disgo.EntityBuilder().CreateEmote(guild.ID, fullGuild.Emotes[i], api.CacheStrategyYes) + for _, emote := range fullGuild.Emotes { + disgo.EntityBuilder().CreateEmote(guild.ID, emote, api.CacheStrategyYes) } // TODO: presence @@ -79,7 +74,6 @@ func (h GuildCreateHandler) HandleGatewayEvent(disgo api.Disgo, eventManager api GenericEvent: events.NewEvent(disgo, sequenceNumber), Guild: guild, } - eventManager.Dispatch(genericGuildEvent) if wasUnavailable { diff --git a/internal/handlers/guild_delete_handler.go b/internal/handlers/guild_delete_handler.go index 4387938a..1c4add1e 100644 --- a/internal/handlers/guild_delete_handler.go +++ b/internal/handlers/guild_delete_handler.go @@ -36,11 +36,11 @@ func (h GuildDeleteHandler) HandleGatewayEvent(disgo api.Disgo, eventManager api if guild.Unavailable { // set guild to unavail for now disgo.Cache().Guild(guild.ID).Unavailable = true + eventManager.Dispatch(events.GuildUnavailableEvent{ GenericGuildEvent: genericGuildEvent, }) } else { - guild = disgo.Cache().Guild(guild.ID) disgo.Cache().UncacheGuild(guild.ID) eventManager.Dispatch(events.GuildLeaveEvent{ diff --git a/internal/handlers/guild_update_handler.go b/internal/handlers/guild_update_handler.go index e335ee98..0179d101 100644 --- a/internal/handlers/guild_update_handler.go +++ b/internal/handlers/guild_update_handler.go @@ -35,8 +35,8 @@ func (h GuildUpdateHandler) HandleGatewayEvent(disgo api.Disgo, eventManager api GenericEvent: events.NewEvent(disgo, sequenceNumber), Guild: guild, } - eventManager.Dispatch(genericGuildEvent) + eventManager.Dispatch(events.GuildUpdateEvent{ GenericGuildEvent: genericGuildEvent, OldGuild: oldGuild, diff --git a/internal/handlers/ready_handler.go b/internal/handlers/ready_handler.go index 76a998b7..e2c95db3 100644 --- a/internal/handlers/ready_handler.go +++ b/internal/handlers/ready_handler.go @@ -4,14 +4,6 @@ import ( "github.com/DisgoOrg/disgo/api" ) -type readyEventData struct { - Version int `json:"v"` - SelfUser api.User `json:"user"` - Guilds []*api.Guild `json:"guilds"` - SessionID string `json:"session_id"` - Shard *[2]int `json:"shard,omitempty"` -} - // ReadyHandler handles api.ReadyGatewayEvent type ReadyHandler struct{} @@ -22,12 +14,12 @@ func (h ReadyHandler) Event() api.GatewayEventType { // New constructs a new payload receiver for the raw gateway event func (h ReadyHandler) New() interface{} { - return &readyEventData{} + return &api.ReadyGatewayEvent{} } // HandleGatewayEvent handles the specific raw gateway event func (h ReadyHandler) HandleGatewayEvent(disgo api.Disgo, eventManager api.EventManager, sequenceNumber int, i interface{}) { - readyEvent, ok := i.(*readyEventData) + readyEvent, ok := i.(*api.ReadyGatewayEvent) if !ok { return }