diff --git a/discord/guild.go b/discord/guild.go index b5e2376..8c7e703 100644 --- a/discord/guild.go +++ b/discord/guild.go @@ -36,16 +36,6 @@ type Guild struct { WidgetChannelID Snowflake `json:"widget_channel_id,string,omitempty"` SystemChannelID Snowflake `json:"system_channel_id,string,omitempty"` - // GUILD_CREATE only. - Joined Timestamp `json:"timestamp,omitempty"` - Large bool `json:"large,omitempty"` - Unavailable bool `json:"unavailable,omitempty"` - MemberCount uint64 `json:"member_count,omitempty"` - VoiceStates []VoiceState `json:"voice_state,omitempty"` - Members []Member `json:"members,omitempty"` - Channels []Channel `json:"channel,omitempty"` - Presences []Presence `json:"presences,omitempty"` - // It's DefaultMaxPresences when MaxPresences is 0. MaxPresences uint64 `json:"max_presences,omitempty"` MaxMembers uint64 `json:"max_members,omitempty"` @@ -78,6 +68,24 @@ type Role struct { type Presence struct { User User `json:"user"` RoleIDs []Snowflake `json:"roles"` + + // These fields are only filled in gateway events, according to the + // documentation. + + Nick string `json:"nick"` + GuildID Snowflake `json:"guild_id"` + + PremiumSince Timestamp `json:"premium_since,omitempty"` + + Game *Activity `json:"game"` + Activities []Activity `json:"activities"` + + Status Status `json:"status"` + ClientStatus struct { + Desktop Status `json:"status,omitempty"` + Mobile Status `json:"mobile,omitempty"` + Web Status `json:"web,omitempty"` + } `json:"client_status"` } type Member struct { diff --git a/discord/user.go b/discord/user.go index c1ee290..512040f 100644 --- a/discord/user.go +++ b/discord/user.go @@ -68,3 +68,78 @@ const ( ConnectionNotVisible ConnectionVisibility = iota ConnectionVisibleEveryone ) + +type Status string + +const ( + UnknownStatus Status = "" + OnlineStatus Status = "online" + DoNotDisturbStatus Status = "dnd" + IdleStatus Status = "idle" + InvisibleStatus Status = "invisible" + OfflineStatus Status = "offline" +) + +type Activity struct { + Name string `json:"name"` + Type ActivityType `json:"type"` + URL URL `json:"url"` + + // User only + + CreatedAt UnixTimestamp `json:"created_at"` + Timestamps struct { + Start UnixMsTimestamp `json:"start,omitempty"` + End UnixMsTimestamp `json:"end,omitempty"` + } `json:"timestamps,omitempty"` + + ApplicationID Snowflake `json:"application_id,omitempty"` + Details string `json:"details,omitempty"` + State string `json:"state,omitempty"` // party status + Emoji Emoji `json:"emoji,omitempty"` + + Party struct { + ID string `json:"id,omitempty"` + Size [2]int `json:"size,omitempty"` // [ current, max ] + } `json:"party,omitempty"` + + Assets struct { + LargeImage string `json:"large_image,omitempty"` // id + LargeText string `json:"large_text,omitempty"` + SmallImage string `json:"small_image,omitempty"` // id + SmallText string `json:"small_text,omitempty"` + } `json:"assets,omitempty"` + + Secrets struct { + Join string `json:"join,omitempty"` + Spectate string `json:"spectate,omitempty"` + Match string `json:"match,omitempty"` + } `json:"secrets,omitempty"` + + Instance bool `json:"instance,omitempty"` + Flags ActivityFlags `json:"flags,omitempty"` +} + +type ActivityType uint8 + +const ( + // Playing $name + GameActivity ActivityType = iota + // Streaming $details + StreamingActivity + // Listening to $name + ListeningActivity + // $emoji $name + CustomActivity +) + +type ActivityFlags uint8 + +const ( + InstanceActivity ActivityFlags = 1 << iota + JoinActivity + SpectateActivity + JoinRequestActivity + SyncActivity + PlayActivity +) diff --git a/gateway/activity.go b/gateway/activity.go deleted file mode 100644 index e5fcbfe..0000000 --- a/gateway/activity.go +++ /dev/null @@ -1,78 +0,0 @@ -package gateway - -import "github.com/diamondburned/arikawa/discord" - -type Status string - -const ( - UnknownStatus Status = "" - OnlineStatus Status = "online" - DoNotDisturbStatus Status = "dnd" - IdleStatus Status = "idle" - InvisibleStatus Status = "invisible" - OfflineStatus Status = "offline" -) - -type Activity struct { - Name string `json:"name"` - Type ActivityType `json:"type"` - URL discord.URL `json:"url"` - - // User only - - CreatedAt discord.UnixTimestamp `json:"created_at"` - Timestamps struct { - Start discord.UnixMsTimestamp `json:"start,omitempty"` - End discord.UnixMsTimestamp `json:"end,omitempty"` - } `json:"timestamps,omitempty"` - - ApplicationID discord.Snowflake `json:"application_id,omitempty"` - Details string `json:"details,omitempty"` - State string `json:"state,omitempty"` // party status - Emoji discord.Emoji `json:"emoji,omitempty"` - - Party struct { - ID string `json:"id,omitempty"` - Size [2]int `json:"size,omitempty"` // [ current, max ] - } `json:"party,omitempty"` - - Assets struct { - LargeImage string `json:"large_image,omitempty"` // id - LargeText string `json:"large_text,omitempty"` - SmallImage string `json:"small_image,omitempty"` // id - SmallText string `json:"small_text,omitempty"` - } `json:"assets,omitempty"` - - Secrets struct { - Join string `json:"join,omitempty"` - Spectate string `json:"spectate,omitempty"` - Match string `json:"match,omitempty"` - } `json:"secrets,omitempty"` - - Instance bool `json:"instance,omitempty"` - Flags ActivityFlags `json:"flags,omitempty"` -} - -type ActivityType uint8 - -const ( - // Playing $name - GameActivity ActivityType = iota - // Streaming $details - StreamingActivity - // Listening to $name - ListeningActivity - // $emoji $name - CustomActivity -) - -type ActivityFlags uint8 - -const ( - InstanceActivity ActivityFlags = 1 << iota - JoinActivity - SpectateActivity - JoinRequestActivity - SyncActivity - PlayActivity -) diff --git a/gateway/events.go b/gateway/events.go index 99f8c9c..ff152a5 100644 --- a/gateway/events.go +++ b/gateway/events.go @@ -20,6 +20,10 @@ type ( Guilds []discord.Guild `json:"guilds"` Shard *Shard `json:"shard"` + + // Undocumented fields + Presences []discord.Presence `json:"presences,omitempty"` + Notes map[discord.Snowflake]string `json:"notes,omitempty"` } ResumedEvent struct{} @@ -42,7 +46,19 @@ type ( // https://discordapp.com/developers/docs/topics/gateway#guilds type ( - GuildCreateEvent discord.Guild + GuildCreateEvent struct { + discord.Guild + + Joined discord.Timestamp `json:"timestamp,omitempty"` + Large bool `json:"large,omitempty"` + Unavailable bool `json:"unavailable,omitempty"` + MemberCount uint64 `json:"member_count,omitempty"` + + VoiceStates []discord.VoiceState `json:"voice_state,omitempty"` + Members []discord.Member `json:"members,omitempty"` + Channels []discord.Channel `json:"channel,omitempty"` + Presences []discord.Presence `json:"presences,omitempty"` + } GuildUpdateEvent discord.Guild GuildDeleteEvent struct { ID discord.Snowflake `json:"id"` @@ -78,7 +94,7 @@ type ( } GuildMemberUpdateEvent struct { GuildID discord.Snowflake `json:"guild_id"` - Roles []discord.Snowflake `json:"roles"` + RoleIDs []discord.Snowflake `json:"roles"` User discord.User `json:"user"` Nick string `json:"nick"` } @@ -109,6 +125,12 @@ type ( } ) +func (u GuildMemberUpdateEvent) Update(m *discord.Member) { + m.RoleIDs = u.RoleIDs + m.User = u.User + m.Nick = u.Nick +} + // https://discordapp.com/developers/docs/topics/gateway#messages type ( MessageCreateEvent discord.Message @@ -151,25 +173,8 @@ type ( // https://discordapp.com/developers/docs/topics/gateway#presence type ( // Clients may only update their game status 5 times per 20 seconds. - PresenceUpdateEvent struct { - User discord.User `json:"user"` - Nick string `json:"nick"` - Roles []discord.Snowflake `json:"roles"` - GuildID discord.Snowflake `json:"guild_id"` - - PremiumSince discord.Timestamp `json:"premium_since,omitempty"` - - Game *Activity `json:"game"` - Activities []Activity `json:"activities"` - - Status Status `json:"status"` - ClientStatus struct { - Desktop Status `json:"status,omitempty"` - Mobile Status `json:"mobile,omitempty"` - Web Status `json:"web,omitempty"` - } `json:"client_status"` - } - TypingStartEvent struct { + PresenceUpdateEvent discord.Presence + TypingStartEvent struct { ChannelID discord.Snowflake `json:"channel_id"` UserID discord.Snowflake `json:"user_id"` Timestamp discord.Timestamp `json:"timestamp"` @@ -180,6 +185,10 @@ type ( UserUpdateEvent discord.User ) +func (u PresenceUpdateEvent) Update(p *discord.Presence) { + +} + // https://discordapp.com/developers/docs/topics/gateway#voice type ( VoiceStateUpdateEvent discord.VoiceState diff --git a/state/state.go b/state/state.go index fc77e2a..7e62c09 100644 --- a/state/state.go +++ b/state/state.go @@ -3,7 +3,10 @@ package state import ( + "log" + "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/gateway" "github.com/diamondburned/arikawa/handler" "github.com/diamondburned/arikawa/session" ) @@ -15,15 +18,22 @@ var ( type State struct { *session.Session + Store + + // Ready is not updated by the state. + Ready gateway.ReadyEvent + + // ErrorLog logs all errors that handler might have, including state fails. + // This handler will also be used for Session, which would also be used for + // Gateway. Defaults to log.Println. + ErrorLog func(error) // PreHandler is the manual hook that is executed before the State handler // is. This should only be used for low-level operations. // It's recommended to set Synchronous to true if you mutate the events. PreHandler *handler.Handler // default nil - MaxMessages uint // default 50 - - Store + // *: State doesn't actually keep track of pinned messages. unhooker func() } @@ -32,6 +42,13 @@ func NewFromSession(s *session.Session, store Store) (*State, error) { state := &State{ Session: s, Store: store, + ErrorLog: func(err error) { + log.Println("arikawa/state error:", err) + }, + } + + s.ErrorLog = func(err error) { + state.ErrorLog(err) } return state, state.hookSession() @@ -49,12 +66,7 @@ func NewWithStore(token string, store Store) (*State, error) { return nil, err } - state := &State{ - Session: s, - Store: store, - } - - return state, state.hookSession() + return NewFromSession(s, store) } // Unhook removes all state handlers from the session handlers. @@ -335,32 +347,3 @@ func (s *State) Roles(guildID discord.Snowflake) ([]discord.Role, error) { return rs, nil } - -//// - -func (s *State) hookSession() error { - /* - s.unhooker = s.Session.AddHandler(func(iface interface{}) { - if s.PreHandler != nil { - s.PreHandler.Call(iface) - } - - s.mut.Lock() - defer s.mut.Unlock() - - switch ev := iface.(type) { - case *gateway.ReadyEvent: - // Override - s.guilds = ev.Guilds - s.privates = ev.PrivateChannels - s.self = ev.User - - case *gateway.MessageCreateEvent: - _ = ev - panic("IMPLEMENT ME") - } - }) - */ - - return nil -} diff --git a/state/state_events.go b/state/state_events.go new file mode 100644 index 0000000..000cd86 --- /dev/null +++ b/state/state_events.go @@ -0,0 +1,178 @@ +package state + +import ( + "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/gateway" + "github.com/pkg/errors" +) + +func (s *State) hookSession() error { + s.unhooker = s.Session.AddHandler(func(iface interface{}) { + if s.PreHandler != nil { + s.PreHandler.Call(iface) + } + s.onEvent(iface) + }) + + return nil +} + +func (s *State) onEvent(iface interface{}) { + // TODO: voice states + + switch ev := iface.(type) { + case *gateway.ReadyEvent: + // Handle guilds + for _, g := range ev.Guilds { + if err := s.Store.GuildSet(&g); err != nil { + s.wrapErr(err, "Failed to set guild in state") + } + } + + // Handle private channels + for _, ch := range ev.PrivateChannels { + if err := s.Store.ChannelSet(&ch); err != nil { + s.wrapErr(err, "Failed to set channel in state") + } + } + + // Handle user + if err := s.Store.SelfSet(&ev.User); err != nil { + s.wrapErr(err, "Failed to set self in state") + } + + // Set Ready to the state + s.Ready = *ev + + case *gateway.GuildCreateEvent: + if err := s.Store.GuildSet(&ev.Guild); err != nil { + s.wrapErr(err, "Failed to create guild in state") + } + + for _, m := range ev.Members { + if err := s.Store.MemberSet(ev.Guild.ID, &m); err != nil { + s.wrapErr(err, "Failed to add a member from guild in state") + } + } + + for _, ch := range ev.Channels { + ch.GuildID = ev.Guild.ID // just to make sure + + if err := s.Store.ChannelSet(&ch); err != nil { + s.wrapErr(err, "Failed to add a channel from guild in state") + } + } + + for _, p := range ev.Presences { + if err := s.Store.PresenceSet(ev.Guild.ID, &p); err != nil { + s.wrapErr(err, "Failed to add a presence from guild in state") + } + } + case *gateway.GuildUpdateEvent: + if err := s.Store.GuildSet((*discord.Guild)(ev)); err != nil { + s.wrapErr(err, "Failed to update guild in state") + } + case *gateway.GuildDeleteEvent: + if err := s.Store.GuildRemove(ev.ID); err != nil { + s.wrapErr(err, "Failed to delete guild in state") + } + + case *gateway.GuildMemberAddEvent: + if err := s.Store.MemberSet(ev.GuildID, &ev.Member); err != nil { + s.wrapErr(err, "Failed to add a member in state") + } + case *gateway.GuildMemberUpdateEvent: + m, err := s.Store.Member(ev.GuildID, ev.User.ID) + if err != nil { + // We can't do much here. + m = &discord.Member{} + } + + // Update available fields from ev into m + ev.Update(m) + + if err := s.Store.MemberSet(ev.GuildID, m); err != nil { + s.wrapErr(err, "Failed to update a member in state") + } + case *gateway.GuildMemberRemoveEvent: + if err := s.Store.MemberRemove(ev.GuildID, ev.User.ID); err != nil { + s.wrapErr(err, "Failed to remove a member in state") + } + + case *gateway.GuildMembersChunkEvent: + for _, m := range ev.Members { + if err := s.Store.MemberSet(ev.GuildID, &m); err != nil { + s.wrapErr(err, "Failed to add a member from chunk in state") + } + } + + for _, p := range ev.Presences { + if err := s.Store.PresenceSet(ev.GuildID, &p); err != nil { + s.wrapErr(err, "Failed to add a presence from chunk in state") + } + } + + case *gateway.GuildRoleCreateEvent: + if err := s.Store.RoleSet(ev.GuildID, &ev.Role); err != nil { + s.wrapErr(err, "Failed to add a role in state") + } + case *gateway.GuildRoleUpdateEvent: + if err := s.Store.RoleSet(ev.GuildID, &ev.Role); err != nil { + s.wrapErr(err, "Failed to update a role in state") + } + case *gateway.GuildRoleDeleteEvent: + if err := s.Store.RoleRemove(ev.GuildID, ev.RoleID); err != nil { + s.wrapErr(err, "Failed to remove a role in state") + } + + case *gateway.GuildEmojisUpdateEvent: + if err := s.Store.EmojiSet(ev.GuildID, ev.Emojis); err != nil { + s.wrapErr(err, "Failed to update emojis in state") + } + + case *gateway.ChannelCreateEvent: + if err := s.Store.ChannelSet((*discord.Channel)(ev)); err != nil { + s.wrapErr(err, "Failed to create a channel in state") + } + case *gateway.ChannelUpdateEvent: + if err := s.Store.ChannelSet((*discord.Channel)(ev)); err != nil { + s.wrapErr(err, "Failed to update a channel in state") + } + case *gateway.ChannelDeleteEvent: + if err := s.Store.ChannelRemove((*discord.Channel)(ev)); err != nil { + s.wrapErr(err, "Failed to remove a channel in state") + } + + // *gateway.ChannelPinsUpdateEvent is not tracked. + + case *gateway.MessageCreateEvent: + if err := s.Store.MessageSet((*discord.Message)(ev)); err != nil { + s.wrapErr(err, "Failed to add a message in state") + } + case *gateway.MessageUpdateEvent: + if err := s.Store.MessageSet((*discord.Message)(ev)); err != nil { + s.wrapErr(err, "Failed to update a message in state") + } + case *gateway.MessageDeleteEvent: + if err := s.Store.MessageRemove(ev.ChannelID, ev.ID); err != nil { + s.wrapErr(err, "Failed to delete a message in state") + } + case *gateway.MessageDeleteBulkEvent: + for _, id := range ev.IDs { + if err := s.Store.MessageRemove(ev.ChannelID, id); err != nil { + s.wrapErr(err, "Failed to delete bulk meessages in state") + } + } + + case *gateway.PresenceUpdateEvent: + if err := s.Store.PresenceSet( + ev.GuildID, (*discord.Presence)(ev)); err != nil { + + s.wrapErr(err, "Failed to update presence in state") + } + } +} + +func (s *State) wrapErr(err error, wrap string) { + s.ErrorLog(errors.Wrap(err, wrap)) +} diff --git a/state/store.go b/state/store.go index 9869607..61bfeca 100644 --- a/state/store.go +++ b/state/store.go @@ -46,19 +46,21 @@ type StoreGetter interface { type StoreModifier interface { SelfSet(me *discord.User) error + // ChannelSet should switch on Type to know if it's a private channel or + // not. ChannelSet(*discord.Channel) error ChannelRemove(*discord.Channel) error EmojiSet(guildID discord.Snowflake, emojis []discord.Emoji) error GuildSet(*discord.Guild) error - GuildRemove(*discord.Guild) error + GuildRemove(id discord.Snowflake) error MemberSet(guildID discord.Snowflake, member *discord.Member) error MemberRemove(guildID, userID discord.Snowflake) error MessageSet(*discord.Message) error - MessageRemove(*discord.Message) error + MessageRemove(channelID, messageID discord.Snowflake) error PresenceSet(guildID discord.Snowflake, presence *discord.Presence) error PresenceRemove(guildID, userID discord.Snowflake) error diff --git a/state/store_default.go b/state/store_default.go index eb0b504..312a6a6 100644 --- a/state/store_default.go +++ b/state/store_default.go @@ -15,9 +15,13 @@ type DefaultStore struct { self discord.User // includes normal and private - privates map[discord.Snowflake]*discord.Channel // channelID:channel - guilds map[discord.Snowflake]*discord.Guild // guildID:guild - messages map[discord.Snowflake][]discord.Message // channelID:messages + privates map[discord.Snowflake]*discord.Channel // channelID:channel + guilds map[discord.Snowflake]*discord.Guild // guildID:guild + + channels map[discord.Snowflake][]discord.Channel // guildID:channels + members map[discord.Snowflake][]discord.Member // guildID:members + presences map[discord.Snowflake][]discord.Presence // guildID:presences + messages map[discord.Snowflake][]discord.Message // channelID:messages mut sync.Mutex } @@ -40,7 +44,10 @@ func NewDefaultStore(opts *DefaultStoreOptions) *DefaultStore { privates: map[discord.Snowflake]*discord.Channel{}, guilds: map[discord.Snowflake]*discord.Guild{}, - messages: map[discord.Snowflake][]discord.Message{}, + + channels: map[discord.Snowflake][]discord.Channel{}, + presences: map[discord.Snowflake][]discord.Presence{}, + messages: map[discord.Snowflake][]discord.Message{}, } } @@ -84,8 +91,8 @@ func (s *DefaultStore) Channel(id discord.Snowflake) (*discord.Channel, error) { s.mut.Lock() defer s.mut.Unlock() - for _, g := range s.guilds { - for _, ch := range g.Channels { + for _, chs := range s.channels { + for _, ch := range chs { if ch.ID == id { return &ch, nil } @@ -101,12 +108,12 @@ func (s *DefaultStore) Channels( s.mut.Lock() defer s.mut.Unlock() - gd, ok := s.guilds[guildID] + chs, ok := s.channels[guildID] if !ok { return nil, ErrStoreNotFound } - return gd.Channels, nil + return chs, nil } func (s *DefaultStore) PrivateChannels() ([]discord.Channel, error) { @@ -136,20 +143,22 @@ func (s *DefaultStore) ChannelSet(channel *discord.Channel) error { s.privates[channel.ID] = channel default: - gd, ok := s.guilds[channel.GuildID] + chs, ok := s.channels[channel.GuildID] if !ok { return ErrStoreNotFound } - for i, ch := range gd.Channels { + for i, ch := range chs { if ch.ID == channel.ID { // Found, just edit - gd.Channels[i] = *channel + chs[i] = *channel + return nil } } - gd.Channels = append(gd.Channels, *channel) + chs = append(chs, *channel) + s.channels[channel.GuildID] = chs } return nil @@ -159,14 +168,16 @@ func (s *DefaultStore) ChannelRemove(channel *discord.Channel) error { s.mut.Lock() defer s.mut.Unlock() - gd, ok := s.guilds[channel.GuildID] + chs, ok := s.channels[channel.GuildID] if !ok { return ErrStoreNotFound } - for i, ch := range gd.Channels { + for i, ch := range chs { if ch.ID == channel.ID { - gd.Channels = append(gd.Channels[:i], gd.Channels[i+1:]...) + chs = append(chs[:i], chs[i+1:]...) + s.channels[channel.GuildID] = chs + return nil } } @@ -277,26 +288,15 @@ func (s *DefaultStore) Guilds() ([]discord.Guild, error) { func (s *DefaultStore) GuildSet(g *discord.Guild) error { s.mut.Lock() - defer s.mut.Unlock() - - old := s.guilds[g.ID] - - // Check the channels too - if len(old.Channels) == 0 { - return nil - } - - if len(g.Channels) == 0 { - g.Channels = old.Channels - } - s.guilds[g.ID] = g + s.mut.Unlock() + return nil } -func (s *DefaultStore) GuildRemove(g *discord.Guild) error { +func (s *DefaultStore) GuildRemove(id discord.Snowflake) error { s.mut.Lock() - delete(s.guilds, g.ID) + delete(s.guilds, id) s.mut.Unlock() return nil @@ -310,14 +310,14 @@ func (s *DefaultStore) Member( s.mut.Lock() defer s.mut.Unlock() - gd, ok := s.guilds[guildID] + ms, ok := s.members[guildID] if !ok { return nil, ErrStoreNotFound } - for _, member := range gd.Members { - if member.User.ID == userID { - return &member, nil + for _, m := range ms { + if m.User.ID == userID { + return &m, nil } } @@ -330,12 +330,12 @@ func (s *DefaultStore) Members( s.mut.Lock() defer s.mut.Unlock() - gd, ok := s.guilds[guildID] + ms, ok := s.members[guildID] if !ok { return nil, ErrStoreNotFound } - return gd.Members, nil + return ms, nil } func (s *DefaultStore) MemberSet( @@ -344,22 +344,26 @@ func (s *DefaultStore) MemberSet( s.mut.Lock() defer s.mut.Unlock() - gd, ok := s.guilds[guildID] + ms, ok := s.members[guildID] if !ok { return ErrStoreNotFound } // Try and see if this member is already in the slice - for i, m := range gd.Members { + for i, m := range ms { if m.User.ID == member.User.ID { // If it is, we simply replace it - gd.Members[i] = *member + ms[i] = *member + s.members[guildID] = ms + return nil } } // Append the new member - gd.Members = append(gd.Members, *member) + ms = append(ms, *member) + s.members[guildID] = ms + return nil } @@ -367,15 +371,17 @@ func (s *DefaultStore) MemberRemove(guildID, userID discord.Snowflake) error { s.mut.Lock() defer s.mut.Unlock() - gd, ok := s.guilds[guildID] + ms, ok := s.members[guildID] if !ok { return ErrStoreNotFound } // Try and see if this member is already in the slice - for i, m := range gd.Members { + for i, m := range ms { if m.User.ID == userID { - gd.Members = append(gd.Members[:i], gd.Members[i+1:]...) + ms = append(ms, ms[i+1:]...) + s.members[guildID] = ms + return nil } } @@ -443,19 +449,21 @@ func (s *DefaultStore) MessageSet(message *discord.Message) error { return nil } -func (s *DefaultStore) MessageRemove(message *discord.Message) error { +func (s *DefaultStore) MessageRemove( + channelID, messageID discord.Snowflake) error { + s.mut.Lock() defer s.mut.Unlock() - ms, ok := s.messages[message.ChannelID] + ms, ok := s.messages[channelID] if !ok { return ErrStoreNotFound } for i, m := range ms { - if m.ID == message.ID { + if m.ID == messageID { ms = append(ms[:i], ms[i+1:]...) - s.messages[message.ChannelID] = ms + s.messages[channelID] = ms return nil } } @@ -471,12 +479,12 @@ func (s *DefaultStore) Presence( s.mut.Lock() defer s.mut.Unlock() - gd, ok := s.guilds[guildID] + ps, ok := s.presences[guildID] if !ok { return nil, ErrStoreNotFound } - for _, p := range gd.Presences { + for _, p := range ps { if p.User.ID == userID { return &p, nil } @@ -491,12 +499,12 @@ func (s *DefaultStore) Presences( s.mut.Lock() defer s.mut.Unlock() - gd, ok := s.guilds[guildID] + ps, ok := s.presences[guildID] if !ok { return nil, ErrStoreNotFound } - return gd.Presences, nil + return ps, nil } func (s *DefaultStore) PresenceSet( @@ -505,19 +513,22 @@ func (s *DefaultStore) PresenceSet( s.mut.Lock() defer s.mut.Unlock() - gd, ok := s.guilds[guildID] + ps, ok := s.presences[guildID] if !ok { return ErrStoreNotFound } - for i, p := range gd.Presences { + for i, p := range ps { if p.User.ID == presence.User.ID { - gd.Presences[i] = *presence + ps[i] = *presence + s.presences[guildID] = ps + return nil } } - gd.Presences = append(gd.Presences, *presence) + ps = append(ps, *presence) + s.presences[guildID] = ps return nil } @@ -525,14 +536,16 @@ func (s *DefaultStore) PresenceRemove(guildID, userID discord.Snowflake) error { s.mut.Lock() defer s.mut.Unlock() - gd, ok := s.guilds[guildID] + ps, ok := s.presences[guildID] if !ok { return ErrStoreNotFound } - for i, p := range gd.Presences { + for i, p := range ps { if p.User.ID == userID { - gd.Presences = append(gd.Presences[:i], gd.Presences[i+1:]...) + ps = append(ps[:i], ps[i+1:]...) + s.presences[guildID] = ps + return nil } }