package matrix
import (
"fmt"
"html"
"net/url"
"strings"
)
// safeHref returns raw if it is an http(s) URL, else "". Keeps feed-supplied
// links from injecting javascript:/data: schemes into the formatted_body href
// (html.EscapeString blocks attribute breakout but not the scheme itself).
func safeHref(raw string) string {
u, err := url.Parse(strings.TrimSpace(raw))
if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
return ""
}
return raw
}
// PostableStory contains the data needed to format and send a story.
type PostableStory struct {
ImageURL string
Headline string
ArticleURL string
Lede string
Source string
Channel string
Platforms []string
}
// formatPost returns plain text and HTML bodies for a Matrix message.
func formatPost(s *PostableStory) (plain, htmlBody string) {
// Plain text body
var plainParts []string
plainParts = append(plainParts, fmt.Sprintf("**%s** \u2192 %s", s.Headline, s.ArticleURL))
if s.Lede != "" {
plainParts = append(plainParts, s.Lede)
}
plainParts = append(plainParts, formatSourceTag(s.Source, s.Platforms, false))
plain = strings.Join(plainParts, "\n")
// HTML body
var htmlParts []string
if href := safeHref(s.ArticleURL); href != "" {
htmlParts = append(htmlParts, fmt.Sprintf(
`%s`,
html.EscapeString(href),
html.EscapeString(s.Headline),
))
} else {
// Non-http(s) link: render the headline without an anchor.
htmlParts = append(htmlParts, fmt.Sprintf(`%s`, html.EscapeString(s.Headline)))
}
if s.Lede != "" {
htmlParts = append(htmlParts, html.EscapeString(s.Lede))
}
htmlParts = append(htmlParts, formatSourceTag(s.Source, s.Platforms, true))
htmlBody = strings.Join(htmlParts, "
")
return plain, htmlBody
}
// formatSourceTag builds the source + platform tags line.
func formatSourceTag(source string, platforms []string, isHTML bool) string {
if isHTML {
parts := []string{fmt.Sprintf("%s", html.EscapeString(strings.ToLower(source)))}
for _, p := range platforms {
parts = append(parts, fmt.Sprintf("%s", html.EscapeString(p)))
}
return strings.Join(parts, " \u00b7 ")
}
parts := []string{fmt.Sprintf("`%s`", strings.ToLower(source))}
for _, p := range platforms {
parts = append(parts, fmt.Sprintf("`%s`", p))
}
return strings.Join(parts, " \u00b7 ")
}