新增: 星座运势+AI资讯+知识卡片+桌面设置窗口+秒显示开关

- 星座运势: 天聚数行API集成,5维进度条+幸运标签+今日概述
- AI资讯: 天聚数行API,图文布局5条展示,文件缓存2小时刷新
- 知识卡片: AI生成,关键字+提示词配置,30分钟刷新
- 桌面设置: 独立WebView2窗口,760x1350,含壁纸/布局/城市/颜色等配置
- 显示控制: 壁纸/时间/天气/星座/知识/AI资讯独立开关,秒显示开关
- 文件缓存: 星座运势+AI资讯缓存到本地,启动即显示上次数据
- initDone防抖: 防止设置窗口初始化触发卡片重载
This commit is contained in:
2026-05-26 04:34:00 +08:00
parent 2287e12e0d
commit 9fd3acede3
15 changed files with 2607 additions and 384 deletions

165
ainews.go Normal file
View File

@@ -0,0 +1,165 @@
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()
url := fmt.Sprintf("%s?key=%s", tianapiAIURL, tianapiKey)
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 {
return
}
// 先推送缓存
if cached := loadAINewsCache(); cached != nil {
pushAINews(cached)
}
time.Sleep(8 * time.Second)
cfg = loadConfig()
if cfg.HideAINews {
return
}
items := fetchAINews()
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)
}
}()
}