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

181 lines
4.9 KiB
Go

package message
import (
"context"
"sort"
"github.com/diamondburned/arikawa/discord"
"github.com/diamondburned/arikawa/gateway"
"github.com/diamondburned/cchat"
"github.com/diamondburned/cchat-discord/internal/discord/channel/message/action"
"github.com/diamondburned/cchat-discord/internal/discord/channel/message/backlog"
"github.com/diamondburned/cchat-discord/internal/discord/channel/message/edit"
"github.com/diamondburned/cchat-discord/internal/discord/channel/message/indicate"
"github.com/diamondburned/cchat-discord/internal/discord/channel/message/memberlist"
"github.com/diamondburned/cchat-discord/internal/discord/channel/message/nickname"
"github.com/diamondburned/cchat-discord/internal/discord/channel/message/send"
"github.com/diamondburned/cchat-discord/internal/discord/channel/shared"
"github.com/diamondburned/cchat-discord/internal/discord/message"
"github.com/diamondburned/cchat-discord/internal/funcutil"
"github.com/diamondburned/cchat/utils/empty"
"github.com/pkg/errors"
)
type Messenger struct {
*empty.Messenger
*shared.Channel
}
var _ cchat.Messenger = (*Messenger)(nil)
func New(ch *shared.Channel) Messenger {
return Messenger{Channel: ch}
}
func (msgr Messenger) JoinServer(ctx context.Context, ct cchat.MessagesContainer) (func(), error) {
state := msgr.State.WithContext(ctx)
m, err := state.Messages(msgr.ID)
if err != nil {
return nil, err
}
var addcancel = funcutil.NewCancels()
var constructor func(discord.Message) cchat.MessageCreate
if msgr.GuildID.IsValid() {
// Create the backlog without any member information.
g, err := state.Guild(msgr.GuildID)
if err != nil {
return nil, errors.Wrap(err, "Failed to get guild")
}
constructor = func(m discord.Message) cchat.MessageCreate {
return message.NewBacklogMessage(m, msgr.State, *g)
}
// Subscribe to typing events.
msgr.State.MemberState.Subscribe(msgr.GuildID)
// Listen to new members before creating the backlog and requesting members.
addcancel(msgr.State.AddHandler(func(c *gateway.GuildMembersChunkEvent) {
if c.GuildID != msgr.GuildID {
return
}
m, err := msgr.Messages()
if err != nil {
// TODO: log
return
}
g, err := msgr.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, msgr.State))
}
}
}))
} else {
constructor = func(m discord.Message) cchat.MessageCreate {
return message.NewDirectMessage(m, msgr.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.
msgr.State.ReadState.MarkRead(msgr.ID, m[len(m)-1].ID)
}
// Bind the handler.
addcancel(
msgr.State.AddHandler(func(m *gateway.MessageCreateEvent) {
if m.ChannelID == msgr.ID {
ct.CreateMessage(message.NewMessageCreate(m, msgr.State))
msgr.State.ReadState.MarkRead(msgr.ID, m.ID)
}
}),
msgr.State.AddHandler(func(m *gateway.MessageUpdateEvent) {
// If the updated content is empty. TODO: add embed support.
if m.ChannelID == msgr.ID {
ct.UpdateMessage(message.NewMessageUpdateContent(m.Message, msgr.State))
}
}),
msgr.State.AddHandler(func(m *gateway.MessageDeleteEvent) {
if m.ChannelID == msgr.ID {
ct.DeleteMessage(message.NewHeaderDelete(m))
}
}),
)
return funcutil.JoinCancels(addcancel()), nil
}
func (msgr Messenger) AsSender() cchat.Sender {
if !msgr.HasPermission(discord.PermissionSendMessages) {
return nil
}
return send.New(msgr.Channel)
}
func (msgr Messenger) AsEditor() cchat.Editor {
if !msgr.HasPermission(discord.PermissionSendMessages) {
return nil
}
return edit.New(msgr.Channel)
}
func (msgr Messenger) AsActioner() cchat.Actioner {
return action.New(msgr.Channel)
}
func (msgr Messenger) AsNicknamer() cchat.Nicknamer {
return nickname.New(msgr.Channel)
}
func (msgr Messenger) AsMemberLister() cchat.MemberLister {
if !msgr.GuildID.IsValid() {
return nil
}
return memberlist.New(msgr.Channel)
}
func (msgr Messenger) AsBacklogger() cchat.Backlogger {
if !msgr.HasPermission(discord.PermissionViewChannel, discord.PermissionReadMessageHistory) {
return nil
}
return backlog.New(msgr.Channel)
}
func (msgr Messenger) AsTypingIndicator() cchat.TypingIndicator {
return indicate.NewTyping(msgr.Channel)
}
func (msgr Messenger) AsUnreadIndicator() cchat.UnreadIndicator {
return indicate.NewUnread(msgr.Channel)
}