2020-12-17 08:01:58 +00:00
|
|
|
package hub
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/diamondburned/arikawa/discord"
|
|
|
|
"github.com/diamondburned/arikawa/gateway"
|
|
|
|
"github.com/diamondburned/cchat"
|
2020-12-19 05:46:12 +00:00
|
|
|
"github.com/diamondburned/cchat-discord/internal/discord/channel/message/send/complete"
|
|
|
|
"github.com/diamondburned/cchat-discord/internal/discord/channel/shared"
|
2020-12-17 08:01:58 +00:00
|
|
|
"github.com/diamondburned/cchat-discord/internal/discord/message"
|
|
|
|
"github.com/diamondburned/cchat-discord/internal/discord/state"
|
2020-12-19 05:46:12 +00:00
|
|
|
"github.com/diamondburned/cchat-discord/internal/discord/state/nonce"
|
2020-12-17 08:01:58 +00:00
|
|
|
"github.com/diamondburned/cchat-discord/internal/funcutil"
|
|
|
|
"github.com/diamondburned/cchat/utils/empty"
|
|
|
|
)
|
|
|
|
|
|
|
|
const maxMessages = 100
|
|
|
|
|
|
|
|
type messageList []discord.Message
|
|
|
|
|
|
|
|
func (list messageList) idx(id discord.MessageID) int {
|
|
|
|
for i, msg := range list {
|
|
|
|
if msg.ID == id {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *messageList) append(msg discord.Message) {
|
|
|
|
*list = append(*list, msg)
|
|
|
|
|
|
|
|
// cap the length
|
|
|
|
if len(*list) > maxMessages {
|
|
|
|
copy(*list, (*list)[1:]) // shift left once
|
|
|
|
(*list)[len(*list)-1] = discord.Message{} // nil out last to not memory leak
|
|
|
|
*list = (*list)[:len(*list)-1] // slice it away
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *messageList) swap(newMsg discord.Message) {
|
|
|
|
if idx := list.idx(newMsg.ID); idx > -1 {
|
|
|
|
(*list)[idx] = newMsg
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (list *messageList) delete(id discord.MessageID) {
|
|
|
|
if idx := list.idx(id); idx > -1 {
|
|
|
|
*list = append((*list)[:idx], (*list)[idx+1:]...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Messages struct {
|
|
|
|
empty.Messenger
|
|
|
|
|
2020-12-19 05:46:12 +00:00
|
|
|
state *state.Instance
|
|
|
|
acList *activeList
|
|
|
|
sentMsgs *nonce.Set
|
2020-12-17 08:01:58 +00:00
|
|
|
|
|
|
|
sender *Sender
|
|
|
|
|
|
|
|
msgMutex sync.Mutex
|
|
|
|
messages messageList
|
|
|
|
|
|
|
|
cancel func()
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewMessages(s *state.Instance, acList *activeList, adder ChannelAdder) *Messages {
|
2020-12-19 05:46:12 +00:00
|
|
|
var sentMsgs nonce.Set
|
|
|
|
|
2020-12-17 08:01:58 +00:00
|
|
|
hubServer := &Messages{
|
|
|
|
state: s,
|
|
|
|
acList: acList,
|
2020-12-19 05:46:12 +00:00
|
|
|
sentMsgs: &sentMsgs,
|
|
|
|
sender: &Sender{
|
|
|
|
adder: adder,
|
|
|
|
acList: acList,
|
|
|
|
sentMsgs: &sentMsgs,
|
|
|
|
state: s,
|
|
|
|
},
|
2020-12-17 08:01:58 +00:00
|
|
|
messages: make(messageList, 0, 100),
|
|
|
|
}
|
|
|
|
|
2020-12-19 05:46:12 +00:00
|
|
|
hubServer.sender.completers = complete.Completer{
|
|
|
|
':': func(word string) []cchat.CompletionEntry {
|
|
|
|
return complete.Emojis(s, 0, word)
|
|
|
|
},
|
|
|
|
'@': func(word string) []cchat.CompletionEntry {
|
|
|
|
if word != "" {
|
|
|
|
return complete.Presences(s, word)
|
|
|
|
}
|
|
|
|
|
|
|
|
hubServer.msgMutex.Lock()
|
|
|
|
defer hubServer.msgMutex.Unlock()
|
|
|
|
return complete.MessageMentions(hubServer.messages)
|
|
|
|
},
|
|
|
|
'#': func(word string) []cchat.CompletionEntry {
|
|
|
|
return complete.DMChannels(s, word)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-12-17 08:01:58 +00:00
|
|
|
hubServer.cancel = funcutil.JoinCancels(
|
|
|
|
s.AddHandler(func(msg *gateway.MessageCreateEvent) {
|
|
|
|
if msg.GuildID.IsValid() || acList.isActive(msg.ChannelID) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-12-19 05:46:12 +00:00
|
|
|
// We're not adding back messages we sent here, since we already
|
|
|
|
// have a separate channel for that.
|
|
|
|
|
2020-12-17 08:01:58 +00:00
|
|
|
hubServer.msgMutex.Lock()
|
|
|
|
hubServer.messages.append(msg.Message)
|
|
|
|
hubServer.msgMutex.Unlock()
|
|
|
|
}),
|
|
|
|
s.AddHandler(func(update *gateway.MessageUpdateEvent) {
|
|
|
|
if update.GuildID.IsValid() || acList.isActive(update.ChannelID) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// The event itself is unreliable, so we must rely on the state.
|
|
|
|
m, err := hubServer.state.Message(update.ChannelID, update.ID)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
hubServer.msgMutex.Lock()
|
|
|
|
hubServer.messages.swap(*m)
|
|
|
|
hubServer.msgMutex.Unlock()
|
|
|
|
}),
|
|
|
|
s.AddHandler(func(del *gateway.MessageDeleteEvent) {
|
|
|
|
if del.GuildID.IsValid() || acList.isActive(del.ChannelID) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
hubServer.msgMutex.Lock()
|
|
|
|
hubServer.messages.delete(del.ID)
|
|
|
|
hubServer.msgMutex.Unlock()
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
|
|
|
|
return hubServer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (msgs *Messages) JoinServer(ctx context.Context, ct cchat.MessagesContainer) (func(), error) {
|
|
|
|
msgs.msgMutex.Lock()
|
|
|
|
|
|
|
|
for _, msg := range msgs.messages {
|
|
|
|
ct.CreateMessage(message.NewDirectMessage(msg, msgs.state))
|
|
|
|
}
|
|
|
|
|
|
|
|
msgs.msgMutex.Unlock()
|
|
|
|
|
|
|
|
// Bind the handler.
|
|
|
|
return funcutil.JoinCancels(
|
|
|
|
msgs.state.AddHandler(func(msg *gateway.MessageCreateEvent) {
|
2020-12-19 05:46:12 +00:00
|
|
|
if msg.GuildID.IsValid() {
|
2020-12-17 08:01:58 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-12-19 05:46:12 +00:00
|
|
|
var isReply = false
|
|
|
|
if msgs.acList.isActive(msg.ChannelID) {
|
|
|
|
if !msgs.sentMsgs.HasAndDelete(msg.Nonce) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
isReply = true
|
|
|
|
}
|
|
|
|
|
|
|
|
var author = message.NewUser(msg.Author, msgs.state)
|
|
|
|
if isReply {
|
|
|
|
c, err := msgs.state.Channel(msg.ChannelID)
|
|
|
|
if err == nil {
|
|
|
|
switch c.Type {
|
|
|
|
case discord.DirectMessage:
|
|
|
|
author.AddUserReply(c.DMRecipients[0], msgs.state)
|
|
|
|
case discord.GroupDM:
|
|
|
|
author.AddReply(shared.PrivateName(*c))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ct.CreateMessage(message.NewMessage(msg.Message, msgs.state, author))
|
2020-12-17 08:01:58 +00:00
|
|
|
msgs.state.ReadState.MarkRead(msg.ChannelID, msg.ID)
|
|
|
|
}),
|
|
|
|
msgs.state.AddHandler(func(update *gateway.MessageUpdateEvent) {
|
|
|
|
if update.GuildID.IsValid() || msgs.acList.isActive(update.ChannelID) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ct.UpdateMessage(message.NewMessageUpdateContent(update.Message, msgs.state))
|
|
|
|
}),
|
|
|
|
msgs.state.AddHandler(func(del *gateway.MessageDeleteEvent) {
|
|
|
|
if del.GuildID.IsValid() || msgs.acList.isActive(del.ChannelID) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ct.DeleteMessage(message.NewHeaderDelete(del))
|
|
|
|
}),
|
|
|
|
), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (msgs *Messages) AsSender() cchat.Sender { return msgs.sender }
|