cchat-discord/segments/mention.go

202 lines
4.6 KiB
Go

package segments
import (
"bytes"
"fmt"
"strings"
"github.com/diamondburned/arikawa/discord"
"github.com/diamondburned/arikawa/state"
"github.com/diamondburned/cchat/text"
"github.com/diamondburned/ningen/md"
"github.com/yuin/goldmark/ast"
)
type MentionSegment struct {
start, end int
*md.Mention
store state.Store
guild discord.Snowflake
}
var (
_ text.Segment = (*MentionSegment)(nil)
_ text.Colorer = (*MentionSegment)(nil)
_ text.Mentioner = (*MentionSegment)(nil)
)
func (r *TextRenderer) mention(n *md.Mention, enter bool) ast.WalkStatus {
if enter {
var seg = MentionSegment{
Mention: n,
store: r.store,
guild: r.msg.GuildID,
}
switch {
case n.Channel != nil:
seg.start, seg.end = r.writeString("#" + n.Channel.Name)
case n.GuildUser != nil:
seg.start, seg.end = r.writeString("@" + n.GuildUser.Username)
case n.GuildRole != nil:
seg.start, seg.end = r.writeString("@" + n.GuildRole.Name)
default:
// Unexpected error; skip.
return ast.WalkSkipChildren
}
r.append(seg)
}
return ast.WalkContinue
}
func (m MentionSegment) Bounds() (start, end int) {
return m.start, m.end
}
// Color tries to return the color of the mention segment, or it returns the
// usual blurple if none.
func (m MentionSegment) Color() (color uint32) {
// Try digging through what we have for a color.
switch {
case m.GuildUser != nil && m.GuildUser.Member != nil:
g, err := m.store.Guild(m.guild)
if err != nil {
return blurple
}
color = discord.MemberColor(*g, *m.GuildUser.Member).Uint32()
case m.GuildRole != nil && m.GuildRole.Color > 0:
color = m.GuildRole.Color.Uint32()
}
if color > 0 {
return
}
return blurple
}
// TODO
func (m MentionSegment) MentionInfo() text.Rich {
switch {
case m.Channel != nil:
return m.channelInfo()
case m.GuildUser != nil:
return m.userInfo()
case m.GuildRole != nil:
return m.roleInfo()
}
// Unknown; return an empty text.
return text.Rich{}
}
func (m MentionSegment) channelInfo() text.Rich {
content := strings.Builder{}
content.WriteByte('#')
content.WriteString(m.Channel.Name)
if m.Channel.NSFW {
content.WriteString(" (NSFW)")
}
if m.Channel.Topic != "" {
content.WriteByte('\n')
content.WriteString(m.Channel.Topic)
}
return text.Rich{
Content: content.String(),
}
}
func (m MentionSegment) userInfo() text.Rich {
var content bytes.Buffer
var segment text.Rich
// Make a large avatar if there's one.
if m.GuildUser.Avatar != "" {
segmentadd(&segment, AvatarSegment{
start: 0,
url: m.GuildUser.AvatarURL(), // full URL
text: "Avatar",
size: 72, // large
})
// Space out.
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)
content.WriteByte(' ')
start, end := writestringbuf(&content, fmt.Sprintf(
"(%s#%s)",
m.GuildUser.Username,
m.GuildUser.Discriminator,
))
segmentadd(&segment, InlineSegment{
start: start,
end: end,
attributes: text.AttrDimmed,
})
} else {
content.WriteString(m.GuildUser.Username)
content.WriteByte('#')
content.WriteString(m.GuildUser.Discriminator)
}
// Write extra information if any.
if m.GuildUser.Member != nil && len(m.GuildUser.Member.RoleIDs) > 0 {
// 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 {
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()))
}
}
// Assign the written content into the text segment and return it.
segment.Content = content.String()
return segment
}
func (m MentionSegment) roleInfo() text.Rich {
// We don't have much to write here.
var segment = text.Rich{
Content: m.GuildRole.Name,
}
// Maybe add a color if we have any.
if c := m.GuildRole.Color.Uint32(); c > 0 {
segment.Segments = []text.Segment{
NewColored(len(m.GuildRole.Name), m.GuildRole.Color.Uint32()),
}
}
return segment
}