mirror of
https://github.com/karust/openserp.git
synced 2026-08-05 16:53:54 +08:00
149 lines
4.2 KiB
Go
149 lines
4.2 KiB
Go
package core
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// RenderMarkdown formats an Envelope as a Markdown document suitable for
|
|
// Slack/Discord/email nodes in n8n workflows.
|
|
func RenderMarkdown(env *Envelope) []byte {
|
|
var b strings.Builder
|
|
|
|
enginesStr := strings.Join(env.Query.EnginesRequested, ", ")
|
|
fmt.Fprintf(&b, "# Search results for %q\n\n", env.Query.Text)
|
|
fmt.Fprintf(&b, "**Query:** %s - **Engines:** %s - **Took:** %dms\n\n",
|
|
env.Query.Text, enginesStr, env.Meta.TookMs)
|
|
|
|
if len(env.Meta.EnginesFailed) > 0 {
|
|
fmt.Fprintf(&b, "> Engines that failed: %s\n\n", strings.Join(env.Meta.EnginesFailed, ", "))
|
|
}
|
|
|
|
renderMarkdownFeatures(&b, env.SerpFeatures, featureRenderOrderBeforeResults())
|
|
|
|
if len(env.Results) > 0 {
|
|
b.WriteString("## Results\n\n")
|
|
}
|
|
for i, r := range env.Results {
|
|
fmt.Fprintf(&b, "### %d. %s\n\n", i+1, escapeMarkdown(r.Title))
|
|
typeLabel := string(r.Type)
|
|
fmt.Fprintf(&b, "**%s** - %s\n\n", r.DisplayURL, typeLabel)
|
|
if r.Snippet != "" {
|
|
fmt.Fprintf(&b, "%s\n\n", r.Snippet)
|
|
}
|
|
fmt.Fprintf(&b, "-> %s\n\n", r.URL)
|
|
if r.Extracted != nil && r.Extracted.Content != "" {
|
|
b.WriteString("#### Extracted content\n\n")
|
|
b.WriteString(shiftMarkdownHeadings(r.Extracted.Content, 4))
|
|
b.WriteString("\n\n")
|
|
}
|
|
}
|
|
|
|
renderMarkdownFeatures(&b, env.SerpFeatures, featureRenderOrderAfterResults(env.SerpFeatures))
|
|
|
|
return []byte(b.String())
|
|
}
|
|
|
|
func shiftMarkdownHeadings(markdown string, minLevel int) string {
|
|
lines := strings.Split(markdown, "\n")
|
|
for i, line := range lines {
|
|
trimmed := strings.TrimLeft(line, " ")
|
|
indent := line[:len(line)-len(trimmed)]
|
|
if !strings.HasPrefix(trimmed, "#") {
|
|
continue
|
|
}
|
|
count := 0
|
|
for count < len(trimmed) && trimmed[count] == '#' {
|
|
count++
|
|
}
|
|
if count == 0 || count >= len(trimmed) || trimmed[count] != ' ' {
|
|
continue
|
|
}
|
|
target := count + minLevel
|
|
if target > 6 {
|
|
target = 6
|
|
}
|
|
lines[i] = indent + strings.Repeat("#", target) + trimmed[count:]
|
|
}
|
|
return strings.TrimSpace(strings.Join(lines, "\n"))
|
|
}
|
|
|
|
// RenderMarkdownImage formats an ImageEnvelope as Markdown.
|
|
func RenderMarkdownImage(env *ImageEnvelope) []byte {
|
|
var b strings.Builder
|
|
|
|
enginesStr := strings.Join(env.Query.EnginesRequested, ", ")
|
|
fmt.Fprintf(&b, "# Image results for %q\n\n", env.Query.Text)
|
|
fmt.Fprintf(&b, "**Query:** %s - **Engines:** %s - **Took:** %dms\n\n",
|
|
env.Query.Text, enginesStr, env.Meta.TookMs)
|
|
|
|
for i, r := range env.Results {
|
|
fmt.Fprintf(&b, "## %d. %s\n\n", i+1, escapeMarkdown(r.Title))
|
|
fmt.Fprintf(&b, "**Source:** %s\n\n", r.Source.Domain)
|
|
fmt.Fprintf(&b, "-> Image: %s\n", r.Image.URL)
|
|
fmt.Fprintf(&b, "-> Page: %s\n\n", r.Source.PageURL)
|
|
}
|
|
|
|
return []byte(b.String())
|
|
}
|
|
|
|
func renderMarkdownFeatures(b *strings.Builder, features []SerpFeature, order []ResultType) {
|
|
forEachFeatureInOrder(features, order, func(feature SerpFeature) {
|
|
renderMarkdownFeature(b, feature)
|
|
})
|
|
}
|
|
|
|
func renderMarkdownFeature(b *strings.Builder, feature SerpFeature) {
|
|
heading := featureHeading(feature)
|
|
if feature.Type == ResultTypeKnowledgePanel && feature.Title != "" {
|
|
heading += " - " + feature.Title
|
|
}
|
|
fmt.Fprintf(b, "## %s\n\n", heading)
|
|
if feature.Type == ResultTypeFeaturedSnippet && feature.Text != "" {
|
|
fmt.Fprintf(b, "> %s\n", feature.Text)
|
|
if len(feature.Links) > 0 {
|
|
fmt.Fprintf(b, "> - [%s](%s)\n", escapeMarkdown(feature.Links[0].Title), feature.Links[0].URL)
|
|
}
|
|
b.WriteString("\n")
|
|
return
|
|
}
|
|
if feature.Text != "" {
|
|
fmt.Fprintf(b, "%s\n\n", feature.Text)
|
|
}
|
|
if len(feature.Items) > 0 {
|
|
for _, item := range feature.Items {
|
|
switch {
|
|
case item.Title != "" && item.Text != "":
|
|
fmt.Fprintf(b, "- **%s** - %s\n", escapeMarkdown(item.Title), item.Text)
|
|
case item.Text != "":
|
|
fmt.Fprintf(b, "- %s\n", item.Text)
|
|
case item.Title != "":
|
|
fmt.Fprintf(b, "- %s\n", escapeMarkdown(item.Title))
|
|
}
|
|
}
|
|
b.WriteString("\n")
|
|
}
|
|
if len(feature.Links) > 0 {
|
|
b.WriteString("Sources:\n")
|
|
for _, link := range feature.Links {
|
|
title := link.Title
|
|
if title == "" {
|
|
title = link.URL
|
|
}
|
|
fmt.Fprintf(b, "- [%s](%s)\n", escapeMarkdown(title), link.URL)
|
|
}
|
|
b.WriteString("\n")
|
|
}
|
|
}
|
|
|
|
func escapeMarkdown(s string) string {
|
|
replacer := strings.NewReplacer(
|
|
"*", `\*`,
|
|
"_", `\_`,
|
|
"`", "\\`",
|
|
"[", `\[`,
|
|
"]", `\]`,
|
|
)
|
|
return replacer.Replace(s)
|
|
}
|