diff options
| author | eltrac <hi@eltr.ac> | 2026-04-07 14:46:20 +0800 |
|---|---|---|
| committer | eltrac <hi@eltr.ac> | 2026-04-07 14:46:20 +0800 |
| commit | d733a7aede6421de9f3077b17d7011fadd598241 (patch) | |
| tree | 6e65401fb82ff0425c7302a984044819a714a370 /scripts | |
chore: move templates from geek-death-project
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/extract_highlights.go | 120 | ||||
| -rw-r--r-- | scripts/get_external_link_rank.go | 77 | ||||
| -rw-r--r-- | scripts/get_internal_link_graph.go | 101 |
3 files changed, 298 insertions, 0 deletions
diff --git a/scripts/extract_highlights.go b/scripts/extract_highlights.go new file mode 100644 index 0000000..c7253d5 --- /dev/null +++ b/scripts/extract_highlights.go @@ -0,0 +1,120 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/fs" + "net/url" + "os" + "path/filepath" + "regexp" + "strings" +) + +type HighlightLink struct { + Text string `json:"text"` + Link string `json:"link"` +} + +type Result struct { + Title string `json:"title"` + URL string `json:"url"` + Highlights []HighlightLink `json:"highlights"` +} + +func main() { + const contentDir = "content" + const outputFile = "data/highlights.json" + + highlightPattern := regexp.MustCompile(`==(.+?)==`) + titlePattern := regexp.MustCompile(`(?m)^title:\s*["']?(.+?)["']?\s*$`) + slugPattern := regexp.MustCompile(`(?m)^slug:\s*["']?(.+?)["']?\s*$`) + + var results []Result + + err := filepath.WalkDir(contentDir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() || !strings.HasSuffix(d.Name(), ".md") { + return nil + } + + contentBytes, err := os.ReadFile(path) + if err != nil { + return err + } + content := string(contentBytes) + + // Title + title := filepath.Base(path) + if m := titlePattern.FindStringSubmatch(content); len(m) > 1 { + title = m[1] + } + + // Slug and URL + urlPath := "" + if m := slugPattern.FindStringSubmatch(content); len(m) > 1 { + slug := strings.Trim(m[1], "/") + relDir, _ := filepath.Rel(contentDir, filepath.Dir(path)) + relDir = filepath.ToSlash(relDir) + if relDir != "." { + urlPath = fmt.Sprintf("/%s/%s/", relDir, slug) + } else { + urlPath = fmt.Sprintf("/%s/", slug) + } + } else { + rel, _ := filepath.Rel(contentDir, path) + rel = filepath.ToSlash(rel) + urlPath = "/" + strings.TrimSuffix(rel, ".md") + "/" + } + + // Highlights + matches := highlightPattern.FindAllStringSubmatch(content, -1) + if len(matches) == 0 { + return nil + } + + var highlightLinks []HighlightLink + for _, m := range matches { + if len(m) > 1 { + h := m[1] + encoded := url.QueryEscape(h) + link := fmt.Sprintf("%s#:~:text=%s", urlPath, encoded) + highlightLinks = append(highlightLinks, HighlightLink{Text: h, Link: link}) + } + } + + results = append(results, Result{ + Title: title, + URL: urlPath, + Highlights: highlightLinks, + }) + + return nil + }) + + if err != nil { + fmt.Println("❌ Error:", err) + return + } + + // Write JSON + if err := os.MkdirAll(filepath.Dir(outputFile), os.ModePerm); err != nil { + fmt.Println("❌ Error creating output directory:", err) + return + } + + jsonData, err := json.MarshalIndent(results, "", " ") + if err != nil { + fmt.Println("❌ Error encoding JSON:", err) + return + } + + if err := os.WriteFile(outputFile, jsonData, 0644); err != nil { + fmt.Println("❌ Error writing JSON file:", err) + return + } + + fmt.Printf("✔ 提取完成,共 %d 篇文章有高亮,已写入 %s\n", len(results), outputFile) +} diff --git a/scripts/get_external_link_rank.go b/scripts/get_external_link_rank.go new file mode 100644 index 0000000..4189c19 --- /dev/null +++ b/scripts/get_external_link_rank.go @@ -0,0 +1,77 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/fs" + "net/url" + "os" + "path/filepath" + "regexp" + "sort" + "strings" +) + +func main() { + externalLinks := countExternalLinks("content") + writeJSON("data/externallinks.json", externalLinks) + fmt.Println("✅ External link rank data generated.") +} + +// countExternalLinks scans markdown files and counts occurrences of each external domain. +func countExternalLinks(root string) [][2]interface{} { + linkRegex := regexp.MustCompile(`https?://[^\s)>"']+`) + counts := make(map[string]int) + + filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if err != nil || d.IsDir() || !strings.HasSuffix(path, ".md") { + return nil + } + data, err := os.ReadFile(path) + if err != nil { + return nil + } + matches := linkRegex.FindAllString(string(data), -1) + for _, match := range matches { + u, err := url.Parse(match) + if err == nil && u.Host != "" { + // skip my domains + if strings.HasSuffix(u.Host, "eltr.ac") || strings.HasSuffix(u.Host, "guhub.cn") { + continue + } + counts[u.Host]++ + } + } + return nil + }) + + // convert map to sorted array (alphabetically by domain) + type kv struct { + Key string + Value int + } + var list []kv + for k, v := range counts { + list = append(list, kv{k, v}) + } + sort.Slice(list, func(i, j int) bool { return list[i].Key < list[j].Key }) + + result := make([][2]interface{}, len(list)) + for i, kv := range list { + result[i] = [2]interface{}{kv.Key, kv.Value} + } + return result +} + +// writeJSON writes the result to file with indentation. +func writeJSON(path string, data interface{}) { + file, err := os.Create(path) + if err != nil { + fmt.Println("Error:", err) + return + } + defer file.Close() + enc := json.NewEncoder(file) + enc.SetIndent("", " ") + enc.Encode(data) +} diff --git a/scripts/get_internal_link_graph.go b/scripts/get_internal_link_graph.go new file mode 100644 index 0000000..8adf559 --- /dev/null +++ b/scripts/get_internal_link_graph.go @@ -0,0 +1,101 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/fs" + "os" + "path/filepath" + "regexp" + "sort" + "strings" +) + +type Node struct { + ID string `json:"id"` + Label string `json:"label"` + URL string `json:"url"` +} + +type Edge struct { + From string `json:"from"` + To string `json:"to"` +} + +func main() { + root := "content" + + nodes := map[string]Node{} + edges := make([]Edge, 0) // non-nil so it becomes [] not null + + err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if err != nil || d.IsDir() || !strings.HasSuffix(strings.ToLower(path), ".md") { + return nil + } + + rel, err := filepath.Rel(root, path) + if err != nil { + return nil + } + rel = filepath.ToSlash(rel) + urlPath := "/" + strings.TrimSuffix(rel, ".md") + "/" + label := strings.TrimSuffix(filepath.Base(path), ".md") + + nodes[urlPath] = Node{ID: urlPath, Label: label, URL: urlPath} + + data, err := os.ReadFile(path) + if err != nil { + return nil + } + content := string(data) + + linkPattern := regexp.MustCompile(`\[[^\]]*\]\((/[^)]+)\)`) + matches := linkPattern.FindAllStringSubmatch(content, -1) + for _, m := range matches { + link := strings.TrimSuffix(m[1], ".md") + if !strings.HasSuffix(link, "/") { + link += "/" + } + edges = append(edges, Edge{From: urlPath, To: link}) + } + + return nil + }) + + if err != nil { + fmt.Println("WalkDir error:", err) + return + } + + nodeKeys := make([]string, 0, len(nodes)) + for k := range nodes { + nodeKeys = append(nodeKeys, k) + } + sort.Strings(nodeKeys) + nodeList := make([]Node, 0, len(nodeKeys)) + for _, k := range nodeKeys { + nodeList = append(nodeList, nodes[k]) + } + + os.MkdirAll("static", os.ModePerm) + output := map[string]interface{}{ + "nodes": nodeList, + "edges": edges, + } + + outfile := "static/link-graph.json" + f, err := os.Create(outfile) + if err != nil { + fmt.Println("create file error:", err) + return + } + defer f.Close() + + enc := json.NewEncoder(f) + enc.SetIndent("", " ") + if err := enc.Encode(output); err != nil { + fmt.Println("json encode error:", err) + } + + fmt.Printf("✅ Link graph written to %s (%d nodes, %d edges)\n", outfile, len(nodeList), len(edges)) +} |
