mirror of
https://github.com/diamondburned/cchat-discord.git
synced 2024-11-01 12:24:15 +00:00
200 lines
5 KiB
Go
200 lines
5 KiB
Go
|
package mention
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/diamondburned/arikawa/discord"
|
||
|
"github.com/diamondburned/arikawa/state"
|
||
|
"github.com/diamondburned/cchat-discord/internal/segments/colored"
|
||
|
"github.com/diamondburned/cchat-discord/internal/segments/inline"
|
||
|
"github.com/diamondburned/cchat-discord/internal/segments/segutil"
|
||
|
"github.com/diamondburned/cchat/text"
|
||
|
"github.com/diamondburned/cchat/utils/empty"
|
||
|
"github.com/diamondburned/ningen"
|
||
|
)
|
||
|
|
||
|
// NameSegment represents a clickable member name; it does not implement colors.
|
||
|
type NameSegment struct {
|
||
|
empty.TextSegment
|
||
|
start int
|
||
|
end int
|
||
|
um User
|
||
|
}
|
||
|
|
||
|
var _ text.Segment = (*NameSegment)(nil)
|
||
|
|
||
|
func UserSegment(start, end int, u discord.User) NameSegment {
|
||
|
return NameSegment{
|
||
|
start: start,
|
||
|
end: end,
|
||
|
um: User{
|
||
|
member: discord.Member{User: u},
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func MemberSegment(start, end int, guild discord.Guild, m discord.Member) NameSegment {
|
||
|
return NameSegment{
|
||
|
start: start,
|
||
|
end: end,
|
||
|
um: User{
|
||
|
guild: guild,
|
||
|
member: m,
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// WithState assigns a ningen state into the given name segment. This allows the
|
||
|
// popovers to have additional information such as user notes.
|
||
|
func (m *NameSegment) WithState(state *ningen.State) {
|
||
|
m.um.state = state
|
||
|
}
|
||
|
|
||
|
func (m NameSegment) Bounds() (start, end int) {
|
||
|
return m.start, m.end
|
||
|
}
|
||
|
|
||
|
func (m NameSegment) AsMentioner() text.Mentioner {
|
||
|
return m.um
|
||
|
}
|
||
|
|
||
|
func (m NameSegment) AsAvatarer() text.Avatarer {
|
||
|
return m.um
|
||
|
}
|
||
|
|
||
|
type User struct {
|
||
|
state state.Store
|
||
|
guild discord.Guild
|
||
|
member discord.Member
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
_ text.Colorer = (*User)(nil)
|
||
|
_ text.Avatarer = (*User)(nil)
|
||
|
_ text.Mentioner = (*User)(nil)
|
||
|
)
|
||
|
|
||
|
// NewUser creates a new user mention. If state is of type *ningen.State, then
|
||
|
// it'll fetch additional information asynchronously.
|
||
|
func NewUser(state state.Store, guild discord.GuildID, guser discord.GuildUser) *User {
|
||
|
if guser.Member == nil {
|
||
|
m, err := state.Member(guild, guser.ID)
|
||
|
if err != nil {
|
||
|
guser.Member = &discord.Member{}
|
||
|
} else {
|
||
|
guser.Member = m
|
||
|
}
|
||
|
}
|
||
|
|
||
|
guser.Member.User = guser.User
|
||
|
|
||
|
// Get the guild for the role slice. If not, then too bad.
|
||
|
g, err := state.Guild(guild)
|
||
|
if err != nil {
|
||
|
g = &discord.Guild{}
|
||
|
}
|
||
|
|
||
|
return &User{
|
||
|
state: state,
|
||
|
guild: *g,
|
||
|
member: *guser.Member,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (um *User) Color() uint32 {
|
||
|
g, err := um.state.Guild(um.guild.ID)
|
||
|
if err != nil {
|
||
|
return colored.Blurple
|
||
|
}
|
||
|
|
||
|
return text.SolidColor(discord.MemberColor(*g, um.member).Uint32())
|
||
|
}
|
||
|
|
||
|
func (um *User) AvatarSize() int {
|
||
|
return 96
|
||
|
}
|
||
|
|
||
|
func (um *User) AvatarText() string {
|
||
|
if um.member.Nick != "" {
|
||
|
return um.member.Nick
|
||
|
}
|
||
|
return um.member.User.Username
|
||
|
}
|
||
|
|
||
|
func (um *User) Avatar() (url string) {
|
||
|
return um.member.User.AvatarURL()
|
||
|
}
|
||
|
|
||
|
func (um *User) MentionInfo() text.Rich {
|
||
|
var content bytes.Buffer
|
||
|
var segment text.Rich
|
||
|
|
||
|
// Write the username if the user has a nickname.
|
||
|
if um.member.Nick != "" {
|
||
|
content.WriteString("Username: ")
|
||
|
content.WriteString(um.member.User.Username)
|
||
|
content.WriteByte('#')
|
||
|
content.WriteString(um.member.User.Discriminator)
|
||
|
content.WriteString("\n\n")
|
||
|
}
|
||
|
|
||
|
// Write extra information if any, but only if we have the guild state.
|
||
|
if len(um.member.RoleIDs) > 0 && um.guild.ID.IsValid() {
|
||
|
// Write a prepended new line, as role writes will always prepend a new
|
||
|
// line. This is to prevent a trailing new line.
|
||
|
formatSectionf(&segment, &content, "Roles")
|
||
|
|
||
|
for _, id := range um.member.RoleIDs {
|
||
|
rl, ok := findRole(um.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 := segutil.WriteStringBuf(&content, "@"+rl.Name)
|
||
|
// But we only add the color if the role has one.
|
||
|
if rgb := rl.Color.Uint32(); rgb > 0 {
|
||
|
segutil.Add(&segment, colored.NewSegment(start, end, rgb))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// End section.
|
||
|
content.WriteString("\n\n")
|
||
|
}
|
||
|
|
||
|
// These information can only be obtained from the state. As such, we check
|
||
|
// if the state is given.
|
||
|
if ningenState, ok := um.state.(*ningen.State); ok {
|
||
|
// Does the user have rich presence? If so, write.
|
||
|
if p, err := um.state.Presence(um.guild.ID, um.member.User.ID); err == nil {
|
||
|
for _, ac := range p.Activities {
|
||
|
formatActivity(&segment, &content, ac)
|
||
|
content.WriteString("\n\n")
|
||
|
}
|
||
|
} else if um.guild.ID.IsValid() {
|
||
|
// If we're still in a guild, then we can ask Discord for that
|
||
|
// member with their presence attached.
|
||
|
ningenState.MemberState.RequestMember(um.guild.ID, um.member.User.ID)
|
||
|
}
|
||
|
|
||
|
// Write the user's note if any.
|
||
|
if note := ningenState.NoteState.Note(um.member.User.ID); note != "" {
|
||
|
formatSectionf(&segment, &content, "Note")
|
||
|
content.WriteRune('\n')
|
||
|
|
||
|
start, end := segutil.WriteStringBuf(&content, note)
|
||
|
segutil.Add(&segment, inline.NewSegment(start, end, text.AttributeMonospace))
|
||
|
|
||
|
content.WriteString("\n\n")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Assign the written content into the text segment and return it after
|
||
|
// trimming the trailing new line.
|
||
|
segment.Content = strings.TrimSuffix(content.String(), "\n")
|
||
|
return segment
|
||
|
}
|