Skip to content

Commit

Permalink
working parser
Browse files Browse the repository at this point in the history
  • Loading branch information
rusq committed Mar 25, 2024
1 parent babac09 commit 8fafda8
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 63 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ require (
github.com/containerd/console v1.0.4 // indirect
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/denisbrodbeck/machineid v1.0.1 // indirect
github.com/enescakir/emoji v1.0.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
github.com/go-stack/stack v1.8.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80N
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog=
github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
Expand Down
5 changes: 3 additions & 2 deletions internal/viewer/renderer/debug.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package renderer

import (
"context"
"encoding/json"
"html"
"html/template"
Expand All @@ -10,11 +11,11 @@ import (

type Debug struct{}

func (d *Debug) RenderText(s string) (v template.HTML) {
func (d *Debug) RenderText(ctx context.Context, s string) (v template.HTML) {
return template.HTML("<pre>" + html.EscapeString(s) + "</pre>")
}

func (d *Debug) Render(m *slack.Message) (v template.HTML) {
func (d *Debug) Render(ctx context.Context, m *slack.Message) (v template.HTML) {
b, err := json.MarshalIndent(m, "", " ")
if err != nil {
panic(err)
Expand Down
5 changes: 3 additions & 2 deletions internal/viewer/renderer/renderer.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package renderer

import (
"context"
"html/template"

"github.com/rusq/slack"
)

type Renderer interface {
RenderText(s string) (v template.HTML)
Render(m *slack.Message) (v template.HTML)
RenderText(ctx context.Context, s string) (v template.HTML)
Render(ctx context.Context, m *slack.Message) (v template.HTML)
}
50 changes: 38 additions & 12 deletions internal/viewer/renderer/slack.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package renderer

import (
"context"
"encoding/json"
"fmt"
"html"
Expand All @@ -15,16 +16,41 @@ import (

const debug = true

type Slack struct{}
type Slack struct {
uu map[string]slack.User // map of user id to user
cc map[string]slack.Channel // map of channel id to channel
}

type SlackOption func(*Slack)

func WithUsers(uu map[string]slack.User) SlackOption {
return func(sm *Slack) {
sm.uu = uu
}
}

func WithChannels(cc map[string]slack.Channel) SlackOption {
return func(sm *Slack) {
sm.cc = cc
}
}

func NewSlack(opts ...SlackOption) *Slack {
s := &Slack{}
for _, opt := range opts {
opt(s)
}
return s
}

func (*Slack) RenderText(s string) (v template.HTML) {
func (*Slack) RenderText(ctx context.Context, s string) (v template.HTML) {
// TODO parse legacy markdown
return template.HTML("<pre>" + html.EscapeString(s) + "</pre>")
}

func (sm *Slack) Render(m *slack.Message) (v template.HTML) {
func (s *Slack) Render(ctx context.Context, m *slack.Message) (v template.HTML) {
if len(m.Blocks.BlockSet) == 0 {
return sm.RenderText(m.Text)
return s.RenderText(ctx, m.Text)
}

attrMsgID := slog.String("message_ts", m.Timestamp)
Expand All @@ -33,17 +59,17 @@ func (sm *Slack) Render(m *slack.Message) (v template.HTML) {
for _, b := range m.Blocks.BlockSet {
fn, ok := blockAction[b.BlockType()]
if !ok {
slog.Warn("unhandled block type", "block_type", b.BlockType(), attrMsgID)
slog.WarnContext(ctx, "unhandled block type", "block_type", b.BlockType(), attrMsgID)
maybeprint(b)
continue
}
s, err := fn(b)
html, err := fn(s, b)
if err != nil {
slog.Error("error rendering block", "error", err, "block_type", b.BlockType(), attrMsgID)
slog.ErrorContext(ctx, "error rendering block", "error", err, "block_type", b.BlockType(), attrMsgID)
maybeprint(b)
continue
}
buf.WriteString(string(s))
buf.WriteString(html)
}
return template.HTML(buf.String())
}
Expand All @@ -57,10 +83,10 @@ func maybeprint(b slack.Block) {
}
}

var blockAction = map[slack.MessageBlockType]func(slack.Block) (string, error){
slack.MBTRichText: mbtRichText,
slack.MBTImage: mbtImage,
slack.MBTContext: mbtContext,
var blockAction = map[slack.MessageBlockType]func(*Slack, slack.Block) (string, error){
slack.MBTRichText: (*Slack).mbtRichText,
slack.MBTImage: (*Slack).mbtImage,
slack.MBTContext: (*Slack).mbtContext,
}

const stackframe = 1
Expand Down
14 changes: 7 additions & 7 deletions internal/viewer/renderer/slack_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/rusq/slack"
)

func mbtContext(ib slack.Block) (string, error) {
func (s *Slack) mbtContext(ib slack.Block) (string, error) {
b, ok := ib.(*slack.ContextBlock)
if !ok {
return "", NewErrIncorrectType(&slack.ContextBlock{}, ib)
Expand All @@ -18,7 +18,7 @@ func mbtContext(ib slack.Block) (string, error) {
if !ok {
return "", NewErrMissingHandler(el.MixedElementType())
}
s, err := fn(el)
s, err := fn(s, el)
if err != nil {
return "", err
}
Expand All @@ -28,20 +28,20 @@ func mbtContext(ib slack.Block) (string, error) {
return buf.String(), nil
}

var contextElementHandlers = map[slack.MixedElementType]func(slack.MixedElement) (string, error){
slack.MixedElementImage: metImage,
slack.MixedElementText: metText,
var contextElementHandlers = map[slack.MixedElementType]func(*Slack, slack.MixedElement) (string, error){
slack.MixedElementImage: (*Slack).metImage,
slack.MixedElementText: (*Slack).metText,
}

func metImage(ie slack.MixedElement) (string, error) {
func (*Slack) metImage(ie slack.MixedElement) (string, error) {
e, ok := ie.(*slack.ImageBlockElement)
if !ok {
return "", NewErrIncorrectType(&slack.ImageBlockElement{}, ie)
}
return fmt.Sprintf(`<img src="%s" alt="%s">`, e.ImageURL, e.AltText), nil
}

func metText(ie slack.MixedElement) (string, error) {
func (*Slack) metText(ie slack.MixedElement) (string, error) {
e, ok := ie.(*slack.TextBlockObject)
if !ok {
return "", NewErrIncorrectType(&slack.TextBlockObject{}, ie)
Expand Down
4 changes: 2 additions & 2 deletions internal/viewer/renderer/slack_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import (
"github.com/rusq/slack"
)

func mbtImage(ib slack.Block) (string, error) {
func (*Slack) mbtImage(ib slack.Block) (string, error) {
b, ok := ib.(*slack.ImageBlock)
if !ok {
return "", NewErrIncorrectType(&slack.ImageBlock{}, ib)
}
return fmt.Sprintf(`<img src="%s" alt="%s">`, b.ImageURL, b.AltText), nil
return fmt.Sprintf(`<figure><img src="%[1]s" alt="%[2]s"><figcaption>%[2]s</figcaption></figure>`, b.ImageURL, b.AltText), nil
}
79 changes: 50 additions & 29 deletions internal/viewer/renderer/slack_rich_text.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@ package renderer

import (
"fmt"
"log/slog"
"strings"

emj "github.com/enescakir/emoji"
"github.com/rusq/slack"
)

var rteTypeHandlers = map[slack.RichTextElementType]func(slack.RichTextElement) (string, error){}
var rteTypeHandlers = map[slack.RichTextElementType]func(*Slack, slack.RichTextElement) (string, error){}

func init() {
rteTypeHandlers[slack.RTESection] = rteSection
rteTypeHandlers[slack.RTEList] = rteList
rteTypeHandlers[slack.RTEQuote] = rteQuote
rteTypeHandlers[slack.RTEPreformatted] = rtePreformatted
rteTypeHandlers[slack.RTESection] = (*Slack).rteSection
rteTypeHandlers[slack.RTEList] = (*Slack).rteList
rteTypeHandlers[slack.RTEQuote] = (*Slack).rteQuote
rteTypeHandlers[slack.RTEPreformatted] = (*Slack).rtePreformatted
}

func mbtRichText(ib slack.Block) (string, error) {
func (s *Slack) mbtRichText(ib slack.Block) (string, error) {
b, ok := ib.(*slack.RichTextBlock)
if !ok {
return "", NewErrIncorrectType(&slack.RichTextBlock{}, ib)
Expand All @@ -27,7 +29,7 @@ func mbtRichText(ib slack.Block) (string, error) {
if !ok {
return "", NewErrMissingHandler(el.RichTextElementType())
}
s, err := fn(el)
s, err := fn(s, el)
if err != nil {
return "", err
}
Expand All @@ -37,7 +39,7 @@ func mbtRichText(ib slack.Block) (string, error) {
return buf.String(), nil
}

func rteSection(ie slack.RichTextElement) (string, error) {
func (s *Slack) rteSection(ie slack.RichTextElement) (string, error) {
e, ok := ie.(*slack.RichTextSection)
if !ok {
return "", NewErrIncorrectType(&slack.RichTextSection{}, ie)
Expand All @@ -48,7 +50,7 @@ func rteSection(ie slack.RichTextElement) (string, error) {
if !ok {
return "", NewErrMissingHandler(el.RichTextSectionElementType())
}
s, err := fn(el)
s, err := fn(s, el)
if err != nil {
return "", err
}
Expand All @@ -58,15 +60,15 @@ func rteSection(ie slack.RichTextElement) (string, error) {
return buf.String(), nil
}

var rtseHandlers = map[slack.RichTextSectionElementType]func(slack.RichTextSectionElement) (string, error){
slack.RTSEText: rtseText,
slack.RTSELink: rtseLink,
slack.RTSEUser: rtseUser,
slack.RTSEEmoji: rtseEmoji,
slack.RTSEChannel: rtseChannel,
var rtseHandlers = map[slack.RichTextSectionElementType]func(*Slack, slack.RichTextSectionElement) (string, error){
slack.RTSEText: (*Slack).rtseText,
slack.RTSELink: (*Slack).rtseLink,
slack.RTSEUser: (*Slack).rtseUser,
slack.RTSEEmoji: (*Slack).rtseEmoji,
slack.RTSEChannel: (*Slack).rtseChannel,
}

func rtseText(ie slack.RichTextSectionElement) (string, error) {
func (s *Slack) rtseText(ie slack.RichTextSectionElement) (string, error) {
e, ok := ie.(*slack.RichTextSectionTextElement)
if !ok {
return "", NewErrIncorrectType(&slack.RichTextSectionTextElement{}, ie)
Expand Down Expand Up @@ -95,7 +97,7 @@ func applyStyle(s string, style *slack.RichTextSectionTextStyle) string {
return s
}

func rtseLink(ie slack.RichTextSectionElement) (string, error) {
func (s *Slack) rtseLink(ie slack.RichTextSectionElement) (string, error) {
e, ok := ie.(*slack.RichTextSectionLinkElement)
if !ok {
return "", NewErrIncorrectType(&slack.RichTextSectionLinkElement{}, ie)
Expand All @@ -106,7 +108,7 @@ func rtseLink(ie slack.RichTextSectionElement) (string, error) {
return fmt.Sprintf("<a href=\"%s\">%s</a>", e.URL, e.Text), nil
}

func rteList(ie slack.RichTextElement) (string, error) {
func (s *Slack) rteList(ie slack.RichTextElement) (string, error) {
e, ok := ie.(*slack.RichTextList)
if !ok {
return "", NewErrIncorrectType(&slack.RichTextList{}, ie)
Expand All @@ -128,7 +130,7 @@ func rteList(ie slack.RichTextElement) (string, error) {
if !ok {
return "", NewErrMissingHandler(el.RichTextElementType())
}
s, err := fn(el)
s, err := fn(s, el)
if err != nil {
return "", err
}
Expand All @@ -138,7 +140,7 @@ func rteList(ie slack.RichTextElement) (string, error) {
return buf.String(), nil
}

func rteQuote(ie slack.RichTextElement) (string, error) {
func (s *Slack) rteQuote(ie slack.RichTextElement) (string, error) {
e, ok := ie.(*slack.RichTextQuote)
if !ok {
return "", NewErrIncorrectType(&slack.RichTextQuote{}, ie)
Expand All @@ -150,7 +152,7 @@ func rteQuote(ie slack.RichTextElement) (string, error) {
if !ok {
return "", NewErrMissingHandler(el.RichTextSectionElementType())
}
s, err := fn(el)
s, err := fn(s, el)
if err != nil {
return "", err
}
Expand All @@ -160,7 +162,7 @@ func rteQuote(ie slack.RichTextElement) (string, error) {
return buf.String(), nil
}

func rtePreformatted(ie slack.RichTextElement) (string, error) {
func (s *Slack) rtePreformatted(ie slack.RichTextElement) (string, error) {
e, ok := ie.(*slack.RichTextPreformatted)
if !ok {
return "", NewErrIncorrectType(&slack.RichTextPreformatted{}, ie)
Expand All @@ -172,7 +174,7 @@ func rtePreformatted(ie slack.RichTextElement) (string, error) {
if !ok {
return "", NewErrMissingHandler(el.RichTextSectionElementType())
}
s, err := fn(el)
s, err := fn(s, el)
if err != nil {
return "", err
}
Expand All @@ -182,28 +184,47 @@ func rtePreformatted(ie slack.RichTextElement) (string, error) {
return buf.String(), nil
}

func rtseUser(ie slack.RichTextSectionElement) (string, error) {
func (s *Slack) rtseUser(ie slack.RichTextSectionElement) (string, error) {
e, ok := ie.(*slack.RichTextSectionUserElement)
if !ok {
return "", NewErrIncorrectType(&slack.RichTextSectionUserElement{}, ie)
}
var name string
u, ok := s.uu[e.UserID]
if ok {
name = u.Name
} else {
slog.Warn("user not found", "user_id", e.UserID, "user", u)
name = e.UserID
}

// TODO: link user.
return applyStyle(fmt.Sprintf("<@%s>", e.UserID), e.Style), nil
return applyStyle(fmt.Sprintf("<@%s>", name), e.Style), nil
}

func rtseEmoji(ie slack.RichTextSectionElement) (string, error) {
func (s *Slack) rtseEmoji(ie slack.RichTextSectionElement) (string, error) {
e, ok := ie.(*slack.RichTextSectionEmojiElement)
if !ok {
return "", NewErrIncorrectType(&slack.RichTextSectionEmojiElement{}, ie)
}
// TODO: resolve and render emoji.
return applyStyle(fmt.Sprintf(":%s:", e.Name), e.Style), nil
em := emj.Parse(fmt.Sprintf(":%s:", e.Name))
return applyStyle(em, e.Style), nil
}

func rtseChannel(ie slack.RichTextSectionElement) (string, error) {
func (s *Slack) rtseChannel(ie slack.RichTextSectionElement) (string, error) {
e, ok := ie.(*slack.RichTextSectionChannelElement)
if !ok {
return "", NewErrIncorrectType(&slack.RichTextSectionChannelElement{}, ie)
}
return applyStyle(fmt.Sprintf("<#%s>", e.ChannelID), e.Style), nil
var name string
c, ok := s.uu[e.ChannelID]
if ok {
name = c.Name
} else {
slog.Warn("channel not found", "channel_id", e.ChannelID)
name = e.ChannelID
}

return applyStyle(fmt.Sprintf("<#%s>", name), e.Style), nil
}
Loading

0 comments on commit 8fafda8

Please sign in to comment.