diff --git a/internal/discord/message/author.go b/internal/discord/message/author.go index cd851e8..165cffc 100644 --- a/internal/discord/message/author.go +++ b/internal/discord/message/author.go @@ -66,11 +66,7 @@ func (a Author) ID() cchat.ID { // Name subscribes the author to the global name label registry. func (a Author) Name(_ context.Context, l cchat.LabelContainer) (func(), error) { - if guildID := a.user.GuildID(); guildID.IsValid() { - return a.state.Labels.AddMemberLabel(guildID, a.user.UserID(), l), nil - } - - return a.state.Labels.AddPresenceLabel(a.user.UserID(), l), nil + return a.state.Labels.AddMemberLabel(a.user.GuildID(), a.user.UserID(), l), nil } const authorReplyingTo = " replying to " diff --git a/internal/discord/state/labels/labels.go b/internal/discord/state/labels/labels.go index 3b8ccc4..290d3a0 100644 --- a/internal/discord/state/labels/labels.go +++ b/internal/discord/state/labels/labels.go @@ -93,6 +93,58 @@ func (r *Repository) AddGuildLabel(guildID discord.GuildID, l cchat.LabelContain } } +func (r *Repository) onChannelDelete(ev *gateway.ChannelDeleteEvent) { + // Not sure what to do. +} + +func (r *Repository) onChannelUpdate(ev *gateway.ChannelUpdateEvent) { + r.mutex.Lock() + defer r.mutex.Unlock() + + if r.stopped { + return + } + + channel, ok := r.stores.channels[ev.ID] + if !ok { + return + } + + rich := mention.NewChannelText(r.state, ev.ID) + + for labeler := range channel { + labeler.SetLabel(rich) + } +} + +// AddChannelLabel adds a label to display the given channel live. Refer to +// Repository for more documentation. +func (r *Repository) AddChannelLabel(chID discord.ChannelID, l cchat.LabelContainer) func() { + l.SetLabel(mention.NewChannelText(r.state, chID)) + + r.mutex.Lock() + defer r.mutex.Unlock() + + llist := r.stores.channels[chID] + llist.Add(l) + r.stores.channels[chID] = llist + + return func() { + r.mutex.Lock() + defer r.mutex.Unlock() + + llist := r.stores.channels[chID] + llist.Remove(l) + + if len(llist) == 0 { + delete(r.stores.channels, chID) + return + } + + r.stores.channels[chID] = llist + } +} + func (r *Repository) onMemberRemove(ev *gateway.GuildMemberRemoveEvent) { // Not sure what to do. } @@ -189,11 +241,7 @@ func (r *Repository) AddMemberLabel( } } -func (r *Repository) onChannelDelete(ev *gateway.ChannelDeleteEvent) { - // Not sure what to do. -} - -func (r *Repository) onChannelUpdate(ev *gateway.ChannelUpdateEvent) { +func (r *Repository) onPresenceUpdate(ev *gateway.PresenceUpdateEvent) { r.mutex.Lock() defer r.mutex.Unlock() @@ -201,53 +249,44 @@ func (r *Repository) onChannelUpdate(ev *gateway.ChannelUpdateEvent) { return } - channel, ok := r.stores.channels[ev.ID] + labels, ok := r.stores.presences[ev.User.ID] if !ok { return } - rich := mention.NewChannelText(r.state, ev.ID) + rich := mention.NewUserText(r.state, ev.User.ID) - for labeler := range channel { - labeler.SetLabel(rich) + for label := range labels { + label.SetLabel(rich) } } -// AddChannelLabel adds a label to display the given channel live. Refer to -// Repository for more documentation. -func (r *Repository) AddChannelLabel(chID discord.ChannelID, l cchat.LabelContainer) func() { - l.SetLabel(mention.NewChannelText(r.state, chID)) +func (r *Repository) AddPresenceLabel(uID discord.UserID, l cchat.LabelContainer) func() { + l.SetLabel(mention.NewUserText(r.state, uID)) r.mutex.Lock() defer r.mutex.Unlock() - llist := r.stores.channels[chID] + llist := r.stores.presences[uID] llist.Add(l) - r.stores.channels[chID] = llist + r.stores.presences[uID] = llist return func() { r.mutex.Lock() defer r.mutex.Unlock() - llist := r.stores.channels[chID] + llist := r.stores.presences[uID] llist.Remove(l) if len(llist) == 0 { - delete(r.stores.channels, chID) + delete(r.stores.presences, uID) return } - r.stores.channels[chID] = llist + r.stores.presences[uID] = llist } } -func (r *Repository) AddPresenceLabel(uID discord.UserID, l cchat.LabelContainer) func() { - // TODO: Presence update events - // TODO: user fallbacks - panic("Implement me") - return nil -} - // Stop detaches all handlers. func (r *Repository) Stop() { r.mutex.Lock() diff --git a/internal/segments/avatar/avatar.go b/internal/segments/avatar/avatar.go index 87fa0d0..a5716ea 100644 --- a/internal/segments/avatar/avatar.go +++ b/internal/segments/avatar/avatar.go @@ -16,22 +16,20 @@ type Segment struct { } func (s Segment) Bounds() (int, int) { return s.Position, s.Position } -func (s Segment) AsAvatarer() text.Avatarer { return avatarURL{s} } +func (s Segment) AsAvatarer() text.Avatarer { return avatarURL(s) } -type avatarURL struct { - seg Segment -} +type avatarURL Segment var _ text.Avatarer = avatarURL{} func (aurl avatarURL) AvatarText() string { - return aurl.seg.Text + return aurl.Text } func (aurl avatarURL) AvatarSize() int { - return aurl.seg.Size + return aurl.Size } func (aurl avatarURL) Avatar() string { - return aurl.seg.URL + return aurl.URL } diff --git a/internal/segments/mention/user.go b/internal/segments/mention/user.go index 6e0a6df..920f621 100644 --- a/internal/segments/mention/user.go +++ b/internal/segments/mention/user.go @@ -49,6 +49,38 @@ func NewMemberText(s *ningen.State, g discord.GuildID, u discord.UserID) text.Ri return rich } +// NewUserText creates a new rich text describing a Discord user using the +// Presence API. +func NewUserText(s *ningen.State, u discord.UserID) text.Rich { + p, err := s.Presence(0, u) + if err != nil { + return text.Plain(u.Mention()) + } + + user := NewUser(p.User) + user.WithPresence(*p) + user.WithState(s) + user.Prefetch() + + rich := text.Rich{Content: user.DisplayName()} + rich.Segments = []text.Segment{ + Segment{ + Start: 0, + End: len(rich.Content), + User: user, + }, + } + + if p.User.Bot { + rich.Content += " " + rich.Segments = append(rich.Segments, + colored.NewBlurple(segutil.Write(&rich, "[BOT]")), + ) + } + + return rich +} + type User struct { user discord.User guildID discord.GuildID