cchat-discord/internal/discord/channel/messenger.go

126 lines
3.2 KiB
Go

package channel
import (
"context"
"sort"
"github.com/diamondburned/arikawa/discord"
"github.com/diamondburned/arikawa/gateway"
"github.com/diamondburned/cchat"
"github.com/diamondburned/cchat-discord/internal/discord/message"
"github.com/diamondburned/cchat-discord/internal/funcutil"
"github.com/pkg/errors"
)
var _ cchat.Messenger = (*Channel)(nil)
// IsMessenger returns true if the current user is allowed to see the channel.
func (ch *Channel) IsMessenger() bool {
p, err := ch.state.StateOnly().Permissions(ch.id, ch.state.UserID)
if err != nil {
return false
}
return p.Has(discord.PermissionViewChannel)
}
func (ch *Channel) JoinServer(ctx context.Context, ct cchat.MessagesContainer) (func(), error) {
state := ch.state.WithContext(ctx)
m, err := state.Messages(ch.id)
if err != nil {
return nil, err
}
var addcancel = funcutil.NewCancels()
var constructor func(discord.Message) cchat.MessageCreate
if ch.guildID.IsValid() {
// Create the backlog without any member information.
g, err := state.Guild(ch.guildID)
if err != nil {
return nil, errors.Wrap(err, "Failed to get guild")
}
constructor = func(m discord.Message) cchat.MessageCreate {
return message.NewBacklogMessage(m, ch.state, *g)
}
// Subscribe to typing events.
ch.state.MemberState.Subscribe(ch.guildID)
// Listen to new members before creating the backlog and requesting members.
addcancel(ch.state.AddHandler(func(c *gateway.GuildMembersChunkEvent) {
if c.GuildID != ch.guildID {
return
}
m, err := ch.messages()
if err != nil {
// TODO: log
return
}
g, err := ch.guild()
if err != nil {
return
}
// Loop over all messages and replace the author. The latest
// messages are in front.
for _, msg := range m {
for _, member := range c.Members {
if msg.Author.ID != member.User.ID {
continue
}
ct.UpdateMessage(message.NewMessageUpdateAuthor(msg, member, *g, ch.state))
}
}
}))
} else {
constructor = func(m discord.Message) cchat.MessageCreate {
return message.NewDirectMessage(m, ch.state)
}
}
// Only do all this if we even have any messages.
if len(m) > 0 {
// Sort messages chronologically using the ID so that the oldest messages
// (ones with the smallest snowflake) is in front.
sort.Slice(m, func(i, j int) bool { return m[i].ID < m[j].ID })
// Iterate from the earliest messages to the latest messages.
for _, m := range m {
ct.CreateMessage(constructor(m))
}
// Mark this channel as read.
ch.state.ReadState.MarkRead(ch.id, m[len(m)-1].ID)
}
// Bind the handler.
addcancel(
ch.state.AddHandler(func(m *gateway.MessageCreateEvent) {
if m.ChannelID == ch.id {
ct.CreateMessage(message.NewMessageCreate(m, ch.state))
ch.state.ReadState.MarkRead(ch.id, m.ID)
}
}),
ch.state.AddHandler(func(m *gateway.MessageUpdateEvent) {
// If the updated content is empty. TODO: add embed support.
if m.ChannelID == ch.id {
ct.UpdateMessage(message.NewMessageUpdateContent(m.Message, ch.state))
}
}),
ch.state.AddHandler(func(m *gateway.MessageDeleteEvent) {
if m.ChannelID == ch.id {
ct.DeleteMessage(message.NewHeaderDelete(m))
}
}),
)
return funcutil.JoinCancels(addcancel()), nil
}