commit 345aadaf1235880291ce03aa14f81c4c2382b0da
parent 775b77b2d918a1d249d82ea5ff764fbb3be0aed4
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Mon, 1 Jan 2024 11:32:41 -0500
move code, better process message
Diffstat:
4 files changed, 474 insertions(+), 446 deletions(-)
diff --git a/pkg/database/renderer.go b/pkg/database/renderer.go
@@ -3,19 +3,27 @@ package database
import (
"dkforest/pkg/bfchroma"
bf "dkforest/pkg/blackfriday/v2"
+ "dkforest/pkg/clockwork"
+ "dkforest/pkg/config"
+ "dkforest/pkg/utils"
+ "fmt"
"github.com/alecthomas/chroma/formatters/html"
html2 "html"
"io"
+ "net/url"
"regexp"
"strings"
+ "time"
)
-func MyRenderer(db *DkfDB, withLineNumbers, lineNumbersInTable bool) *Renderer {
+func MyRenderer(db *DkfDB, withLineNumbers, lineNumbersInTable bool, roomID RoomID, authUserID UserID) *Renderer {
// Defines the HTML rendering flags that are used
var flags = bf.UseXHTML | bf.SkipImages
r := &Renderer{
- DB: db,
+ RoomID: roomID,
+ AuthUserID: authUserID,
+ DB: db,
Base: bfchroma.NewRenderer(
bfchroma.WithoutAutodetect(),
bfchroma.ChromaOptions(
@@ -54,29 +62,468 @@ func MyRendererForum(withLineNumbers, lineNumbersInTable bool) *Renderer {
}
type Renderer struct {
- DB *DkfDB
- Base *bfchroma.Renderer
+ DB *DkfDB
+ Base *bfchroma.Renderer
+ RoomID RoomID
+ AuthUserID UserID
}
var roomNameF = `\w{3,50}`
var roomTagRgx = regexp.MustCompile(`#(` + roomNameF + `)`)
+func linkRoomTags(db *DkfDB, html string) string {
+ if roomTagRgx.MatchString(html) {
+ html = roomTagRgx.ReplaceAllStringFunc(html, func(s string) string {
+ if room, err := db.GetChatRoomByName(strings.TrimPrefix(s, "#")); err == nil {
+ return `<a href="/chat/` + room.Name + `" target="_top">` + s + `</a>`
+ }
+ return s
+ })
+ }
+ return html
+}
+
+func convertBangShortcuts(html string) string {
+ r := strings.NewReplacer(
+ "!bhc", config.BhcOnion,
+ "!cryptbb", config.CryptbbOnion,
+ "!dread", config.DreadOnion,
+ "!dkf", config.DkfOnion,
+ "!rroom", config.DkfOnion+`/red-room`,
+ "!dnmx", config.DnmxOnion,
+ "!whonix", config.WhonixOnion,
+ "!age", config.AgeUrl,
+ )
+ return r.Replace(html)
+}
+
+// Convert timestamps such as 01:23:45 to an archive link if a message with that timestamp exists.
+// eg: "Some text 14:31:46 some more text"
+func convertArchiveLinks(db *DkfDB, html string, roomID RoomID, authUserID UserID) string {
+ start, rest := "", html
+
+ // Do not replace timestamps that are inside a quote text
+ const quoteSuffix = `”`
+ endOfQuoteIdx := strings.LastIndex(html, quoteSuffix)
+ if endOfQuoteIdx != -1 {
+ start, rest = html[:endOfQuoteIdx], html[endOfQuoteIdx:]
+ }
+
+ archiveRgx := regexp.MustCompile(`(\d{2}-\d{2} )?\d{2}:\d{2}:\d{2}`)
+ if archiveRgx.MatchString(rest) {
+ rest = archiveRgx.ReplaceAllStringFunc(rest, func(s string) string {
+ var dt time.Time
+ var err error
+ if len(s) == 8 { // HH:MM:SS
+ dt, err = utils.ParsePrevDatetimeAt(s, clockwork.NewRealClock())
+ } else if len(s) == 14 { // mm-dd HH:MM:SS
+ dt, err = utils.ParsePrevDatetimeAt2(s, clockwork.NewRealClock())
+ }
+ if err != nil {
+ return s
+ }
+ if msgs, err := db.GetRoomChatMessagesByDate(roomID, dt.UTC()); err == nil && len(msgs) > 0 {
+ msg := msgs[0]
+ if len(msgs) > 1 {
+ for _, msgTmp := range msgs {
+ if msgTmp.User.ID == authUserID || (msgTmp.ToUserID != nil && *msgTmp.ToUserID == authUserID) {
+ msg = msgTmp
+ break
+ }
+ }
+ }
+ return fmt.Sprintf(`<a href="/chat/%s/archive#%s" target="_blank" rel="noopener noreferrer">%s</a>`, msg.Room.Name, msg.UUID, s)
+ }
+ return s
+ })
+ }
+ return start + rest
+}
+
+var LibredditURLs = []string{
+ "http://spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion",
+ "http://fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion",
+ "http://kphht2jcflojtqte4b4kyx7p2ahagv4debjj32nre67dxz7y57seqwyd.onion",
+ "http://inytumdgnri7xsqtvpntjevaelxtgbjqkuqhtf6txxhwbll2fwqtakqd.onion",
+ "http://liredejj74h5xjqr2dylnl5howb2bpikfowqoveub55ru27x43357iid.onion",
+ "http://kzhfp3nvb4qp575vy23ccbrgfocezjtl5dx66uthgrhu7nscu6rcwjyd.onion",
+ "http://ecue64ybzvn6vjzl37kcsnwt4ycmbsyf74nbttyg7rkc3t3qwnj7mcyd.onion",
+ "http://ledditqo2mxfvlgobxnlhrkq4dh34jss6evfkdkb2thlvy6dn4f4gpyd.onion",
+ "http://ol5begilptoou34emq2sshf3may3hlblvipdjtybbovpb7c7zodxmtqd.onion",
+ "http://lbrdtjaj7567ptdd4rv74lv27qhxfkraabnyphgcvptl64ijx2tijwid.onion",
+}
+
+var InvidiousURLs = []string{
+ "http://c7hqkpkpemu6e7emz5b4vyz7idjgdvgaaa3dyimmeojqbgpea3xqjoid.onion",
+ "http://kbjggqkzv65ivcqj6bumvp337z6264huv5kpkwuv6gu5yjiskvan7fad.onion",
+ "http://grwp24hodrefzvjjuccrkw3mjq4tzhaaq32amf33dzpmuxe7ilepcmad.onion"}
+
+var WikilessURLs = []string{
+ "http://c2pesewpalbi6lbfc5hf53q4g3ovnxe4s7tfa6k2aqkf7jd7a7dlz5ad.onion",
+ "http://dj2tbh2nqfxyfmvq33cjmhuw7nb6am7thzd3zsjvizeqf374fixbrxyd.onion"}
+
+var NitterURLs = []string{
+ "http://nitraeju2mipeziu2wtcrqsxg7h62v5y4eqgwi75uprynkj74gevvuqd.onion"}
+
+var RimgoURLs = []string{
+ "http://be7udfhmnzqyt7cxysg6c4pbawarvaofjjywp35nhd5qamewdfxl6sid.onion"}
+
+func convertLinks(in string,
+ roomID RoomID,
+ getUserByUsername func(Username) (User, error),
+ getLinkByShorthand func(string) (Link, error),
+ getChatMessageByUUID func(string) (ChatMessage, error)) string {
+ quote, rest := splitQuote(in)
+
+ knownOnions := [][]string{
+ {"http://git.dkf.onion", config.DkfGitOnion},
+ {"http://dkfgit.onion", config.DkfGit1Onion},
+ {"http://dread.onion", config.DreadOnion},
+ {"http://cryptbb.onion", config.CryptbbOnion},
+ {"http://blkhat.onion", config.BhcOnion},
+ {"http://dnmx.onion", config.DnmxOnion},
+ {"http://whonix.onion", config.WhonixOnion},
+ }
+
+ newRest := linkOrProfileRgx.ReplaceAllStringFunc(rest, func(link string) string {
+ // Convert all occurrences of "/u/username" to a link to user profile page if the user exists
+ if userProfileLinkRgx.MatchString(link) {
+ user, err := getUserByUsername(Username(strings.TrimPrefix(link, "/u/")))
+ if err != nil {
+ return link
+ }
+ href := "/u/" + string(user.Username)
+ return makeHtmlLink(href, href)
+ }
+
+ // Convert all occurrences of "/l/shorthand" to a link to link page if the shorthand exists
+ if linkShorthandPageLinkRgx.MatchString(link) {
+ l, err := getLinkByShorthand(strings.TrimPrefix(link, "/l/"))
+ if err != nil {
+ return link
+ }
+ href := "/l/" + *l.Shorthand
+ return makeHtmlLink(href, href)
+ }
+
+ // Handle reddit links
+ if strings.HasPrefix(link, "https://www.reddit.com/") {
+ old := strings.Replace(link, "https://www.reddit.com/", "https://old.reddit.com/", 1)
+ libredditLink := "/external-link/libreddit/" + url.PathEscape(strings.TrimPrefix(link, "https://www.reddit.com/"))
+ oldHtmlLink := makeHtmlLink("old", old)
+ libredditHtmlLink := makeHtmlLink("libredditLink", libredditLink)
+ htmlLink := makeHtmlLink(link, link)
+ return htmlLink + ` (` + oldHtmlLink + ` | ` + libredditHtmlLink + `)`
+ } else if strings.HasPrefix(link, "https://old.reddit.com/") {
+ libredditLink := "/external-link/libreddit/" + url.PathEscape(strings.TrimPrefix(link, "https://old.reddit.com/"))
+ libredditHtmlLink := makeHtmlLink("libredditLink", libredditLink)
+ htmlLink := makeHtmlLink(link, link)
+ return htmlLink + ` (` + libredditHtmlLink + `)`
+ }
+ for _, libredditURL := range LibredditURLs {
+ if strings.HasPrefix(link, libredditURL) {
+ newPrefix := strings.Replace(link, libredditURL, "http://reddit.onion", 1)
+ old := strings.Replace(link, libredditURL, "https://old.reddit.com", 1)
+ oldHtmlLink := makeHtmlLink("old", old)
+ htmlLink := makeHtmlLink(newPrefix, link)
+ return htmlLink + ` (` + oldHtmlLink + `)`
+ }
+ }
+
+ // Append YouTube link to invidious link
+ for _, invidiousURL := range InvidiousURLs {
+ if strings.HasPrefix(link, invidiousURL) {
+ if strings.Contains(link, ".onion/watch?v=") {
+ newPrefix := strings.Replace(link, invidiousURL, "http://invidious.onion", 1)
+ m := invidiousIDRgx.FindStringSubmatch(link)
+ if len(m) == 2 {
+ videoID := m[1]
+ youtubeLink := "https://www.youtube.com/watch?v=" + videoID
+ youtubeHtmlLink := makeHtmlLink("Youtube", youtubeLink)
+ htmlLink := makeHtmlLink(newPrefix, link)
+ return htmlLink + ` (` + youtubeHtmlLink + `)`
+ }
+ }
+ }
+ }
+ // Unknown invidious links
+ if strings.Contains(link, ".onion/watch?v=") {
+ m := invidiousIDRgx.FindStringSubmatch(link)
+ if len(m) == 2 {
+ videoID := m[1]
+ youtubeLink := "https://www.youtube.com/watch?v=" + videoID
+ youtubeHtmlLink := makeHtmlLink("Youtube", youtubeLink)
+ htmlLink := makeHtmlLink(link, link)
+ return htmlLink + ` (` + youtubeHtmlLink + `)`
+ }
+ }
+
+ // Append wikiless link to wikipedia link
+ if strings.HasPrefix(link, "https://en.wikipedia.org/") {
+ wikilessLink := "/external-link/nitter/" + url.PathEscape(strings.TrimPrefix(link, "https://en.wikipedia.org/"))
+ wikilessHtmlLink := makeHtmlLink("Wikiless", wikilessLink)
+ htmlLink := makeHtmlLink(link, link)
+ return htmlLink + ` (` + wikilessHtmlLink + `)`
+ }
+ for _, wikilessURL := range WikilessURLs {
+ if strings.HasPrefix(link, wikilessURL) {
+ newPrefix := strings.Replace(link, wikilessURL, "http://wikiless.onion", 1)
+ wikipediaPrefix := strings.Replace(link, wikilessURL, "https://en.wikipedia.org", 1)
+ wikipediaHtmlLink := makeHtmlLink("Wikipedia", wikipediaPrefix)
+ htmlLink := makeHtmlLink(newPrefix, link)
+ return htmlLink + ` (` + wikipediaHtmlLink + `)`
+ }
+ }
+
+ // Append nitter link to twitter link
+ if strings.HasPrefix(link, "https://twitter.com/") {
+ nitterLink := "/external-link/nitter/" + url.PathEscape(strings.TrimPrefix(link, "https://twitter.com/"))
+ nitterHtmlLink := makeHtmlLink("Nitter", nitterLink)
+ htmlLink := makeHtmlLink(link, link)
+ return htmlLink + ` (` + nitterHtmlLink + `)`
+ }
+ for _, nitterURL := range NitterURLs {
+ if strings.HasPrefix(link, nitterURL) {
+ newPrefix := strings.Replace(link, nitterURL, "http://nitter.onion", 1)
+ twitterPrefix := strings.Replace(link, nitterURL, "https://twitter.com", 1)
+ twitterHtmlLink := makeHtmlLink("Twitter", twitterPrefix)
+ htmlLink := makeHtmlLink(newPrefix, link)
+ return htmlLink + ` (` + twitterHtmlLink + `)`
+ }
+ }
+
+ // Append rimgo link to imgur link
+ if strings.HasPrefix(link, "https://imgur.com/") {
+ rimgoLink := "/external-link/rimgo/" + url.PathEscape(strings.TrimPrefix(link, "https://imgur.com/"))
+ rimgoHtmlLink := makeHtmlLink("Rimgo", rimgoLink)
+ htmlLink := makeHtmlLink(link, link)
+ return htmlLink + ` (` + rimgoHtmlLink + `)`
+ }
+ for _, rimgoURL := range RimgoURLs {
+ if strings.HasPrefix(link, rimgoURL) {
+ newPrefix := strings.Replace(link, rimgoURL, "http://rimgo.onion", 1)
+ imgurPrefix := strings.Replace(link, rimgoURL, "https://imgur.com", 1)
+ imgurHtmlLink := makeHtmlLink("Imgur", imgurPrefix)
+ htmlLink := makeHtmlLink(newPrefix, link)
+ return htmlLink + ` (` + imgurHtmlLink + `)`
+ }
+ }
+
+ // Append invidious link to YouTube/yewtube link
+ var videoID string
+ var m []string
+ var isShortUrl, isYewtube bool
+ if strings.HasPrefix(link, "https://youtu.be/") {
+ m = youtuBeIDRgx.FindStringSubmatch(link)
+ } else if strings.HasPrefix(link, "https://www.youtube.com/watch?v=") {
+ m = youtubeComIDRgx.FindStringSubmatch(link)
+ } else if strings.HasPrefix(link, "https://yewtu.be/") || strings.HasPrefix(link, "https://www.yewtu.be/") {
+ m = yewtubeBeIDRgx.FindStringSubmatch(link)
+ isYewtube = true
+ } else if strings.HasPrefix(link, "https://www.youtube.com/shorts/") {
+ m = youtubeComShortsIDRgx.FindStringSubmatch(link)
+ isShortUrl = true
+ }
+ if len(m) == 2 {
+ videoID = m[1]
+ }
+ if videoID != "" {
+ invidiousLink := "/external-link/invidious/" + url.PathEscape("watch?v="+videoID+"&local=true")
+ invidiousHtmlLink := makeHtmlLink("Invidious", invidiousLink)
+ htmlLink := makeHtmlLink(link, link)
+ youtubeLink := "https://www.youtube.com/watch?v=" + videoID
+ youtubeHtmlLink := makeHtmlLink("YT", youtubeLink)
+ out := htmlLink + ` (` + invidiousHtmlLink + `)`
+ if isShortUrl || isYewtube {
+ out = htmlLink + ` (` + youtubeHtmlLink + ` | ` + invidiousHtmlLink + `)`
+ }
+ return out
+ }
+
+ // Special case for dkf links.
+ {
+ dkfLocalPrefix := "http://127.0.0.1:8080"
+ dkfShortPrefix := "http://dkf.onion"
+ dkfLongPrefix := config.DkfOnion
+ hasLocalPrefix := strings.HasPrefix(link, dkfLocalPrefix)
+ hasDkfShortPrefix := strings.HasPrefix(link, dkfShortPrefix)
+ hasDkfLongPrefix := strings.HasPrefix(link, dkfLongPrefix)
+ if hasLocalPrefix || hasDkfLongPrefix || hasDkfShortPrefix {
+ var trimmed string
+ if hasLocalPrefix {
+ trimmed = strings.TrimPrefix(link, dkfLocalPrefix)
+ } else if hasDkfLongPrefix {
+ trimmed = strings.TrimPrefix(link, dkfLongPrefix)
+ } else if hasDkfShortPrefix {
+ trimmed = strings.TrimPrefix(link, dkfShortPrefix)
+ }
+ label := dkfShortPrefix + trimmed
+ href := trimmed
+ // Shorten archive links
+ if m := dkfArchiveRgx.FindStringSubmatch(label); len(m) == 3 {
+ if msg, err := getChatMessageByUUID(m[2]); err == nil {
+ if roomID == msg.RoomID {
+ label = msg.CreatedAt.Format("[Jan 02 03:04:05]")
+ } else {
+ label = msg.CreatedAt.Format("[#" + m[1] + " Jan 02 03:04:05]")
+ }
+ }
+ }
+ // Allows to have messages such as: "my profile is /u/username :)"
+ if userProfileLinkRgx.MatchString(trimmed) {
+ if user, err := getUserByUsername(Username(strings.TrimPrefix(trimmed, "/u/"))); err == nil {
+ label = "/u/" + string(user.Username)
+ href = "/u/" + string(user.Username)
+ }
+ } else if linkShorthandPageLinkRgx.MatchString(trimmed) {
+ // Convert all occurrences of "/l/shorthand" to a link to link page if the shorthand exists
+ if l, err := getLinkByShorthand(strings.TrimPrefix(trimmed, "/l/")); err == nil {
+ label = "/l/" + *l.Shorthand
+ href = "/l/" + *l.Shorthand
+ }
+ }
+ return makeHtmlLink(label, href)
+ }
+ }
+
+ for _, el := range knownOnions {
+ shortPrefix := el[0]
+ longPrefix := el[1]
+ if strings.HasPrefix(link, longPrefix) {
+ return makeHtmlLink(shortPrefix+strings.TrimPrefix(link, longPrefix), link)
+ } else if strings.HasPrefix(link, shortPrefix) {
+ return makeHtmlLink(link, longPrefix+strings.TrimPrefix(link, shortPrefix))
+ }
+ }
+ return makeHtmlLink(link, link)
+ })
+
+ return quote + newRest
+}
+
+var linkRgxStr = `(http|ftp|https):\/\/([\w\-_]+(?:(?:\.[\w\-_]+)+))([\w\-\.,@?^=%&:/~\+#\(\)]*[\w\-\@?^=%&/~\+#\(\)])?`
+var profileRgxStr = `/u/\w{3,20}`
+var linkShorthandRgxStr = `/l/\w{3,20}`
+var dkfArchiveRgx = regexp.MustCompile(`/chat/([\w_]{3,50})/archive\?uuid=([\w-]{36})#[\w-]{36}`)
+var linkOrProfileRgx = regexp.MustCompile(`(` + linkRgxStr + `|` + profileRgxStr + `|` + linkShorthandRgxStr + `)`)
+var userProfileLinkRgx = regexp.MustCompile(`^` + profileRgxStr + `$`)
+var linkShorthandPageLinkRgx = regexp.MustCompile(`^` + linkShorthandRgxStr + `$`)
+var youtubeComIDRgx = regexp.MustCompile(`watch\?v=([\w-]+)`)
+var youtubeComShortsIDRgx = regexp.MustCompile(`/shorts/([\w-]+)`)
+var youtuBeIDRgx = regexp.MustCompile(`https://youtu\.be/([\w-]+)`)
+var yewtubeBeIDRgx = youtubeComIDRgx
+var invidiousIDRgx = youtubeComIDRgx
+
+func makeHtmlLink(label, link string) string {
+ // We replace @ to prevent ColorifyTaggedUsers from trying to generate html inside the links.
+ r := strings.NewReplacer("@", "@", "#", "#")
+ label = r.Replace(label)
+ link = r.Replace(link)
+ return fmt.Sprintf(`<a href="%s" rel="noopener noreferrer" target="_blank">%s</a>`, link, label)
+}
+
+func splitQuote(in string) (string, string) {
+ const quotePrefix = `<p>“[`
+ const quoteSuffix = `”`
+ idx := strings.Index(in, quoteSuffix)
+ if idx == -1 || !strings.HasPrefix(in, quotePrefix) {
+ return "", in
+ }
+ return in[:idx], in[idx:]
+}
+
+func linkDefaultRooms(html string) string {
+ r := strings.NewReplacer(
+ "#general", `<a href="/chat/general" target="_top">#general</a>`,
+ "#programming", `<a href="/chat/programming" target="_top">#programming</a>`,
+ "#hacking", `<a href="/chat/hacking" target="_top">#hacking</a>`,
+ "#suggestions", `<a href="/chat/suggestions" target="_top">#suggestions</a>`,
+ "#announcements", `<a href="/chat/announcements" target="_top">#announcements</a>`,
+ )
+ return r.Replace(html)
+}
+
+var emojiReplacer = strings.NewReplacer(
+ ":):", `<span class="emoji" title=":):">☺</span>`,
+ ":smile:", `<span class="emoji" title=":smile:">☺</span>`,
+ ":happy:", `<span class="emoji" title=":happy:">😃</span>`,
+ ":see-no-evil:", `<span class="emoji" title=":see-no-evil:">🙈</span>`,
+ ":hear-no-evil:", `<span class="emoji" title=":hear-no-evil:">🙉</span>`,
+ ":speak-no-evil:", `<span class="emoji" title=":speak-no-evil:">🙊</span>`,
+ ":poop:", `<span class="emoji" title=":poop:">💩</span>`,
+ ":+1:", `<span class="emoji" title=":+1:">👍</span>`,
+ ":evil:", `<span class="emoji" title=":evil:">😈</span>`,
+ ":cat-happy:", `<span class="emoji" title=":cat-happy:">😸</span>`,
+ ":eyes:", `<span class="emoji" title=":eyes:">👀</span>`,
+ ":wave:", `<span class="emoji" title=":wave:">👋</span>`,
+ ":clap:", `<span class="emoji" title=":clap:">👏</span>`,
+ ":fire:", `<span class="emoji" title=":fire:">🔥</span>`,
+ ":sparkles:", `<span class="emoji" title=":sparkles:">✨</span>`,
+ ":sweat:", `<span class="emoji" title=":sweat:">💦</span>`,
+ ":heart:", `<span class="emoji" title=":heart:">❤</span>`,
+ ":broken-heart:", `<span class="emoji" title=":broken-heart:">💔</span>`,
+ ":anatomical-heart:", `<span class="emoji" title=":anatomical-heart:">🫀</span>`,
+ ":zzz:", `<span class="emoji" title=":zzz:">💤</span>`,
+ ":praise:", `<span class="emoji" title=":praise:">🙌</span>`,
+ ":joy:", `<span class="emoji" title=":joy:">😂</span>`,
+ ":sob:", `<span class="emoji" title=":sob:">😭</span>`,
+ ":pleading-face:", `<span class="emoji" title=":pleading-face:">🥺</span>`,
+ ":shush:", `<span class="emoji" title=":shush:">🤫</span>`,
+ ":scream:", `<span class="emoji" title=":scream:">😱</span>`,
+ ":heart-eyes:", `<span class="emoji" title=":heart-eyes:">😍</span>`,
+ ":blush:", `<span class="emoji" title=":blush:">☺</span>`,
+ ":crazy:", `<span class="emoji" title=":crazy:">😜</span>`,
+ ":angry:", `<span class="emoji" title=":angry:">😡</span>`,
+ ":triumph:", `<span class="emoji" title=":triumph:">😤</span>`,
+ ":vomit:", `<span class="emoji" title=":vomit:">🤮</span>`,
+ ":skull:", `<span class="emoji" title=":skull:">💀</span>`,
+ ":alien:", `<span class="emoji" title=":alien:">👽</span>`,
+ ":sleeping:", `<span class="emoji" title=":sleeping:">😴</span>`,
+ ":tongue:", `<span class="emoji" title=":tongue:">😛</span>`,
+ ":cool:", `<span class="emoji" title=":cool:">😎</span>`,
+ ":wink:", `<span class="emoji" title=":wink:">😉</span>`,
+ ":thinking:", `<span class="emoji" title=":thinking:">🤔</span>`,
+ ":happy-sweat:", `<span class="emoji" title=":happy-sweat:">😅</span>`,
+ ":nerd:", `<span class="emoji" title=":nerd:">🤓</span>`,
+ ":money-mouth:", `<span class="emoji" title=":money-mouth:">🤑</span>`,
+ ":fox:", `<span class="emoji" title=":fox:">🦊</span>`,
+ ":popcorn:", `<span class="emoji" title=":popcorn:">🍿</span>`,
+ ":money-bag:", `<span class="emoji" title=":money-bag:">💰</span>`,
+ ":facepalm:", `<span class="emoji" title=":facepalm:">🤦</span>`,
+ ":lungs:", `<span class="emoji" title=":lungs:">🫁</span>`,
+ ":shrug:", `¯\_(ツ)_/¯`,
+ ":flip:", `(╯°□°)╯︵ ┻━┻`,
+ ":flip-all:", `┻━┻︵ \(°□°)/ ︵ ┻━┻`,
+ ":fix-table:", `(ヘ・_・)ヘ┳━┳`,
+ ":disap:", `ಠ_ಠ`,
+)
+
+var noSchemeOnionLinkRgx = regexp.MustCompile(`\s[a-z2-7]{56}\.onion`)
+
+// Fix up onion links that are missing the http scheme. This often happen when copy/pasting a link.
+func convertLinksWithoutScheme(in string) string {
+ out := noSchemeOnionLinkRgx.ReplaceAllStringFunc(in, func(s string) string {
+ return " http://" + strings.TrimSpace(s)
+ })
+ return out
+}
+
func (r Renderer) RenderNode(w io.Writer, node *bf.Node, entering bool) bf.WalkStatus {
switch node.Type {
case bf.Text:
if node.Parent.Type != bf.Link {
- node.Literal = []byte(html2.UnescapeString(string(node.Literal)))
+ out := string(node.Literal)
+ out = linkRoomTags(r.DB, out)
+ out = convertBangShortcuts(out)
+ out = convertArchiveLinks(r.DB, out, r.RoomID, r.AuthUserID)
+ out = convertLinks(out, r.RoomID, r.DB.GetUserByUsername, r.DB.GetLinkByShorthand, r.DB.GetChatMessageByUUID)
+ out = linkDefaultRooms(out)
+ out = emojiReplacer.Replace(out)
+ out = convertLinksWithoutScheme(out)
- if roomTagRgx.MatchString(string(node.Literal)) {
- node.Literal = []byte(roomTagRgx.ReplaceAllStringFunc(string(node.Literal), func(s string) string {
- if room, err := r.DB.GetChatRoomByName(strings.TrimPrefix(s, "#")); err == nil {
- return `<a href="/chat/` + room.Name + `" target="_top">` + s + `</a>`
- }
- return s
- }))
- _, _ = w.Write(node.Literal)
- return bf.GoToNext
- }
+ node.Literal = []byte(out)
+ _, _ = w.Write(node.Literal)
+ return bf.GoToNext
}
case bf.Code:
node.Literal = []byte(html2.UnescapeString(string(node.Literal)))
diff --git a/pkg/database/utils/processMessage.go b/pkg/database/utils/processMessage.go
@@ -14,10 +14,8 @@ import (
"github.com/microcosm-cc/bluemonday"
html2 "html"
"math"
- "net/url"
"regexp"
"strings"
- "time"
)
const (
@@ -31,61 +29,6 @@ const (
pgpSignedSuffix = "-----END PGP SIGNATURE-----"
)
-var emojiReplacer = strings.NewReplacer(
- ":):", `<span class="emoji" title=":):">☺</span>`,
- ":smile:", `<span class="emoji" title=":smile:">☺</span>`,
- ":happy:", `<span class="emoji" title=":happy:">😃</span>`,
- ":see-no-evil:", `<span class="emoji" title=":see-no-evil:">🙈</span>`,
- ":hear-no-evil:", `<span class="emoji" title=":hear-no-evil:">🙉</span>`,
- ":speak-no-evil:", `<span class="emoji" title=":speak-no-evil:">🙊</span>`,
- ":poop:", `<span class="emoji" title=":poop:">💩</span>`,
- ":+1:", `<span class="emoji" title=":+1:">👍</span>`,
- ":evil:", `<span class="emoji" title=":evil:">😈</span>`,
- ":cat-happy:", `<span class="emoji" title=":cat-happy:">😸</span>`,
- ":eyes:", `<span class="emoji" title=":eyes:">👀</span>`,
- ":wave:", `<span class="emoji" title=":wave:">👋</span>`,
- ":clap:", `<span class="emoji" title=":clap:">👏</span>`,
- ":fire:", `<span class="emoji" title=":fire:">🔥</span>`,
- ":sparkles:", `<span class="emoji" title=":sparkles:">✨</span>`,
- ":sweat:", `<span class="emoji" title=":sweat:">💦</span>`,
- ":heart:", `<span class="emoji" title=":heart:">❤</span>`,
- ":broken-heart:", `<span class="emoji" title=":broken-heart:">💔</span>`,
- ":anatomical-heart:", `<span class="emoji" title=":anatomical-heart:">🫀</span>`,
- ":zzz:", `<span class="emoji" title=":zzz:">💤</span>`,
- ":praise:", `<span class="emoji" title=":praise:">🙌</span>`,
- ":joy:", `<span class="emoji" title=":joy:">😂</span>`,
- ":sob:", `<span class="emoji" title=":sob:">😭</span>`,
- ":pleading-face:", `<span class="emoji" title=":pleading-face:">🥺</span>`,
- ":shush:", `<span class="emoji" title=":shush:">🤫</span>`,
- ":scream:", `<span class="emoji" title=":scream:">😱</span>`,
- ":heart-eyes:", `<span class="emoji" title=":heart-eyes:">😍</span>`,
- ":blush:", `<span class="emoji" title=":blush:">☺</span>`,
- ":crazy:", `<span class="emoji" title=":crazy:">😜</span>`,
- ":angry:", `<span class="emoji" title=":angry:">😡</span>`,
- ":triumph:", `<span class="emoji" title=":triumph:">😤</span>`,
- ":vomit:", `<span class="emoji" title=":vomit:">🤮</span>`,
- ":skull:", `<span class="emoji" title=":skull:">💀</span>`,
- ":alien:", `<span class="emoji" title=":alien:">👽</span>`,
- ":sleeping:", `<span class="emoji" title=":sleeping:">😴</span>`,
- ":tongue:", `<span class="emoji" title=":tongue:">😛</span>`,
- ":cool:", `<span class="emoji" title=":cool:">😎</span>`,
- ":wink:", `<span class="emoji" title=":wink:">😉</span>`,
- ":thinking:", `<span class="emoji" title=":thinking:">🤔</span>`,
- ":happy-sweat:", `<span class="emoji" title=":happy-sweat:">😅</span>`,
- ":nerd:", `<span class="emoji" title=":nerd:">🤓</span>`,
- ":money-mouth:", `<span class="emoji" title=":money-mouth:">🤑</span>`,
- ":fox:", `<span class="emoji" title=":fox:">🦊</span>`,
- ":popcorn:", `<span class="emoji" title=":popcorn:">🍿</span>`,
- ":money-bag:", `<span class="emoji" title=":money-bag:">💰</span>`,
- ":facepalm:", `<span class="emoji" title=":facepalm:">🤦</span>`,
- ":lungs:", `<span class="emoji" title=":lungs:">🫁</span>`,
- ":shrug:", `¯\_(ツ)_/¯`,
- ":flip:", `(╯°□°)╯︵ ┻━┻`,
- ":flip-all:", `┻━┻︵ \(°□°)/ ︵ ┻━┻`,
- ":fix-table:", `(ヘ・_・)ヘ┳━┳`,
- ":disap:", `ಠ_ಠ`,
-)
-
var usernameF = `\w{3,20}` // username (regex Fragment)
var roomNameF = `\w{3,50}`
var userOr0 = usernameF + `|0`
@@ -93,7 +36,6 @@ var optAtGUserOr0 = `@?(` + userOr0 + `)` // Optional @, Grouped, Username or 0
var pmRgx = regexp.MustCompile(`^/pm ` + optAtGUserOr0 + `(?:\s(?s:(.*)))?`)
var tagRgx = regexp.MustCompile(`(?:\\?)@(` + userOr0 + `)`)
var roomTagRgx = regexp.MustCompile(`#(` + roomNameF + `)`)
-var noSchemeOnionLinkRgx = regexp.MustCompile(`\s[a-z2-7]{56}\.onion`)
var msgPolicy = bluemonday.NewPolicy().
AllowElements("a", "p", "span", "strong", "del", "code", "pre", "em", "ul", "li", "br", "small", "i").
@@ -114,14 +56,9 @@ func ProcessRawMessage(db *database.DkfDB, in, roomKey string, authUserID databa
html = convertPGPMessageToFile(db, html, authUserID)
html = convertPGPPublicKeyToFile(db, html, authUserID)
html = convertAgeMessageToFile(db, html, authUserID)
- html = convertLinksWithoutScheme(html)
- html = convertMarkdown(db, html, canUseMultiline, manualML)
- html = convertBangShortcuts(html)
- html = convertArchiveLinks(db, html, roomID, authUserID)
- html = convertLinks(html, roomID, db.GetUserByUsername, db.GetLinkByShorthand, db.GetChatMessageByUUID)
- html = linkDefaultRooms(html)
+ html = convertMarkdown(db, html, canUseMultiline, manualML, roomID, authUserID)
+
html, taggedUsersIDsMap := ColorifyTaggedUsers(html, db.GetUsersByUsername)
- html = emojiReplacer.Replace(html)
html = styleQuote(html, quoted)
html = appendUploadLink(html, upload)
if quoted != nil { // Add quoted message owner for inboxes
@@ -389,14 +326,6 @@ func convertInlinePGPPublicKey(inlinePKey string) string {
return inlinePKey
}
-// Fix up onion links that are missing the http scheme. This often happen when copy/pasting a link.
-func convertLinksWithoutScheme(in string) string {
- html := noSchemeOnionLinkRgx.ReplaceAllStringFunc(in, func(s string) string {
- return " http://" + strings.TrimSpace(s)
- })
- return html
-}
-
var linkRgxStr = `(http|ftp|https):\/\/([\w\-_]+(?:(?:\.[\w\-_]+)+))([\w\-\.,@?^=%&:/~\+#\(\)]*[\w\-\@?^=%&/~\+#\(\)])?`
var profileRgxStr = `/u/\w{3,20}`
var linkShorthandRgxStr = `/l/\w{3,20}`
@@ -410,285 +339,6 @@ var youtuBeIDRgx = regexp.MustCompile(`https://youtu\.be/([\w-]+)`)
var yewtubeBeIDRgx = youtubeComIDRgx
var invidiousIDRgx = youtubeComIDRgx
-func makeHtmlLink(label, link string) string {
- // We replace @ to prevent ColorifyTaggedUsers from trying to generate html inside the links.
- r := strings.NewReplacer("@", "@", "#", "#")
- label = r.Replace(label)
- link = r.Replace(link)
- return fmt.Sprintf(`<a href="%s" rel="noopener noreferrer" target="_blank">%s</a>`, link, label)
-}
-
-func splitQuote(in string) (string, string) {
- const quotePrefix = `<p>“[`
- const quoteSuffix = `”`
- idx := strings.Index(in, quoteSuffix)
- if idx == -1 || !strings.HasPrefix(in, quotePrefix) {
- return "", in
- }
- return in[:idx], in[idx:]
-}
-
-var LibredditURLs = []string{
- "http://spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion",
- "http://fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion",
- "http://kphht2jcflojtqte4b4kyx7p2ahagv4debjj32nre67dxz7y57seqwyd.onion",
- "http://inytumdgnri7xsqtvpntjevaelxtgbjqkuqhtf6txxhwbll2fwqtakqd.onion",
- "http://liredejj74h5xjqr2dylnl5howb2bpikfowqoveub55ru27x43357iid.onion",
- "http://kzhfp3nvb4qp575vy23ccbrgfocezjtl5dx66uthgrhu7nscu6rcwjyd.onion",
- "http://ecue64ybzvn6vjzl37kcsnwt4ycmbsyf74nbttyg7rkc3t3qwnj7mcyd.onion",
- "http://ledditqo2mxfvlgobxnlhrkq4dh34jss6evfkdkb2thlvy6dn4f4gpyd.onion",
- "http://ol5begilptoou34emq2sshf3may3hlblvipdjtybbovpb7c7zodxmtqd.onion",
- "http://lbrdtjaj7567ptdd4rv74lv27qhxfkraabnyphgcvptl64ijx2tijwid.onion",
-}
-
-var InvidiousURLs = []string{
- "http://c7hqkpkpemu6e7emz5b4vyz7idjgdvgaaa3dyimmeojqbgpea3xqjoid.onion",
- "http://kbjggqkzv65ivcqj6bumvp337z6264huv5kpkwuv6gu5yjiskvan7fad.onion",
- "http://grwp24hodrefzvjjuccrkw3mjq4tzhaaq32amf33dzpmuxe7ilepcmad.onion"}
-
-var WikilessURLs = []string{
- "http://c2pesewpalbi6lbfc5hf53q4g3ovnxe4s7tfa6k2aqkf7jd7a7dlz5ad.onion",
- "http://dj2tbh2nqfxyfmvq33cjmhuw7nb6am7thzd3zsjvizeqf374fixbrxyd.onion"}
-
-var NitterURLs = []string{
- "http://nitraeju2mipeziu2wtcrqsxg7h62v5y4eqgwi75uprynkj74gevvuqd.onion"}
-
-var RimgoURLs = []string{
- "http://be7udfhmnzqyt7cxysg6c4pbawarvaofjjywp35nhd5qamewdfxl6sid.onion"}
-
-func convertLinks(in string,
- roomID database.RoomID,
- getUserByUsername func(database.Username) (database.User, error),
- getLinkByShorthand func(string) (database.Link, error),
- getChatMessageByUUID func(string) (database.ChatMessage, error)) string {
- quote, rest := splitQuote(in)
-
- knownOnions := [][]string{
- {"http://git.dkf.onion", config.DkfGitOnion},
- {"http://dkfgit.onion", config.DkfGit1Onion},
- {"http://dread.onion", config.DreadOnion},
- {"http://cryptbb.onion", config.CryptbbOnion},
- {"http://blkhat.onion", config.BhcOnion},
- {"http://dnmx.onion", config.DnmxOnion},
- {"http://whonix.onion", config.WhonixOnion},
- }
-
- newRest := linkOrProfileRgx.ReplaceAllStringFunc(rest, func(link string) string {
- // Convert all occurrences of "/u/username" to a link to user profile page if the user exists
- if userProfileLinkRgx.MatchString(link) {
- user, err := getUserByUsername(database.Username(strings.TrimPrefix(link, "/u/")))
- if err != nil {
- return link
- }
- href := "/u/" + string(user.Username)
- return makeHtmlLink(href, href)
- }
-
- // Convert all occurrences of "/l/shorthand" to a link to link page if the shorthand exists
- if linkShorthandPageLinkRgx.MatchString(link) {
- l, err := getLinkByShorthand(strings.TrimPrefix(link, "/l/"))
- if err != nil {
- return link
- }
- href := "/l/" + *l.Shorthand
- return makeHtmlLink(href, href)
- }
-
- // Handle reddit links
- if strings.HasPrefix(link, "https://www.reddit.com/") {
- old := strings.Replace(link, "https://www.reddit.com/", "https://old.reddit.com/", 1)
- libredditLink := "/external-link/libreddit/" + url.PathEscape(strings.TrimPrefix(link, "https://www.reddit.com/"))
- oldHtmlLink := makeHtmlLink("old", old)
- libredditHtmlLink := makeHtmlLink("libredditLink", libredditLink)
- htmlLink := makeHtmlLink(link, link)
- return htmlLink + ` (` + oldHtmlLink + ` | ` + libredditHtmlLink + `)`
- } else if strings.HasPrefix(link, "https://old.reddit.com/") {
- libredditLink := "/external-link/libreddit/" + url.PathEscape(strings.TrimPrefix(link, "https://old.reddit.com/"))
- libredditHtmlLink := makeHtmlLink("libredditLink", libredditLink)
- htmlLink := makeHtmlLink(link, link)
- return htmlLink + ` (` + libredditHtmlLink + `)`
- }
- for _, libredditURL := range LibredditURLs {
- if strings.HasPrefix(link, libredditURL) {
- newPrefix := strings.Replace(link, libredditURL, "http://reddit.onion", 1)
- old := strings.Replace(link, libredditURL, "https://old.reddit.com", 1)
- oldHtmlLink := makeHtmlLink("old", old)
- htmlLink := makeHtmlLink(newPrefix, link)
- return htmlLink + ` (` + oldHtmlLink + `)`
- }
- }
-
- // Append YouTube link to invidious link
- for _, invidiousURL := range InvidiousURLs {
- if strings.HasPrefix(link, invidiousURL) {
- if strings.Contains(link, ".onion/watch?v=") {
- newPrefix := strings.Replace(link, invidiousURL, "http://invidious.onion", 1)
- m := invidiousIDRgx.FindStringSubmatch(link)
- if len(m) == 2 {
- videoID := m[1]
- youtubeLink := "https://www.youtube.com/watch?v=" + videoID
- youtubeHtmlLink := makeHtmlLink("Youtube", youtubeLink)
- htmlLink := makeHtmlLink(newPrefix, link)
- return htmlLink + ` (` + youtubeHtmlLink + `)`
- }
- }
- }
- }
- // Unknown invidious links
- if strings.Contains(link, ".onion/watch?v=") {
- m := invidiousIDRgx.FindStringSubmatch(link)
- if len(m) == 2 {
- videoID := m[1]
- youtubeLink := "https://www.youtube.com/watch?v=" + videoID
- youtubeHtmlLink := makeHtmlLink("Youtube", youtubeLink)
- htmlLink := makeHtmlLink(link, link)
- return htmlLink + ` (` + youtubeHtmlLink + `)`
- }
- }
-
- // Append wikiless link to wikipedia link
- if strings.HasPrefix(link, "https://en.wikipedia.org/") {
- wikilessLink := "/external-link/nitter/" + url.PathEscape(strings.TrimPrefix(link, "https://en.wikipedia.org/"))
- wikilessHtmlLink := makeHtmlLink("Wikiless", wikilessLink)
- htmlLink := makeHtmlLink(link, link)
- return htmlLink + ` (` + wikilessHtmlLink + `)`
- }
- for _, wikilessURL := range WikilessURLs {
- if strings.HasPrefix(link, wikilessURL) {
- newPrefix := strings.Replace(link, wikilessURL, "http://wikiless.onion", 1)
- wikipediaPrefix := strings.Replace(link, wikilessURL, "https://en.wikipedia.org", 1)
- wikipediaHtmlLink := makeHtmlLink("Wikipedia", wikipediaPrefix)
- htmlLink := makeHtmlLink(newPrefix, link)
- return htmlLink + ` (` + wikipediaHtmlLink + `)`
- }
- }
-
- // Append nitter link to twitter link
- if strings.HasPrefix(link, "https://twitter.com/") {
- nitterLink := "/external-link/nitter/" + url.PathEscape(strings.TrimPrefix(link, "https://twitter.com/"))
- nitterHtmlLink := makeHtmlLink("Nitter", nitterLink)
- htmlLink := makeHtmlLink(link, link)
- return htmlLink + ` (` + nitterHtmlLink + `)`
- }
- for _, nitterURL := range NitterURLs {
- if strings.HasPrefix(link, nitterURL) {
- newPrefix := strings.Replace(link, nitterURL, "http://nitter.onion", 1)
- twitterPrefix := strings.Replace(link, nitterURL, "https://twitter.com", 1)
- twitterHtmlLink := makeHtmlLink("Twitter", twitterPrefix)
- htmlLink := makeHtmlLink(newPrefix, link)
- return htmlLink + ` (` + twitterHtmlLink + `)`
- }
- }
-
- // Append rimgo link to imgur link
- if strings.HasPrefix(link, "https://imgur.com/") {
- rimgoLink := "/external-link/rimgo/" + url.PathEscape(strings.TrimPrefix(link, "https://imgur.com/"))
- rimgoHtmlLink := makeHtmlLink("Rimgo", rimgoLink)
- htmlLink := makeHtmlLink(link, link)
- return htmlLink + ` (` + rimgoHtmlLink + `)`
- }
- for _, rimgoURL := range RimgoURLs {
- if strings.HasPrefix(link, rimgoURL) {
- newPrefix := strings.Replace(link, rimgoURL, "http://rimgo.onion", 1)
- imgurPrefix := strings.Replace(link, rimgoURL, "https://imgur.com", 1)
- imgurHtmlLink := makeHtmlLink("Imgur", imgurPrefix)
- htmlLink := makeHtmlLink(newPrefix, link)
- return htmlLink + ` (` + imgurHtmlLink + `)`
- }
- }
-
- // Append invidious link to YouTube/yewtube link
- var videoID string
- var m []string
- var isShortUrl, isYewtube bool
- if strings.HasPrefix(link, "https://youtu.be/") {
- m = youtuBeIDRgx.FindStringSubmatch(link)
- } else if strings.HasPrefix(link, "https://www.youtube.com/watch?v=") {
- m = youtubeComIDRgx.FindStringSubmatch(link)
- } else if strings.HasPrefix(link, "https://yewtu.be/") || strings.HasPrefix(link, "https://www.yewtu.be/") {
- m = yewtubeBeIDRgx.FindStringSubmatch(link)
- isYewtube = true
- } else if strings.HasPrefix(link, "https://www.youtube.com/shorts/") {
- m = youtubeComShortsIDRgx.FindStringSubmatch(link)
- isShortUrl = true
- }
- if len(m) == 2 {
- videoID = m[1]
- }
- if videoID != "" {
- invidiousLink := "/external-link/invidious/" + url.PathEscape("watch?v="+videoID+"&local=true")
- invidiousHtmlLink := makeHtmlLink("Invidious", invidiousLink)
- htmlLink := makeHtmlLink(link, link)
- youtubeLink := "https://www.youtube.com/watch?v=" + videoID
- youtubeHtmlLink := makeHtmlLink("YT", youtubeLink)
- out := htmlLink + ` (` + invidiousHtmlLink + `)`
- if isShortUrl || isYewtube {
- out = htmlLink + ` (` + youtubeHtmlLink + ` | ` + invidiousHtmlLink + `)`
- }
- return out
- }
-
- // Special case for dkf links.
- {
- dkfLocalPrefix := "http://127.0.0.1:8080"
- dkfShortPrefix := "http://dkf.onion"
- dkfLongPrefix := config.DkfOnion
- hasLocalPrefix := strings.HasPrefix(link, dkfLocalPrefix)
- hasDkfShortPrefix := strings.HasPrefix(link, dkfShortPrefix)
- hasDkfLongPrefix := strings.HasPrefix(link, dkfLongPrefix)
- if hasLocalPrefix || hasDkfLongPrefix || hasDkfShortPrefix {
- var trimmed string
- if hasLocalPrefix {
- trimmed = strings.TrimPrefix(link, dkfLocalPrefix)
- } else if hasDkfLongPrefix {
- trimmed = strings.TrimPrefix(link, dkfLongPrefix)
- } else if hasDkfShortPrefix {
- trimmed = strings.TrimPrefix(link, dkfShortPrefix)
- }
- label := dkfShortPrefix + trimmed
- href := trimmed
- // Shorten archive links
- if m := dkfArchiveRgx.FindStringSubmatch(label); len(m) == 3 {
- if msg, err := getChatMessageByUUID(m[2]); err == nil {
- if roomID == msg.RoomID {
- label = msg.CreatedAt.Format("[Jan 02 03:04:05]")
- } else {
- label = msg.CreatedAt.Format("[#" + m[1] + " Jan 02 03:04:05]")
- }
- }
- }
- // Allows to have messages such as: "my profile is /u/username :)"
- if userProfileLinkRgx.MatchString(trimmed) {
- if user, err := getUserByUsername(database.Username(strings.TrimPrefix(trimmed, "/u/"))); err == nil {
- label = "/u/" + string(user.Username)
- href = "/u/" + string(user.Username)
- }
- } else if linkShorthandPageLinkRgx.MatchString(trimmed) {
- // Convert all occurrences of "/l/shorthand" to a link to link page if the shorthand exists
- if l, err := getLinkByShorthand(strings.TrimPrefix(trimmed, "/l/")); err == nil {
- label = "/l/" + *l.Shorthand
- href = "/l/" + *l.Shorthand
- }
- }
- return makeHtmlLink(label, href)
- }
- }
-
- for _, el := range knownOnions {
- shortPrefix := el[0]
- longPrefix := el[1]
- if strings.HasPrefix(link, longPrefix) {
- return makeHtmlLink(shortPrefix+strings.TrimPrefix(link, longPrefix), link)
- } else if strings.HasPrefix(link, shortPrefix) {
- return makeHtmlLink(link, longPrefix+strings.TrimPrefix(link, shortPrefix))
- }
- }
- return makeHtmlLink(link, link)
- })
-
- return quote + newRest
-}
-
func appendUploadLink(html string, upload *database.Upload) string {
if upload != nil {
if html != "" {
@@ -761,82 +411,14 @@ func ColorifyTaggedUsers(html string, getUsersByUsername getUsersByUsernameFn) (
return html, taggedUsersIDsMap
}
-func linkDefaultRooms(html string) string {
- r := strings.NewReplacer(
- "#general", `<a href="/chat/general" target="_top">#general</a>`,
- "#programming", `<a href="/chat/programming" target="_top">#programming</a>`,
- "#hacking", `<a href="/chat/hacking" target="_top">#hacking</a>`,
- "#suggestions", `<a href="/chat/suggestions" target="_top">#suggestions</a>`,
- "#announcements", `<a href="/chat/announcements" target="_top">#announcements</a>`,
- )
- return r.Replace(html)
-}
-
-// Convert timestamps such as 01:23:45 to an archive link if a message with that timestamp exists.
-// eg: "Some text 14:31:46 some more text"
-func convertArchiveLinks(db *database.DkfDB, html string, roomID database.RoomID, authUserID database.UserID) string {
- start, rest := "", html
-
- // Do not replace timestamps that are inside a quote text
- const quoteSuffix = `”`
- endOfQuoteIdx := strings.LastIndex(html, quoteSuffix)
- if endOfQuoteIdx != -1 {
- start, rest = html[:endOfQuoteIdx], html[endOfQuoteIdx:]
- }
-
- archiveRgx := regexp.MustCompile(`(\d{2}-\d{2} )?\d{2}:\d{2}:\d{2}`)
- if archiveRgx.MatchString(rest) {
- rest = archiveRgx.ReplaceAllStringFunc(rest, func(s string) string {
- var dt time.Time
- var err error
- if len(s) == 8 { // HH:MM:SS
- dt, err = utils.ParsePrevDatetimeAt(s, clockwork.NewRealClock())
- } else if len(s) == 14 { // mm-dd HH:MM:SS
- dt, err = utils.ParsePrevDatetimeAt2(s, clockwork.NewRealClock())
- }
- if err != nil {
- return s
- }
- if msgs, err := db.GetRoomChatMessagesByDate(roomID, dt.UTC()); err == nil && len(msgs) > 0 {
- msg := msgs[0]
- if len(msgs) > 1 {
- for _, msgTmp := range msgs {
- if msgTmp.User.ID == authUserID || (msgTmp.ToUserID != nil && *msgTmp.ToUserID == authUserID) {
- msg = msgTmp
- break
- }
- }
- }
- return fmt.Sprintf(`<a href="/chat/%s/archive#%s" target="_blank" rel="noopener noreferrer">%s</a>`, msg.Room.Name, msg.UUID, s)
- }
- return s
- })
- }
- return start + rest
-}
-
-func convertBangShortcuts(html string) string {
- r := strings.NewReplacer(
- "!bhc", config.BhcOnion,
- "!cryptbb", config.CryptbbOnion,
- "!dread", config.DreadOnion,
- "!dkf", config.DkfOnion,
- "!rroom", config.DkfOnion+`/red-room`,
- "!dnmx", config.DnmxOnion,
- "!whonix", config.WhonixOnion,
- "!age", config.AgeUrl,
- )
- return r.Replace(html)
-}
-
-func convertMarkdown(db *database.DkfDB, in string, canUseMultiline, manualML bool) string {
+func convertMarkdown(db *database.DkfDB, in string, canUseMultiline, manualML bool, roomID database.RoomID, authUserID database.UserID) string {
out := strings.Replace(in, "\r", "", -1)
flags := bf.NoIntraEmphasis | bf.Tables | bf.FencedCode | bf.Strikethrough | bf.SpaceHeadings |
bf.DefinitionLists | bf.HardLineBreak | bf.NoLink
if canUseMultiline && manualML {
flags |= bf.ManualLineBreak
}
- resBytes := bf.Run([]byte(out), bf.WithRenderer(database.MyRenderer(db, false, false)), bf.WithExtensions(flags))
+ resBytes := bf.Run([]byte(out), bf.WithRenderer(database.MyRenderer(db, false, false, roomID, authUserID)), bf.WithExtensions(flags))
out = string(resBytes)
return out
}
diff --git a/pkg/database/utils/processMessage_test.go b/pkg/database/utils/processMessage_test.go
@@ -12,13 +12,13 @@ import (
func TestConvertMarkdown(t *testing.T) {
// Convert markdown will not remove dangerous html
msg := `<noscript>`
- out := convertMarkdown(msg, false, false)
+ out := convertMarkdown(nil, msg, false, false)
expected := "<p><noscript></p>\n"
assert.Equal(t, expected, out)
-
+
// Testing censored feature
msg = `This #is censored# text`
- out = convertMarkdown(msg, false, false)
+ out = convertMarkdown(nil, msg, false, false)
expected = "<p>This <span class=\"censored\">is censored</span> text</p>\n"
assert.Equal(t, expected, out)
}
diff --git a/pkg/web/handlers/handlers.go b/pkg/web/handlers/handlers.go
@@ -6,7 +6,6 @@ import (
"dkforest/pkg/captcha"
"dkforest/pkg/config"
"dkforest/pkg/database"
- dutils "dkforest/pkg/database/utils"
"dkforest/pkg/odometer"
"dkforest/pkg/utils"
hutils "dkforest/pkg/web/handlers/utils"
@@ -265,15 +264,15 @@ func ExternalLinkHandler(c echo.Context) error {
original, _ := url.PathUnescape(c.Param("original"))
baseURL := "/"
if service == "invidious" {
- baseURL = utils.RandChoice(dutils.InvidiousURLs)
+ baseURL = utils.RandChoice(database.InvidiousURLs)
} else if service == "libreddit" {
- baseURL = utils.RandChoice(dutils.LibredditURLs)
+ baseURL = utils.RandChoice(database.LibredditURLs)
} else if service == "wikiless" {
- baseURL = utils.RandChoice(dutils.WikilessURLs)
+ baseURL = utils.RandChoice(database.WikilessURLs)
} else if service == "nitter" {
- baseURL = utils.RandChoice(dutils.NitterURLs)
+ baseURL = utils.RandChoice(database.NitterURLs)
} else if service == "rimgo" {
- baseURL = utils.RandChoice(dutils.RimgoURLs)
+ baseURL = utils.RandChoice(database.RimgoURLs)
} else {
return c.String(http.StatusNotFound, "Not found")
}