From 508f355b9657b06dda8a25597a824aa1e8a6b616 Mon Sep 17 00:00:00 2001 From: diamondburned Date: Thu, 16 Jul 2020 17:25:43 -0700 Subject: [PATCH] Message author is now clickable; added Bot, minor tweaks --- message.go | 11 +++++ segments/colored.go | 2 +- segments/mention.go | 110 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 100 insertions(+), 23 deletions(-) diff --git a/message.go b/message.go index 68ed8a0..6e6d18b 100644 --- a/message.go +++ b/message.go @@ -72,6 +72,11 @@ func NewUser(u discord.User) Author { ) } + // Append a clickable user popup. + name.Segments = append(name.Segments, + segments.UserSegment(0, len(name.Content), u), + ) + return Author{ id: u.ID, name: name, @@ -104,6 +109,7 @@ func RenderMemberName(m discord.Member, g discord.Guild) text.Rich { } } + // Append the bot prefix if the user is a bot. if m.User.Bot { name.Content += " " name.Segments = append(name.Segments, @@ -111,6 +117,11 @@ func RenderMemberName(m discord.Member, g discord.Guild) text.Rich { ) } + // Append a clickable user popup. + name.Segments = append(name.Segments, + segments.MemberSegment(0, len(name.Content), g, m), + ) + return name } diff --git a/segments/colored.go b/segments/colored.go index 77cd188..4c89de3 100644 --- a/segments/colored.go +++ b/segments/colored.go @@ -20,7 +20,7 @@ func NewColored(strlen int, color uint32) Colored { } func NewBlurpleSegment(start, end int) Colored { - return NewColoredSegment(start, end, blurple) + return Colored{start, end, blurple} } func NewColoredSegment(start, end int, color uint32) Colored { diff --git a/segments/mention.go b/segments/mention.go index 0f0057f..c3df3d5 100644 --- a/segments/mention.go +++ b/segments/mention.go @@ -12,6 +12,44 @@ import ( "github.com/yuin/goldmark/ast" ) +// NameSegment represents a clickable member name; it does not implement colors. +type NameSegment struct { + start, end int + + guild discord.Guild + member discord.Member +} + +var ( + _ text.Segment = (*NameSegment)(nil) + _ text.Mentioner = (*NameSegment)(nil) +) + +func UserSegment(start, end int, u discord.User) NameSegment { + return NameSegment{ + start: start, + end: end, + member: discord.Member{User: u}, + } +} + +func MemberSegment(start, end int, guild discord.Guild, m discord.Member) NameSegment { + return NameSegment{ + start: start, + end: end, + guild: guild, + member: m, + } +} + +func (m NameSegment) Bounds() (start, end int) { + return m.start, m.end +} + +func (m NameSegment) MentionInfo() text.Rich { + return userInfo(m.guild, m.member) +} + type MentionSegment struct { start, end int *md.Mention @@ -115,14 +153,37 @@ func (m MentionSegment) channelInfo() text.Rich { } func (m MentionSegment) userInfo() text.Rich { + // // We should have a member if there's nil. Sometimes when the members aren't + // // prefetched, the markdown parser can miss them. We can check this again. + // if m.GuildUser.Member == nil && m.guild.Valid() { + // // Best effort; fine if it's nil. + // m.GuildUser.Member, _ = m.store.Member(m.guild, m.GuildUser.ID) + // } + + if m.GuildUser.Member == nil { + m.GuildUser.Member = &discord.Member{ + User: m.GuildUser.User, + } + } + + // Get the guild for the role slice. If not, then too bad. + g, err := m.store.Guild(m.guild) + if err != nil { + g = &discord.Guild{} + } + + return userInfo(*g, *m.GuildUser.Member) +} + +func userInfo(guild discord.Guild, member discord.Member) text.Rich { var content bytes.Buffer var segment text.Rich // Make a large avatar if there's one. - if m.GuildUser.Avatar != "" { + if member.User.Avatar != "" { segmentadd(&segment, AvatarSegment{ start: 0, - url: m.GuildUser.AvatarURL(), // full URL + url: member.User.AvatarURL(), // full URL text: "Avatar", size: 72, // large }) @@ -130,22 +191,15 @@ func (m MentionSegment) userInfo() text.Rich { content.WriteByte(' ') } - // We should have a member if there's nil. Sometimes when the members aren't - // prefetched, the markdown parser can miss them. We can check this again. - if m.GuildUser.Member == nil && m.guild.Valid() { - // Best effort; fine if it's nil. - m.GuildUser.Member, _ = m.store.Member(m.guild, m.GuildUser.ID) - } - // Write the nickname if there's one; else, write the username only. - if m.GuildUser.Member != nil && m.GuildUser.Member.Nick != "" { - content.WriteString(m.GuildUser.Member.Nick) + if member.Nick != "" { + content.WriteString(member.Nick) content.WriteByte(' ') start, end := writestringbuf(&content, fmt.Sprintf( "(%s#%s)", - m.GuildUser.Username, - m.GuildUser.Discriminator, + member.User.Username, + member.User.Discriminator, )) segmentadd(&segment, InlineSegment{ @@ -154,28 +208,31 @@ func (m MentionSegment) userInfo() text.Rich { attributes: text.AttrDimmed, }) } else { - content.WriteString(m.GuildUser.Username) + content.WriteString(member.User.Username) content.WriteByte('#') - content.WriteString(m.GuildUser.Discriminator) + content.WriteString(member.User.Discriminator) } - // Write extra information if any. - if m.GuildUser.Member != nil && len(m.GuildUser.Member.RoleIDs) > 0 { + // Write extra information if any, but only if we have the guild state. + if len(member.RoleIDs) > 0 && guild.ID.Valid() { // Write a prepended new line, as role writes will always prepend a new // line. This is to prevent a trailing new line. content.WriteString("\n\n--- Roles ---") - for _, id := range m.GuildUser.Member.RoleIDs { - r, err := m.store.Role(m.guild, id) - if err != nil { + for _, id := range member.RoleIDs { + rl, ok := findRole(guild.Roles, id) + if !ok { continue } // Prepend a new line before each item. content.WriteByte('\n') // Write exactly the role name, then grab the segment and color it. - start, end := writestringbuf(&content, "@"+r.Name) - segmentadd(&segment, NewColoredSegment(start, end, r.Color.Uint32())) + start, end := writestringbuf(&content, "@"+rl.Name) + // But we only add the color if the role has one. + if color := rl.Color.Uint32(); color > 0 { + segmentadd(&segment, NewColoredSegment(start, end, rl.Color.Uint32())) + } } } @@ -184,6 +241,15 @@ func (m MentionSegment) userInfo() text.Rich { return segment } +func findRole(roles []discord.Role, id discord.Snowflake) (discord.Role, bool) { + for _, role := range roles { + if role.ID == id { + return role, true + } + } + return discord.Role{}, false +} + func (m MentionSegment) roleInfo() text.Rich { // We don't have much to write here. var segment = text.Rich{