discordo/internal/ui/chat/state.go
2026-09-06 19:38:11 -04:00

182 lines
4.6 KiB
Go

package chat
import (
"log/slog"
"slices"
"github.com/ayn2op/arikawa/v3/discord"
"github.com/ayn2op/arikawa/v3/gateway"
"github.com/ayn2op/arikawa/v3/utils/httputil/httpdriver"
"github.com/ayn2op/arikawa/v3/utils/ws"
"github.com/ayn2op/ningen/v3/states/read"
"github.com/ayn2op/tview"
)
func (m *Model) onRequest(r httpdriver.Request) error {
if req, ok := r.(*httpdriver.DefaultRequest); ok {
slog.Debug("new HTTP request", "method", req.Method, "url", req.URL)
}
return nil
}
func (m *Model) onRaw(event *ws.RawEvent) {
slog.Debug(
"new raw event",
"code", event.OriginalCode,
"type", event.OriginalType,
// "data", event.Raw,
)
}
func (m *Model) onReady(event *gateway.ReadyEvent) tview.Cmd {
m.guildsTree.Update(event)
m.setFocus(m.guildsTree)
return nil
}
func (m *Model) onMessageCreate(message *gateway.MessageCreateEvent) tview.Cmd {
m.guildsTree.Update(message)
selectedChannel, ok := m.SelectedChannel()
if ok && selectedChannel.ID == message.ChannelID {
m.removeTyper(message.Author.ID)
m.messagesList.addMessage(message.Message)
return nil
}
return m.notify(*message)
}
func (m *Model) notify(message gateway.MessageCreateEvent) tview.Cmd {
return func() tview.Msg {
if !m.cfg.Notifications.Enabled || m.cfg.Status == discord.DoNotDisturbStatus {
return nil
}
mentions := m.state.MessageMentions(&message.Message)
if mentions == 0 {
return nil
}
// Handle sent files
content := message.Content
if message.Content == "" && len(message.Attachments) > 0 {
content = "Uploaded " + message.Attachments[0].Filename
}
if content == "" {
return nil
}
title := message.Author.DisplayOrUsername()
channel, err := m.state.Cabinet.Channel(message.ChannelID)
if err != nil {
slog.Error("failed to get channel from state", "err", err, "channel_id", message.ChannelID)
return nil
}
if channel.GuildID.IsValid() {
guild, err := m.state.Cabinet.Guild(channel.GuildID)
if err != nil {
slog.Error("failed to get guild from state", "err", err, "guild_id", channel.GuildID)
return nil
}
if member := message.Member; member != nil && member.Nick != "" {
title = member.Nick
}
title += " (#" + channel.Name + ", " + guild.Name + ")"
}
return tview.Notify(title, content)()
}
}
func (m *Model) onPresenceUpdate(presence *gateway.PresenceUpdateEvent) {
m.guildsTree.Update(presence)
}
func (m *Model) onMessageUpdate(message *gateway.MessageUpdateEvent) {
selectedChannel, ok := m.SelectedChannel()
if !ok || selectedChannel.ID != message.ChannelID {
return
}
index := slices.IndexFunc(m.messagesList.items, func(m messageItem) bool {
return !m.separator && m.message.ID == message.ID
})
if index < 0 {
return
}
m.messagesList.setMessage(index, message.Message)
}
func (m *Model) onMessageDelete(message *gateway.MessageDeleteEvent) {
selectedChannel, ok := m.SelectedChannel()
if !ok || selectedChannel.ID != message.ChannelID {
return
}
deletedIndex := slices.IndexFunc(m.messagesList.items, func(m messageItem) bool {
return !m.separator && m.message.ID == message.ID
})
if deletedIndex < 0 {
return
}
m.messagesList.deleteMessage(deletedIndex)
}
func (m *Model) onMessageReaction(channelID discord.ChannelID, messageID discord.MessageID) {
selectedChannel, ok := m.SelectedChannel()
if !ok || selectedChannel.ID != channelID {
return
}
index := slices.IndexFunc(m.messagesList.items, func(item messageItem) bool {
return !item.separator && item.message.ID == messageID
})
message, err := m.state.Cabinet.Message(channelID, messageID)
if index >= 0 && err == nil {
m.messagesList.setMessage(index, *message)
}
}
func (m *Model) onGuildMembersChunk(event *gateway.GuildMembersChunkEvent) tview.Cmd {
m.messagesList.invalidateRenderedMessages()
return m.composer.onGuildMembersChunk(event)
}
func (m *Model) onGuildMemberRemove(event *gateway.GuildMemberRemoveEvent) {
memberSearchCache := m.composer.memberSearchCache
for name := event.GuildID.String() + " " + event.User.Username; name != ""; name = name[:len(name)-1] {
if count, ok := memberSearchCache[name]; ok && count >= m.state.MemberState.SearchLimit {
// A full result set may now be missing members; search these prefixes again.
for name != "" {
delete(memberSearchCache, name)
name = name[:len(name)-1]
}
return
}
}
}
func (m *Model) onTypingStart(event *gateway.TypingStartEvent) {
selectedChannel, ok := m.SelectedChannel()
if !ok || selectedChannel.ID != event.ChannelID {
return
}
if m.isMe(event.UserID) {
return
}
m.addTyper(event.UserID)
}
func (m *Model) onReadUpdate(event *read.UpdateEvent) {
m.guildsTree.Update(event)
}