Skip to content

Commit

Permalink
Merge branch 'feature/all-major-events' into development
Browse files Browse the repository at this point in the history
  • Loading branch information
topi314 committed Apr 13, 2021
2 parents 4961ab4 + 31bfe90 commit 10f129f
Show file tree
Hide file tree
Showing 12 changed files with 96 additions and 90 deletions.
8 changes: 8 additions & 0 deletions api/audio_controller.go
Original file line number Diff line number Diff line change
@@ -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
Expand Down
1 change: 0 additions & 1 deletion api/events/dm_message_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion api/events/guild_message_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
type GenericGuildMessageEvent struct {
GenericMessageEvent
GuildID api.Snowflake
Message *api.Message
}

// Guild returns the api.Guild the GenericGuildMessageEvent happened in
Expand Down
2 changes: 1 addition & 1 deletion api/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type Gateway interface {
Disgo() Disgo
Open() error
Status() GatewayStatus
Close(bool)
Close()
Conn() *websocket.Conn
Latency() time.Duration
}
Expand Down
12 changes: 5 additions & 7 deletions api/gateway_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 26 additions & 3 deletions internal/audio_controller_impl.go
Original file line number Diff line number Diff line change
@@ -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}
Expand All @@ -18,16 +21,36 @@ 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,
}))
}

// 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
}
2 changes: 1 addition & 1 deletion internal/disgo_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
81 changes: 37 additions & 44 deletions internal/gateway_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
}
Expand All @@ -184,47 +184,32 @@ 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)
g.quit = nil
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
Expand Down Expand Up @@ -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),
Expand All @@ -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()
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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")
}
Expand All @@ -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()
}

Expand Down
34 changes: 14 additions & 20 deletions internal/handlers/guild_create_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion internal/handlers/guild_delete_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
2 changes: 1 addition & 1 deletion internal/handlers/guild_update_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
12 changes: 2 additions & 10 deletions internal/handlers/ready_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}

Expand All @@ -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
}
Expand Down

0 comments on commit 10f129f

Please sign in to comment.