mirror of
https://github.com/aljazceru/njump.git
synced 2025-12-17 14:24:27 +01:00
improve and fix telegram instant preview for markdown articles.
This commit is contained in:
2
main.go
2
main.go
@@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
|
||||||
"html"
|
"html"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -39,7 +38,6 @@ func updateArchives(ctx context.Context) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
fmt.Println("exit updateArchives gracefully...")
|
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
loadNpubsArchive(ctx)
|
loadNpubsArchive(ctx)
|
||||||
|
|||||||
2
nostr.go
2
nostr.go
@@ -213,7 +213,7 @@ func contactsForPubkey(ctx context.Context, pubkey string, extraRelays ...string
|
|||||||
pubkeyContacts := make([]string, 0, 100)
|
pubkeyContacts := make([]string, 0, 100)
|
||||||
relays := make([]string, 0, 12)
|
relays := make([]string, 0, 12)
|
||||||
if ok := cache.GetJSON("cc:"+pubkey, &pubkeyContacts); !ok {
|
if ok := cache.GetJSON("cc:"+pubkey, &pubkeyContacts); !ok {
|
||||||
fmt.Printf("Searching contacts for %s\n", pubkey)
|
log.Debug().Msgf("searching contacts for %s", pubkey)
|
||||||
ctx, cancel := context.WithTimeout(ctx, time.Second*3)
|
ctx, cancel := context.WithTimeout(ctx, time.Second*3)
|
||||||
|
|
||||||
pubkeyRelays := relaysForPubkey(ctx, pubkey, relays...)
|
pubkeyRelays := relaysForPubkey(ctx, pubkey, relays...)
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ func render(w http.ResponseWriter, r *http.Request) {
|
|||||||
content = strings.ReplaceAll(content, placeholderTag, "nostr:"+nreplace)
|
content = strings.ReplaceAll(content, placeholderTag, "nostr:"+nreplace)
|
||||||
}
|
}
|
||||||
if event.Kind == 30023 || event.Kind == 30024 {
|
if event.Kind == 30023 || event.Kind == 30024 {
|
||||||
content = mdToHTML(content)
|
content = mdToHTML(content, typ == "telegram_instant_view")
|
||||||
} else {
|
} else {
|
||||||
content = renderInlineMentions(basicFormatting(html.EscapeString(content)))
|
content = renderInlineMentions(basicFormatting(html.EscapeString(content)))
|
||||||
}
|
}
|
||||||
@@ -327,6 +327,7 @@ func render(w http.ResponseWriter, r *http.Request) {
|
|||||||
"authorLong": authorLong,
|
"authorLong": authorLong,
|
||||||
"subject": subject,
|
"subject": subject,
|
||||||
"description": description,
|
"description": description,
|
||||||
|
"summary": summary,
|
||||||
"event": event,
|
"event": event,
|
||||||
"eventJSON": string(eventJSON),
|
"eventJSON": string(eventJSON),
|
||||||
"content": content,
|
"content": content,
|
||||||
|
|||||||
@@ -6,7 +6,10 @@
|
|||||||
<meta property="article:published_time" content="{{.createdAt}}" />
|
<meta property="article:published_time" content="{{.createdAt}}" />
|
||||||
|
|
||||||
<!-- stuff that goes in the actual telegram message preview -->
|
<!-- stuff that goes in the actual telegram message preview -->
|
||||||
<meta property="og:site_name" content="{{.authorLong | escapeString}}" />
|
<meta
|
||||||
|
property="og:site_name"
|
||||||
|
content="{{ if .subject }} {{.subject | escapeString}} {{ else }} {{.authorLong | escapeString}} {{ end }}"
|
||||||
|
/>
|
||||||
{{ if .description }}
|
{{ if .description }}
|
||||||
<meta property="og:description" content="{{.description | escapeString}}" />
|
<meta property="og:description" content="{{.description | escapeString}}" />
|
||||||
{{ end }}
|
{{ end }}
|
||||||
@@ -28,8 +31,14 @@
|
|||||||
<!-- basic content of the preview window -->
|
<!-- basic content of the preview window -->
|
||||||
<article>
|
<article>
|
||||||
<h1>
|
<h1>
|
||||||
{{ if (not (eq .subject ""))}} {{.subject | escapeString}} {{ else }}
|
{{ if .subject }} {{.subject | escapeString}} {{ else }} {{.metadata.Name |
|
||||||
{{.metadata.Name | escapeString}} wrote: {{ end }}
|
escapeString}} wrote: {{ end }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
{{ if .summary }}
|
||||||
|
<h2>{{ .summary }}</h2>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!---->
|
||||||
{{.content}}
|
{{.content}}
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
54
utils.go
54
utils.go
@@ -4,12 +4,15 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gomarkdown/markdown"
|
"github.com/gomarkdown/markdown"
|
||||||
|
"github.com/gomarkdown/markdown/ast"
|
||||||
|
"github.com/gomarkdown/markdown/html"
|
||||||
mdhtml "github.com/gomarkdown/markdown/html"
|
mdhtml "github.com/gomarkdown/markdown/html"
|
||||||
"github.com/gomarkdown/markdown/parser"
|
"github.com/gomarkdown/markdown/parser"
|
||||||
"github.com/microcosm-cc/bluemonday"
|
"github.com/microcosm-cc/bluemonday"
|
||||||
@@ -201,7 +204,7 @@ func getParentNevent(event *nostr.Event) string {
|
|||||||
// Rendering functions
|
// Rendering functions
|
||||||
// ### ### ### ### ### ### ### ### ### ### ###
|
// ### ### ### ### ### ### ### ### ### ### ###
|
||||||
|
|
||||||
func replateImageURLsWithTags(input string, replacement string) string {
|
func replaceImageURLsWithTags(input string, replacement string) string {
|
||||||
// Match and replace image URLs with a custom replacement
|
// Match and replace image URLs with a custom replacement
|
||||||
// Usually is html <img> => ` <img src="%s" alt=""> `
|
// Usually is html <img> => ` <img src="%s" alt=""> `
|
||||||
// or markdown !()[...] tags for further processing => ``
|
// or markdown !()[...] tags for further processing => ``
|
||||||
@@ -221,7 +224,7 @@ func replateImageURLsWithTags(input string, replacement string) string {
|
|||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
|
|
||||||
func replateVideoURLsWithTags(input string, replacement string) string {
|
func replaceVideoURLsWithTags(input string, replacement string) string {
|
||||||
// Match and replace video URLs with a custom replacement
|
// Match and replace video URLs with a custom replacement
|
||||||
// Usually is html <video> => ` <video controls width="100%%"><source src="%s"></video> `
|
// Usually is html <video> => ` <video controls width="100%%"><source src="%s"></video> `
|
||||||
// or markdown !()[...] tags for further processing => ``
|
// or markdown !()[...] tags for further processing => ``
|
||||||
@@ -287,12 +290,12 @@ func renderInlineMentions(input string) string {
|
|||||||
func replaceURLsWithTags(line string) string {
|
func replaceURLsWithTags(line string) string {
|
||||||
var rline string
|
var rline string
|
||||||
|
|
||||||
rline = replateImageURLsWithTags(line, ` <img src="%s" alt=""> `)
|
rline = replaceImageURLsWithTags(line, ` <img src="%s" alt=""> `)
|
||||||
if rline != line {
|
if rline != line {
|
||||||
return rline
|
return rline
|
||||||
}
|
}
|
||||||
|
|
||||||
rline = replateVideoURLsWithTags(line, `<video controls width="100%%"><source src="%s"></video>`)
|
rline = replaceVideoURLsWithTags(line, `<video controls width="100%%"><source src="%s"></video>`)
|
||||||
if rline != line {
|
if rline != line {
|
||||||
return rline
|
return rline
|
||||||
}
|
}
|
||||||
@@ -333,24 +336,51 @@ func basicFormatting(input string) string {
|
|||||||
return strings.Join(processedLines, "<br/>")
|
return strings.Join(processedLines, "<br/>")
|
||||||
}
|
}
|
||||||
|
|
||||||
func mdToHTML(md string) string {
|
func mdToHTML(md string, usingTelegramInstantView bool) string {
|
||||||
md = strings.ReplaceAll(md, "\u00A0", " ")
|
md = strings.ReplaceAll(md, "\u00A0", " ")
|
||||||
md = replateImageURLsWithTags(md, ``)
|
md = replaceImageURLsWithTags(md, ``)
|
||||||
md = replateVideoURLsWithTags(md, `<video controls width="100%%"><source src="%s"></video>`)
|
md = replaceVideoURLsWithTags(md, `<video controls width="100%%"><source src="%s"></video>`)
|
||||||
md = replaceNostrURLsWithTags(md)
|
md = replaceNostrURLsWithTags(md)
|
||||||
|
|
||||||
// create markdown parser with extensions
|
// create markdown parser with extensions
|
||||||
extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock | parser.Footnotes
|
p := parser.NewWithExtensions(
|
||||||
p := parser.NewWithExtensions(extensions)
|
parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock | parser.Footnotes)
|
||||||
doc := p.Parse([]byte(md))
|
doc := p.Parse([]byte(md))
|
||||||
|
|
||||||
|
var customNodeHook html.RenderNodeFunc = nil
|
||||||
|
if usingTelegramInstantView {
|
||||||
|
// telegram instant view really doesn't like when there is an image inside a paragraph (like <p><img></p>)
|
||||||
|
// so we use this custom thing to stop all paragraphs before the images, print the images then start a new
|
||||||
|
// paragraph afterwards.
|
||||||
|
customNodeHook = func(w io.Writer, node ast.Node, entering bool) (ast.WalkStatus, bool) {
|
||||||
|
if img, ok := node.(*ast.Image); ok {
|
||||||
|
if entering {
|
||||||
|
src := img.Destination
|
||||||
|
w.Write([]byte(`</p><img src="`))
|
||||||
|
html.EscLink(w, src)
|
||||||
|
w.Write([]byte(`" alt="`))
|
||||||
|
} else {
|
||||||
|
if img.Title != nil {
|
||||||
|
w.Write([]byte(`" title="`))
|
||||||
|
html.EscapeHTML(w, img.Title)
|
||||||
|
}
|
||||||
|
w.Write([]byte(`" /><p>`))
|
||||||
|
}
|
||||||
|
return ast.GoToNext, true
|
||||||
|
}
|
||||||
|
return ast.GoToNext, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// create HTML renderer with extensions
|
// create HTML renderer with extensions
|
||||||
htmlFlags := mdhtml.CommonFlags | mdhtml.HrefTargetBlank
|
opts := mdhtml.RendererOptions{
|
||||||
opts := mdhtml.RendererOptions{Flags: htmlFlags}
|
Flags: mdhtml.CommonFlags | mdhtml.HrefTargetBlank,
|
||||||
|
RenderNodeHook: customNodeHook,
|
||||||
|
}
|
||||||
renderer := mdhtml.NewRenderer(opts)
|
renderer := mdhtml.NewRenderer(opts)
|
||||||
output := string(markdown.Render(doc, renderer))
|
output := string(markdown.Render(doc, renderer))
|
||||||
|
|
||||||
// Sanitize content
|
// sanitize content
|
||||||
output = sanitizeXSS(output)
|
output = sanitizeXSS(output)
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|||||||
Reference in New Issue
Block a user