mirror of
https://github.com/owncast/owncast.git
synced 2024-12-18 15:23:55 +03:00
8a74af202d
* update message viz in db * create admin endpoint to update message visibility * convert UpdateMessageVisibility api to take in an array of IDs to change visibility on instead * Support requesting filtered or unfiltered chat messages * Handle UPDATE chat events on front and backend for toggling messages * Return entire message with UPDATE events * Remove the UPDATE message type * Revert "Remove the UPDATE message type" This reverts commit 3a83df3d492f7ecf2bab65e845aa2b0365d3a7f6. * update -> visibility update * completely remove messages when they turn hidden on VISIBILITY-UPDATEs, and insert them if they turn visible * Explicitly set visibility * Fix multi-id sql updates * increate scroll buffer a bit so chat scrolls when new large messages come in * Add automated test around chat moderation * Add new chat admin APIs to api spec * Commit updated API documentation Co-authored-by: Gabe Kangas <gabek@real-ity.com> Co-authored-by: Owncast <owncast@owncast.online>
115 lines
2.8 KiB
Go
115 lines
2.8 KiB
Go
package models
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/microcosm-cc/bluemonday"
|
|
"github.com/yuin/goldmark"
|
|
"github.com/yuin/goldmark/extension"
|
|
"github.com/yuin/goldmark/renderer/html"
|
|
"mvdan.cc/xurls"
|
|
)
|
|
|
|
// ChatEvent represents a single chat message.
|
|
type ChatEvent struct {
|
|
ClientID string `json:"-"`
|
|
|
|
Author string `json:"author,omitempty"`
|
|
Body string `json:"body,omitempty"`
|
|
ID string `json:"id"`
|
|
MessageType string `json:"type"`
|
|
Visible bool `json:"visible"`
|
|
Timestamp time.Time `json:"timestamp,omitempty"`
|
|
}
|
|
|
|
// Valid checks to ensure the message is valid.
|
|
func (m ChatEvent) Valid() bool {
|
|
return m.Author != "" && m.Body != "" && m.ID != ""
|
|
}
|
|
|
|
// RenderAndSanitizeMessageBody will turn markdown into HTML, sanitize raw user-supplied HTML and standardize
|
|
// the message into something safe and renderable for clients.
|
|
func (m *ChatEvent) RenderAndSanitizeMessageBody() {
|
|
raw := m.Body
|
|
|
|
// Set the new, sanitized and rendered message body
|
|
m.Body = RenderAndSanitize(raw)
|
|
}
|
|
|
|
// RenderAndSanitize will turn markdown into HTML, sanitize raw user-supplied HTML and standardize
|
|
// the message into something safe and renderable for clients.
|
|
func RenderAndSanitize(raw string) string {
|
|
rendered := renderMarkdown(raw)
|
|
safe := sanitize(rendered)
|
|
|
|
// Set the new, sanitized and rendered message body
|
|
return strings.TrimSpace(safe)
|
|
}
|
|
|
|
func renderMarkdown(raw string) string {
|
|
markdown := goldmark.New(
|
|
goldmark.WithRendererOptions(
|
|
html.WithUnsafe(),
|
|
),
|
|
goldmark.WithExtensions(
|
|
extension.NewLinkify(
|
|
extension.WithLinkifyAllowedProtocols([][]byte{
|
|
[]byte("http:"),
|
|
[]byte("https:"),
|
|
}),
|
|
extension.WithLinkifyURLRegexp(
|
|
xurls.Strict,
|
|
),
|
|
),
|
|
),
|
|
)
|
|
|
|
trimmed := strings.TrimSpace(raw)
|
|
var buf bytes.Buffer
|
|
if err := markdown.Convert([]byte(trimmed), &buf); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
func sanitize(raw string) string {
|
|
p := bluemonday.StrictPolicy()
|
|
|
|
// Require URLs to be parseable by net/url.Parse
|
|
p.AllowStandardURLs()
|
|
|
|
// Allow links
|
|
p.AllowAttrs("href").OnElements("a")
|
|
|
|
// Force all URLs to have "noreferrer" in their rel attribute.
|
|
p.RequireNoReferrerOnLinks(true)
|
|
|
|
// Links will get target="_blank" added to them.
|
|
p.AddTargetBlankToFullyQualifiedLinks(true)
|
|
|
|
// Allow paragraphs
|
|
p.AllowElements("br")
|
|
p.AllowElements("p")
|
|
|
|
// Allow img tags
|
|
p.AllowElements("img")
|
|
p.AllowAttrs("src").OnElements("img")
|
|
p.AllowAttrs("alt").OnElements("img")
|
|
p.AllowAttrs("title").OnElements("img")
|
|
|
|
// Custom emoji have a class already specified.
|
|
// We should only allow classes on emoji, not *all* imgs.
|
|
// But TODO.
|
|
p.AllowAttrs("class").OnElements("img")
|
|
|
|
// Allow bold
|
|
p.AllowElements("strong")
|
|
|
|
// Allow emphasis
|
|
p.AllowElements("em")
|
|
|
|
return p.Sanitize(raw)
|
|
}
|