From 96e97aa11705ef7b5cb8959274e93ddc43492455 Mon Sep 17 00:00:00 2001 From: diamondburned Date: Wed, 30 Dec 2020 18:58:36 -0800 Subject: [PATCH] Added message reference read support --- internal/discord/message/author.go | 8 +---- internal/discord/message/message.go | 45 +++++++++++++++++++++++++- internal/segments/md.go | 20 +++++++++--- internal/segments/mention/mention.go | 23 ++++++++++++- internal/segments/mention/user.go | 18 ++++++----- internal/segments/renderer/renderer.go | 10 +++--- 6 files changed, 99 insertions(+), 25 deletions(-) diff --git a/internal/discord/message/author.go b/internal/discord/message/author.go index 674362e..3b65ea4 100644 --- a/internal/discord/message/author.go +++ b/internal/discord/message/author.go @@ -56,13 +56,6 @@ func richMember( start, end = segutil.Write(rich, displayName) - // Update the color. - if c := discord.MemberColor(g, m); c > 0 { - rich.Segments = append(rich.Segments, - colored.NewSegment(start, end, c.Uint32()), - ) - } - // Append the bot prefix if the user is a bot. if m.User.Bot { rich.Content += " " @@ -157,6 +150,7 @@ func (a *Author) AddMessageReference(msgref discord.Message, s *state.Instance) m, err := s.Cabinet.Member(g.ID, msgref.Author.ID) if err != nil { a.addAuthorReference(msgref, s) + s.MemberState.RequestMember(msgref.GuildID, msgref.Author.ID) return } diff --git a/internal/discord/message/message.go b/internal/discord/message/message.go index a1528e8..c61d409 100644 --- a/internal/discord/message/message.go +++ b/internal/discord/message/message.go @@ -9,6 +9,7 @@ import ( "github.com/diamondburned/cchat-discord/internal/discord/state" "github.com/diamondburned/cchat-discord/internal/segments" "github.com/diamondburned/cchat-discord/internal/segments/mention" + "github.com/diamondburned/cchat-discord/internal/segments/reference" "github.com/diamondburned/cchat/text" ) @@ -101,6 +102,11 @@ func NewMessageUpdateContent(msg discord.Message, s *state.Instance) Message { func NewMessageUpdateAuthor( msg discord.Message, member discord.Member, g discord.Guild, s *state.Instance) Message { + author := NewGuildMember(member, g, s) + if ref := ReferencedMessage(msg, s, true); ref != nil { + author.AddMessageReference(*ref, s) + } + return Message{ messageHeader: newHeader(msg), author: NewGuildMember(member, g, s), @@ -156,8 +162,25 @@ func NewDirectMessage(m discord.Message, s *state.Instance) Message { } func NewMessage(m discord.Message, s *state.Instance, author Author) Message { + var content text.Rich + + if ref := ReferencedMessage(m, s, true); ref != nil { + // TODO: markup support + var refmsg = "> " + ref.Content + if len(refmsg) > 120 { + refmsg = refmsg[:120] + "..." + } + + content.Content = refmsg + "\n" + content.Segments = []text.Segment{ + reference.NewMessageSegment(0, len(refmsg), ref.ID), + } + + author.AddMessageReference(*ref, s) + } + // Render the message content. - var content = segments.ParseMessage(&m, s.Cabinet) + segments.ParseMessageRich(&content, &m, s.Cabinet) // Request members in mentions if we're in a guild. if m.GuildID.IsValid() { @@ -206,3 +229,23 @@ func (m Message) Nonce() string { func (m Message) Mentioned() bool { return m.mentioned } + +// ReferencedMessage searches for the referenced message if needed. +func ReferencedMessage(m discord.Message, s *state.Instance, wait bool) (reply *discord.Message) { + if m.ReferencedMessage != nil { + return m.ReferencedMessage + } + + // Deleted or does not exist. + if m.Reference == nil || !m.Reference.MessageID.IsValid() { + return nil + } + + if !wait { + reply, _ = s.Cabinet.Message(m.Reference.ChannelID, m.Reference.MessageID) + } else { + reply, _ = s.Message(m.Reference.ChannelID, m.Reference.MessageID) + } + + return +} diff --git a/internal/segments/md.go b/internal/segments/md.go index 15d49aa..9ca242f 100644 --- a/internal/segments/md.go +++ b/internal/segments/md.go @@ -18,10 +18,19 @@ import ( ) func ParseMessage(m *discord.Message, s store.Cabinet) text.Rich { + var rich text.Rich + ParseMessageRich(&rich, m, s) + return rich +} + +func ParseMessageRich(rich *text.Rich, m *discord.Message, s store.Cabinet) { var content = []byte(m.Content) var node = md.ParseWithMessage(content, s, m, true) r := renderer.New(content, node) + r.Buffer.Grow(len(rich.Content)) + r.Buffer.WriteString(rich.Content) + // Register the needed states for some renderers. r.WithState(m, s) // Render the main message body. @@ -30,13 +39,16 @@ func ParseMessage(m *discord.Message, s store.Cabinet) text.Rich { embed.RenderAttachments(r, m.Attachments) embed.RenderEmbeds(r, m.Embeds, m, s) - return text.Rich{ - Content: r.String(), - Segments: r.Segments, - } + rich.Content = r.String() + rich.Segments = append(rich.Segments, r.Segments...) } func ParseWithMessage(b []byte, m *discord.Message, s store.Cabinet, msg bool) text.Rich { node := md.ParseWithMessage(b, s, m, msg) return renderer.RenderNode(b, node) } + +func ParseWithMessageRich(b []byte, m *discord.Message, s store.Cabinet, msg bool) text.Rich { + node := md.ParseWithMessage(b, s, m, msg) + return renderer.RenderNode(b, node) +} diff --git a/internal/segments/mention/mention.go b/internal/segments/mention/mention.go index 7bfa438..c1ea544 100644 --- a/internal/segments/mention/mention.go +++ b/internal/segments/mention/mention.go @@ -1,6 +1,7 @@ package mention import ( + "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/cchat-discord/internal/segments/renderer" "github.com/diamondburned/cchat/text" "github.com/diamondburned/cchat/utils/empty" @@ -55,7 +56,7 @@ func (s Segment) Bounds() (start, end int) { func (s Segment) AsColorer() text.Colorer { switch { - case s.User != nil: + case s.User != nil && s.User.HasColor(): return s.User case s.Role != nil: return s.Role @@ -81,3 +82,23 @@ func (s Segment) AsMentioner() text.Mentioner { } return nil } + +func MemberColor(guild discord.Guild, member discord.Member) (c uint32, ok bool) { + var pos int + + for _, r := range guild.Roles { + for _, mr := range member.RoleIDs { + if mr != r.ID { + continue + } + + if r.Color > 0 && r.Position > pos { + c = r.Color.Uint32() + ok = true + pos = r.Position + } + } + } + + return +} diff --git a/internal/segments/mention/user.go b/internal/segments/mention/user.go index 12ed841..b5e1968 100644 --- a/internal/segments/mention/user.go +++ b/internal/segments/mention/user.go @@ -9,6 +9,7 @@ import ( "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-discord/internal/urlutils" "github.com/diamondburned/cchat/text" "github.com/diamondburned/cchat/utils/empty" "github.com/diamondburned/ningen/v2" @@ -63,7 +64,7 @@ func (m NameSegment) AsAvatarer() text.Avatarer { return &m.um } // AsColorer only returns User if the user actually has a colored role. func (m NameSegment) AsColorer() text.Colorer { if m.um.HasColor() { - return &m.um + return m.um } return nil } @@ -82,8 +83,7 @@ var ( _ text.Mentioner = (*User)(nil) ) -// NewUser creates a new user mention. If state is of type *ningen.State, then -// it'll fetch additional information asynchronously. +// NewUser creates a new user mention. func NewUser(store store.Cabinet, guild discord.GuildID, guser discord.GuildUser) *User { if guser.Member == nil { m, err := store.Member(guild, guser.ID) @@ -111,6 +111,7 @@ func NewUser(store store.Cabinet, guild discord.GuildID, guser discord.GuildUser func (um *User) WithState(state *ningen.State) { um.ningen = state + um.store = state.Cabinet } // HasColor returns true if the current user has a color. @@ -125,7 +126,8 @@ func (um User) HasColor() bool { return false } - return discord.MemberColor(*g, um.Member) > 0 + _, ok := MemberColor(*g, um.Member) + return ok } func (um User) Color() uint32 { @@ -134,12 +136,12 @@ func (um User) Color() uint32 { return colored.Blurple } - var color = discord.MemberColor(*g, um.Member) - if color == 0 { + color, ok := MemberColor(*g, um.Member) + if !ok { return colored.Blurple } - return text.SolidColor(color.Uint32()) + return text.SolidColor(color) } func (um User) AvatarSize() int { @@ -154,7 +156,7 @@ func (um User) AvatarText() string { } func (um User) Avatar() (url string) { - return um.Member.User.AvatarURL() + return urlutils.AvatarURL(um.Member.User.AvatarURL()) } func (um User) MentionInfo() text.Rich { diff --git a/internal/segments/renderer/renderer.go b/internal/segments/renderer/renderer.go index 0a831d7..27bd47c 100644 --- a/internal/segments/renderer/renderer.go +++ b/internal/segments/renderer/renderer.go @@ -21,6 +21,8 @@ func Register(kind ast.NodeKind, r Renderer) { renderers[kind] = r } +var smallRenderers = map[ast.NodeKind]Renderer{} + // Parse parses the raw Markdown bytes into a rich text. func Parse(b []byte) text.Rich { node := md.Parse(b) @@ -188,10 +190,10 @@ func (r *Text) RenderNode(n ast.Node, enter bool) (ast.WalkStatus, error) { switch n := n.(type) { case *ast.Document: case *ast.Paragraph: - if !enter { - // TODO: investigate - // r.Buffer.WriteByte('\n') - } + // if !enter { + // TODO: investigate + // r.Buffer.WriteByte('\n') + // } case *ast.String: if enter { r.Buffer.Write(n.Value)