179 lines
3.6 KiB
Go
179 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const tianapiAIURL = "https://apis.tianapi.com/ai/index"
|
|
|
|
type aiNewsResp struct {
|
|
Code int `json:"code"`
|
|
Result struct {
|
|
Newslist []struct {
|
|
ID string `json:"id"`
|
|
CTime string `json:"ctime"`
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
Source string `json:"source"`
|
|
URL string `json:"url"`
|
|
PicUrl string `json:"picUrl"`
|
|
} `json:"newslist"`
|
|
} `json:"result"`
|
|
}
|
|
|
|
type aiNewsItem struct {
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
Source string `json:"source"`
|
|
CTime string `json:"ctime"`
|
|
URL string `json:"url"`
|
|
PicURL string `json:"picUrl"`
|
|
}
|
|
|
|
var (
|
|
aiNewsMu sync.Mutex
|
|
aiNewsCache []aiNewsItem
|
|
aiNewsCacheAt time.Time
|
|
)
|
|
|
|
func aiNewsCachePath() string {
|
|
return filepath.Join(configDir(), "ainews_cache.json")
|
|
}
|
|
|
|
func fetchAINews() []aiNewsItem {
|
|
aiNewsMu.Lock()
|
|
if aiNewsCache != nil && time.Since(aiNewsCacheAt) < 2*time.Hour {
|
|
cached := aiNewsCache
|
|
aiNewsMu.Unlock()
|
|
return cached
|
|
}
|
|
aiNewsMu.Unlock()
|
|
|
|
key := loadConfig().tianapiKey()
|
|
if key == "" {
|
|
log.Println("未配置天聚数行 API Key")
|
|
return nil
|
|
}
|
|
url := fmt.Sprintf("%s?key=%s", tianapiAIURL, key)
|
|
data, err := httpGet(url)
|
|
if err != nil {
|
|
log.Println("AI资讯请求失败:", err)
|
|
return nil
|
|
}
|
|
|
|
var resp aiNewsResp
|
|
if json.Unmarshal(data, &resp) != nil || resp.Code != 200 {
|
|
log.Println("AI资讯解析失败:", string(data[:min(len(data), 100)]))
|
|
return nil
|
|
}
|
|
|
|
var items []aiNewsItem
|
|
for _, n := range resp.Result.Newslist {
|
|
items = append(items, aiNewsItem{
|
|
Title: n.Title,
|
|
Description: n.Description,
|
|
Source: n.Source,
|
|
CTime: n.CTime,
|
|
URL: n.URL,
|
|
PicURL: n.PicUrl,
|
|
})
|
|
}
|
|
|
|
aiNewsMu.Lock()
|
|
aiNewsCache = items
|
|
aiNewsCacheAt = time.Now()
|
|
aiNewsMu.Unlock()
|
|
|
|
// 缓存到文件
|
|
cacheData, _ := json.Marshal(map[string]interface{}{
|
|
"items": items,
|
|
"at": time.Now().Format(time.RFC3339),
|
|
})
|
|
if err := os.WriteFile(aiNewsCachePath(), cacheData, 0644); err != nil {
|
|
log.Println("AI资讯缓存写入失败:", err)
|
|
}
|
|
|
|
log.Printf("AI资讯已获取: %d条", len(items))
|
|
return items
|
|
}
|
|
|
|
func loadAINewsCache() []aiNewsItem {
|
|
data, err := os.ReadFile(aiNewsCachePath())
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
var cached struct {
|
|
Items []aiNewsItem `json:"items"`
|
|
At string `json:"at"`
|
|
}
|
|
if json.Unmarshal(data, &cached) != nil {
|
|
return nil
|
|
}
|
|
return cached.Items
|
|
}
|
|
|
|
func pushAINews(items []aiNewsItem) {
|
|
if len(items) == 0 {
|
|
return
|
|
}
|
|
jsonData, _ := json.Marshal(items)
|
|
js := fmt.Sprintf(`if(window.updateAINewsFromGo) window.updateAINewsFromGo(%s)`, string(jsonData))
|
|
evalJS(js)
|
|
}
|
|
|
|
func aiNewsLoop() {
|
|
cfg := loadConfig()
|
|
if !cfg.HideAINews {
|
|
// 先推送缓存
|
|
if cached := loadAINewsCache(); cached != nil {
|
|
pushAINews(cached)
|
|
}
|
|
} else if cached := loadAINewsCache(); cached != nil {
|
|
aiNewsMu.Lock()
|
|
aiNewsCache = cached
|
|
aiNewsCacheAt = time.Now()
|
|
aiNewsMu.Unlock()
|
|
}
|
|
|
|
time.Sleep(8 * time.Second)
|
|
|
|
cfg = loadConfig()
|
|
if !cfg.HideAINews {
|
|
var items []aiNewsItem
|
|
for i := 0; i < 3; i++ {
|
|
items = fetchAINews()
|
|
if items != nil {
|
|
break
|
|
}
|
|
time.Sleep(30 * time.Second)
|
|
}
|
|
if items != nil {
|
|
pushAINews(items)
|
|
}
|
|
}
|
|
|
|
ticker := time.NewTicker(2 * time.Hour)
|
|
for range ticker.C {
|
|
cfg := loadConfig()
|
|
if !cfg.HideAINews {
|
|
if items := fetchAINews(); items != nil {
|
|
pushAINews(items)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func triggerAINewsRefresh() {
|
|
go func() {
|
|
if items := fetchAINews(); items != nil {
|
|
pushAINews(items)
|
|
}
|
|
}()
|
|
}
|