2020-11-30 00:57:58 +00:00
|
|
|
package defaultstore
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/diamondburned/arikawa/v2/discord"
|
|
|
|
"github.com/diamondburned/arikawa/v2/internal/moreatomic"
|
|
|
|
"github.com/diamondburned/arikawa/v2/state/store"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Message struct {
|
|
|
|
channels moreatomic.Map
|
|
|
|
maxMsgs int
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ store.MessageStore = (*Message)(nil)
|
|
|
|
|
|
|
|
type messages struct {
|
|
|
|
mut sync.Mutex
|
|
|
|
messages []discord.Message
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewMessage(maxMsgs int) *Message {
|
|
|
|
return &Message{
|
|
|
|
channels: *moreatomic.NewMap(func() interface{} {
|
|
|
|
return &messages{
|
|
|
|
messages: []discord.Message{}, // never use a nil slice
|
|
|
|
}
|
|
|
|
}),
|
2020-11-30 02:21:33 +00:00
|
|
|
maxMsgs: maxMsgs,
|
2020-11-30 00:57:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Message) Reset() error {
|
|
|
|
return s.channels.Reset()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Message) Message(chID discord.ChannelID, mID discord.MessageID) (*discord.Message, error) {
|
|
|
|
iv, ok := s.channels.Load(chID)
|
|
|
|
if !ok {
|
|
|
|
return nil, store.ErrNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
msgs := iv.(*messages)
|
|
|
|
|
|
|
|
msgs.mut.Lock()
|
|
|
|
defer msgs.mut.Unlock()
|
|
|
|
|
|
|
|
for _, m := range msgs.messages {
|
|
|
|
if m.ID == mID {
|
|
|
|
return &m, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, store.ErrNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Message) Messages(channelID discord.ChannelID) ([]discord.Message, error) {
|
|
|
|
iv, ok := s.channels.Load(channelID)
|
|
|
|
if !ok {
|
|
|
|
return nil, store.ErrNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
msgs := iv.(*messages)
|
|
|
|
|
|
|
|
msgs.mut.Lock()
|
|
|
|
defer msgs.mut.Unlock()
|
|
|
|
|
|
|
|
return append([]discord.Message(nil), msgs.messages...), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Message) MaxMessages() int {
|
|
|
|
return s.maxMsgs
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Message) MessageSet(message discord.Message) error {
|
|
|
|
iv, _ := s.channels.LoadOrStore(message.ChannelID)
|
|
|
|
|
|
|
|
msgs := iv.(*messages)
|
|
|
|
|
|
|
|
msgs.mut.Lock()
|
|
|
|
defer msgs.mut.Unlock()
|
|
|
|
|
|
|
|
for i, m := range msgs.messages {
|
|
|
|
if m.ID == message.ID {
|
|
|
|
DiffMessage(message, &m)
|
|
|
|
msgs.messages[i] = m
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Order: latest to earliest, similar to the API.
|
|
|
|
|
2021-02-26 07:57:45 +00:00
|
|
|
// Check if we already have the message. Try to derive the order otherwise.
|
|
|
|
var insertAt int
|
|
|
|
|
|
|
|
// Since we make the order guarantee ourselves, we can trust that we're
|
|
|
|
// iterating from latest to earliest.
|
|
|
|
for insertAt < len(msgs.messages) {
|
|
|
|
// Check if the new message is older. If it is, then we should insert it
|
|
|
|
// right after this message (or before this message in the list; i-1).
|
|
|
|
if message.ID > msgs.messages[insertAt].ID {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
insertAt++
|
|
|
|
}
|
|
|
|
|
|
|
|
end := len(msgs.messages)
|
|
|
|
max := s.MaxMessages()
|
|
|
|
|
|
|
|
if end == max {
|
|
|
|
// If insertAt is larger than the length, then the message is older than
|
|
|
|
// every other messages we have. We have to discard this message here,
|
|
|
|
// since the store is already full.
|
|
|
|
if insertAt == end {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the end (length) is approaching the maximum amount, then cap it.
|
2020-11-30 00:57:58 +00:00
|
|
|
end = max
|
|
|
|
} else {
|
|
|
|
// Else, append an empty message to the end.
|
|
|
|
msgs.messages = append(msgs.messages, discord.Message{})
|
|
|
|
// Increment to update the length.
|
|
|
|
end++
|
|
|
|
}
|
|
|
|
|
2021-02-26 07:57:45 +00:00
|
|
|
// Shift the slice right-wards if the current item is not the last.
|
|
|
|
if start := insertAt + 1; start < end {
|
|
|
|
copy(msgs.messages[insertAt+1:], msgs.messages[insertAt:end-1])
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then, set the nth entry.
|
|
|
|
msgs.messages[insertAt] = message
|
2020-11-30 00:57:58 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DiffMessage fills non-empty fields from src to dst.
|
|
|
|
func DiffMessage(src discord.Message, dst *discord.Message) {
|
|
|
|
// Thanks, Discord.
|
|
|
|
if src.Content != "" {
|
|
|
|
dst.Content = src.Content
|
|
|
|
}
|
|
|
|
if src.EditedTimestamp.IsValid() {
|
|
|
|
dst.EditedTimestamp = src.EditedTimestamp
|
|
|
|
}
|
|
|
|
if src.Mentions != nil {
|
|
|
|
dst.Mentions = src.Mentions
|
|
|
|
}
|
|
|
|
if src.Embeds != nil {
|
|
|
|
dst.Embeds = src.Embeds
|
|
|
|
}
|
|
|
|
if src.Attachments != nil {
|
|
|
|
dst.Attachments = src.Attachments
|
|
|
|
}
|
|
|
|
if src.Timestamp.IsValid() {
|
|
|
|
dst.Timestamp = src.Timestamp
|
|
|
|
}
|
|
|
|
if src.Author.ID.IsValid() {
|
|
|
|
dst.Author = src.Author
|
|
|
|
}
|
|
|
|
if src.Reactions != nil {
|
|
|
|
dst.Reactions = src.Reactions
|
|
|
|
}
|
2021-05-30 04:28:37 +00:00
|
|
|
if src.Components != nil {
|
|
|
|
dst.Components = src.Components
|
|
|
|
}
|
2020-11-30 00:57:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Message) MessageRemove(channelID discord.ChannelID, messageID discord.MessageID) error {
|
|
|
|
iv, ok := s.channels.Load(channelID)
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
msgs := iv.(*messages)
|
|
|
|
|
|
|
|
msgs.mut.Lock()
|
|
|
|
defer msgs.mut.Unlock()
|
|
|
|
|
|
|
|
for i, m := range msgs.messages {
|
|
|
|
if m.ID == messageID {
|
|
|
|
msgs.messages = append(msgs.messages[:i], msgs.messages[i+1:]...)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|