thread-master/backend/internal/library/viral/free_trends.go

122 lines
3.0 KiB
Go
Raw Permalink Normal View History

package viral
import (
"context"
"encoding/xml"
"html"
"net/http"
"net/url"
"regexp"
"strings"
"time"
)
var rssTagRE = regexp.MustCompile(`<[^>]+>`)
type freeRSS struct {
Channel struct {
Items []freeRSSItem `xml:"item"`
} `xml:"channel"`
}
type freeRSSItem struct {
Title string `xml:"title"`
Description string `xml:"description"`
Link string `xml:"link"`
}
func CollectFreeTopicTrends(ctx context.Context, keyword, personaBrief string) []MissionInspireTrendSnippet {
queries := freeTrendFeedQueries(keyword, personaBrief)
client := &http.Client{Timeout: 7 * time.Second}
out := make([]MissionInspireTrendSnippet, 0, 12)
seen := map[string]struct{}{}
add := func(query, title, snippet, link string) {
title = cleanRSSField(title)
snippet = cleanRSSField(snippet)
link = strings.TrimSpace(link)
if title == "" && snippet == "" {
return
}
key := strings.ToLower(title + "|" + snippet)
if _, ok := seen[key]; ok {
return
}
seen[key] = struct{}{}
out = append(out, MissionInspireTrendSnippet{Query: query, Title: title, Snippet: snippet, URL: link})
}
for _, feed := range queries {
if len(out) >= 12 {
break
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, feed.url, nil)
if err != nil {
continue
}
req.Header.Set("User-Agent", "HaixunTopicBot/1.0")
resp, err := client.Do(req)
if err != nil || resp == nil {
continue
}
func() {
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return
}
var parsed freeRSS
if err := xml.NewDecoder(resp.Body).Decode(&parsed); err != nil {
return
}
for _, item := range parsed.Channel.Items {
if len(out) >= 12 {
break
}
add(feed.label, item.Title, item.Description, item.Link)
}
}()
}
return out
}
type freeTrendFeed struct {
label string
url string
}
func freeTrendFeedQueries(keyword, personaBrief string) []freeTrendFeed {
feeds := []freeTrendFeed{
{label: "Google Trends 台灣每日趨勢", url: "https://trends.google.com/trends/trendingsearches/daily/rss?geo=TW"},
{label: "Google Trends 台灣即時趨勢", url: "https://trends.google.com/trends/trendingsearches/realtime/rss?geo=TW&category=all"},
}
terms := []string{strings.TrimSpace(keyword)}
brief := strings.TrimSpace(personaBrief)
if brief != "" {
if len([]rune(brief)) > 18 {
brief = string([]rune(brief)[:18])
}
terms = append(terms, brief)
}
for _, term := range terms {
if term == "" {
continue
}
q := url.QueryEscape(term + " 共鳴 OR 痛點 OR 趨勢")
feeds = append(feeds, freeTrendFeed{
label: "Google News 搜尋:" + term,
url: "https://news.google.com/rss/search?q=" + q + "&hl=zh-TW&gl=TW&ceid=TW:zh-Hant",
})
}
return feeds
}
func cleanRSSField(raw string) string {
raw = html.UnescapeString(raw)
raw = rssTagRE.ReplaceAllString(raw, " ")
raw = strings.Join(strings.Fields(raw), " ")
if len([]rune(raw)) > 220 {
return string([]rune(raw)[:220]) + "…"
}
return raw
}