The source tag credits an outlet Pete is relaying (`ars technica`). On his own reporting it rendered as a trailing `pete` under a message he already sent - him signing his own name. Drop Source on adventure posts and omit the tag line entirely when there's nothing to credit; RSS posts keep theirs.
89 lines
2.5 KiB
Go
89 lines
2.5 KiB
Go
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)
|
|
}
|
|
if tag := formatSourceTag(s.Source, s.Platforms, false); tag != "" {
|
|
plainParts = append(plainParts, tag)
|
|
}
|
|
plain = strings.Join(plainParts, "\n")
|
|
|
|
// HTML body
|
|
var htmlParts []string
|
|
if href := safeHref(s.ArticleURL); href != "" {
|
|
htmlParts = append(htmlParts, fmt.Sprintf(
|
|
`<strong><a href="%s">%s</a></strong>`,
|
|
html.EscapeString(href),
|
|
html.EscapeString(s.Headline),
|
|
))
|
|
} else {
|
|
// Non-http(s) link: render the headline without an anchor.
|
|
htmlParts = append(htmlParts, fmt.Sprintf(`<strong>%s</strong>`, html.EscapeString(s.Headline)))
|
|
}
|
|
if s.Lede != "" {
|
|
htmlParts = append(htmlParts, html.EscapeString(s.Lede))
|
|
}
|
|
if tag := formatSourceTag(s.Source, s.Platforms, true); tag != "" {
|
|
htmlParts = append(htmlParts, tag)
|
|
}
|
|
htmlBody = strings.Join(htmlParts, "<br/>")
|
|
|
|
return plain, htmlBody
|
|
}
|
|
|
|
// formatSourceTag builds the source + platform tags line. An empty source is
|
|
// omitted rather than tagged: Pete's own reporting has no outlet to credit, and
|
|
// an empty tag would read as him signing his own name.
|
|
func formatSourceTag(source string, platforms []string, isHTML bool) string {
|
|
var parts []string
|
|
if source != "" {
|
|
parts = append(parts, strings.ToLower(source))
|
|
}
|
|
parts = append(parts, platforms...)
|
|
if len(parts) == 0 {
|
|
return ""
|
|
}
|
|
for i, p := range parts {
|
|
if isHTML {
|
|
parts[i] = fmt.Sprintf("<code>%s</code>", html.EscapeString(p))
|
|
} else {
|
|
parts[i] = fmt.Sprintf("`%s`", p)
|
|
}
|
|
}
|
|
return strings.Join(parts, " \u00b7 ")
|
|
}
|