From 79c4c1caca0cbca34f4899e75077599fa1865ca8 Mon Sep 17 00:00:00 2001 From: "diamondburned (Forefront)" Date: Mon, 24 Feb 2020 21:50:13 -0800 Subject: [PATCH] Gateway/State: Added message acking and more user update events for State --- api/channel.go | 16 ++++++++++++++++ bot/ctx.go | 2 +- gateway/ready.go | 37 +++++++++++++++++++++++-------------- state/state.go | 6 ++++++ state/state_events.go | 19 +++++++++++++++++++ 5 files changed, 65 insertions(+), 15 deletions(-) diff --git a/api/channel.go b/api/channel.go index 3f354f9..c4ad97c 100644 --- a/api/channel.go +++ b/api/channel.go @@ -179,3 +179,19 @@ func (c *Client) RemoveRecipient(channelID, userID discord.Snowflake) error { return c.FastRequest("DELETE", EndpointChannels+channelID.String()+"/recipients/"+userID.String()) } + +// ACk is the read state of a channel. This is undocumented. +type Ack struct { + Token string `json:"token"` +} + +// Ack marks the read state of a channel. This is undocumented. The method will +// write to the ack variable passed in. +func (c *Client) Ack(channelID, messageID discord.Snowflake, ack *Ack) error { + return c.RequestJSON( + ack, "POST", + EndpointChannels+channelID.String()+ + "/messages/"+messageID.String()+"/ack", + httputil.WithJSONBody(c, ack), + ) +} diff --git a/bot/ctx.go b/bot/ctx.go index ec33f77..71e609e 100644 --- a/bot/ctx.go +++ b/bot/ctx.go @@ -254,7 +254,7 @@ func (ctx *Context) RegisterSubcommand(cmd interface{}) (*Subcommand, error) { // The returned function is a delete function, which removes itself from the // Session handlers. func (ctx *Context) Start() func() { - return ctx.Session.AddHandler(func(v interface{}) { + return ctx.State.AddHandler(func(v interface{}) { err := ctx.callCmd(v) if err == nil { return diff --git a/gateway/ready.go b/gateway/ready.go index 86e673c..29cf9fc 100644 --- a/gateway/ready.go +++ b/gateway/ready.go @@ -14,11 +14,14 @@ type ReadyEvent struct { Shard *Shard `json:"shard"` // Undocumented fields - Settings *UserSettings `json:"user_settings"` - UserGuildSettings []UserGuildSettings `json:"user_guild_settings"` - Relationships []Relationship `json:"relationships"` - Presences []discord.Presence `json:"presences,omitempty"` - Notes map[discord.Snowflake]string `json:"notes,omitempty"` + Settings *UserSettings `json:"user_settings,omitempty"` + UserGuildSettings []UserGuildSettings `json:"user_guild_settings,omitempty"` + + ReadState []ReadState `json:"read_state,omitempty"` + Presences []discord.Presence `json:"presences,omitempty"` + + Relationships []Relationship `json:"relationships,omitempty"` + Notes map[discord.Snowflake]string `json:"notes,omitempty"` } type UserSettings struct { @@ -64,15 +67,6 @@ type UserSettings struct { } `json:"custom_status"` } -// A UserGuildSettingsChannelOverride stores data for a channel override for a -// users guild settings. -type SettingsChannelOverride struct { - Muted bool `json:"muted"` - MessageNotifications int `json:"message_notifications"` // TODO: document - - ChannelID discord.Snowflake `json:"channel_id"` -} - // A UserGuildSettings stores data for a users guild settings. type UserGuildSettings struct { SupressEveryone bool `json:"suppress_everyone"` @@ -84,6 +78,21 @@ type UserGuildSettings struct { ChannelOverrides []SettingsChannelOverride `json:"channel_overrides"` } +type ReadState struct { + ChannelID discord.Snowflake `json:"id"` + LastMessageID discord.Snowflake `json:"last_message_id"` + MentionCount int `json:"mention_count"` +} + +// A UserGuildSettingsChannelOverride stores data for a channel override for a +// users guild settings. +type SettingsChannelOverride struct { + Muted bool `json:"muted"` + MessageNotifications int `json:"message_notifications"` // TODO: document + + ChannelID discord.Snowflake `json:"channel_id"` +} + // GuildFolder holds a single folder that you see in the left guild panel. type GuildFolder struct { Name string `json:"name"` diff --git a/state/state.go b/state/state.go index 7cc665e..a62b129 100644 --- a/state/state.go +++ b/state/state.go @@ -36,6 +36,11 @@ type State struct { // It's recommended to set Synchronous to true if you mutate the events. PreHandler *handler.Handler // default nil + // Command handler with inherited methods. Ran after PreHandler. You should + // most of the time use this instead of Session's, to avoid race conditions + // with the State + *handler.Handler + unhooker func() // List of channels with few messages, so it doesn't bother hitting the API @@ -48,6 +53,7 @@ func NewFromSession(s *session.Session, store Store) (*State, error) { state := &State{ Session: s, Store: store, + Handler: handler.New(), StateLog: func(err error) {}, fewMessages: map[discord.Snowflake]struct{}{}, } diff --git a/state/state_events.go b/state/state_events.go index 322fae9..3bef83f 100644 --- a/state/state_events.go +++ b/state/state_events.go @@ -12,6 +12,7 @@ func (s *State) hookSession() error { s.PreHandler.Call(iface) } s.onEvent(iface) + s.Handler.Call(iface) }) return nil @@ -191,6 +192,24 @@ func (s *State) onEvent(iface interface{}) { s.stateErr(err, "Failed to update presence in state") } } + + case *gateway.UserGuildSettingsUpdateEvent: + for i, ugs := range s.Ready.UserGuildSettings { + if ugs.GuildID == ev.GuildID { + s.Ready.UserGuildSettings[i] = gateway.UserGuildSettings(*ev) + } + } + + case *gateway.UserSettingsUpdateEvent: + s.Ready.Settings = (*gateway.UserSettings)(ev) + + case *gateway.UserNoteUpdateEvent: + s.Ready.Notes[ev.ID] = ev.Note + + case *gateway.UserUpdateEvent: + if err := s.Store.MyselfSet((*discord.User)(ev)); err != nil { + s.stateErr(err, "Failed to update myself from USER_UPDATE") + } } }