2020-06-19 01:00:24 +00:00
|
|
|
package segments
|
|
|
|
|
|
|
|
import (
|
2020-07-08 08:35:30 +00:00
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/diamondburned/arikawa/discord"
|
2020-07-12 06:37:36 +00:00
|
|
|
"github.com/diamondburned/arikawa/state"
|
2020-06-19 01:00:24 +00:00
|
|
|
"github.com/diamondburned/cchat/text"
|
|
|
|
"github.com/diamondburned/ningen/md"
|
|
|
|
"github.com/yuin/goldmark/ast"
|
|
|
|
)
|
|
|
|
|
2020-07-08 08:35:30 +00:00
|
|
|
const blurple = 0x7289DA
|
|
|
|
|
2020-06-19 01:00:24 +00:00
|
|
|
type MentionSegment struct {
|
|
|
|
start, end int
|
2020-07-08 08:35:30 +00:00
|
|
|
*md.Mention
|
|
|
|
|
2020-07-12 06:37:36 +00:00
|
|
|
store state.Store
|
|
|
|
guild discord.Snowflake
|
2020-06-19 01:00:24 +00:00
|
|
|
}
|
|
|
|
|
2020-07-08 08:35:30 +00:00
|
|
|
var (
|
|
|
|
_ text.Segment = (*MentionSegment)(nil)
|
|
|
|
_ text.Colorer = (*MentionSegment)(nil)
|
|
|
|
_ text.Mentioner = (*MentionSegment)(nil)
|
|
|
|
)
|
2020-06-19 01:00:24 +00:00
|
|
|
|
|
|
|
func (r *TextRenderer) mention(n *md.Mention, enter bool) ast.WalkStatus {
|
|
|
|
if enter {
|
2020-07-12 06:37:36 +00:00
|
|
|
var seg = MentionSegment{
|
|
|
|
Mention: n,
|
|
|
|
store: r.store,
|
|
|
|
guild: r.msg.GuildID,
|
|
|
|
}
|
2020-06-19 01:00:24 +00:00
|
|
|
|
|
|
|
switch {
|
|
|
|
case n.Channel != nil:
|
2020-07-08 08:35:30 +00:00
|
|
|
seg.start, seg.end = r.writeString("#" + n.Channel.Name)
|
2020-06-19 01:00:24 +00:00
|
|
|
case n.GuildUser != nil:
|
2020-07-08 08:35:30 +00:00
|
|
|
seg.start, seg.end = r.writeString("@" + n.GuildUser.Username)
|
2020-06-19 01:00:24 +00:00
|
|
|
case n.GuildRole != nil:
|
2020-07-08 08:35:30 +00:00
|
|
|
seg.start, seg.end = r.writeString("@" + n.GuildRole.Name)
|
|
|
|
default:
|
|
|
|
// Unexpected error; skip.
|
|
|
|
return ast.WalkSkipChildren
|
2020-06-19 01:00:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
r.append(seg)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ast.WalkContinue
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m MentionSegment) Bounds() (start, end int) {
|
|
|
|
return m.start, m.end
|
|
|
|
}
|
|
|
|
|
2020-07-08 08:35:30 +00:00
|
|
|
// Color tries to return the color of the mention segment, or it returns the
|
|
|
|
// usual blurple if none.
|
2020-07-14 01:29:13 +00:00
|
|
|
func (m MentionSegment) Color() (color uint32) {
|
2020-07-08 08:35:30 +00:00
|
|
|
// Try digging through what we have for a color.
|
|
|
|
switch {
|
2020-07-12 06:37:36 +00:00
|
|
|
case m.GuildUser != nil && m.GuildUser.Member != nil:
|
|
|
|
g, err := m.store.Guild(m.guild)
|
|
|
|
if err != nil {
|
|
|
|
return blurple
|
2020-07-08 08:35:30 +00:00
|
|
|
}
|
2020-07-12 06:37:36 +00:00
|
|
|
|
2020-07-14 01:29:13 +00:00
|
|
|
color = discord.MemberColor(*g, *m.GuildUser.Member).Uint32()
|
2020-07-12 06:37:36 +00:00
|
|
|
|
2020-07-08 08:35:30 +00:00
|
|
|
case m.GuildRole != nil && m.GuildRole.Color > 0:
|
2020-07-14 01:29:13 +00:00
|
|
|
color = m.GuildRole.Color.Uint32()
|
|
|
|
}
|
|
|
|
|
|
|
|
if color > 0 {
|
|
|
|
return
|
2020-07-08 08:35:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return blurple
|
|
|
|
}
|
|
|
|
|
2020-06-19 01:00:24 +00:00
|
|
|
// TODO
|
|
|
|
func (m MentionSegment) MentionInfo() text.Rich {
|
2020-07-08 08:35:30 +00:00
|
|
|
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.
|
2020-06-19 01:00:24 +00:00
|
|
|
return text.Rich{}
|
|
|
|
}
|
2020-07-08 08:35:30 +00:00
|
|
|
|
|
|
|
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.
|
2020-07-12 06:37:36 +00:00
|
|
|
if m.GuildUser.Avatar != "" {
|
|
|
|
segmentadd(&segment, AvatarSegment{
|
2020-07-08 08:35:30 +00:00
|
|
|
start: 0,
|
2020-07-12 06:37:36 +00:00
|
|
|
url: m.GuildUser.AvatarURL(), // full URL
|
2020-07-08 08:35:30 +00:00
|
|
|
text: "Avatar",
|
2020-07-12 06:37:36 +00:00
|
|
|
size: 72, // large
|
2020-07-08 08:35:30 +00:00
|
|
|
})
|
|
|
|
// Space out.
|
|
|
|
content.WriteByte(' ')
|
|
|
|
}
|
|
|
|
|
2020-07-12 06:37:36 +00:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2020-07-08 08:35:30 +00:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2020-07-12 06:37:36 +00:00
|
|
|
// Write extra information if any.
|
|
|
|
if m.GuildUser.Member != nil && len(m.GuildUser.Member.RoleIDs) > 0 {
|
2020-07-08 08:35:30 +00:00
|
|
|
// Write a prepended new line, as role writes will always prepend a new
|
|
|
|
// line. This is to prevent a trailing new line.
|
2020-07-12 06:37:36 +00:00
|
|
|
content.WriteString("\n\n--- Roles ---")
|
|
|
|
|
|
|
|
for _, id := range m.GuildUser.Member.RoleIDs {
|
|
|
|
r, err := m.store.Role(m.guild, id)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2020-07-08 08:35:30 +00:00
|
|
|
|
|
|
|
// Prepend a new line before each item.
|
|
|
|
content.WriteByte('\n')
|
|
|
|
// Write exactly the role name, then grab the segment and color it.
|
2020-07-12 06:37:36 +00:00
|
|
|
start, end := writestringbuf(&content, "@"+r.Name)
|
|
|
|
segmentadd(&segment, NewColoredSegment(start, end, r.Color.Uint32()))
|
2020-07-08 08:35:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|