Added additional bonus stuff and bug fixes
This commit is contained in:
parent
af912db554
commit
f7c016f5a1
|
@ -227,7 +227,7 @@ func (ch *Channel) JoinServer(ctx context.Context, ct cchat.MessagesContainer) (
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ct.UpdateMessage(NewMessageUpdateAuthor(msg, member, *g))
|
ct.UpdateMessage(NewMessageUpdateAuthor(msg, member, *g, ch.session))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
@ -472,7 +472,7 @@ func (ch *Channel) TypingSubscribe(ti cchat.TypingIndicator) (func(), error) {
|
||||||
if t.ChannelID != ch.id || t.UserID == ch.session.userID {
|
if t.ChannelID != ch.id || t.UserID == ch.session.userID {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if typer, err := NewTyper(ch.session.Store, t); err == nil {
|
if typer, err := NewTyper(ch.session, t); err == nil {
|
||||||
ti.AddTyper(typer)
|
ti.AddTyper(typer)
|
||||||
}
|
}
|
||||||
}), nil
|
}), nil
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/diamondburned/arikawa/discord"
|
"github.com/diamondburned/arikawa/discord"
|
||||||
"github.com/diamondburned/arikawa/state"
|
|
||||||
"github.com/diamondburned/cchat"
|
"github.com/diamondburned/cchat"
|
||||||
"github.com/diamondburned/cchat-discord/urlutils"
|
"github.com/diamondburned/cchat-discord/urlutils"
|
||||||
"github.com/diamondburned/cchat/text"
|
"github.com/diamondburned/cchat/text"
|
||||||
|
@ -12,13 +11,13 @@ import (
|
||||||
|
|
||||||
const MaxCompletion = 15
|
const MaxCompletion = 15
|
||||||
|
|
||||||
func completionUserEntry(s state.Store, u discord.User, g *discord.Guild) cchat.CompletionEntry {
|
func completionUserEntry(s *Session, u discord.User, g *discord.Guild) cchat.CompletionEntry {
|
||||||
if g != nil {
|
if g != nil {
|
||||||
m, err := s.Member(g.ID, u.ID)
|
m, err := s.Store.Member(g.ID, u.ID)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return cchat.CompletionEntry{
|
return cchat.CompletionEntry{
|
||||||
Raw: u.Mention(),
|
Raw: u.Mention(),
|
||||||
Text: RenderMemberName(*m, *g),
|
Text: RenderMemberName(*m, *g, s),
|
||||||
Secondary: text.Rich{Content: u.Username + "#" + u.Discriminator},
|
Secondary: text.Rich{Content: u.Username + "#" + u.Discriminator},
|
||||||
IconURL: u.AvatarURL(),
|
IconURL: u.AvatarURL(),
|
||||||
}
|
}
|
||||||
|
@ -51,7 +50,7 @@ func (ch *Channel) completeMentions(word string) (entries []cchat.CompletionEntr
|
||||||
|
|
||||||
// Record the current author and add the entry to the list.
|
// Record the current author and add the entry to the list.
|
||||||
authors[msg.Author.ID] = struct{}{}
|
authors[msg.Author.ID] = struct{}{}
|
||||||
entries = append(entries, completionUserEntry(ch.session.Store, msg.Author, g))
|
entries = append(entries, completionUserEntry(ch.session, msg.Author, g))
|
||||||
|
|
||||||
if len(entries) >= MaxCompletion {
|
if len(entries) >= MaxCompletion {
|
||||||
return
|
return
|
||||||
|
@ -108,7 +107,7 @@ func (ch *Channel) completeMentions(word string) (entries []cchat.CompletionEntr
|
||||||
if contains(match, mem.User.Username, mem.Nick) {
|
if contains(match, mem.User.Username, mem.Nick) {
|
||||||
entries = append(entries, cchat.CompletionEntry{
|
entries = append(entries, cchat.CompletionEntry{
|
||||||
Raw: mem.User.Mention(),
|
Raw: mem.User.Mention(),
|
||||||
Text: RenderMemberName(mem, *g),
|
Text: RenderMemberName(mem, *g, ch.session),
|
||||||
Secondary: text.Rich{Content: mem.User.Username + "#" + mem.User.Discriminator},
|
Secondary: text.Rich{Content: mem.User.Username + "#" + mem.User.Discriminator},
|
||||||
IconURL: mem.User.AvatarURL(),
|
IconURL: mem.User.AvatarURL(),
|
||||||
})
|
})
|
||||||
|
|
67
message.go
67
message.go
|
@ -63,7 +63,7 @@ type Author struct {
|
||||||
avatar string
|
avatar string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUser(u discord.User) Author {
|
func NewUser(u discord.User, s *Session) Author {
|
||||||
var name = text.Rich{Content: u.Username}
|
var name = text.Rich{Content: u.Username}
|
||||||
if u.Bot {
|
if u.Bot {
|
||||||
name.Content += " "
|
name.Content += " "
|
||||||
|
@ -73,9 +73,9 @@ func NewUser(u discord.User) Author {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append a clickable user popup.
|
// Append a clickable user popup.
|
||||||
name.Segments = append(name.Segments,
|
useg := segments.UserSegment(0, len(name.Content), u)
|
||||||
segments.UserSegment(0, len(name.Content), u),
|
useg.WithState(s.State)
|
||||||
)
|
name.Segments = append(name.Segments, useg)
|
||||||
|
|
||||||
return Author{
|
return Author{
|
||||||
id: u.ID,
|
id: u.ID,
|
||||||
|
@ -84,15 +84,15 @@ func NewUser(u discord.User) Author {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGuildMember(m discord.Member, g discord.Guild) Author {
|
func NewGuildMember(m discord.Member, g discord.Guild, s *Session) Author {
|
||||||
return Author{
|
return Author{
|
||||||
id: m.User.ID,
|
id: m.User.ID,
|
||||||
name: RenderMemberName(m, g),
|
name: RenderMemberName(m, g, s),
|
||||||
avatar: AvatarURL(m.User.AvatarURL()),
|
avatar: AvatarURL(m.User.AvatarURL()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RenderMemberName(m discord.Member, g discord.Guild) text.Rich {
|
func RenderMemberName(m discord.Member, g discord.Guild, s *Session) text.Rich {
|
||||||
var name = text.Rich{
|
var name = text.Rich{
|
||||||
Content: m.User.Username,
|
Content: m.User.Username,
|
||||||
}
|
}
|
||||||
|
@ -118,9 +118,9 @@ func RenderMemberName(m discord.Member, g discord.Guild) text.Rich {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append a clickable user popup.
|
// Append a clickable user popup.
|
||||||
name.Segments = append(name.Segments,
|
useg := segments.MemberSegment(0, len(name.Content), g, m)
|
||||||
segments.MemberSegment(0, len(name.Content), g, m),
|
useg.WithState(s.State)
|
||||||
)
|
name.Segments = append(name.Segments, useg)
|
||||||
|
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
@ -163,10 +163,12 @@ func NewMessageUpdateContent(msg discord.Message, s *Session) Message {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMessageUpdateAuthor(msg discord.Message, member discord.Member, g discord.Guild) Message {
|
func NewMessageUpdateAuthor(
|
||||||
|
msg discord.Message, member discord.Member, g discord.Guild, s *Session) Message {
|
||||||
|
|
||||||
return Message{
|
return Message{
|
||||||
messageHeader: newHeader(msg),
|
messageHeader: newHeader(msg),
|
||||||
author: NewGuildMember(member, g),
|
author: NewGuildMember(member, g, s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +178,7 @@ func NewMessageCreate(c *gateway.MessageCreateEvent, s *Session) Message {
|
||||||
// This should not error.
|
// This should not error.
|
||||||
g, err := s.Store.Guild(c.GuildID)
|
g, err := s.Store.Guild(c.GuildID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return NewMessage(c.Message, s, NewUser(c.Author))
|
return NewMessage(c.Message, s, NewUser(c.Author, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Member == nil {
|
if c.Member == nil {
|
||||||
|
@ -184,10 +186,10 @@ func NewMessageCreate(c *gateway.MessageCreateEvent, s *Session) Message {
|
||||||
}
|
}
|
||||||
if c.Member == nil {
|
if c.Member == nil {
|
||||||
s.MemberState.RequestMember(c.GuildID, c.Author.ID)
|
s.MemberState.RequestMember(c.GuildID, c.Author.ID)
|
||||||
return NewMessage(c.Message, s, NewUser(c.Author))
|
return NewMessage(c.Message, s, NewUser(c.Author, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewMessage(c.Message, s, NewGuildMember(*c.Member, *g))
|
return NewMessage(c.Message, s, NewGuildMember(*c.Member, *g, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBacklogMessage uses the session to create a message fetched from the
|
// NewBacklogMessage uses the session to create a message fetched from the
|
||||||
|
@ -197,27 +199,52 @@ func NewBacklogMessage(m discord.Message, s *Session, g discord.Guild) Message {
|
||||||
// If the message doesn't have a guild, then we don't need all the
|
// If the message doesn't have a guild, then we don't need all the
|
||||||
// complicated member fetching process.
|
// complicated member fetching process.
|
||||||
if !m.GuildID.Valid() {
|
if !m.GuildID.Valid() {
|
||||||
return NewMessage(m, s, NewUser(m.Author))
|
return NewMessage(m, s, NewUser(m.Author, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
mem, err := s.Store.Member(m.GuildID, m.Author.ID)
|
mem, err := s.Store.Member(m.GuildID, m.Author.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.MemberState.RequestMember(m.GuildID, m.Author.ID)
|
s.MemberState.RequestMember(m.GuildID, m.Author.ID)
|
||||||
return NewMessage(m, s, NewUser(m.Author))
|
return NewMessage(m, s, NewUser(m.Author, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewMessage(m, s, NewGuildMember(*mem, g))
|
return NewMessage(m, s, NewGuildMember(*mem, g, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDirectMessage(m discord.Message, s *Session) Message {
|
func NewDirectMessage(m discord.Message, s *Session) Message {
|
||||||
return NewMessage(m, s, NewUser(m.Author))
|
return NewMessage(m, s, NewUser(m.Author, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMessage(m discord.Message, s *Session, author Author) Message {
|
func NewMessage(m discord.Message, s *Session, author Author) Message {
|
||||||
|
// Render the message content.
|
||||||
|
var content = segments.ParseMessage(&m, s.Store)
|
||||||
|
|
||||||
|
// Request members in mentions if we're in a guild.
|
||||||
|
if m.GuildID.Valid() {
|
||||||
|
for _, segment := range content.Segments {
|
||||||
|
if mention, ok := segment.(*segments.MentionSegment); ok {
|
||||||
|
// If this is not a user mention, then skip.
|
||||||
|
if mention.GuildUser == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we already have a member, then skip. We could check this
|
||||||
|
// using the timestamp, as we might have a user set into the
|
||||||
|
// member field
|
||||||
|
if m := mention.GuildUser.Member; m != nil && m.Joined.Valid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request the member.
|
||||||
|
s.MemberState.RequestMember(m.GuildID, mention.GuildUser.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Message{
|
return Message{
|
||||||
messageHeader: newHeader(m),
|
messageHeader: newHeader(m),
|
||||||
author: author,
|
author: author,
|
||||||
content: segments.ParseMessage(&m, s.Store),
|
content: content,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,9 @@ import (
|
||||||
|
|
||||||
"github.com/diamondburned/arikawa/discord"
|
"github.com/diamondburned/arikawa/discord"
|
||||||
"github.com/diamondburned/arikawa/state"
|
"github.com/diamondburned/arikawa/state"
|
||||||
|
"github.com/diamondburned/cchat-discord/urlutils"
|
||||||
"github.com/diamondburned/cchat/text"
|
"github.com/diamondburned/cchat/text"
|
||||||
|
"github.com/diamondburned/ningen"
|
||||||
"github.com/diamondburned/ningen/md"
|
"github.com/diamondburned/ningen/md"
|
||||||
"github.com/yuin/goldmark/ast"
|
"github.com/yuin/goldmark/ast"
|
||||||
)
|
)
|
||||||
|
@ -18,11 +20,13 @@ type NameSegment struct {
|
||||||
|
|
||||||
guild discord.Guild
|
guild discord.Guild
|
||||||
member discord.Member
|
member discord.Member
|
||||||
|
state *ningen.State // optional
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ text.Segment = (*NameSegment)(nil)
|
_ text.Segment = (*NameSegment)(nil)
|
||||||
_ text.Mentioner = (*NameSegment)(nil)
|
_ text.Mentioner = (*NameSegment)(nil)
|
||||||
|
_ text.MentionerAvatar = (*NameSegment)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
func UserSegment(start, end int, u discord.User) NameSegment {
|
func UserSegment(start, end int, u discord.User) NameSegment {
|
||||||
|
@ -42,12 +46,23 @@ func MemberSegment(start, end int, guild discord.Guild, m discord.Member) NameSe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.state = state
|
||||||
|
}
|
||||||
|
|
||||||
func (m NameSegment) Bounds() (start, end int) {
|
func (m NameSegment) Bounds() (start, end int) {
|
||||||
return m.start, m.end
|
return m.start, m.end
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m NameSegment) MentionInfo() text.Rich {
|
func (m NameSegment) MentionInfo() text.Rich {
|
||||||
return userInfo(m.guild, m.member)
|
return userInfo(m.guild, m.member, m.state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avatar returns the large avatar URL.
|
||||||
|
func (m NameSegment) Avatar() string {
|
||||||
|
return m.member.User.AvatarURL()
|
||||||
}
|
}
|
||||||
|
|
||||||
type MentionSegment struct {
|
type MentionSegment struct {
|
||||||
|
@ -59,9 +74,10 @@ type MentionSegment struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ text.Segment = (*MentionSegment)(nil)
|
_ text.Segment = (*MentionSegment)(nil)
|
||||||
_ text.Colorer = (*MentionSegment)(nil)
|
_ text.Colorer = (*MentionSegment)(nil)
|
||||||
_ text.Mentioner = (*MentionSegment)(nil)
|
_ text.Mentioner = (*MentionSegment)(nil)
|
||||||
|
_ text.MentionerAvatar = (*MentionSegment)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *TextRenderer) mention(n *md.Mention, enter bool) ast.WalkStatus {
|
func (r *TextRenderer) mention(n *md.Mention, enter bool) ast.WalkStatus {
|
||||||
|
@ -133,33 +149,29 @@ func (m MentionSegment) MentionInfo() text.Rich {
|
||||||
return text.Rich{}
|
return text.Rich{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Avatar returns the user avatar if any, else it returns an empty URL.
|
||||||
|
func (m MentionSegment) Avatar() string {
|
||||||
|
if m.GuildUser != nil {
|
||||||
|
return m.GuildUser.AvatarURL()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (m MentionSegment) channelInfo() text.Rich {
|
func (m MentionSegment) channelInfo() text.Rich {
|
||||||
content := strings.Builder{}
|
var topic = m.Channel.Topic
|
||||||
content.WriteByte('#')
|
|
||||||
content.WriteString(m.Channel.Name)
|
|
||||||
|
|
||||||
if m.Channel.NSFW {
|
if m.Channel.NSFW {
|
||||||
content.WriteString(" (NSFW)")
|
topic = "(NSFW)\n" + topic
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.Channel.Topic != "" {
|
if topic == "" {
|
||||||
content.WriteByte('\n')
|
return text.Rich{}
|
||||||
content.WriteString(m.Channel.Topic)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return text.Rich{
|
return Parse([]byte(topic))
|
||||||
Content: content.String(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MentionSegment) userInfo() 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 {
|
if m.GuildUser.Member == nil {
|
||||||
m.GuildUser.Member = &discord.Member{
|
m.GuildUser.Member = &discord.Member{
|
||||||
User: m.GuildUser.User,
|
User: m.GuildUser.User,
|
||||||
|
@ -172,52 +184,67 @@ func (m MentionSegment) userInfo() text.Rich {
|
||||||
g = &discord.Guild{}
|
g = &discord.Guild{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return userInfo(*g, *m.GuildUser.Member)
|
return userInfo(*g, *m.GuildUser.Member, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func userInfo(guild discord.Guild, member discord.Member) text.Rich {
|
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 text.Rich{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type LargeActivityImage struct {
|
||||||
|
start int
|
||||||
|
url string
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLargeActivityImage(start int, ac discord.Activity) LargeActivityImage {
|
||||||
|
var text = ac.Assets.LargeText
|
||||||
|
if text == "" {
|
||||||
|
text = "Activity Image"
|
||||||
|
}
|
||||||
|
|
||||||
|
return LargeActivityImage{
|
||||||
|
start: start,
|
||||||
|
url: urlutils.AssetURL(ac.ApplicationID, ac.Assets.LargeImage),
|
||||||
|
text: ac.Assets.LargeText,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i LargeActivityImage) Bounds() (start, end int) { return i.start, i.start }
|
||||||
|
func (i LargeActivityImage) Image() string { return i.url }
|
||||||
|
func (i LargeActivityImage) ImageSize() (w, h int) { return 60, 60 }
|
||||||
|
func (i LargeActivityImage) ImageText() string { return i.text }
|
||||||
|
|
||||||
|
func userInfo(guild discord.Guild, member discord.Member, state *ningen.State) text.Rich {
|
||||||
var content bytes.Buffer
|
var content bytes.Buffer
|
||||||
var segment text.Rich
|
var segment text.Rich
|
||||||
|
|
||||||
// Make a large avatar if there's one.
|
// Write the username if the user has a nickname.
|
||||||
if member.User.Avatar != "" {
|
|
||||||
segmentadd(&segment, AvatarSegment{
|
|
||||||
start: 0,
|
|
||||||
url: member.User.AvatarURL(), // full URL
|
|
||||||
text: "Avatar",
|
|
||||||
size: 72, // large
|
|
||||||
})
|
|
||||||
// Space out.
|
|
||||||
content.WriteByte(' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the nickname if there's one; else, write the username only.
|
|
||||||
if member.Nick != "" {
|
if member.Nick != "" {
|
||||||
content.WriteString(member.Nick)
|
content.WriteString("Username: ")
|
||||||
content.WriteByte(' ')
|
|
||||||
|
|
||||||
start, end := writestringbuf(&content, fmt.Sprintf(
|
|
||||||
"(%s#%s)",
|
|
||||||
member.User.Username,
|
|
||||||
member.User.Discriminator,
|
|
||||||
))
|
|
||||||
|
|
||||||
segmentadd(&segment, InlineSegment{
|
|
||||||
start: start,
|
|
||||||
end: end,
|
|
||||||
attributes: text.AttrDimmed,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
content.WriteString(member.User.Username)
|
content.WriteString(member.User.Username)
|
||||||
content.WriteByte('#')
|
content.WriteByte('#')
|
||||||
content.WriteString(member.User.Discriminator)
|
content.WriteString(member.User.Discriminator)
|
||||||
|
content.WriteString("\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write extra information if any, but only if we have the guild state.
|
// Write extra information if any, but only if we have the guild state.
|
||||||
if len(member.RoleIDs) > 0 && guild.ID.Valid() {
|
if len(member.RoleIDs) > 0 && guild.ID.Valid() {
|
||||||
// Write a prepended new line, as role writes will always prepend a new
|
// Write a prepended new line, as role writes will always prepend a new
|
||||||
// line. This is to prevent a trailing new line.
|
// line. This is to prevent a trailing new line.
|
||||||
content.WriteString("\n\n--- Roles ---")
|
formatSectionf(&segment, &content, "Roles")
|
||||||
|
|
||||||
for _, id := range member.RoleIDs {
|
for _, id := range member.RoleIDs {
|
||||||
rl, ok := findRole(guild.Roles, id)
|
rl, ok := findRole(guild.Roles, id)
|
||||||
|
@ -234,13 +261,125 @@ func userInfo(guild discord.Guild, member discord.Member) text.Rich {
|
||||||
segmentadd(&segment, NewColoredSegment(start, end, rl.Color.Uint32()))
|
segmentadd(&segment, NewColoredSegment(start, end, rl.Color.Uint32()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// End section.
|
||||||
|
content.WriteString("\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign the written content into the text segment and return it.
|
// These information can only be obtained from the state. As such, we check
|
||||||
segment.Content = content.String()
|
// if the state is given.
|
||||||
|
if state != nil {
|
||||||
|
// Does the user have rich presence? If so, write.
|
||||||
|
if p, err := state.Presence(guild.ID, member.User.ID); err == nil {
|
||||||
|
for _, ac := range p.Activities {
|
||||||
|
formatActivity(&segment, &content, ac)
|
||||||
|
content.WriteString("\n\n")
|
||||||
|
}
|
||||||
|
} else if guild.ID.Valid() {
|
||||||
|
// If we're still in a guild, then we can ask Discord for that
|
||||||
|
// member with their presence attached.
|
||||||
|
state.MemberState.RequestMember(guild.ID, member.User.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the user's note if any.
|
||||||
|
if note := state.NoteState.Note(member.User.ID); note != "" {
|
||||||
|
formatSectionf(&segment, &content, "Note")
|
||||||
|
content.WriteRune('\n')
|
||||||
|
|
||||||
|
start, end := writestringbuf(&content, note)
|
||||||
|
segmentadd(&segment, InlineSegment{start, end, text.AttrMonospace})
|
||||||
|
|
||||||
|
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
|
return segment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatSectionf(segment *text.Rich, content *bytes.Buffer, f string, argv ...interface{}) {
|
||||||
|
// Treat f as a regular string at first.
|
||||||
|
var str = fmt.Sprintf("%s", f)
|
||||||
|
|
||||||
|
// If there are argvs, then treat f as a format string.
|
||||||
|
if len(argv) > 0 {
|
||||||
|
str = fmt.Sprintf(str, argv...)
|
||||||
|
}
|
||||||
|
|
||||||
|
start, end := writestringbuf(content, str)
|
||||||
|
segmentadd(segment, InlineSegment{start, end, text.AttrBold | text.AttrUnderline})
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatActivity(segment *text.Rich, content *bytes.Buffer, ac discord.Activity) {
|
||||||
|
switch ac.Type {
|
||||||
|
case discord.GameActivity:
|
||||||
|
formatSectionf(segment, content, "Playing %s", ac.Name)
|
||||||
|
content.WriteByte('\n')
|
||||||
|
|
||||||
|
case discord.ListeningActivity:
|
||||||
|
formatSectionf(segment, content, "Listening to %s", ac.Name)
|
||||||
|
content.WriteByte('\n')
|
||||||
|
|
||||||
|
case discord.StreamingActivity:
|
||||||
|
formatSectionf(segment, content, "Streaming on %s", ac.Name)
|
||||||
|
content.WriteByte('\n')
|
||||||
|
|
||||||
|
case discord.CustomActivity:
|
||||||
|
formatSectionf(segment, content, "Status")
|
||||||
|
content.WriteByte('\n')
|
||||||
|
|
||||||
|
if ac.Emoji != nil {
|
||||||
|
if !ac.Emoji.ID.Valid() {
|
||||||
|
content.WriteString(ac.Emoji.Name)
|
||||||
|
} else {
|
||||||
|
segmentadd(segment, EmojiSegment{
|
||||||
|
start: content.Len(),
|
||||||
|
name: ac.Emoji.Name,
|
||||||
|
emojiURL: ac.Emoji.EmojiURL() + "&size=64",
|
||||||
|
large: ac.State == "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
content.WriteByte(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
formatSectionf(segment, content, "Status")
|
||||||
|
content.WriteByte('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert an image if there's any.
|
||||||
|
if ac.Assets != nil && ac.Assets.LargeImage != "" {
|
||||||
|
segmentadd(segment, NewLargeActivityImage(content.Len(), ac))
|
||||||
|
content.WriteString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ac.Details != "" {
|
||||||
|
start, end := writestringbuf(content, ac.Details)
|
||||||
|
segmentadd(segment, InlineSegment{start, end, text.AttrBold})
|
||||||
|
content.WriteByte('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
if ac.State != "" {
|
||||||
|
content.WriteString(ac.State)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPresence(state *ningen.State, guildID, userID discord.Snowflake) *discord.Activity {
|
||||||
|
p, err := state.Presence(guildID, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.Activities) > 0 {
|
||||||
|
return &p.Activities[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.Game
|
||||||
|
}
|
||||||
|
|
||||||
func findRole(roles []discord.Role, id discord.Snowflake) (discord.Role, bool) {
|
func findRole(roles []discord.Role, id discord.Snowflake) (discord.Role, bool) {
|
||||||
for _, role := range roles {
|
for _, role := range roles {
|
||||||
if role.ID == id {
|
if role.ID == id {
|
||||||
|
@ -249,19 +388,3 @@ func findRole(roles []discord.Role, id discord.Snowflake) (discord.Role, bool) {
|
||||||
}
|
}
|
||||||
return discord.Role{}, false
|
return discord.Role{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
13
typer.go
13
typer.go
|
@ -5,7 +5,6 @@ import (
|
||||||
|
|
||||||
"github.com/diamondburned/arikawa/discord"
|
"github.com/diamondburned/arikawa/discord"
|
||||||
"github.com/diamondburned/arikawa/gateway"
|
"github.com/diamondburned/arikawa/gateway"
|
||||||
"github.com/diamondburned/arikawa/state"
|
|
||||||
"github.com/diamondburned/cchat"
|
"github.com/diamondburned/cchat"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
@ -24,27 +23,27 @@ func NewTyperAuthor(author Author, ev *gateway.TypingStartEvent) Typer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTyper(store state.Store, ev *gateway.TypingStartEvent) (*Typer, error) {
|
func NewTyper(s *Session, ev *gateway.TypingStartEvent) (*Typer, error) {
|
||||||
if ev.GuildID.Valid() {
|
if ev.GuildID.Valid() {
|
||||||
g, err := store.Guild(ev.GuildID)
|
g, err := s.Store.Guild(ev.GuildID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ev.Member == nil {
|
if ev.Member == nil {
|
||||||
ev.Member, err = store.Member(ev.GuildID, ev.UserID)
|
ev.Member, err = s.Store.Member(ev.GuildID, ev.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Typer{
|
return &Typer{
|
||||||
Author: NewGuildMember(*ev.Member, *g),
|
Author: NewGuildMember(*ev.Member, *g, s),
|
||||||
time: ev.Timestamp,
|
time: ev.Timestamp,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := store.Channel(ev.ChannelID)
|
c, err := s.Store.Channel(ev.ChannelID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -52,7 +51,7 @@ func NewTyper(store state.Store, ev *gateway.TypingStartEvent) (*Typer, error) {
|
||||||
for _, user := range c.DMRecipients {
|
for _, user := range c.DMRecipients {
|
||||||
if user.ID == ev.UserID {
|
if user.ID == ev.UserID {
|
||||||
return &Typer{
|
return &Typer{
|
||||||
Author: NewUser(user),
|
Author: NewUser(user, s),
|
||||||
time: ev.Timestamp,
|
time: ev.Timestamp,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/diamondburned/arikawa/discord"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AvatarURL wraps the URL with URL queries for the avatar.
|
// AvatarURL wraps the URL with URL queries for the avatar.
|
||||||
|
@ -55,3 +57,11 @@ func ExtIs(URL string, exts []string) bool {
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssetURL generates the image URL from the given asset image ID.
|
||||||
|
func AssetURL(appID discord.Snowflake, imageID string) string {
|
||||||
|
if strings.HasPrefix(imageID, "spotify:") {
|
||||||
|
return "https://i.scdn.co/image/" + strings.TrimPrefix(imageID, "spotify:")
|
||||||
|
}
|
||||||
|
return "https://cdn.discordapp.com/app-assets/" + appID.String() + "/" + imageID + ".png"
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue