]+>\s*
]+>\d+
\s*
]+>(.*?)
`
35 | matches := utils.ExtractMatches(string(pageBytes), pattern)
36 |
37 | // 检查是否匹配到数据
38 | if len(matches) == 0 {
39 | return map[string]interface{}{
40 | "code": 500,
41 | "message": "未匹配到数据,可能页面结构已变更",
42 | "icon": "https://www.hupu.com/favicon.ico",
43 | "obj": []map[string]interface{}{},
44 | }, nil
45 | }
46 |
47 | var obj []map[string]interface{}
48 | for index, item := range matches {
49 | // 添加边界检查
50 | if len(item) >= 3 {
51 | url := item[1]
52 | title := item[2]
53 |
54 | // 确保 URL 是完整的
55 | if len(url) > 0 && url[0] == '/' {
56 | url = "https://www.hupu.com" + url
57 | }
58 |
59 | obj = append(obj, map[string]interface{}{
60 | "index": index + 1,
61 | "title": title,
62 | "url": url,
63 | })
64 | }
65 | }
66 |
67 | api := map[string]interface{}{
68 | "code": 200,
69 | "message": "虎扑",
70 | "icon": "https://www.hupu.com/favicon.ico", // 32 x 32
71 | "obj": obj,
72 | }
73 | return api, nil
74 | }
75 |
--------------------------------------------------------------------------------
/app/acfun.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | type acfunResponse struct {
12 | RankList []acfunData `json:"rankList"`
13 | }
14 | type acfunData struct {
15 | Title string `json:"contentTitle"`
16 | URL string `json:"shareUrl"`
17 | }
18 |
19 | func Acfun() (map[string]interface{}, error) {
20 | // 创建带超时的 HTTP 客户端
21 | client := &http.Client{
22 | Timeout: 10 * time.Second,
23 | }
24 |
25 | url := "https://www.acfun.cn/rest/pc-direct/rank/channel?channelId=&subChannelId=&rankLimit=30&rankPeriod=DAY"
26 | // 创建一个自定义请求
27 | req, err := http.NewRequest("GET", url, nil)
28 | if err != nil {
29 | return nil, fmt.Errorf("http.NewRequest error: %w", err)
30 | }
31 |
32 | // 设置 Headers
33 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36")
34 |
35 | resp, err := client.Do(req)
36 | if err != nil {
37 | return nil, fmt.Errorf("http.Client.Do error: %w", err)
38 | }
39 | defer resp.Body.Close()
40 |
41 | pageBytes, err := io.ReadAll(resp.Body)
42 | if err != nil {
43 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
44 | }
45 |
46 | var resultMap acfunResponse
47 | err = json.Unmarshal(pageBytes, &resultMap)
48 | if err != nil {
49 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
50 | }
51 |
52 | var obj []map[string]interface{}
53 | for index, item := range resultMap.RankList {
54 | obj = append(obj, map[string]interface{}{
55 | "index": index + 1,
56 | "title": item.Title,
57 | "url": item.URL,
58 | })
59 | }
60 |
61 | api := map[string]interface{}{
62 | "code": 200,
63 | "message": "AcFun",
64 | "icon": "https://cdn.aixifan.com/ico/favicon.ico", // 32 x 32
65 | "obj": obj,
66 | }
67 |
68 | return api, nil
69 | }
70 |
--------------------------------------------------------------------------------
/app/wangyinews.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "api/utils"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | func WangyiNews() (map[string]interface{}, error) {
13 | // 创建带超时的 HTTP 客户端
14 | client := &http.Client{
15 | Timeout: 10 * time.Second,
16 | }
17 | url := "https://news.163.com/"
18 | resp, err := client.Get(url)
19 | if err != nil {
20 | return nil, fmt.Errorf("http.Get error: %w", err)
21 | }
22 | defer resp.Body.Close()
23 |
24 | // 检查状态码
25 | if resp.StatusCode != http.StatusOK {
26 | return nil, fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
27 | }
28 |
29 | pageBytes, _ := io.ReadAll(resp.Body)
30 | if err != nil {
31 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
32 | }
33 |
34 | pattern := `
\d*\s*
]*>(.*?)\s*
(\d*)`
35 | matched := utils.ExtractMatches(string(pageBytes), pattern)
36 | // 检查是否匹配到数据
37 | if len(matched) == 0 {
38 | return map[string]interface{}{
39 | "code": 500,
40 | "message": "未匹配到数据,可能页面结构已变更",
41 | "icon": "https://news.163.com/favicon.ico",
42 | "obj": []map[string]interface{}{},
43 | }, nil
44 | }
45 |
46 | var obj []map[string]interface{}
47 | for index, item := range matched {
48 | hot, err := strconv.ParseFloat(item[3], 64)
49 | if err != nil {
50 | // 处理转换错误,可以给默认值或者跳过该项
51 | hot = 0
52 | // 或者使用日志记录错误但不中断程序
53 | // log.Printf("parse hot value error for item %s: %v", title, err)
54 | }
55 | obj = append(obj, map[string]interface{}{
56 | "index": index + 1,
57 | "title": item[2],
58 | "url": item[1],
59 | "hotValue": fmt.Sprintf("%.1f万", hot/10000),
60 | })
61 | }
62 | api := map[string]interface{}{
63 | "code": 200,
64 | "message": "网易新闻",
65 | "icon": "https://news.163.com/favicon.ico", // 16 x 16
66 | "obj": obj,
67 | }
68 | return api, nil
69 | }
70 |
--------------------------------------------------------------------------------
/app/cctv.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | type cctvResponse struct {
12 | Data cctvData `json:"data"`
13 | }
14 | type cctvData struct {
15 | List []cctvList `json:"list"`
16 | }
17 |
18 | type cctvList struct {
19 | Title string `json:"title"`
20 | URL string `json:"url"`
21 | }
22 |
23 | func CCTV() (map[string]interface{}, error) {
24 | // 创建带超时的 HTTP 客户端
25 | client := &http.Client{
26 | Timeout: 10 * time.Second,
27 | }
28 |
29 | url := "https://news.cctv.com/2019/07/gaiban/cmsdatainterface/page/world_1.jsonp"
30 | resp, err := client.Get(url)
31 | if err != nil {
32 | return nil, fmt.Errorf("http.Get error: %w", err)
33 | }
34 | defer resp.Body.Close()
35 |
36 | pageBytes, err := io.ReadAll(resp.Body)
37 | if err != nil {
38 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
39 | }
40 |
41 | // 检查响应长度是否足够
42 | if len(pageBytes) <= 6 {
43 | return nil, fmt.Errorf("API返回数据长度不足")
44 | }
45 |
46 | var resultMap cctvResponse
47 | // 删除 JSONP 回调函数包裹,解析实际 JSON 数据
48 | err = json.Unmarshal(pageBytes[6:len(pageBytes)-1], &resultMap)
49 | if err != nil {
50 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
51 | }
52 |
53 | // 检查数据是否为空
54 | if len(resultMap.Data.List) == 0 {
55 | return map[string]interface{}{
56 | "code": 500,
57 | "message": "API返回数据为空",
58 | "icon": "https://news.cctv.com/favicon.ico",
59 | "obj": []map[string]interface{}{},
60 | }, nil
61 | }
62 |
63 | var obj []map[string]interface{}
64 | for index, item := range resultMap.Data.List {
65 | obj = append(obj, map[string]interface{}{
66 | "index": index + 1,
67 | "title": item.Title,
68 | "url": item.URL,
69 | })
70 | }
71 |
72 | api := map[string]interface{}{
73 | "code": 200,
74 | "message": "CCTV新闻",
75 | "icon": "https://news.cctv.com/favicon.ico", // 16 x 16
76 | "obj": obj,
77 | }
78 | return api, nil
79 | }
80 |
--------------------------------------------------------------------------------
/app/360search.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | type search360Item struct {
13 | LongTitle string `json:"long_title"`
14 | Title string `json:"title"`
15 | Score string `json:"score"`
16 | Rank string `json:"rank"`
17 | }
18 |
19 | func Search360() (map[string]interface{}, error) {
20 | url := "https://ranks.hao.360.com/mbsug-api/hotnewsquery?type=news&realhot_limit=50"
21 | client := &http.Client{
22 | Timeout: 10 * time.Second,
23 | }
24 | resp, err := client.Get(url)
25 | if err != nil {
26 | return nil, fmt.Errorf("http.Get error: %w", err)
27 | }
28 | defer resp.Body.Close()
29 |
30 | // 2.读取页面内容
31 | pageBytes, err := io.ReadAll(resp.Body)
32 | if err != nil {
33 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
34 | }
35 |
36 | var resultSlice []search360Item
37 | err = json.Unmarshal(pageBytes, &resultSlice)
38 | if err != nil {
39 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
40 | }
41 |
42 | var obj []map[string]interface{}
43 | for _, item := range resultSlice {
44 | title := item.Title
45 | if item.LongTitle != "" {
46 | title = item.LongTitle
47 | }
48 |
49 | hot, err := strconv.ParseFloat(item.Score, 64)
50 | if err != nil {
51 | // 处理转换错误,可以给默认值或者跳过该项
52 | hot = 0
53 | // 或者使用日志记录错误但不中断程序
54 | // log.Printf("parse hot value error for item %s: %v", title, err)
55 | }
56 |
57 | obj = append(obj, map[string]interface{}{
58 | "index": item.Rank,
59 | "title": title,
60 | "hotValue": fmt.Sprintf("%.1f万", hot/10000),
61 | "url": "https://www.so.com/s?q=" + title,
62 | })
63 | }
64 |
65 | api := map[string]interface{}{
66 | "code": 200,
67 | "message": "360搜索",
68 | "icon": "https://ss.360tres.com/static/121a1737750aa53d.ico",
69 | "obj": obj,
70 | }
71 | return api, nil
72 | }
73 |
--------------------------------------------------------------------------------
/app/shaoshupai.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | type sspResponse struct {
12 | Data []sspData `json:"data"`
13 | }
14 | type sspData struct {
15 | Title string `json:"title"`
16 | ID int `json:"id"`
17 | }
18 |
19 | func Shaoshupai() (map[string]interface{}, error) {
20 | // 创建带超时的 HTTP 客户端
21 | client := &http.Client{
22 | Timeout: 10 * time.Second,
23 | }
24 | url := "https://sspai.com/api/v1/article/tag/page/get?limit=100000&tag=%E7%83%AD%E9%97%A8%E6%96%87%E7%AB%A0"
25 | resp, err := client.Get(url)
26 | if err != nil {
27 | return nil, fmt.Errorf("http.Get error: %w", err)
28 | }
29 | defer resp.Body.Close()
30 | // 检查状态码
31 | if resp.StatusCode != http.StatusOK {
32 | return nil, fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
33 | }
34 | pageBytes, err := io.ReadAll(resp.Body)
35 | if err != nil {
36 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
37 | }
38 |
39 | var resultMap sspResponse
40 | err = json.Unmarshal(pageBytes, &resultMap)
41 | if err != nil {
42 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
43 | }
44 | // 检查数据是否为空
45 | if len(resultMap.Data) == 0 {
46 | return map[string]interface{}{
47 | "code": 500,
48 | "message": "API返回数据为空",
49 | "icon": "https://cdn-static.sspai.com/favicon/sspai.ico",
50 | "obj": []map[string]interface{}{},
51 | }, nil
52 | }
53 |
54 | data := resultMap.Data
55 | var obj []map[string]interface{}
56 | for index, item := range data {
57 | obj = append(obj, map[string]interface{}{
58 | "index": index + 1,
59 | "title": item.Title,
60 | "url": "https://sspai.com/post/" + fmt.Sprint(item.ID),
61 | })
62 | }
63 | api := map[string]interface{}{
64 | "code": 200,
65 | "message": "少数派",
66 | "icon": "https://cdn-static.sspai.com/favicon/sspai.ico", // 64 x 64
67 | "obj": obj,
68 | }
69 | return api, nil
70 | }
71 |
--------------------------------------------------------------------------------
/app/nanfangzhoumo.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | type nfResponse struct {
13 | NfzmData nfData `json:"data"`
14 | }
15 | type nfData struct {
16 | HotContents []contents `json:"hot_contents"`
17 | }
18 | type contents struct {
19 | Title string `json:"subject"`
20 | ID float64 `json:"id"`
21 | }
22 |
23 | func Nanfangzhoumo() (map[string]interface{}, error) {
24 | // 创建带超时的 HTTP 客户端
25 | client := &http.Client{
26 | Timeout: 10 * time.Second,
27 | }
28 |
29 | url := "https://www.infzm.com/hot_contents?format=json"
30 | resp, err := client.Get(url)
31 | if err != nil {
32 | return nil, fmt.Errorf("http.Get error: %w", err)
33 | }
34 | defer resp.Body.Close()
35 | // 检查状态码
36 | if resp.StatusCode != http.StatusOK {
37 | return nil, fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
38 | }
39 |
40 | pageBytes, err := io.ReadAll(resp.Body)
41 | if err != nil {
42 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
43 | }
44 | var resultMap nfResponse
45 | err = json.Unmarshal(pageBytes, &resultMap)
46 | if err != nil {
47 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
48 | }
49 | wordList := resultMap.NfzmData.HotContents
50 | // 检查数据是否为空
51 | if len(wordList) == 0 {
52 | return map[string]interface{}{
53 | "code": 500,
54 | "message": "API返回数据为空",
55 | "icon": "https://www.infzm.com/favicon.ico",
56 | "obj": []map[string]interface{}{},
57 | }, nil
58 | }
59 | var obj []map[string]interface{}
60 | for index, item := range wordList {
61 | obj = append(obj, map[string]interface{}{
62 | "index": index + 1,
63 | "title": item.Title,
64 | "url": "https://www.infzm.com/contents/" + strconv.FormatFloat(item.ID, 'f', -1, 64),
65 | })
66 | }
67 | api := map[string]interface{}{
68 | "code": 200,
69 | "message": "南方周末",
70 | "icon": "https://www.infzm.com/favicon.ico", // 32 x 32
71 | "obj": obj,
72 | }
73 | return api, nil
74 | }
75 |
--------------------------------------------------------------------------------
/app/github.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "api/utils"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "strings"
9 | "time"
10 | )
11 |
12 | func Github() (map[string]interface{}, error) {
13 | // 创建带超时的 HTTP 客户端
14 | client := &http.Client{
15 | Timeout: 10 * time.Second,
16 | }
17 |
18 | url := "https://github.com/trending"
19 | resp, err := client.Get(url)
20 | if err != nil {
21 | return nil, fmt.Errorf("http.Get error: %w", err)
22 | }
23 | defer resp.Body.Close()
24 |
25 | pageBytes, err := io.ReadAll(resp.Body)
26 | if err != nil {
27 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
28 | }
29 |
30 | pattern := `
\s*([^<]+)\s*<\/span>\s*([^<]+)<\/a>\s*<\/h2>\s*\s*([^<]+)\s*<\/p>`
31 | // 注意:这里假设 utils.ExtractMatches 没有改变,如果它也返回 error,需要修改
32 | matched := utils.ExtractMatches(string(pageBytes), pattern)
33 |
34 | // 检查是否匹配到数据
35 | if len(matched) == 0 {
36 | return map[string]interface{}{
37 | "code": 500,
38 | "message": "未匹配到 trending 数据,可能页面结构已变更",
39 | "icon": "https://github.githubassets.com/favicons/favicon.png",
40 | "obj": []map[string]interface{}{},
41 | }, nil
42 | }
43 |
44 | var obj []map[string]interface{}
45 | for index, item := range matched {
46 | // 添加边界检查
47 | if len(item) >= 4 {
48 | // 清理用户名和仓库名
49 | user := strings.TrimSpace(item[1])
50 | repo := strings.TrimSpace(item[2])
51 | // 合并并移除所有空格
52 | trimed := strings.ReplaceAll(user+repo, " ", "")
53 |
54 | desc := ""
55 | if len(item) >= 4 {
56 | desc = strings.TrimSpace(item[3])
57 | }
58 |
59 | obj = append(obj, map[string]interface{}{
60 | "index": index + 1,
61 | "title": trimed,
62 | "desc": desc,
63 | "url": "https://github.com/" + trimed,
64 | })
65 | }
66 | }
67 |
68 | api := map[string]interface{}{
69 | "code": 200,
70 | "message": "GitHub",
71 | "icon": "https://github.githubassets.com/favicons/favicon.png", // 32 x 32
72 | "obj": obj,
73 | }
74 | return api, nil
75 | }
76 |
--------------------------------------------------------------------------------
/app/ithome.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "api/utils"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | func Ithome() (map[string]interface{}, error) {
12 | // 创建带超时的 HTTP 客户端
13 | client := &http.Client{
14 | Timeout: 10 * time.Second,
15 | }
16 |
17 | url := "https://m.ithome.com/rankm/"
18 | resp, err := client.Get(url)
19 | if err != nil {
20 | return nil, fmt.Errorf("http.Get error: %w", err)
21 | }
22 | defer resp.Body.Close()
23 |
24 | // 检查状态码
25 | if resp.StatusCode != http.StatusOK {
26 | return nil, fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
27 | }
28 |
29 | pageBytes, err := io.ReadAll(resp.Body)
30 | if err != nil {
31 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
32 | }
33 |
34 | pattern := `]*>[\s\S]*?([^<]+)
`
35 | matches := utils.ExtractMatches(string(pageBytes), pattern)
36 |
37 | // 检查是否匹配到数据
38 | if len(matches) == 0 {
39 | return map[string]interface{}{
40 | "code": 500,
41 | "message": "未匹配到数据,可能页面结构已变更",
42 | "icon": "https://www.ithome.com/favicon.ico",
43 | "obj": []map[string]interface{}{},
44 | }, nil
45 | }
46 |
47 | // 确定要取多少条数据(最多12条)
48 | count := len(matches)
49 | if count > 12 {
50 | count = 12
51 | }
52 |
53 | var obj []map[string]interface{}
54 | for index := 0; index < count; index++ {
55 | item := matches[index]
56 | // 添加边界检查
57 | if len(item) >= 3 {
58 | obj = append(obj, map[string]interface{}{
59 | "index": index + 1,
60 | "title": item[2],
61 | "url": item[1],
62 | })
63 | }
64 | }
65 |
66 | // 确保有有效数据
67 | if len(obj) == 0 {
68 | return map[string]interface{}{
69 | "code": 500,
70 | "message": "处理后的数据为空",
71 | "icon": "https://www.ithome.com/favicon.ico",
72 | "obj": []map[string]interface{}{},
73 | }, nil
74 | }
75 |
76 | api := map[string]interface{}{
77 | "code": 200,
78 | "message": "IT之家",
79 | "icon": "https://www.ithome.com/favicon.ico", // 32 x 32
80 | "obj": obj,
81 | }
82 | return api, nil
83 | }
84 |
--------------------------------------------------------------------------------
/app/douyin.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "net/url"
9 | "time"
10 | )
11 |
12 | type Douyinresponse struct {
13 | WordList []Douyindata `json:"word_list"`
14 | }
15 |
16 | type Douyindata struct {
17 | Title string `json:"word"`
18 | HotVaule float64 `json:"hot_value"`
19 | }
20 |
21 | func Douyin() (map[string]interface{}, error) {
22 | // 创建带超时的 HTTP 客户端
23 | client := &http.Client{
24 | Timeout: 10 * time.Second,
25 | }
26 |
27 | urlStr := "https://www.iesdouyin.com/web/api/v2/hotsearch/billboard/word/"
28 | resp, err := client.Get(urlStr)
29 | if err != nil {
30 | return nil, fmt.Errorf("http.Get error: %w", err)
31 | }
32 | defer resp.Body.Close()
33 |
34 | pageBytes, err := io.ReadAll(resp.Body)
35 | if err != nil {
36 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
37 | }
38 |
39 | var resultMap Douyinresponse
40 | err = json.Unmarshal(pageBytes, &resultMap)
41 | if err != nil {
42 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
43 | }
44 |
45 | // 检查数据是否为空
46 | if len(resultMap.WordList) == 0 {
47 | return map[string]interface{}{
48 | "code": 500,
49 | "message": "API返回数据为空",
50 | "icon": "https://lf1-cdn-tos.bytegoofy.com/goofy/ies/douyin_web/public/favicon.ico",
51 | "obj": []map[string]interface{}{},
52 | }, nil
53 | }
54 |
55 | var obj []map[string]interface{}
56 | for index, item := range resultMap.WordList {
57 | // URL 编码标题,确保特殊字符正确处理
58 | encodedTitle := url.QueryEscape(item.Title)
59 |
60 | hotValue := ""
61 | if item.HotVaule > 0 {
62 | hotValue = fmt.Sprintf("%.2f万", item.HotVaule/10000)
63 | }
64 |
65 | obj = append(obj, map[string]interface{}{
66 | "index": index + 1,
67 | "title": item.Title,
68 | "url": "https://www.douyin.com/search/" + encodedTitle,
69 | "hotValue": hotValue,
70 | })
71 | }
72 |
73 | api := map[string]interface{}{
74 | "code": 200,
75 | "message": "抖音",
76 | "icon": "https://lf1-cdn-tos.bytegoofy.com/goofy/ies/douyin_web/public/favicon.ico", // 32 x 32
77 | "obj": obj,
78 | }
79 | return api, nil
80 | }
81 |
--------------------------------------------------------------------------------
/app/bilibili.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | type bilibiliResponse struct {
12 | Data bilibiliList `json:"data"`
13 | }
14 |
15 | type bilibiliList struct {
16 | List []bilibiliData `json:"list"`
17 | }
18 | type bilibiliData struct {
19 | Title string `json:"title"`
20 | Bvid string `json:"bvid"`
21 | }
22 |
23 | func Bilibili() (map[string]interface{}, error) {
24 | // 创建带超时的 HTTP 客户端
25 | client := &http.Client{
26 | Timeout: 10 * time.Second,
27 | }
28 |
29 | url := "https://api.bilibili.com/x/web-interface/ranking/v2?rid=0&type=all"
30 |
31 | req, err := http.NewRequest("GET", url, nil)
32 | if err != nil {
33 | return nil, fmt.Errorf("http.NewRequest error: %w", err)
34 | }
35 |
36 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36")
37 |
38 | resp, err := client.Do(req)
39 | if err != nil {
40 | return nil, fmt.Errorf("http.Client.Do error: %w", err)
41 | }
42 | defer resp.Body.Close()
43 |
44 | pageBytes, err := io.ReadAll(resp.Body)
45 | if err != nil {
46 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
47 | }
48 |
49 | var resultMap bilibiliResponse
50 | err = json.Unmarshal(pageBytes, &resultMap)
51 | if err != nil {
52 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
53 | }
54 |
55 | // 检查数据是否为空
56 | if len(resultMap.Data.List) == 0 {
57 | return map[string]interface{}{
58 | "code": 500,
59 | "message": "API返回数据为空或格式不正确,实际返回数据:" + fmt.Sprintf("%+v", resultMap.Data),
60 | }, nil // 这里返回 nil error,因为这是业务逻辑错误,不是程序错误
61 | }
62 |
63 | var obj []map[string]interface{}
64 | for index, item := range resultMap.Data.List {
65 | obj = append(obj, map[string]interface{}{
66 | "index": index + 1,
67 | "title": item.Title,
68 | "url": "https://www.bilibili.com/video/" + item.Bvid,
69 | })
70 | }
71 |
72 | api := map[string]interface{}{
73 | "code": 200,
74 | "message": "哔哩哔哩",
75 | "icon": "https://www.bilibili.com/favicon.ico", // 32 x 32
76 | "obj": obj,
77 | }
78 | return api, nil
79 | }
80 |
--------------------------------------------------------------------------------
/app/sougou.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "api/utils"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | func Sougou() (map[string]interface{}, error) {
12 | // 创建带超时的 HTTP 客户端
13 | client := &http.Client{
14 | Timeout: 10 * time.Second,
15 | }
16 | url := "https://www.sogou.com/web?query=%E6%90%9C%E7%8B%97%E7%83%AD%E6%90%9C"
17 | req, err := http.NewRequest("GET", url, nil)
18 | if err != nil {
19 | return nil, fmt.Errorf("http.NewRequest error: %w", err)
20 | }
21 | // 设置请求头
22 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36")
23 | req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
24 | req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
25 |
26 | resp, err := client.Do(req)
27 | if err != nil {
28 | return nil, fmt.Errorf("http.Client.Do error: %w", err)
29 | }
30 | defer resp.Body.Close()
31 | // 检查状态码
32 | if resp.StatusCode != http.StatusOK {
33 | return nil, fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
34 | }
35 |
36 | pageBytes, err := io.ReadAll(resp.Body)
37 | if err != nil {
38 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
39 | }
40 |
41 | pattern := `]*>[\s\S]*?\s*]*>(.*?)\s*
[\s\S]*?\s*(.*?)`
42 | matched := utils.ExtractMatches(string(pageBytes), pattern)
43 | // 检查是否匹配到数据
44 | if len(matched) == 0 {
45 | return map[string]interface{}{
46 | "code": 500,
47 | "message": "未匹配到数据,可能页面结构已变更",
48 | "icon": "https://www.sogou.com/favicon.ico",
49 | "obj": []map[string]interface{}{},
50 | }, nil
51 | }
52 | var obj []map[string]interface{}
53 | for index, item := range matched {
54 | obj = append(obj, map[string]interface{}{
55 | "index": index + 1,
56 | "title": item[2],
57 | "url": item[1],
58 | "hotValue": item[3],
59 | })
60 | }
61 | api := map[string]interface{}{
62 | "code": 200,
63 | "message": "搜狗",
64 | "icon": "https://www.sogou.com/favicon.ico", // 32 x 32
65 | "obj": obj,
66 | }
67 | return api, nil
68 | }
69 |
--------------------------------------------------------------------------------
/app/douban.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | type doubanItem struct {
12 | Score float64 `json:"score"`
13 | Name string `json:"name"`
14 | URI string `json:"uri"`
15 | }
16 |
17 | func Douban() (map[string]interface{}, error) {
18 | // 创建带超时的 HTTP 客户端
19 | client := &http.Client{
20 | Timeout: 10 * time.Second,
21 | }
22 |
23 | url := "https://m.douban.com/rexxar/api/v2/chart/hot_search_board?count=10&start=0"
24 |
25 | req, err := http.NewRequest("GET", url, nil)
26 | if err != nil {
27 | return nil, fmt.Errorf("http.NewRequest error: %w", err)
28 | }
29 |
30 | // 设置 Headers(模拟浏览器)
31 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36")
32 | req.Header.Set("Referer", "https://www.douban.com/gallery/")
33 |
34 | // 发送请求
35 | resp, err := client.Do(req)
36 | if err != nil {
37 | return nil, fmt.Errorf("http.Client.Do error: %w", err)
38 | }
39 | defer resp.Body.Close()
40 |
41 | // 读取响应
42 | pageBytes, err := io.ReadAll(resp.Body)
43 | if err != nil {
44 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
45 | }
46 |
47 | var items []doubanItem
48 | err = json.Unmarshal(pageBytes, &items)
49 | if err != nil {
50 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
51 | }
52 |
53 | // 检查数据是否为空
54 | if len(items) == 0 {
55 | return map[string]interface{}{
56 | "code": 500,
57 | "message": "API返回数据为空",
58 | "icon": "https://www.douban.com/favicon.ico",
59 | "obj": []map[string]interface{}{},
60 | }, nil
61 | }
62 |
63 | var obj []map[string]interface{}
64 | for index, item := range items {
65 | hotValue := ""
66 | if item.Score > 0 {
67 | hotValue = fmt.Sprintf("%.2f万", item.Score/10000)
68 | }
69 |
70 | obj = append(obj, map[string]interface{}{
71 | "index": index + 1,
72 | "title": item.Name,
73 | "url": item.URI,
74 | "hotValue": hotValue,
75 | })
76 | }
77 |
78 | api := map[string]interface{}{
79 | "code": 200,
80 | "message": "豆瓣",
81 | "icon": "https://www.douban.com/favicon.ico", // 32 x 32
82 | "obj": obj,
83 | }
84 | return api, nil
85 | }
86 |
--------------------------------------------------------------------------------
/app/toutiao.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | type ttResponse struct {
13 | Data []ttData `json:"data"`
14 | }
15 | type ttData struct {
16 | Title string `json:"Title"`
17 | URL string `json:"Url"`
18 | HotValue string `json:"HotValue"`
19 | }
20 |
21 | func Toutiao() (map[string]interface{}, error) {
22 | // 创建带超时的 HTTP 客户端
23 | client := &http.Client{
24 | Timeout: 10 * time.Second,
25 | }
26 | url := "https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc"
27 | resp, err := client.Get(url)
28 | if err != nil {
29 | return nil, fmt.Errorf("http.Get error: %w", err)
30 | }
31 | defer resp.Body.Close()
32 | // 检查状态码
33 | if resp.StatusCode != http.StatusOK {
34 | return nil, fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
35 | }
36 | pageBytes, err := io.ReadAll(resp.Body)
37 | if err != nil {
38 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
39 | }
40 |
41 | var resultMap ttResponse
42 | err = json.Unmarshal(pageBytes, &resultMap)
43 | if err != nil {
44 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
45 | }
46 | // 检查数据是否为空
47 | if len(resultMap.Data) == 0 {
48 | return map[string]interface{}{
49 | "code": 500,
50 | "message": "API返回数据为空",
51 | "icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/pipieh7nupabozups/toutiao_web_pc/tt-icon.png",
52 | "obj": []map[string]interface{}{},
53 | }, nil
54 | }
55 |
56 | data := resultMap.Data
57 | var obj []map[string]interface{}
58 | for index, item := range data {
59 | parsedHot, err := strconv.ParseFloat(item.HotValue, 64)
60 | hot := 0.0
61 | if err != nil {
62 | // 解析失败时使用默认值
63 | hot = 0
64 | } else {
65 | hot = parsedHot
66 | }
67 | obj = append(obj, map[string]interface{}{
68 | "index": index + 1,
69 | "title": item.Title,
70 | "url": item.URL,
71 | "hotValue": fmt.Sprintf("%.1f万", hot/10000),
72 | })
73 | }
74 | api := map[string]interface{}{
75 | "code": 200,
76 | "message": "今日头条",
77 | "icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/pipieh7nupabozups/toutiao_web_pc/tt-icon.png", // 144 x 144
78 | "obj": obj,
79 | }
80 | return api, nil
81 | }
82 |
--------------------------------------------------------------------------------
/app/quark.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | type quarkResponse struct {
13 | Data quarkData `json:"data"`
14 | }
15 | type quarkData struct {
16 | HotNews hotNews `json:"hotNews"`
17 | }
18 | type hotNews struct {
19 | Item []quarkItem `json:"item"`
20 | }
21 | type quarkItem struct {
22 | URL string `json:"url"`
23 | Title string `json:"title"`
24 | HotValue string `json:"hot"`
25 | }
26 |
27 | func Quark() (map[string]interface{}, error) {
28 | // 创建带超时的 HTTP 客户端
29 | client := &http.Client{
30 | Timeout: 10 * time.Second,
31 | }
32 |
33 | url := "https://biz.quark.cn/api/trending/ranking/getNewsRanking?modules=hotNews&uc_param_str=dnfrpfbivessbtbmnilauputogpintnwmtsvcppcprsnnnchmicckpgixsnx"
34 | resp, err := client.Get(url)
35 | if err != nil {
36 | return nil, fmt.Errorf("http.Get error: %w", err)
37 | }
38 | defer resp.Body.Close()
39 | // 检查状态码
40 | if resp.StatusCode != http.StatusOK {
41 | return nil, fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
42 | }
43 | pageBytes, err := io.ReadAll(resp.Body)
44 | if err != nil {
45 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
46 | }
47 | var resultMap quarkResponse
48 | err = json.Unmarshal(pageBytes, &resultMap)
49 | if err != nil {
50 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
51 | }
52 | // 检查数据是否为空
53 | if len(resultMap.Data.HotNews.Item) == 0 {
54 | return map[string]interface{}{
55 | "code": 500,
56 | "message": "API返回数据为空",
57 | "icon": "https://gw.alicdn.com/imgextra/i3/O1CN018r2tKf28YP7ev0fPF_!!6000000007944-2-tps-48-48.png",
58 | "obj": []map[string]interface{}{},
59 | }, nil
60 | }
61 | data := resultMap.Data.HotNews.Item
62 | obj := make([]map[string]interface{}, 0, len(data))
63 |
64 | for i, item := range data {
65 | hot, err := strconv.ParseFloat(item.HotValue, 64)
66 | if err != nil {
67 | hot = 0
68 | }
69 |
70 | obj = append(obj, map[string]interface{}{
71 | "index": i + 1,
72 | "title": item.Title,
73 | "url": item.URL,
74 | "hotValue": fmt.Sprintf("%.1f万", hot/10000),
75 | })
76 | }
77 |
78 | api := map[string]interface{}{
79 | "code": 200,
80 | "message": "夸克",
81 | "obj": obj,
82 | "icon": "https://gw.alicdn.com/imgextra/i3/O1CN018r2tKf28YP7ev0fPF_!!6000000007944-2-tps-48-48.png",
83 | }
84 | return api, nil
85 | }
86 |
--------------------------------------------------------------------------------
/app/pengpai.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | type ppResponse struct {
12 | Data ppData `json:"data"`
13 | }
14 | type ppData struct {
15 | HotNews []news `json:"hotNews"`
16 | }
17 | type news struct {
18 | Title string `json:"name"`
19 | ContId string `json:"contId"`
20 | }
21 |
22 | func Pengpai() (map[string]interface{}, error) {
23 | // 创建带超时的 HTTP 客户端
24 | client := &http.Client{
25 | Timeout: 10 * time.Second,
26 | }
27 |
28 | url := "https://cache.thepaper.cn/contentapi/wwwIndex/rightSidebar"
29 | resp, err := client.Get(url)
30 | if err != nil {
31 | return nil, fmt.Errorf("http.Get error: %w", err)
32 | }
33 | defer resp.Body.Close()
34 | // 检查状态码
35 | if resp.StatusCode != http.StatusOK {
36 | return nil, fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
37 | }
38 |
39 | pageBytes, err := io.ReadAll(resp.Body)
40 | if err != nil {
41 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
42 | }
43 | var resultMap ppResponse
44 | err = json.Unmarshal(pageBytes, &resultMap)
45 | if err != nil {
46 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
47 | }
48 | // 检查数据是否为空
49 | if len(resultMap.Data.HotNews) == 0 {
50 | return map[string]interface{}{
51 | "code": 500,
52 | "message": "API返回数据为空",
53 | "icon": "https://www.thepaper.cn/favicon.ico",
54 | "obj": []map[string]interface{}{},
55 | }, nil
56 | }
57 |
58 | data := resultMap.Data.HotNews
59 | var obj []map[string]interface{}
60 | for index, item := range data {
61 | // 确保 ContId 不为空
62 | if item.ContId == "" {
63 | continue // 跳过没有 ContId 的新闻
64 | }
65 |
66 | obj = append(obj, map[string]interface{}{
67 | "index": index + 1,
68 | "title": item.Title,
69 | "url": "https://www.thepaper.cn/newsDetail_forward_" + item.ContId,
70 | })
71 | }
72 |
73 | // 如果所有数据都因为没有 ContId 被跳过
74 | if len(obj) == 0 {
75 | return map[string]interface{}{
76 | "code": 500,
77 | "message": "API返回数据格式异常,缺少必要字段",
78 | "icon": "https://www.thepaper.cn/favicon.ico",
79 | "obj": []map[string]interface{}{},
80 | }, nil
81 | }
82 | api := map[string]interface{}{
83 | "code": 200,
84 | "message": "澎湃新闻",
85 | "icon": "https://www.thepaper.cn/favicon.ico", // 32 x 32
86 | "obj": obj,
87 | }
88 | return api, nil
89 | }
90 |
--------------------------------------------------------------------------------
/app/zhihu.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | type zhResponse struct {
12 | Response response `json:"recommend_queries"`
13 | }
14 | type response struct {
15 | Data []zhData `json:"queries"`
16 | }
17 | type zhData struct {
18 | Title string `json:"query"`
19 | }
20 |
21 | func Zhihu() (map[string]interface{}, error) {
22 | // 创建带超时的 HTTP 客户端
23 | client := &http.Client{
24 | Timeout: 10 * time.Second,
25 | }
26 |
27 | urlStr := "https://www.zhihu.com/api/v4/search/recommend_query/v2"
28 | req, err := http.NewRequest("GET", urlStr, nil)
29 | if err != nil {
30 | return nil, fmt.Errorf("http.NewRequest error: %w", err)
31 | }
32 |
33 | // 设置请求头,模拟正常浏览器访问
34 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36")
35 | req.Header.Set("Accept", "application/json, text/plain, */*")
36 | req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
37 | req.Header.Set("Referer", "https://www.zhihu.com/")
38 |
39 | resp, err := client.Do(req)
40 | if err != nil {
41 | return nil, fmt.Errorf("http.Client.Do error: %w", err)
42 | }
43 | defer resp.Body.Close()
44 |
45 | // 检查状态码
46 | if resp.StatusCode != http.StatusOK {
47 | return nil, fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
48 | }
49 |
50 | pageBytes, err := io.ReadAll(resp.Body)
51 | if err != nil {
52 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
53 | }
54 |
55 | var resultMap zhResponse
56 | err = json.Unmarshal(pageBytes, &resultMap)
57 | if err != nil {
58 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
59 | }
60 | // 检查数据是否为空
61 | if len(resultMap.Response.Data) == 0 {
62 | return map[string]interface{}{
63 | "code": 500,
64 | "message": "API返回数据为空",
65 | "icon": "https://static.zhihu.com/static/favicon.ico",
66 | "obj": []map[string]interface{}{},
67 | }, nil
68 | }
69 |
70 | data := resultMap.Response.Data
71 | var obj []map[string]interface{}
72 | for index, item := range data {
73 | obj = append(obj, map[string]interface{}{
74 | "index": index + 1,
75 | "title": item.Title,
76 | "url": "https://www.zhihu.com/search?q=" + item.Title,
77 | })
78 | }
79 | api := map[string]interface{}{
80 | "code": 200,
81 | "message": "知乎",
82 | "icon": "https://static.zhihu.com/static/favicon.ico", // 32 x 32
83 | "obj": obj,
84 | }
85 | return api, nil
86 | }
87 |
--------------------------------------------------------------------------------
/app/qqnews.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | type qqResponse struct {
12 | IdList []idListItem `json:"idlist"`
13 | }
14 |
15 | type idListItem struct {
16 | IdsHash string `json:"ids_hash"`
17 | NewsList []newsItem `json:"newslist"`
18 | }
19 |
20 | type newsItem struct {
21 | Title string `json:"title"`
22 | Url string `json:"url"`
23 | Time string `json:"time"`
24 | HotEvent hotEvent `json:"hotEvent"`
25 | }
26 |
27 | type hotEvent struct {
28 | HotScore float64 `json:"hotScore"`
29 | }
30 |
31 | func Qqnews() (map[string]interface{}, error) {
32 | // 创建带超时的 HTTP 客户端
33 | client := &http.Client{
34 | Timeout: 10 * time.Second,
35 | }
36 |
37 | url := "https://r.inews.qq.com/gw/event/hot_ranking_list?page_size=51"
38 | resp, err := client.Get(url)
39 | if err != nil {
40 | return nil, fmt.Errorf("http.Get error: %w", err)
41 | }
42 | defer resp.Body.Close()
43 | // 检查状态码
44 | if resp.StatusCode != http.StatusOK {
45 | return nil, fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
46 | }
47 |
48 | pageBytes, err := io.ReadAll(resp.Body)
49 | if err != nil {
50 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
51 | }
52 |
53 | // 使用结构体解析响应
54 | var result qqResponse
55 | err = json.Unmarshal(pageBytes, &result)
56 | if err != nil {
57 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
58 | }
59 |
60 | // 检查数据是否为空或格式不正确
61 | if len(result.IdList) == 0 || len(result.IdList[0].NewsList) == 0 {
62 | return map[string]interface{}{
63 | "code": 500,
64 | "message": "API返回数据为空",
65 | "icon": "https://mat1.gtimg.com/qqcdn/qqindex2021/favicon.ico",
66 | "obj": []map[string]interface{}{},
67 | }, nil
68 | }
69 |
70 | // 获取新闻列表数据
71 | newsListData := result.IdList[0].NewsList
72 |
73 | var obj []map[string]interface{}
74 | for index, item := range newsListData {
75 | if index == 0 {
76 | continue
77 | }
78 | hot := item.HotEvent.HotScore / 10000
79 | hotValue := fmt.Sprintf("%.1f万", hot)
80 |
81 | obj = append(obj, map[string]interface{}{
82 | "index": index,
83 | "title": item.Title,
84 | "url": item.Url,
85 | "time": item.Time,
86 | "hotValue": hotValue,
87 | })
88 | }
89 | api := map[string]interface{}{
90 | "code": 200,
91 | "message": "腾讯新闻",
92 | "icon": "https://mat1.gtimg.com/qqcdn/qqindex2021/favicon.ico", // 96 x 96
93 | "obj": obj,
94 | }
95 | return api, nil
96 | }
97 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "api/all"
5 | "api/app"
6 |
7 | "github.com/gin-gonic/gin"
8 | )
9 |
10 | func main() {
11 | r := gin.Default()
12 |
13 | // 创建路由映射,使用包装函数
14 | routes := map[string]func(c *gin.Context){
15 | "/bilibili": createHandler(app.Bilibili),
16 | "/360search": createHandler(app.Search360),
17 | "/acfun": createHandler(app.Acfun),
18 | "/csdn": createHandler(app.CSDN),
19 | "/dongqiudi": createHandler(app.Dongqiudi),
20 | "/douban": createHandler(app.Douban),
21 | "/douyin": createHandler(app.Douyin),
22 | "/github": createHandler(app.Github),
23 | "/guojiadili": createHandler(app.Guojiadili),
24 | "/history": createHandler(app.History),
25 | "/hupu": createHandler(app.Hupu),
26 | "/ithome": createHandler(app.Ithome),
27 | "/lishipin": createHandler(app.Lishipin),
28 | "/pengpai": createHandler(app.Pengpai),
29 | "/qqnews": createHandler(app.Qqnews),
30 | "/shaoshupai": createHandler(app.Shaoshupai),
31 | "/sougou": createHandler(app.Sougou),
32 | "/toutiao": createHandler(app.Toutiao),
33 | "/v2ex": createHandler(app.V2ex),
34 | "/wangyinews": createHandler(app.WangyiNews),
35 | "/weibo": createHandler(app.WeiboHot),
36 | "/xinjingbao": createHandler(app.Xinjingbao),
37 | "/zhihu": createHandler(app.Zhihu),
38 | "/kuake": createHandler(app.Quark),
39 | "/souhu": createHandler(app.Souhu),
40 | "/baidu": createHandler(app.Baidu),
41 | "/renmin": createHandler(app.Renminwang),
42 | "/nanfang": createHandler(app.Nanfangzhoumo),
43 | "/360doc": createHandler(app.Doc360),
44 | "/cctv": createHandler(app.CCTV),
45 | "/all": allHandler, // 单独处理 all,因为它签名不同
46 | }
47 |
48 | // 注册路由
49 | for path, handler := range routes {
50 | r.GET(path, handler)
51 | }
52 |
53 | r.Run(":1111")
54 | }
55 |
56 | // 创建通用处理器函数,处理返回 error 的函数
57 | func createHandler(fn func() (map[string]interface{}, error)) gin.HandlerFunc {
58 | return func(c *gin.Context) {
59 | result, err := fn()
60 | if err != nil {
61 | // 发生错误时返回错误响应
62 | c.JSON(500, map[string]interface{}{
63 | "code": 500,
64 | "message": "服务器内部错误: " + err.Error(),
65 | "obj": []map[string]interface{}{},
66 | })
67 | return
68 | }
69 |
70 | // 检查 API 返回的 code
71 | if code, ok := result["code"].(int); ok && code != 200 {
72 | // API 返回业务错误
73 | c.JSON(code, result)
74 | return
75 | }
76 |
77 | // 成功返回
78 | c.JSON(200, result)
79 | }
80 | }
81 |
82 | // allHandler 专门处理 all.All() 函数
83 | func allHandler(c *gin.Context) {
84 | result := all.All()
85 |
86 | // 检查 API 返回的 code
87 | if code, ok := result["code"].(int); ok && code != 200 {
88 | // API 返回业务错误
89 | c.JSON(code, result)
90 | return
91 | }
92 |
93 | // 成功返回
94 | c.JSON(200, result)
95 | }
96 |
--------------------------------------------------------------------------------
/app/souhu.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "strconv"
9 | "sync"
10 | "time"
11 | )
12 |
13 | type souhuResponse struct {
14 | Data []newsArticles `json:"newsArticles"`
15 | }
16 | type newsArticles struct {
17 | Title string `json:"title"`
18 | URL string `json:"h5Link"`
19 | Hot string `json:"score"`
20 | }
21 |
22 | func fetchSouhuPage(page int) ([]newsArticles, error) {
23 | // 创建带超时的 HTTP 客户端
24 | client := &http.Client{
25 | Timeout: 10 * time.Second,
26 | }
27 |
28 | url := fmt.Sprintf("https://3g.k.sohu.com/api/channel/hotchart/hotnews.go?p1=NjY2NjY2&page=%d", page)
29 | resp, err := client.Get(url)
30 | if err != nil {
31 | return nil, fmt.Errorf("fetch page %d: http.Get error: %w", page, err)
32 | }
33 | defer resp.Body.Close()
34 |
35 | // 检查状态码
36 | if resp.StatusCode != http.StatusOK {
37 | return nil, fmt.Errorf("fetch page %d: HTTP请求失败,状态码: %d", page, resp.StatusCode)
38 | }
39 |
40 | pageBytes, err := io.ReadAll(resp.Body)
41 | if err != nil {
42 | return nil, fmt.Errorf("fetch page %d: io.ReadAll error: %w", page, err)
43 | }
44 |
45 | var resultMap souhuResponse
46 | err = json.Unmarshal(pageBytes, &resultMap)
47 | if err != nil {
48 | return nil, fmt.Errorf("fetch page %d: json.Unmarshal error: %w", page, err)
49 | }
50 |
51 | return resultMap.Data, nil
52 | }
53 |
54 | func Souhu() (map[string]interface{}, error) {
55 | var wordList []newsArticles
56 | var wg sync.WaitGroup
57 | var mutex sync.Mutex
58 | var fetchErrors []error
59 |
60 | // 并发获取两个页面的数据
61 | for i := 1; i <= 2; i++ {
62 | wg.Add(1)
63 | go func(page int) {
64 | defer wg.Done()
65 | data, err := fetchSouhuPage(page)
66 | if err != nil {
67 | mutex.Lock()
68 | fetchErrors = append(fetchErrors, err)
69 | mutex.Unlock()
70 | return
71 | }
72 | mutex.Lock()
73 | wordList = append(wordList, data...)
74 | mutex.Unlock()
75 | }(i)
76 | }
77 |
78 | wg.Wait()
79 |
80 | // 如果有错误发生,检查是否至少获取到一些数据
81 | if len(fetchErrors) > 0 {
82 | // 如果完全没有获取到数据,返回错误
83 | if len(wordList) == 0 {
84 | return nil, fmt.Errorf("获取数据失败: %v", fetchErrors)
85 | }
86 | // 如果获取到部分数据,继续处理但记录错误
87 | fmt.Printf("部分页面获取失败,但继续处理已获取的数据: %v\n", fetchErrors)
88 | }
89 | // 检查数据是否为空
90 | if len(wordList) == 0 {
91 | return map[string]interface{}{
92 | "code": 500,
93 | "message": "API返回数据为空",
94 | "icon": "https://3g.k.sohu.com/favicon.ico",
95 | "obj": []map[string]interface{}{},
96 | }, nil
97 | }
98 | var obj []map[string]interface{}
99 | for index, item := range wordList {
100 | hotValue, err := strconv.ParseFloat(item.Hot, 64)
101 | if err != nil {
102 | hotValue = 0 // 如果解析失败,设置为0
103 | }
104 | obj = append(obj, map[string]interface{}{
105 | "index": index + 1,
106 | "title": item.Title,
107 | "url": item.URL,
108 | "hotValue": fmt.Sprintf("%.2f万", hotValue),
109 | })
110 | }
111 |
112 | api := map[string]interface{}{
113 | "code": 200,
114 | "message": "搜狐新闻",
115 | "icon": "https://3g.k.sohu.com/favicon.ico", // 48 x 48
116 | "obj": obj,
117 | }
118 | return api, nil
119 | }
120 |
--------------------------------------------------------------------------------
/app/history.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "strings"
9 | "time"
10 |
11 | "golang.org/x/net/html"
12 | )
13 |
14 | func stripHTML(htmlString string) string {
15 | // 使用 html.Parse 解析 HTML 字符串
16 | doc, err := html.Parse(strings.NewReader(htmlString))
17 | if err != nil {
18 | // 解析失败时返回原始字符串
19 | return htmlString
20 | }
21 |
22 | // 使用一个 buffer 保存结果
23 | var result strings.Builder
24 |
25 | // 定义一个递归函数来遍历 HTML 树
26 | var visit func(n *html.Node)
27 | visit = func(n *html.Node) {
28 | // 如果当前节点是文本节点,将文本内容追加到结果中
29 | if n.Type == html.TextNode {
30 | result.WriteString(n.Data)
31 | }
32 | // 递归处理子节点
33 | for c := n.FirstChild; c != nil; c = c.NextSibling {
34 | visit(c)
35 | }
36 | }
37 |
38 | // 调用递归函数开始遍历 HTML 树
39 | visit(doc)
40 |
41 | // 返回结果的字符串形式
42 | return result.String()
43 | }
44 |
45 | func History() (map[string]interface{}, error) {
46 | // 创建带超时的 HTTP 客户端
47 | client := &http.Client{
48 | Timeout: 10 * time.Second,
49 | }
50 |
51 | currentTime := time.Now()
52 | month := fmt.Sprintf("%02d", currentTime.Month())
53 | day := fmt.Sprintf("%02d", currentTime.Day())
54 | url := "https://baike.baidu.com/cms/home/eventsOnHistory/" + month + ".json"
55 |
56 | resp, err := client.Get(url)
57 | if err != nil {
58 | return nil, fmt.Errorf("http.Get error: %w", err)
59 | }
60 | defer resp.Body.Close()
61 |
62 | pageBytes, err := io.ReadAll(resp.Body)
63 | if err != nil {
64 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
65 | }
66 |
67 | var resultMap map[string]interface{}
68 | err = json.Unmarshal(pageBytes, &resultMap)
69 | if err != nil {
70 | return nil, fmt.Errorf("json.Unmarshal error: %w", err)
71 | }
72 |
73 | // 检查数据结构
74 | monthData, ok := resultMap[month].(map[string]interface{})
75 | if !ok {
76 | return nil, fmt.Errorf("API返回数据格式异常: 月份数据不存在")
77 | }
78 |
79 | date := month + day
80 | dateListInterface, ok := monthData[date]
81 | if !ok {
82 | return nil, fmt.Errorf("今天(%s月%s日)没有历史事件数据", month, day)
83 | }
84 |
85 | dateList, ok := dateListInterface.([]interface{})
86 | if !ok {
87 | return nil, fmt.Errorf("API返回数据格式异常: 日期列表格式不正确")
88 | }
89 |
90 | // 检查数据是否为空
91 | if len(dateList) == 0 {
92 | return map[string]interface{}{
93 | "code": 500,
94 | "message": "今天(" + month + "月" + day + "日)没有历史事件数据",
95 | "icon": "https://baike.baidu.com/favicon.ico",
96 | "obj": []map[string]interface{}{},
97 | }, nil
98 | }
99 |
100 | var obj []map[string]interface{}
101 | for index, item := range dateList {
102 | itemMap, ok := item.(map[string]interface{})
103 | if !ok {
104 | continue // 跳过格式不正确的项
105 | }
106 |
107 | // 提取标题
108 | titleInterface, ok := itemMap["title"]
109 | if !ok {
110 | continue
111 | }
112 | title, ok := titleInterface.(string)
113 | if !ok {
114 | continue
115 | }
116 |
117 | // 提取链接
118 | urlInterface, ok := itemMap["link"]
119 | urlStr := ""
120 | if ok {
121 | if link, ok := urlInterface.(string); ok {
122 | urlStr = link
123 | }
124 | }
125 |
126 | obj = append(obj, map[string]interface{}{
127 | "index": index + 1,
128 | "title": stripHTML(title),
129 | "url": urlStr,
130 | })
131 | }
132 |
133 | api := map[string]interface{}{
134 | "code": 200,
135 | "message": "历史上的今天",
136 | "icon": "https://baike.baidu.com/favicon.ico", // 64 x 64
137 | "obj": obj,
138 | }
139 | return api, nil
140 | }
141 |
--------------------------------------------------------------------------------
/app/weibo.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "api/utils"
5 | "fmt"
6 | "io"
7 | "net/http"
8 | "regexp"
9 | "strings"
10 | "time"
11 | )
12 |
13 | func WeiboHot() (map[string]interface{}, error) {
14 | // 创建带超时的 HTTP 客户端
15 | client := &http.Client{
16 | Timeout: 10 * time.Second,
17 | }
18 | url := "https://s.weibo.com/top/summary"
19 | req, err := http.NewRequest("GET", url, nil)
20 | if err != nil {
21 | return nil, fmt.Errorf("http.NewRequest error: %w", err)
22 | }
23 | // 设置请求头
24 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36")
25 | req.Header.Set("Cookie", "SUB=_2AkMasdasdqadTy2Pna4Rl77p7cJZAXC")
26 | req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
27 | req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
28 | req.Header.Set("Referer", "https://s.weibo.com/")
29 |
30 | resp, err := client.Do(req)
31 | if err != nil {
32 | return nil, fmt.Errorf("http.Client.Do error: %w", err)
33 | }
34 | defer resp.Body.Close()
35 | // 检查状态码
36 | if resp.StatusCode != http.StatusOK {
37 | return nil, fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode)
38 | }
39 | pageBytes, err := io.ReadAll(resp.Body)
40 | if err != nil {
41 | return nil, fmt.Errorf("io.ReadAll error: %w", err)
42 | }
43 |
44 | pageContent := string(pageBytes)
45 |
46 | // 方法1:使用正则表达式提取热搜数据
47 | var obj []map[string]interface{}
48 |
49 | // 正则表达式匹配热搜条目
50 | pattern := `]*target="_blank">([^<]+)\s*([^<]*)?`
51 | matched := utils.ExtractMatches(pageContent, pattern)
52 | // 预编译正则表达式,避免重复编译
53 | nonDigitRegexp := regexp.MustCompile(`[^\d]`)
54 | for index, item := range matched {
55 | if len(item) >= 3 {
56 | title := strings.TrimSpace(item[2])
57 | url := "https://s.weibo.com" + item[1]
58 | hotValue := ""
59 | if len(item) >= 4 {
60 | hotValue = strings.TrimSpace(item[3])
61 | }
62 |
63 | // 清理热度值中的非数字字符
64 | if hotValue != "" {
65 | hotValue = strings.TrimSpace(nonDigitRegexp.ReplaceAllString(hotValue, ""))
66 | }
67 |
68 | obj = append(obj, map[string]interface{}{
69 | "index": index + 1,
70 | "title": title,
71 | "url": url,
72 | "hotValue": hotValue,
73 | })
74 | }
75 | }
76 |
77 | // 如果正则匹配失败,尝试备用方法
78 | if len(obj) == 0 {
79 | obj = extractWeiboHotSearchFallback(pageContent)
80 | }
81 | // 检查是否获取到数据
82 | if len(obj) == 0 {
83 | return map[string]interface{}{
84 | "code": 500,
85 | "message": "无法提取热搜数据,页面结构可能已变更",
86 | "icon": "https://weibo.com/favicon.ico",
87 | "obj": []map[string]interface{}{},
88 | }, nil
89 | }
90 | api := map[string]interface{}{
91 | "code": 200,
92 | "message": "微博热搜",
93 | "icon": "https://weibo.com/favicon.ico",
94 | "obj": obj,
95 | }
96 | return api, nil
97 | }
98 |
99 | // 备用提取方法
100 | func extractWeiboHotSearchFallback(content string) []map[string]interface{} {
101 | var obj []map[string]interface{}
102 |
103 | // 尝试匹配更简单的模式
104 | patterns := []string{
105 | `]*>([^<]+)`,
106 | `class="td-02".*?]*>([^<]+)`,
107 | }
108 |
109 | for _, pattern := range patterns {
110 | matched := utils.ExtractMatches(content, pattern)
111 | for index, item := range matched {
112 | if len(item) >= 3 {
113 | title := strings.TrimSpace(item[2])
114 | url := "https://s.weibo.com" + item[1]
115 |
116 | obj = append(obj, map[string]interface{}{
117 | "index": index + 1,
118 | "title": title,
119 | "url": url,
120 | "hotValue": "", // 备用方法可能无法获取热度值
121 | })
122 | }
123 | }
124 | if len(obj) > 0 {
125 | break
126 | }
127 | }
128 |
129 | return obj
130 | }
131 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/bytedance/sonic v1.12.7 h1:CQU8pxOy9HToxhndH0Kx/S1qU/CuS9GnKYrGioDcU1Q=
2 | github.com/bytedance/sonic v1.12.7/go.mod h1:tnbal4mxOMju17EGfknm2XyYcpyCnIROYOEYuemj13I=
3 | github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
4 | github.com/bytedance/sonic/loader v0.2.2 h1:jxAJuN9fOot/cyz5Q6dUuMJF5OqQ6+5GfA8FjjQ0R4o=
5 | github.com/bytedance/sonic/loader v0.2.2/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
6 | github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
7 | github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
8 | github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
9 | github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
10 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
12 | github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
13 | github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
14 | github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
15 | github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
16 | github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
17 | github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
18 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
19 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
20 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
21 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
22 | github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
23 | github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
24 | github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
25 | github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
26 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
27 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
28 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
29 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
30 | github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
31 | github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
32 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
33 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
34 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
35 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
36 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
37 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
38 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
39 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
40 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
41 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
42 | github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
43 | github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
44 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
45 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
46 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
47 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
48 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
49 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
50 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
51 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
52 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
53 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
54 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
55 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
56 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
57 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
58 | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
59 | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
60 | golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA=
61 | golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
62 | golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
63 | golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
64 | golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
65 | golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
66 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
67 | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
68 | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
69 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
70 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
71 | google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
72 | google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
73 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
74 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
75 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
76 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
77 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
78 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
79 |
--------------------------------------------------------------------------------